1# coding=utf-8
2#
3# %CopyrightBegin%
4#
5# Copyright Ericsson AB 2013-2021. All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11#     http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# %CopyrightEnd%
20#
21#
22# This script was orinally written by Anthony Ramine (aka nox) in 2013.
23# A lot of things have changed since then, but the same base remains.
24#
25
26import re
27import lldb
28import shlex
29
30unquoted_atom_re = re.compile(u'^[a-zß-öø-ÿ][a-zA-Zß-öø-ÿ0-9@]*$')
31code_pointers = {}
32
33def __lldb_init_module(debugger, internal_dict):
34    debugger.HandleCommand('type format add -f hex Eterm')
35    debugger.HandleCommand('type format add -f hex BeamInstr')
36    debugger.HandleCommand('type summary add -F etp.eterm_summary Eterm')
37    debugger.HandleCommand('command script add -f etp.processes_cmd etp-processes')
38    debugger.HandleCommand('command script add -f etp.process_info_cmd etp-process-info')
39    debugger.HandleCommand('command script add -f etp.stacktrace_cmd etp-stacktrace')
40    debugger.HandleCommand('command script add -f etp.stackdump_cmd etp-stackdump')
41    debugger.HandleCommand('command script add -f etp.eterm_cmd etp')
42
43####################################
44## Print all processes in the system
45####################################
46def processes_cmd(debugger, command, result, internal_dict):
47    target = debugger.GetSelectedTarget()
48    init(target)
49    proc = erts_proc(target)
50    proc_r_o = proc.GetChildMemberWithName('r').GetChildMemberWithName('o')
51    proc_max_ix = proc_r_o.GetChildMemberWithName('max')
52    proc_tab = proc_r_o.GetChildMemberWithName('tab').Cast(ProcessPtrPtr(target))
53    proc_cnt = atomic_value(proc.GetChildMemberWithName('vola').GetChildMemberWithName('tile').GetChildMemberWithName('count'))
54    invalid_proc = global_var('erts_invalid_process', target).address_of
55    for proc_ix in range(0, proc_max_ix.unsigned):
56        proc = offset(proc_ix, proc_tab).deref
57        if proc.unsigned != 0 and proc.unsigned != invalid_proc.unsigned:
58            print('---')
59            print('  Pix: %d' % proc_ix)
60            process_info(proc)
61            proc_cnt -= 1
62            if proc_cnt == 0:
63                break
64
65############################################
66## Print process-info about a single process
67############################################
68def process_info_cmd(debugger, command, result, internal_dict):
69    target = debugger.GetSelectedTarget()
70    init(target)
71    proc = target.process.selected_thread.GetSelectedFrame().EvaluateExpression(command).Cast(ProcessPtr(target))
72    process_info(proc)
73
74def process_info(proc):
75    print('  Pid: %s' % eterm(proc.GetChildMemberWithName('common').GetChildMemberWithName('id')))
76    print('  State: %s' % process_state(proc))
77    print('  Flags: %s' % process_flags(proc))
78    print_process_reg_name(proc)
79    current = proc.GetChildMemberWithName('current')
80    if current.unsigned != 0 and atomic_value(proc.GetChildMemberWithName('state')) & 0x800 == 0:
81        print('  Current function: %s' % mfa(current))
82    else:
83        print('  Current function: %s' % 'unknown')
84    i = proc.GetChildMemberWithName('i')
85    if i.unsigned != 0:
86        print('  I: %s' % eterm(i))
87    else:
88        print('  I: %s' % 'unknown')
89    print('  Pointer: %#x' % proc.unsigned)
90
91def print_process_reg_name(proc):
92    state = proc.GetChildMemberWithName('state').unsigned
93    if (state & 0x800) == 0:
94        # Not exiting.
95        reg = proc.GetChildMemberWithName('common').GetChildMemberWithName('u').GetChildMemberWithName('alive').GetChildMemberWithName('reg')
96        if reg.unsigned != 0:
97            print('  Registered name: %s' % eterm(reg.GetChildMemberWithName('name')))
98
99def process_state(proc):
100    state = proc.GetChildMemberWithName('state').unsigned
101    res = ''
102    if state & 0x80000000:
103        res += "GARBAGE<0x80000000> | "
104    if state & 0x40000000:
105        res += "dirty-running-sys | "
106    if state & 0x20000000:
107        res += "dirty-running | "
108    if state & 0x10000000:
109        res += "dirty-active-sys | "
110    if state & 0x8000000:
111        res += "dirty-io-proc | "
112    if state & 0x4000000:
113        res += "dirty-cpu-proc | "
114    if state & 0x2000000:
115        res += "sig-q | "
116    if state & 0x1000000:
117        res += "off-heap-msgq | "
118    if state & 0x800000:
119        res += "delayed-sys | "
120    if state & 0x400000:
121        res += "proxy | "
122        proxy_process = True
123    else:
124        proxy_process = False
125    if state & 0x200000:
126        res += "running-sys | "
127    if state & 0x100000:
128        res += "active-sys | "
129    if state & 0x80000:
130        res += "sig-in-q | "
131    if state & 0x40000:
132        res += "sys-tasks | "
133    if state & 0x20000:
134        res += "garbage-collecting | "
135    if state & 0x10000:
136        res += "suspended | "
137    if state & 0x8000:
138        res += "running | "
139    if state & 0x4000:
140        res += "in-run-queue | "
141    if state & 0x2000:
142        res += "active | "
143    if state & 0x1000:
144        res += "unused | "
145    if state & 0x800:
146        res += "exiting | "
147    if state & 0x400:
148        res += "free | "
149    if state & 0x200:
150        res += "in-prq-low | "
151    if state & 0x100:
152        res += "in-prq-normal | "
153    if state & 0x80:
154        res += "in-prq-high | "
155    if state & 0x40:
156        res += "in-prq-max | "
157    if state & 0x30 == 0x0:
158        res += "prq-prio-max | "
159    elif state & 0x30 == 0x10:
160      res += "prq-prio-high | "
161    elif state & 0x30 == 0x20:
162        res += "prq-prio-normal | "
163    else:
164        res += "prq-prio-low | "
165    if state & 0xc == 0x0:
166        res += "usr-prio-max | "
167    elif state & 0xc == 0x4:
168      res += "usr-prio-high | "
169    elif state & 0xc == 0x8:
170        res += "usr-prio-normal | "
171    else:
172        res += "usr-prio-low | "
173    if state & 0x3 == 0x0:
174        res += "act-prio-max"
175    elif state & 0x3 == 0x1:
176      res += "act-prio-high"
177    elif state & 0x3 == 0x2:
178        res += "act-prio-normal"
179    else:
180        res += "act-prio-low"
181    return res
182
183def process_flags(proc):
184    flags = proc.GetChildMemberWithName('flags').unsigned
185    res = ''
186    if flags & ~((1 << 24)-1):
187        res += "GARBAGE<%#x> " % (flags & ~((1 << 24)-1))
188    if flags & (1 << 22):
189        res += "trap-exit "
190    if flags & (1 << 21):
191        res += "hibernated "
192    if flags & (1 << 20):
193        res += "dirty-minor-gc "
194    if flags & (1 << 19):
195        res += "dirty-major-gc "
196    if flags & (1 << 18):
197        res += "dirty-gc-hibernate "
198    if flags & (1 << 17):
199        res += "dirty-cla "
200    if flags & (1 << 16):
201        res += "delayed-del-proc "
202    if flags & (1 << 15):
203        res += "have-blocked-nmsb "
204    if flags & (1 << 14):
205        res += "shdlr-onln-wait-q "
206    if flags & (1 << 13):
207        res += "delay-gc "
208    if flags & (1 << 12):
209        res += "abandoned-heap-use "
210    if flags & (1 << 11):
211        res += "disable-gc "
212    if flags & (1 << 10):
213        res += "force-gc "
214    if flags & (1 << 9):
215        res += "ets-super-user "
216    if flags & (1 << 8):
217        res += "have-blocked-msb "
218    if flags & (1 << 7):
219        res += "using-ddll "
220    if flags & (1 << 6):
221        res += "distribution "
222    if flags & (1 << 5):
223        res += "using-db "
224    if flags & (1 << 4):
225        res += "need-fullsweep "
226    if flags & (1 << 3):
227        res += "heap-grow "
228    if flags & (1 << 2):
229        res += "timo "
230    if flags & (1 << 1):
231        res += "inslpqueue "
232    if flags & (1 << 0):
233        res += "hibernate-sched "
234    return res
235
236############################################
237## Print the stacktrace of a single process
238############################################
239def stacktrace_cmd(debugger, command, result, internal_dict):
240    target = debugger.GetSelectedTarget()
241    init(target)
242    proc = target.process.selected_thread.GetSelectedFrame().EvaluateExpression(command).Cast(ProcessPtr(target))
243    stackdump(proc, False)
244
245############################################
246## Print the stackdump of a single process
247############################################
248def stackdump_cmd(debugger, command, result, internal_dict):
249    target = debugger.GetSelectedTarget()
250    init(target)
251    proc = target.process.selected_thread.GetSelectedFrame().EvaluateExpression(command).Cast(ProcessPtr(target))
252    stackdump(proc, True)
253
254def stackdump(proc, dump):
255    stop = proc.GetChildMemberWithName('stop')
256    send = proc.GetChildMemberWithName('hend')
257    cnt = 0
258    if atomic_value(proc.GetChildMemberWithName('state')) & 0x8000:
259        print('%%%%%% WARNING: The process is currently running, so c_p->stop will not be correct')
260    print(F'%% Stacktrace ({send.unsigned - stop.unsigned})');
261    i = proc.GetChildMemberWithName('i')
262    if i.unsigned != 0:
263        print(F'I: {eterm(i)}')
264    while stop.unsigned < send.unsigned:
265        if stop.deref.unsigned & 0x3 == 0x0 or dump:
266            print(F'{cnt}: {eterm(stop.deref)}')
267        cnt += 1
268        stop = offset(1, stop)
269
270############################################
271## Print an eterm
272############################################
273def eterm_cmd(debugger, command, result, internal_dict):
274    args = shlex.split(command)
275    target = debugger.GetSelectedTarget()
276    init(target)
277    term = target.process.selected_thread.GetSelectedFrame().EvaluateExpression(args[0]).Cast(EtermPtr(target))
278    if len(args) >= 2:
279        print(eterm(term, int(args[1])))
280    else:
281        print(eterm(term))
282
283############################################
284## Print the summary of an Eterm
285############################################
286def eterm_summary(valobj, internal_dict):
287    if valobj.TypeIsPointerType():
288        return ''
289    return F'{valobj.unsigned:#x} {eterm(valobj, 5)}'
290
291def eterm(valobj, depth = float('inf')):
292    val = valobj.unsigned
293    valobj = strip_literal_tag(valobj)
294    tag = val & 0x3
295    if tag == 0x1:
296        return cons(valobj, depth)
297    elif tag == 0x2:
298        return boxed(valobj, depth)
299    elif tag == 0x3:
300        return imm(valobj)
301    elif val == 0x0:
302        return '<the non-value>'
303    elif val == 0x4:
304        return '<the non-value debug>'
305    else:
306        return cp(valobj)
307
308def cons(valobj, depth = float('inf')):
309    items = []
310    cdr = strip_literal_tag(valobj)
311    improper = False
312    truncated = False
313    depth *= 20
314
315    ptr = cdr.CreateValueFromData(
316        "unconsed",
317        lldb.SBData.CreateDataFromInt(cdr.unsigned - 1),
318        EtermPtr(cdr.target))
319    if ptr.deref.error.fail:
320        return "#ConsError<%x>" % cdr.unsigned;
321
322    while True:
323        cdr = strip_literal_tag(cdr)
324        ptr = cdr.CreateValueFromData(
325            "unconsed",
326            lldb.SBData.CreateDataFromInt(cdr.unsigned - 1),
327            EtermPtr(cdr.target))
328        items.append((ptr.deref, depth // 20)); # Append depth, car
329        if ptr.deref.unsigned & 0xF == 0xF:
330            depth -= 1
331        else:
332            depth -= 20
333        cdr = offset(1,ptr).deref
334        if is_nil(cdr):
335            break
336        if cdr.unsigned & 0x1 == 0:
337            improper = True
338            break
339        if depth <= 1:
340            truncated = True
341            break
342
343    if improper:
344        return '#ImproperList'
345
346    ## Try to print as ascii first
347    chars = ''
348    isprintable = True
349    for car, car_depth in items:
350        if car.unsigned & 0xF == 0xF:
351            if car.unsigned >> 4 == 10:
352                chars += '\\n'
353            elif car.unsigned >> 4 == 9:
354                chars += '\\t'
355            else:
356                chars += f'{car.unsigned >> 4:c}'
357        else:
358            isprintable = False
359            break
360    isprintable = isprintable and chars.isprintable()
361    if isprintable:
362        if not truncated:
363            return F'"{chars}"'
364        else:
365            return F'"{chars}..."'
366
367    ## If not printable, we print the objects
368    objs = []
369    chars = '['
370    for car, car_depth in items:
371        objs.append(eterm(car, car_depth))
372    if not truncated:
373        return '[' + ','.join(objs) + ']'
374    else:
375        return '[' + ','.join(objs) + '|...]'
376
377def boxed(valobj, depth = float('inf')):
378    ptr = valobj.CreateValueFromData(
379        "unboxed",
380        lldb.SBData.CreateDataFromInt(valobj.unsigned - 2),
381        EtermPtr(valobj.target))
382    boxed_hdr = ptr.deref
383    if boxed_hdr.error.fail:
384        return "#BoxedError<%x>" % valobj.unsigned;
385    boxed_hdr = boxed_hdr.unsigned
386    if boxed_hdr & 0x3f == 0x00:
387        arity = (boxed_hdr >> 6)
388        terms = []
389        for x in range(1, arity+1):
390            if depth <= 1:
391                terms.append('...')
392                break
393            depth -= 1
394            terms.append(eterm(offset(x, ptr).deref, depth))
395        res = ','.join(terms)
396        return F"{{{res}}}"
397    if boxed_hdr & 0x3c == 0x3c:
398        if boxed_hdr & 0xc0 == 0x0:
399            return "#FlatMap"
400        else:
401            return "#HashMap"
402    boxed_type = (boxed_hdr >> 2) & 0xF
403    if boxed_type == 0xC:
404        return '#ExternalPid'
405    if boxed_type == 0xD:
406        return '#ExternalPort'
407    if boxed_type == 0x2 or boxed_type == 0x3:
408        return '#Bignum'
409    if boxed_type == 0x6:
410        return '#Float'
411    if boxed_type == 0x4:
412        return '#Ref'
413    if boxed_type == 0xE:
414        return '#ExternalRef'
415    if boxed_type == 0x5:
416        return '#Fun'
417    if boxed_type == 0x8:
418        return '#RefcBin'
419    if boxed_type == 0x9:
420        return '#HeapBin'
421    if boxed_type == 0xA:
422        return '#SubBin'
423    return F'#Boxed<{valobj.unsigned}>'
424
425def imm(valobj):
426    val = valobj.unsigned
427    if (val & 0x3) != 3:
428        return '#NotImmediate<%#x>' % val
429    tag = val & 0xF
430    if tag == 0x3:
431        return pid(valobj)
432    elif tag == 0x7:
433        return port(valobj)
434    elif tag == 0xF:
435        return str(val >> 4)
436    elif tag == 0xB:
437        # Immediate2
438        tag2 = val & 0x3F
439        if tag2 == 0x0B:
440            return atom(valobj)
441        elif tag2 == 0x1B:
442            return F'#Catch<{val>>6:#x}>'
443        elif is_nil(valobj):
444            return '[]'
445    return '#UnknownImmediate<%#x>' % val
446
447# Continuation pointers
448
449def cp(valobj):
450    mfaptr = erts_lookup_function_info(valobj)
451    if mfaptr == None:
452        pointer = code_pointers.get(valobj.unsigned)
453        if pointer != None:
454            return '#Cp<%s>' % pointer
455        else:
456            return '#Cp<%#x>' % valobj.unsigned
457    else:
458        return '#Cp<%s>' % mfa(mfaptr)
459
460# Pids and ports
461
462def pid(valobj):
463    val = valobj.unsigned
464    if (val & 0xF) == 0x3:
465        target = valobj.target
466        if etp_arch_bits(target) == 64:
467            if etp_big_endian(target):
468                data = (val >> 35) & 0x0FFFFFFF
469            else:
470                data = (val >> 4) & 0x0FFFFFFF
471        else:
472            data = pixdata2data(valobj)
473        return '<0.%u.%u>' % (data & 0x7FFF, (data >> 15) & 0x1FFF)
474    else:
475        return '#NotPid<%#x>' % val
476
477def port(valobj):
478    val = valobj.unsigned
479    if (val & 0xF) == 0x7:
480        target = valobj.target
481        if etp_arch_bits(target) == 64 and not etp_halfword(target):
482            if etp_big_endian(target):
483                data = (val >> 36) & 0x0FFFFFFF
484            else:
485                data = (val >> 4) & 0x0FFFFFFF
486        else:
487            data = pixdata2data(valobj)
488        return '#Port<0.%u>' % data
489    else:
490        return '#NotPort<%#x>' % val
491
492# Strings and atoms
493
494def atom(valobj):
495    val = valobj.unsigned
496    if (val & 0x3F) == 0x0B:
497        name = atom_name(atom_tab(valobj))
498        if unquoted_atom_re.match(name):
499            return str(name)
500        else:
501            return quoted_name(name, "'")
502    else:
503        return '#NotAtom<%#x>' % val
504
505def atom_name(entry):
506    name = entry.GetChildMemberWithName('name')
507    length = entry.GetChildMemberWithName('len').unsigned
508    data = name.GetPointeeData(0, length).uint8s
509    return ''.join(map(chr, data))
510
511def quoted_name(name, quote):
512    return quote + ''.join(map(lambda c: quoted_char(c, quote), name)) + quote
513
514def quoted_char(c, quote):
515    point = ord(c)
516    if c == quote:
517        return '\\' + quote
518    elif point == 0x08:
519        return '\\b'
520    elif point == 0x09:
521        return '\\t'
522    elif point == 0x0A:
523        return '\\n'
524    elif point == 0x0B:
525        return '\\v'
526    elif point == 0x0C:
527        return '\\f'
528    elif point == 0x0D:
529        return '\\e'
530    elif point >= 0x20 and point <= 0x7E or point >= 0xA0:
531        return c
532    elif (point > 0xFF):
533        return '#NotChar<%#x>' % c
534    else:
535        return '\\%03o' % point
536
537# Constants
538
539MI_FUNCTIONS = 13
540MI_NUM_FUNCTIONS = 0
541
542def is_nil(value):
543    ## We handle both -5 and 0x3b as NIL values so that this script
544    ## works with more versions
545    return value.signed == -5 or value.unsigned == 0x3b
546
547# Types
548
549def Atom(target):
550    return target.FindFirstType('Atom')
551
552def Char(target):
553    return target.FindFirstType('char')
554def CharPtr(target):
555    return Char(target).GetPointerType()
556
557def Eterm(target):
558    return target.FindFirstType('Eterm')
559def EtermPtr(target):
560    return Eterm(target).GetPointerType()
561def EtermPtrPtr(target):
562    return EtermPtr(target).GetPointerType()
563
564def Range(target):
565    return target.FindFirstType('Range')
566
567def BeamInstr(target):
568    return target.FindFirstType('BeamInstr')
569def BeamInstrPtr(target):
570    return BeamInstr(target).GetPointerType()
571
572def ErtsCodeInfo(target):
573    return target.FindFirstType('ErtsCodeInfo')
574def ErtsCodeInfoPtr(target):
575    return ErtsCodeInfo(target).GetPointerType()
576
577def Process(target):
578    return target.FindFirstType('Process')
579def ProcessPtr(target):
580    return Process(target).GetPointerType()
581def ProcessPtrPtr(target):
582    return ProcessPtr(target).GetPointerType()
583
584# Globals
585
586def erts_atom_table(target):
587    return global_var('erts_atom_table', target)
588
589def erts_proc(target):
590    return global_var('erts_proc', target)
591
592def etp_arch_bits(target):
593    return global_var('etp_arch_bits', target).unsigned
594
595def etp_big_endian(target):
596    return global_var('etp_endianness', target).unsigned > 0
597
598def etp_halfword(target):
599    return False
600
601def the_active_code_index(target):
602    return global_var('the_active_code_index', target)
603
604# Functions
605
606def atom_tab(valobj):
607    idx = valobj.unsigned
608    target = valobj.target
609    seg = erts_atom_table(target).GetChildMemberWithName('seg_table')
610    slot = offset(idx >> 16, seg).deref
611    entry = offset(idx >> 6 & 0x3FF, slot).deref
612    return entry.Cast(Atom(target).GetPointerType())
613
614def erts_lookup_function_info(valobj):
615    r = find_range(valobj)
616    if r is None:
617        return None
618    pc = valobj.unsigned
619    target = valobj.target
620    start = r.GetChildMemberWithName('start').Cast(EtermPtr(target))
621    curr = offset(MI_FUNCTIONS, start).Cast(ErtsCodeInfoPtr(target).GetPointerType())
622    prev = curr
623    cnt = offset(MI_NUM_FUNCTIONS, start).deref.unsigned
624    for x in range(0, cnt):
625        prev = curr
626        curr = offset(1, curr)
627        if pc < curr.deref.unsigned:
628            return prev.deref.GetChildMemberWithName('mfa')
629    return None
630
631def find_range(valobj):
632    pc = valobj.unsigned
633    target = valobj.target
634    active = the_active_code_index(target).unsigned
635    ranges = offset(active, global_var('r', target))
636    n = ranges.GetChildMemberWithName('n').unsigned
637    low = ranges.GetChildMemberWithName('modules')
638    high = offset(n, low)
639    range_type = Range(target)
640    range_pointer_type = range_type.GetPointerType()
641    mid = ranges.GetChildMemberWithName('mid').Cast(range_pointer_type)
642    while low.unsigned < high.unsigned:
643        start = mid.GetChildMemberWithName('start').unsigned
644        end = atomic_value(mid.GetChildMemberWithName('end'))
645        if pc < start:
646            high = mid
647        elif pc > end:
648            low = offset(1, mid).Cast(range_pointer_type)
649        else:
650            return mid
651        length = (high.unsigned - low.unsigned) // range_type.size
652        mid = offset(length // 2, low)
653    return None
654
655def mfa(mfa):
656    return '%s:%s/%d' % (eterm(mfa.GetChildMemberWithName('module')),
657                         eterm(mfa.GetChildMemberWithName('function')),
658                         mfa.GetChildMemberWithName('arity').unsigned)
659
660def pixdata2data(valobj):
661    pixdata = valobj.unsigned
662    proc = erts_proc(target)
663    ro = proc.GetChildMemberWithName('r').GetChildMemberWithName('o')
664    pix_mask = ro.GetChildMemberWithName('pix_mask').unsigned
665    pix_cl_mask = ro.GetChildMemberWithName('pix_cl_mask').unsigned
666    pix_cl_shift = ro.GetChildMemberWithName('pix_cl_shift').unsigned
667    pix_cli_mask = ro.GetChildMemberWithName('pix_cli_mask').unsigned
668    pix_cli_shift = ro.GetChildMemberWithName('pix_cli_shift').unsigned
669    data = pixdata & ~pix_mask
670    data |= (pixdata >> pix_cl_shift) & pix_cl_mask
671    data |= (pixdata & pix_cli_mask) << pix_cli_shift
672    return data
673
674def strip_literal_tag(valobj):
675    if valobj.size == 4:
676        # This is a 32-bit executable. There are no literal tags.
677        return valobj
678
679    # Strip literal tags from list and boxed terms.
680    primary_tag = valobj.unsigned & 0x03
681    if (primary_tag == 1 or primary_tag == 2) and valobj.unsigned & 0x04:
682        valobj = valobj.CreateValueFromData(
683            valobj.name,
684            lldb.SBData.CreateDataFromInt(valobj.unsigned - 0x04),
685            Eterm(valobj.target))
686    return valobj
687
688def init(target):
689    names = ['beam_apply', 'beam_normal_exit', 'beam_exit', 'beam_save_calls',
690             'beam_bif_export_trap', 'beam_export_trampoline', 'beam_continue_exit',
691             'beam_return_to_trace', 'beam_return_trace', 'beam_exception_trace',
692             'beam_return_time_trace']
693    for name in names:
694        code_pointers[global_var(name, target).unsigned] = name
695
696# LLDB utils
697
698def global_var(name, target):
699    return target.FindGlobalVariables(name, 1)[0]
700
701def offset(i, valobj):
702    # print("offset(" + str(i) + ", " + str(valobj.unsigned) + ")")
703    val = valobj.GetChildAtIndex(i, lldb.eNoDynamicValues, True)
704    if valobj.TypeIsPointerType():
705        return val.address_of.Cast(valobj.GetType())
706    else:
707        return val
708
709def atomic_value(valobj):
710    value = valobj.GetChildMemberWithName('counter')
711    if value.IsValid():
712        return value.unsigned
713    else:
714        return valobj.GetChildMemberWithName('value').unsigned
715