1
2""" Please make sure you read the README file COMPLETELY BEFORE reading anything below.
3    It is very critical that you read coding guidelines in Section E in README file.
4"""
5from xnu import *
6import sys, shlex
7from utils import *
8import xnudefines
9from process import *
10
11# Macro: memstats
12@lldb_command('memstats')
13def Memstats(cmd_args=None):
14    """ Prints out a summary of various memory statistics. In particular vm_page_wire_count should be greater than 2K or you are under memory pressure.
15    """
16    try:
17        print "memorystatus_level: {: >10d}".format(kern.globals.memorystatus_level)
18    except ValueError:
19        pass
20    try:
21        print "memorystatus_available_pages: {: >10d}".format(kern.globals.memorystatus_available_pages)
22    except ValueError:
23        pass
24    print "vm_page_throttled_count: {: >10d}".format(kern.globals.vm_page_throttled_count)
25    print "vm_page_active_count:    {: >10d}".format(kern.globals.vm_page_active_count)
26    print "vm_page_inactive_count:  {: >10d}".format(kern.globals.vm_page_inactive_count)
27    print "vm_page_wire_count:      {: >10d}".format(kern.globals.vm_page_wire_count)
28    print "vm_page_free_count:      {: >10d}".format(kern.globals.vm_page_free_count)
29    print "vm_page_purgeable_count: {: >10d}".format(kern.globals.vm_page_purgeable_count)
30    print "vm_page_inactive_target: {: >10d}".format(kern.globals.vm_page_inactive_target)
31    print "vm_page_free_target:     {: >10d}".format(kern.globals.vm_page_free_target)
32    print "inuse_ptepages_count:    {: >10d}".format(kern.globals.inuse_ptepages_count)
33    print "vm_page_free_reserved:   {: >10d}".format(kern.globals.vm_page_free_reserved)
34
35@xnudebug_test('test_memstats')
36def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
37    """ Test the functionality of memstats command
38        returns
39         - False on failure
40         - True on success
41    """
42    if not isConnected:
43        print "Target is not connected. Cannot test memstats"
44        return False
45    res = lldb.SBCommandReturnObject()
46    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res)
47    result = res.GetOutput()
48    if result.split(":")[1].strip().find('None') == -1 :
49        return True
50    else:
51        return False
52
53# EndMacro: memstats
54
55# Macro: showmemorystatus
56def CalculateLedgerPeak(phys_footprint_entry):
57    """ Internal function to calculate ledger peak value for the given phys footprint entry
58        params: phys_footprint_entry - value representing struct ledger_entry *
59        return: value - representing the ledger peak for the given phys footprint entry
60    """
61    now = kern.globals.sched_tick / 20
62    ledger_peak = phys_footprint_entry.le_credit - phys_footprint_entry.le_debit
63    if (now - phys_footprint_entry._le.le_peaks[0].le_time <= 1) and (phys_footprint_entry._le.le_peaks[0].le_max > ledger_peak):
64        ledger_peak = phys_footprint_entry._le.le_peaks[0].le_max
65    if (now - phys_footprint_entry._le.le_peaks[1].le_time <= 1) and (phys_footprint_entry._le.le_peaks[1].le_max > ledger_peak):
66        ledger_peak = phys_footprint_entry._le.le_peaks[1].le_max
67    return ledger_peak
68
69@header("{: >8s} {: >22s} {: >22s} {: >11s} {: >11s} {: >12s} {: >10s} {: >13s} {: ^10s} {: >8s}  {: <20s}\n".format(
70'pid', 'effective priority', 'requested priority', 'state', 'user_data', 'physical', 'iokit', 'footprint',
71'spike', 'limit', 'command'))
72def GetMemoryStatusNode(proc_val):
73    """ Internal function to get memorystatus information from the given proc
74        params: proc - value representing struct proc *
75        return: str - formatted output information for proc object
76    """
77    out_str = ''
78    task_val = Cast(proc_val.task, 'task *')
79    task_ledgerp = task_val.ledger
80
81    task_physmem_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_mem]
82    task_iokit_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.iokit_mem]
83    task_phys_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_footprint]
84    page_size = kern.globals.page_size
85
86    phys_mem_footprint = (task_physmem_footprint_ledger_entry.le_credit - task_physmem_footprint_ledger_entry.le_debit) / page_size
87    iokit_footprint = (task_iokit_footprint_ledger_entry.le_credit - task_iokit_footprint_ledger_entry.le_debit) / page_size
88    phys_footprint = (task_phys_footprint_ledger_entry.le_credit - task_phys_footprint_ledger_entry.le_debit) / page_size
89    phys_footprint_limit = task_phys_footprint_ledger_entry.le_limit / page_size
90    ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry)
91    phys_footprint_spike = ledger_peak / page_size
92
93    format_string = '{0: >8d} {1: >22d} {2: >22d} {3: #011x} {4: #011x} {5: >12d} {6: >10d} {7: >13d}'
94    out_str += format_string.format(proc_val.p_pid, proc_val.p_memstat_effectivepriority,
95        proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_userdata,
96        phys_mem_footprint, iokit_footprint, phys_footprint)
97    if phys_footprint != phys_footprint_spike:
98        out_str += "{: ^12d}".format(phys_footprint_spike)
99    else:
100        out_str += "{: ^12s}".format('-')
101    out_str += "{: 8d}  {: <20s}\n".format(phys_footprint_limit, proc_val.p_comm)
102    return out_str
103
104@lldb_command('showmemorystatus')
105def ShowMemoryStatus(cmd_args=None):
106    """  Routine to display each entry in jetsam list with a summary of pressure statistics
107         Usage: showmemorystatus
108    """
109    bucket_index = 0
110    bucket_count = 20
111    print GetMemoryStatusNode.header
112    print "{: >91s} {: >10s} {: >13s} {: ^10s} {: >8s}\n".format("(pages)", "(pages)", "(pages)",
113        "(pages)", "(pages)")
114    while bucket_index < bucket_count:
115        current_bucket = kern.globals.memstat_bucket[bucket_index]
116        current_list = current_bucket.list
117        current_proc = Cast(current_list.tqh_first, 'proc *')
118        while unsigned(current_proc) != 0:
119            print GetMemoryStatusNode(current_proc)
120            current_proc = current_proc.p_memstat_list.tqe_next
121        bucket_index += 1
122    print "\n\n"
123    Memstats()
124
125# EndMacro: showmemorystatus
126
127# Macro: zprint
128
129@lldb_type_summary(['zone','zone_t'])
130@header("{:^18s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s}({:>6s} {:>6s} {:>6s}) {:^14s} {:<20s}".format(
131'ZONE', 'TOT_SZ', 'PAGE_COUNT', 'ALLOC_ELTS', 'FREE_ELTS', 'FREE_SZ', 'ELT_SZ', 'ALLOC', 'ELTS', 'PGS', 'SLK', 'FLAGS', 'NAME'))
132def GetZoneSummary(zone):
133    """ Summarize a zone with important information. See help zprint for description of each field
134        params:
135          zone: value - obj representing a zone in kernel
136        returns:
137          str - summary of the zone
138    """
139    out_string = ""
140    format_string = '{:#018x} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:6d} {:6d} {:6d}  {markings} {name:s} '
141    pagesize = 4096
142
143    free_elements = (zone.cur_size / zone.elem_size) - zone.count
144    free_size = free_elements * zone.elem_size
145
146    alloc_count = zone.alloc_size / zone.elem_size
147    alloc_pages = zone.alloc_size / pagesize
148    alloc_slack = zone.alloc_size % zone.elem_size
149    marks = [
150            ["collectable",        "C"],
151            ["expandable",         "X"],
152            ["noencrypt",          "$"],
153            ["caller_acct",        "@"],
154            ["exhaustible",        "H"],
155            ["allows_foreign",     "F"],
156            ["async_prio_refill",  "R"],
157            ["no_callout",         "O"],
158            ["zleak_on",           "L"],
159            ["doing_alloc",        "A"],
160            ["waiting",            "W"],
161            ["doing_gc",           "G"]
162            ]
163    if kern.arch == 'x86_64':
164        marks.append(["gzalloc_exempt",     "M"])
165        marks.append(["alignment_required", "N"])
166
167    markings=""
168    for mark in marks:
169        if zone.__getattr__(mark[0]) :
170            markings+=mark[1]
171        else:
172            markings+=" "
173    out_string += format_string.format(zone, zone.cur_size, zone.page_count,
174                    zone.count, free_elements, free_size,
175                    zone.elem_size, zone.alloc_size, alloc_count,
176                    alloc_pages, alloc_slack, name = zone.zone_name, markings=markings)
177
178    if zone.exhaustible :
179            out_string += "(max: {:d})".format(zone.max_size)
180
181    return out_string
182
183@lldb_command('zprint')
184def Zprint(cmd_args=None):
185    """ Routine to print a summary listing of all the kernel zones
186    All columns are printed in decimal
187    Legend:
188        C - collectable
189        X - expandable
190        $ - not encrypted during hibernation
191        @ - allocs and frees are accounted to caller process for KPRVT
192        H - exhaustible
193        F - allows foreign memory (memory not allocated from zone_map)
194        M - gzalloc will avoid monitoring this zone
195        R - will be refilled when below low water mark
196        O - does not allow refill callout to fill zone on noblock allocation
197        N - zone requires alignment (avoids padding this zone for debugging)
198        A - currently trying to allocate more backing memory from kernel_memory_allocate
199        W - another thread is waiting for more memory
200        L - zone is being monitored by zleaks
201        G - currently running GC
202    """
203    global kern
204    print GetZoneSummary.header
205    for zval in kern.zones:
206        print GetZoneSummary(zval)
207
208@xnudebug_test('test_zprint')
209def TestZprint(kernel_target, config, lldb_obj, isConnected ):
210    """ Test the functionality of zprint command
211        returns
212         - False on failure
213         - True on success
214    """
215    if not isConnected:
216        print "Target is not connected. Cannot test memstats"
217        return False
218    res = lldb.SBCommandReturnObject()
219    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("zprint", res)
220    result = res.GetOutput()
221    if len(result.split("\n")) > 2:
222        return True
223    else:
224        return False
225
226
227# EndMacro: zprint
228
229# Macro: showzfreelist
230
231def ShowZfreeListHeader(zone):
232    """ Helper routine to print a header for zone freelist.
233        (Since the freelist does not have a custom type, this is not defined as a Type Summary).
234        params:
235            zone:zone_t - Zone object to print header info
236        returns:
237            None
238    """
239    out_str = ""
240    out_str += "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
241    out_str += "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
242                zone.elem_size, zone.count, kern.globals.zp_nopoison_cookie, kern.globals.zp_poisoned_cookie, zone.zp_count, kern.globals.zp_factor)
243    out_str += "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
244                'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
245    print out_str
246
247def ShowZfreeListChain(zone, zfirst, zlimit):
248    """ Helper routine to print a zone free list chain
249        params:
250            zone: zone_t - Zone object
251            zfirst: void * - A pointer to the first element of the free list chain
252            zlimit: int - Limit for the number of elements to be printed by showzfreelist
253        returns:
254            None
255    """
256    current = Cast(zfirst, 'void *')
257    while ShowZfreeList.elts_found < zlimit:
258        ShowZfreeList.elts_found += 1
259        znext = dereference(Cast(current, 'vm_offset_t *'))
260        backup_ptr = kern.GetValueFromAddress((unsigned(Cast(current, 'vm_offset_t')) + unsigned(zone.elem_size) - sizeof('vm_offset_t')), 'vm_offset_t *')
261        backup_val = dereference(backup_ptr)
262        n_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_nopoison_cookie))
263        p_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_poisoned_cookie))
264        poison_str = ''
265        if p_unobfuscated == unsigned(znext):
266            poison_str = "P ({0: <d})".format(ShowZfreeList.elts_found - ShowZfreeList.last_poisoned)
267            ShowZfreeList.last_poisoned = ShowZfreeList.elts_found
268        else:
269            if n_unobfuscated != unsigned(znext):
270                poison_str = "INVALID"
271        print "{0: <7d} 0x{1:0>16x} 0x{2:0>16x} 0x{3:0>16x} 0x{4:0>16x} 0x{5:0>16x} {6: <14s}\n".format(
272              ShowZfreeList.elts_found, unsigned(current), unsigned(znext), unsigned(backup_val), n_unobfuscated, p_unobfuscated, poison_str)
273        if unsigned(znext) == 0:
274            break
275        current = Cast(znext, 'void *')
276
277@static_var('elts_found',0)
278@static_var('last_poisoned',0)
279@lldb_command('showzfreelist')
280def ShowZfreeList(cmd_args=None):
281    """ Walk the freelist for a zone, printing out the primary and backup next pointers, the poisoning cookies, and the poisoning status of each element.
282    Usage: showzfreelist <zone> [iterations]
283
284        Will walk up to 50 elements by default, pass a limit in 'iterations' to override.
285    """
286    if not cmd_args:
287        print ShowZfreeList.__doc__
288        return
289    ShowZfreeList.elts_found = 0
290    ShowZfreeList.last_poisoned = 0
291
292    zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
293    zlimit = 50
294    if len(cmd_args) >= 2:
295        zlimit = ArgumentStringToInt(cmd_args[1])
296    ShowZfreeListHeader(zone)
297
298    if unsigned(zone.use_page_list) == 1:
299        if unsigned(zone.allows_foreign) == 1:
300            for free_page_meta in IterateQueue(zone.pages.any_free_foreign, 'struct zone_page_metadata *', 'pages'):
301                if ShowZfreeList.elts_found == zlimit:
302                    break
303                zfirst = Cast(free_page_meta.elements, 'void *')
304                if unsigned(zfirst) != 0:
305                    ShowZfreeListChain(zone, zfirst, zlimit)
306        for free_page_meta in IterateQueue(zone.pages.intermediate, 'struct zone_page_metadata *', 'pages'):
307            if ShowZfreeList.elts_found == zlimit:
308                break
309            zfirst = Cast(free_page_meta.elements, 'void *')
310            if unsigned(zfirst) != 0:
311                ShowZfreeListChain(zone, zfirst, zlimit)
312        for free_page_meta in IterateQueue(zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
313            if ShowZfreeList.elts_found == zlimit:
314                break
315            zfirst = Cast(free_page_meta.elements, 'void *')
316            if unsigned(zfirst) != 0:
317                ShowZfreeListChain(zone, zfirst, zlimit)
318    else:
319        zfirst = Cast(zone.free_elements, 'void *')
320        if unsigned(zfirst) != 0:
321            ShowZfreeListChain(zone, zfirst, zlimit)
322
323    if ShowZfreeList.elts_found == zlimit:
324        print "Stopped at {0: <d} elements!".format(zlimit)
325    else:
326        print "Found {0: <d} elements!".format(ShowZfreeList.elts_found)
327
328# EndMacro: showzfreelist
329
330# Macro: zstack
331
332@lldb_command('zstack')
333def Zstack(cmd_args=None):
334    """ Zone leak debugging: Print the stack trace of log element at <index>. If a <count> is supplied, it prints <count> log elements starting at <index>.
335        Usage: zstack <index> [<count>]
336
337        The suggested usage is to look at indexes below zcurrent and look for common stack traces.
338        The stack trace that occurs the most is probably the cause of the leak.  Find the pc of the
339        function calling into zalloc and use the countpcs command to find out how often that pc occurs in the log.
340        The pc occuring in a high percentage of records is most likely the source of the leak.
341
342        The findoldest command is also useful for leak debugging since it identifies the oldest record
343        in the log, which may indicate the leaker.
344    """
345    if not cmd_args:
346        print Zstack.__doc__
347        return
348    if int(kern.globals.log_records) == 0:
349        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
350        return
351    if int(kern.globals.zlog_btlog) == 0:
352        print "Zone logging enabled, but zone has not been initialized yet."
353        return
354
355    count = 1
356    if len(cmd_args) >= 2:
357        count = ArgumentStringToInt(cmd_args[1])
358    zstack_index = unsigned(cmd_args[0])
359    while count and (zstack_index != 0xffffff):
360        zstack_record_offset = zstack_index * unsigned(kern.globals.zlog_btlog.btrecord_size)
361        zstack_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + zstack_record_offset, 'btlog_record_t *')
362        ShowZStackRecord(zstack_record, zstack_index)
363        zstack_index = zstack_record.next
364        count -= 1
365
366# EndMacro : zstack
367
368# Macro: findoldest
369
370@lldb_command('findoldest')
371def FindOldest(cmd_args=None):
372    """ Zone leak debugging: find and print the oldest record in the log.
373
374        Once it prints a stack trace, find the pc of the caller above all the zalloc, kalloc and
375        IOKit layers.  Then use the countpcs command to see how often this caller has allocated
376        memory.  A caller with a high percentage of records in the log is probably the leaker.
377    """
378    if int(kern.globals.log_records) == 0:
379        print FindOldest.__doc__
380        return
381    if int(kern.globals.zlog_btlog) == 0:
382        print "Zone logging enabled, but zone has not been initialized yet."
383        return
384    index = kern.globals.zlog_btlog.head
385    if unsigned(index) != 0xffffff:
386        print "Oldest record is at log index: {0: <d}".format(index)
387        Zstack([index])
388    else:
389        print "No Records Present"
390
391# EndMacro : findoldest
392
393# Macro: countpcs
394
395@lldb_command('countpcs')
396def Countpcs(cmd_args=None):
397    """ Zone leak debugging: search the log and print a count of all log entries that contain the given <pc>
398        in the stack trace.
399        Usage: countpcs <pc>
400
401        This is useful for verifying a suspected <pc> as being the source of
402        the leak.  If a high percentage of the log entries contain the given <pc>, then it's most
403        likely the source of the leak.  Note that this command can take several minutes to run.
404    """
405    if not cmd_args:
406        print Countpcs.__doc__
407        return
408    if int(kern.globals.log_records) == 0:
409        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
410        return
411    if int(kern.globals.zlog_btlog) == 0:
412        print "Zone logging enabled, but zone has not been initialized yet."
413        return
414
415    cpcs_index = unsigned(kern.globals.zlog_btlog.head)
416    target_pc = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *'))
417    found = 0
418    depth = unsigned(kern.globals.zlog_btlog.btrecord_btdepth)
419
420    while cpcs_index != 0xffffff:
421        cpcs_record_offset = cpcs_index * unsigned(kern.globals.zlog_btlog.btrecord_size)
422        cpcs_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + cpcs_record_offset, 'btlog_record_t *')
423        frame = 0
424        while frame < depth:
425            frame_pc = unsigned(cpcs_record.bt[frame])
426            if frame_pc == target_pc:
427                found += 1
428                break
429            frame += 1
430        cpcs_index = cpcs_record.next
431    print "Occured {0: <d} times in log ({1: <d}{2: <s} of records)".format(found, (found * 100)/unsigned(kern.globals.zlog_btlog.activecount), '%')
432
433# EndMacro: countpcs
434
435# Macro: findelem
436
437@lldb_command('findelem')
438def FindElem(cmd_args=None):
439    """ Zone corruption debugging: search the log and print out the stack traces for all log entries that
440        refer to the given zone element.
441        Usage: findelem <elem addr>
442
443        When the kernel panics due to a corrupted zone element, get the
444        element address and use this command.  This will show you the stack traces of all logged zalloc and
445        zfree operations which tells you who touched the element in the recent past.  This also makes
446        double-frees readily apparent.
447    """
448    if not cmd_args:
449        print FindElem.__doc__
450        return
451    if int(kern.globals.log_records) == 0:
452        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
453        return
454    if int(kern.globals.zlog_btlog) == 0:
455        print "Zone logging enabled, but zone has not been initialized yet."
456        return
457
458    target_element = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *'))
459    index = unsigned(kern.globals.zlog_btlog.head)
460    prev_op = -1
461
462    while index != 0xffffff:
463        findelem_record_offset = index * unsigned(kern.globals.zlog_btlog.btrecord_size)
464        findelem_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + findelem_record_offset, 'btlog_record_t *')
465        if unsigned(findelem_record.element) == target_element:
466            Zstack([index])
467            if int(findelem_record.operation) == prev_op:
468                print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
469                prev_op = int(findelem_record.operation)
470        index = findelem_record.next
471
472# EndMacro: findelem
473
474# Macro: btlog_find
475
476@lldb_command('btlog_find', "A")
477def BtlogFind(cmd_args=None, cmd_options={}):
478    """ Search the btlog_t for entries corresponding to the given element.
479        Use -A flag to print all entries.
480        Usage: btlog_find <btlog_t> <element>
481        Usage: btlog_find <btlog_t> -A
482        Note: Backtraces will be in chronological order, with oldest entries aged out in FIFO order as needed.
483    """
484    if not cmd_args:
485        raise ArgumentError("Need a btlog_t parameter")
486    btlog = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
487    printall = False
488    target_elem = 0xffffff
489
490    if "-A" in cmd_options:
491        printall = True
492    else:
493        if not printall and len(cmd_args) < 2:
494            raise ArgumentError("<element> is missing in args. Need a search pointer.")
495        target_elem = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
496
497    index = unsigned(btlog.head)
498    progress = 0
499    record_size = unsigned(btlog.btrecord_size)
500    while index != 0xffffff:
501        record_offset = index * record_size
502        record = kern.GetValueFromAddress(unsigned(btlog.btrecords) + record_offset, 'btlog_record_t *')
503        if unsigned(record.element) == target_elem or printall:
504            print '{0: <s} OP {1: <d} {2: <#0x} {3: <s}\n'.format(('-' * 8), record.operation, target_elem, ('-' * 8))
505            ShowBtlogBacktrace(btlog.btrecord_btdepth, record)
506        index = record.next
507        progress += 1
508        if (progress % 1000) == 0: print '{0: <d} entries searched!\n'.format(progress)
509
510#EndMacro: btlog_find
511
512#Macro: showzalloc
513
514@lldb_command('showzalloc')
515def ShowZalloc(cmd_args=None):
516    """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
517        Usage: showzalloc <index>
518    """
519    if not cmd_args:
520        print ShowZalloc.__doc__
521        return
522    if unsigned(kern.globals.zallocations) == 0:
523        print "zallocations array not initialized!"
524        return
525    zallocation = kern.globals.zallocations[ArgumentStringToInt(cmd_args[0])]
526    print zallocation
527    ShowZTrace([str(int(zallocation.za_trace_index))])
528
529#EndMacro: showzalloc
530
531#Macro: showztrace
532
533@lldb_command('showztrace')
534def ShowZTrace(cmd_args=None):
535    """ Prints the backtrace from the ztraces array at index
536        Usage: showztrace <trace index>
537    """
538    if not cmd_args:
539        print ShowZTrace.__doc__
540        return
541    if unsigned(kern.globals.ztraces) == 0:
542        print "ztraces array not initialized!"
543        return
544    ztrace_addr = kern.globals.ztraces[ArgumentStringToInt(cmd_args[0])]
545    print ztrace_addr
546    ShowZstackTraceHelper(ztrace_addr.zt_stack, ztrace_addr.zt_depth)
547
548#EndMacro: showztrace
549
550#Macro: showztraceaddr
551
552@lldb_command('showztraceaddr')
553def ShowZTraceAddr(cmd_args=None):
554    """ Prints the struct ztrace passed in.
555        Usage: showztraceaddr <trace address>
556    """
557    if not cmd_args:
558        print ShowZTraceAddr.__doc__
559        return
560    ztrace_ptr = kern.GetValueFromAddress(cmd_args[0], 'struct ztrace *')
561    print dereference(ztrace_ptr)
562    ShowZstackTraceHelper(ztrace_ptr.zt_stack, ztrace_ptr.zt_depth)
563
564#EndMacro: showztraceaddr
565
566#Macro: showzstacktrace
567
568@lldb_command('showzstacktrace')
569def ShowZstackTrace(cmd_args=None):
570    """ Routine to print a stacktrace stored by OSBacktrace.
571        Usage: showzstacktrace <saved stacktrace> [size]
572
573        size is optional, defaults to 15.
574    """
575    if not cmd_args:
576        print ShowZstackTrace.__doc__
577        return
578    void_ptr_type = gettype('void *')
579    void_double_ptr_type = void_ptr_type.GetPointerType()
580    trace = kern.GetValueFromAddress(cmd_args[0], void_double_ptr_type)
581    trace_size = 15
582    if len(cmd_args) >= 2:
583        trace_size = ArgumentStringToInt(cmd_args[1])
584    ShowZstackTraceHelper(trace, trace_size)
585
586#EndMacro: showzstacktrace
587
588def ShowZstackTraceHelper(stack, depth):
589    """ Helper routine for printing a zstack.
590        params:
591            stack: void *[] - An array of pointers representing the Zstack
592            depth: int - The depth of the ztrace stack
593        returns:
594            None
595    """
596    trace_current = 0
597    while trace_current < depth:
598        trace_addr = stack[trace_current]
599        symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr))
600        if symbol_arr:
601            symbol_str = str(symbol_arr[0].addr)
602        else:
603            symbol_str = ''
604        print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str)
605        trace_current += 1
606
607#Macro: showtopztrace
608
609@lldb_command('showtopztrace')
610def ShowTopZtrace(cmd_args=None):
611    """ Shows the ztrace with the biggest size.
612        (According to top_ztrace, not by iterating through the hash table)
613    """
614    top_trace = kern.globals.top_ztrace
615    print 'Index: {0: <d}'.format((unsigned(top_trace) - unsigned(kern.globals.ztraces)) / sizeof('struct ztrace'))
616    print dereference(top_trace)
617    ShowZstackTraceHelper(top_trace.zt_stack, top_trace.zt_depth)
618
619#EndMacro: showtopztrace
620
621#Macro: showzallocs
622
623@lldb_command('showzallocs')
624def ShowZallocs(cmd_args=None):
625    """ Prints all allocations in the zallocations table
626    """
627    if unsigned(kern.globals.zallocations) == 0:
628        print "zallocations array not initialized!"
629        return
630    print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
631    current_index = 0
632    max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
633    allocation_count = 0
634    while current_index < max_zallocation:
635        current_zalloc = kern.globals.zallocations[current_index]
636        if int(current_zalloc.za_element) != 0:
637            print '{0: <5d} {1: <#018x} {2: <5d} {3: <15d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_trace_index, unsigned(current_zalloc.za_size))
638            allocation_count += 1
639        current_index += 1
640    print 'Total Allocations: {0: <d}'.format(allocation_count)
641
642#EndMacro: showzallocs
643
644#Macro: showzallocsfortrace
645
646@lldb_command('showzallocsfortrace')
647def ShowZallocsForTrace(cmd_args=None):
648    """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
649        Usage:  showzallocsfortrace <trace index>
650    """
651    if not cmd_args:
652        print ShowZallocsForTrace.__doc__
653        return
654    print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
655    target_index = ArgumentStringToInt(cmd_args[0])
656    current_index = 0
657    max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
658    allocation_count = 0
659    while current_index < max_zallocation:
660        current_zalloc = kern.globals.zallocations[current_index]
661        if unsigned(current_zalloc.za_element) != 0 and (unsigned(current_zalloc.za_trace_index) == unsigned(target_index)):
662            print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_size)
663            allocation_count += 1
664        current_index += 1
665    print 'Total Allocations: {0: <d}'.format(allocation_count)
666
667#EndMacro: showzallocsfortrace
668
669#Macro: showztraces
670
671@lldb_command('showztraces')
672def ShowZTraces(cmd_args=None):
673    """ Prints all traces with size > 0
674    """
675    ShowZTracesAbove([0])
676
677#EndMacro: showztraces
678
679#Macro: showztracesabove
680
681@lldb_command('showztracesabove')
682def ShowZTracesAbove(cmd_args=None):
683    """ Prints all traces with size greater than X
684        Usage: showztracesabove <size>
685    """
686    if not cmd_args:
687        print ShowZTracesAbove.__doc__
688        return
689    print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
690    current_index = 0
691    ztrace_count = 0
692    max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
693    while current_index < max_ztrace:
694        ztrace_current = kern.globals.ztraces[current_index]
695        if ztrace_current.zt_size > unsigned(cmd_args[0]):
696            print '{0: <5d} {1: <6d}'.format(current_index, int(ztrace_current.zt_size))
697            ztrace_count += 1
698        current_index += 1
699    print 'Total traces: {0: <d}'.format(ztrace_count)
700
701#EndMacro: showztracesabove
702
703#Macro: showztracehistogram
704
705@lldb_command('showztracehistogram')
706def ShowZtraceHistogram(cmd_args=None):
707    """ Prints the histogram of the ztrace table
708    """
709    print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
710    current_index = 0
711    ztrace_count = 0
712    max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
713    while current_index < max_ztrace:
714        ztrace_current = kern.globals.ztraces[current_index]
715        if ztrace_current.zt_hit_count != 0:
716            print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index, ztrace_current.zt_hit_count, ztrace_current.zt_collisions)
717            ztrace_count += 1
718        current_index += 1
719    print 'Total traces: {0: <d}'.format(ztrace_count)
720
721#EndMacro: showztracehistogram
722
723#Macro: showzallochistogram
724
725@lldb_command('showzallochistogram')
726def ShowZallocHistogram(cmd_args=None):
727    """ Prints the histogram for the zalloc table
728    """
729    print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
730    current_index = 0
731    zallocation_count = 0
732    max_ztrace = unsigned(kern.globals.zleak_alloc_buckets)
733    while current_index < max_ztrace:
734        zallocation_current = kern.globals.zallocations[current_index]
735        if zallocation_current.za_hit_count != 0:
736            print '{0: <5d} {1: <9d}'.format(current_index, zallocation_current.za_hit_count)
737            zallocation_count += 1
738        current_index += 1
739    print 'Total Allocations: {0: <d}'.format(zallocation_count)
740
741#EndMacro: showzallochistogram
742
743#Macro: showzstats
744
745@lldb_command('showzstats')
746def ShowZstats(cmd_args=None):
747    """ Prints the zone leak detection stats
748    """
749    print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern.globals.z_alloc_collisions), unsigned(kern.globals.z_trace_collisions))
750    print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern.globals.z_alloc_overwrites), unsigned(kern.globals.z_trace_overwrites))
751    print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern.globals.z_alloc_recorded), unsigned(kern.globals.z_trace_recorded))
752
753#EndMacro: showzstats
754
755def ShowBtlogBacktrace(depth, zstack_record):
756    """ Helper routine for printing a BT Log record backtrace stack.
757        params:
758            depth:int - The depth of the zstack record
759            zstack_record:btlog_record_t * - A BTLog record
760        returns:
761            None
762    """
763    out_str = ''
764    frame = 0
765    if not zstack_record:
766        print "Zstack record none!"
767        return
768    depth_val = unsigned(depth)
769    while frame < depth_val:
770        frame_pc = zstack_record.bt[frame]
771        if not frame_pc or int(frame_pc) == 0:
772            break
773        symbol_arr = kern.SymbolicateFromAddress(frame_pc)
774        if symbol_arr:
775            symbol_str = str(symbol_arr[0].addr)
776        else:
777            symbol_str = ''
778        out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str)
779        frame += 1
780    print out_str
781
782def ShowZStackRecord(zstack_record, zstack_index):
783    """ Helper routine for printing a single zstack record
784        params:
785            zstack_record:btlog_record_t * -  A BTLog record
786            zstack_index:int - Index for the record in the BTLog table
787        returns:
788            None
789    """
790    out_str = ('-' * 8)
791    if zstack_record.operation == 1:
792        out_str += "ALLOC  "
793    else:
794        out_str += "FREE   "
795    out_str += "{0: <#0x} : Index {1: <d} {2: <s}\n".format(zstack_record.element, zstack_index, ('-' * 8))
796    print out_str
797    ShowBtlogBacktrace(kern.globals.zlog_btlog.btrecord_btdepth, zstack_record)
798
799# Macro: showioalloc
800
801@lldb_command('showioalloc')
802def ShowIOAllocations(cmd_args=None):
803    """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
804        Routine to display a summary of memory accounting allocated by IOKit allocators.
805    """
806    print "Instance allocation  = {0: <#0x} = {1: d}K".format(kern.globals.debug_ivars_size, (kern.globals.debug_ivars_size / 1024))
807    print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024))
808    print "IOMalloc allocation  = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024))
809    print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024))
810
811
812# EndMacro: showioalloc
813
814
815
816
817# Macro: showtaskvme
818@lldb_command('showtaskvme')
819def ShowTaskVmeHelper(cmd_args=None):
820    """ Display a summary list of the specified vm_map's entries
821        Usage: showtaskvme <task address>  (ex. showtaskvme 0x00ataskptr00 )
822    """
823    task = kern.GetValueFromAddress(cmd_args[0], 'task *')
824    ShowTaskVMEntries(task)
825
826@lldb_command('showallvme')
827def ShowAllVME(cmd_args=None):
828    """ Routine to print a summary listing of all the vm map entries
829        Go Through each task in system and show the vm info
830    """
831    for task in kern.tasks:
832        ShowTaskVMEntries(task)
833
834@lldb_command('showallvm')
835def ShowAllVM(cmd_args=None):
836    """ Routine to print a summary listing of all the vm maps
837    """
838    for task in kern.tasks:
839        print GetTaskSummary.header + ' ' + GetProcSummary.header
840        print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
841        print GetVMMapSummary.header
842        print GetVMMapSummary(task.map)
843
844@lldb_command("showtaskvm")
845def ShowTaskVM(cmd_args=None):
846    """ Display info about the specified task's vm_map
847        syntax: (lldb) showtaskvm <task_ptr>
848    """
849    if not cmd_args:
850        print ShowTaskVM.__doc__
851        return False
852    task = kern.GetValueFromAddress(cmd_args[0], 'task *')
853    if not task:
854        print "Unknown arguments."
855        return False
856    print GetTaskSummary.header + ' ' + GetProcSummary.header
857    print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
858    print GetVMMapSummary.header
859    print GetVMMapSummary(task.map)
860    return True
861
862@lldb_command('showallvmstats')
863def ShowAllVMStats(cmd_args=None):
864    """ Print a summary of vm statistics in a table format
865    """
866    vmstats = lambda:None
867    vmstats.wired_count = 0
868    vmstats.resident_count = 0
869    vmstats.resident_max = 0
870    vmstats.internal = 0
871    vmstats.external = 0
872    vmstats.reusable = 0
873    vmstats.compressed = 0
874    vmstats.compressed_peak = 0
875    vmstats.compressed_lifetime = 0
876    vmstats.error = ''
877
878    hdr_format = "{0: >10s} {1: <20s} {2: >6s} {3: >10s} {4: >10s} {5: >10s} {6: >10s} {7: >10s} {8: >10s} {9: >10s} {10: >10s} {11: >10s} {12: >10s} {13: >10s} {14:}"
879    print hdr_format.format('pid', 'command', '#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', '')
880    print hdr_format.format('', '', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '')
881    entry_format = "{p.p_pid: >10d} {p.p_comm: <20s} {m.hdr.nentries: >6d} {s.wired_count: >10d} {vsize: >10d} {s.resident_count: >10d} {s.new_resident_count: >10d} {s.resident_max: >10d} {s.internal: >10d} {s.external: >10d} {s.reusable: >10d} {s.compressed: >10d} {s.compressed_peak: >10d} {s.compressed_lifetime: >10d} {s.error}"
882
883    for task in kern.tasks:
884        proc = Cast(task.bsd_info, 'proc *')
885        vmmap = Cast(task.map, '_vm_map *')
886        vmstats.error = ''
887        vmstats.wired_count = vmmap.pmap.stats.wired_count;
888        vmstats.resident_count = unsigned(vmmap.pmap.stats.resident_count);
889        vmstats.resident_max = vmmap.pmap.stats.resident_max;
890        vmstats.internal = unsigned(vmmap.pmap.stats.internal);
891        vmstats.external = unsigned(vmmap.pmap.stats.external);
892        vmstats.reusable = unsigned(vmmap.pmap.stats.reusable);
893        vmstats.compressed = unsigned(vmmap.pmap.stats.compressed);
894        vmstats.compressed_peak = unsigned(vmmap.pmap.stats.compressed_peak);
895        vmstats.compressed_lifetime = unsigned(vmmap.pmap.stats.compressed_lifetime);
896        vmstats.new_resident_count = vmstats.internal + vmstats.external
897
898        if vmstats.internal < 0:
899            vmstats.error += '*'
900        if vmstats.external < 0:
901            vmstats.error += '*'
902        if vmstats.reusable < 0:
903            vmstats.error += '*'
904        if vmstats.compressed < 0:
905            vmstats.error += '*'
906        if vmstats.compressed_peak < 0:
907            vmstats.error += '*'
908        if vmstats.compressed_lifetime < 0:
909            vmstats.error += '*'
910        if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count:
911            vmstats.error += '*'
912
913        print entry_format.format(p=proc, m=vmmap, vsize=(unsigned(vmmap.size) >> 12), t=task, s=vmstats)
914
915
916def ShowTaskVMEntries(task):
917    """  Routine to print out a summary listing of all the entries in a vm_map
918        params:
919            task - core.value : a object of type 'task *'
920        returns:
921            None
922    """
923    print "vm_map entries for task " + hex(task)
924    print GetTaskSummary.header
925    print GetTaskSummary(task)
926    if not task.map:
927        print "Task {0: <#020x} has map = 0x0"
928        return None
929    print GetVMMapSummary.header
930    print GetVMMapSummary(task.map)
931    vme_list_head = task.map.hdr.links
932    vme_ptr_type = GetType('vm_map_entry *')
933    print GetVMEntrySummary.header
934    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
935        print GetVMEntrySummary(vme)
936    return None
937
938@lldb_command("showmap")
939def ShowMap(cmd_args=None):
940    """ Routine to print out info about the specified vm_map
941        usage: showmap <vm_map>
942    """
943    if cmd_args == None or len(cmd_args) < 1:
944        print "Invalid argument.", ShowMap.__doc__
945        return
946    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
947    print GetVMMapSummary.header
948    print GetVMMapSummary(map_val)
949
950@lldb_command("showmapvme")
951def ShowMapVME(cmd_args=None):
952    """Routine to print out info about the specified vm_map and its vm entries
953        usage: showmapvme <vm_map>
954    """
955    if cmd_args == None or len(cmd_args) < 1:
956        print "Invalid argument.", ShowMap.__doc__
957        return
958    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
959    print GetVMMapSummary.header
960    print GetVMMapSummary(map_val)
961    vme_list_head = map_val.hdr.links
962    vme_ptr_type = GetType('vm_map_entry *')
963    print GetVMEntrySummary.header
964    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
965        print GetVMEntrySummary(vme)
966    return None
967
968@lldb_type_summary(['_vm_map *', 'vm_map_t'])
969@header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free"))
970def GetVMMapSummary(vmmap):
971    """ Display interesting bits from vm_map struct """
972    out_string = ""
973    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}"
974    vm_size = uint64_t(vmmap.size).value
975    resident_pages = 0
976    if vmmap.pmap != 0: resident_pages = int(vmmap.pmap.stats.resident_count)
977    out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, vmmap.first_free)
978    return out_string
979
980@lldb_type_summary(['vm_map_entry'])
981@header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
982def GetVMEntrySummary(vme):
983    """ Display vm entry specific information. """
984    out_string = ""
985    format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
986    vme_protection = int(vme.protection)
987    vme_max_protection = int(vme.max_protection)
988    vme_extra_info_str ="SC-Ds"[int(vme.inheritance)]
989    if int(vme.is_sub_map) != 0 :
990        vme_extra_info_str +="s"
991    elif int(vme.needs_copy) != 0 :
992        vme_extra_info_str +="n"
993    num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) >> 12
994    out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.object.vm_object, vme.offset)
995    return out_string
996
997# EndMacro: showtaskvme
998@lldb_command('showmapwired')
999def ShowMapWired(cmd_args=None):
1000    """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1001    """
1002    if cmd_args == None or len(cmd_args) < 1:
1003        print "Invalid argument", ShowMapWired.__doc__
1004        return
1005    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1006
1007
1008@lldb_type_summary(['kmod_info_t *'])
1009@header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: >20s} {6: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'version', 'name'))
1010def GetKextSummary(kmod):
1011    """ returns a string representation of kext information
1012    """
1013    out_string = ""
1014    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: >20s} {6: <30s}"
1015    out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, kmod.version, kmod.name)
1016    return out_string
1017
1018@lldb_type_summary(['uuid_t'])
1019@header("")
1020def GetUUIDSummary(uuid):
1021    """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1022    """
1023    arr = Cast(addressof(uuid), 'uint8_t *')
1024    data = []
1025    for i in range(16):
1026        data.append(int(arr[i]))
1027    return "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a=data)
1028
1029@lldb_command('showallkmods')
1030def ShowAllKexts(cmd_args=None):
1031    """Display a summary listing of all loaded kexts (alias: showallkmods)
1032    """
1033    kmod_val = kern.globals.kmod
1034    print "{: <36s} ".format("UUID") + GetKextSummary.header
1035    kextuuidinfo = GetKextLoadInformation()
1036    for kval in IterateLinkedList(kmod_val, 'next'):
1037        uuid = "........-....-....-....-............"
1038        kaddr = unsigned(kval.address)
1039        for l in kextuuidinfo :
1040            if kaddr == int(l[1],16):
1041                uuid = l[0]
1042                break
1043        print uuid + " " + GetKextSummary(kval)
1044
1045def GetKextLoadInformation(addr=0):
1046    """ Extract the kext uuid and load address information from the kernel data structure.
1047        params:
1048            addr - int - optional integer that is the address to search for.
1049        returns:
1050            [] - array with each entry of format ( 'UUID', 'Hex Load Address')
1051    """
1052    # because of <rdar://problem/12683084>, we can't find summaries directly
1053    #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
1054    baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10
1055    summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *')
1056    total_summaries = int(kern.globals.gLoadedKextSummaries.numSummaries)
1057    kext_version = int(kern.globals.gLoadedKextSummaries.version)
1058    entry_size = 64 + 16 + 8 + 8 + 8 + 4 + 4
1059    if kext_version >= 2 :
1060        entry_size = int(kern.globals.gLoadedKextSummaries.entry_size)
1061    retval = []
1062    for i in range(total_summaries):
1063        tmpaddress = unsigned(summaries_begin) + (i * entry_size)
1064        current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *')
1065        if addr != 0 :
1066            if addr == unsigned(current_kext.address):
1067                retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) ))
1068        else:
1069            retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) ))
1070
1071    return retval
1072
1073lldb_alias('showallkexts', 'showallkmods')
1074
1075def GetOSKextVersion(version_num):
1076    """ returns a string of format 1.2.3x from the version_num
1077        params: version_num - int
1078        return: str
1079    """
1080    if version_num == -1 :
1081        return "invalid"
1082    (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000)
1083    version = version_num
1084
1085    vers_major = version / MAJ_MULT
1086    version = version - (vers_major * MAJ_MULT)
1087
1088    vers_minor = version / MIN_MULT
1089    version = version - (vers_minor * MIN_MULT)
1090
1091    vers_revision = version / REV_MULT
1092    version = version - (vers_revision * REV_MULT)
1093
1094    vers_stage = version / STAGE_MULT
1095    version = version - (vers_stage * STAGE_MULT)
1096
1097    vers_stage_level = version
1098
1099    out_str = "%d.%d" % (vers_major, vers_minor)
1100    if vers_revision > 0: out_str += ".%d" % vers_revision
1101    if vers_stage == 1 : out_str += "d%d" % vers_stage_level
1102    if vers_stage == 3 : out_str += "a%d" % vers_stage_level
1103    if vers_stage == 5 : out_str += "b%d" % vers_stage_level
1104    if vers_stage == 6 : out_str += "fc%d" % vers_stage_level
1105
1106    return out_str
1107
1108@lldb_command('showallknownkmods')
1109def ShowAllKnownKexts(cmd_args=None):
1110    """ Display a summary listing of all kexts known in the system.
1111        This is particularly useful to find if some kext was unloaded before this crash'ed state.
1112    """
1113    kext_count = int(kern.globals.sKextsByID.count)
1114    index = 0
1115    kext_dictionary = kern.globals.sKextsByID.dictionary
1116    print "%d kexts in sKextsByID:" % kext_count
1117    print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
1118    format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
1119
1120    while index < kext_count:
1121        kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index)
1122        kext_name = str(kext_dict.key.string)
1123        osk = Cast(kext_dict.value, 'OSKext *')
1124        if int(osk.flags.loaded) :
1125            load_addr = "{0: <#020x}".format(osk.kmod_info)
1126            id = "{0: >5d}".format(osk.loadTag)
1127        else:
1128            load_addr = "------"
1129            id = "--"
1130        version_num = unsigned(osk.version)
1131        version = GetOSKextVersion(version_num)
1132        print format_string.format(osk, load_addr, id, version, kext_name)
1133        index += 1
1134
1135    return
1136
1137@lldb_command('showkmodaddr')
1138def ShowKmodAddr(cmd_args=[]):
1139    """ Given an address, print the offset and name for the kmod containing it
1140        Syntax: (lldb) showkmodaddr <addr>
1141    """
1142    if len(cmd_args) < 1:
1143        raise ArgumentError("Insufficient arguments")
1144
1145    addr = ArgumentStringToInt(cmd_args[0])
1146    kmod_val = kern.globals.kmod
1147    for kval in IterateLinkedList(kmod_val, 'next'):
1148        if addr >= unsigned(kval.address) and addr <= (unsigned(kval.address) + unsigned(kval.size)):
1149            print GetKextSummary.header
1150            print GetKextSummary(kval) + " offset = {0: #0x}".format((addr - unsigned(kval.address)))
1151            return True
1152    return False
1153
1154@lldb_command('addkext','F:N:')
1155def AddKextSyms(cmd_args=[], cmd_options={}):
1156    """ Add kext symbols into lldb.
1157        This command finds symbols for a uuid and load the required executable
1158        Usage:
1159            addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
1160            addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
1161            addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
1162            addkext all    : Will load all the kext symbols - SLOW
1163    """
1164
1165
1166    if "-F" in cmd_options:
1167        exec_path = cmd_options["-F"]
1168        exec_full_path = ResolveFSPath(exec_path)
1169        if not os.path.exists(exec_full_path):
1170            raise ArgumentError("Unable to resolve {:s}".format(exec_path))
1171
1172        if not os.path.isfile(exec_full_path):
1173            raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
1174\nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
1175\nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path))
1176        if not os.access(exec_full_path, os.X_OK):
1177            raise ArgumentError("Path is {:s} not an executable file".format(exec_path))
1178
1179        slide_value = None
1180        if cmd_args:
1181            slide_value = cmd_args[0]
1182            debuglog("loading slide value from user input %s" % cmd_args[0])
1183
1184        filespec = lldb.SBFileSpec(exec_full_path, False)
1185        print "target modules add %s" % exec_full_path
1186        print lldb_run_command("target modules add %s" % exec_full_path)
1187        loaded_module = LazyTarget.GetTarget().FindModule(filespec)
1188        if loaded_module.IsValid():
1189            uuid_str = loaded_module.GetUUIDString()
1190            debuglog("added module %s with uuid %s" % (exec_full_path, uuid_str))
1191            if slide_value is None:
1192                all_kexts_info = GetKextLoadInformation()
1193                for k in all_kexts_info:
1194                    debuglog(k[0])
1195                    if k[0].lower() == uuid_str.lower():
1196                        slide_value = k[1]
1197                        debuglog("found the slide %s for uuid %s" % (k[1], k[0]))
1198
1199        if slide_value is None:
1200            raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path)
1201        load_cmd = "target modules load --file %s --slide %s" % (exec_full_path, str(slide_value))
1202        print load_cmd
1203        print lldb_run_command(load_cmd)
1204        kern.symbolicator = None
1205        return True
1206
1207    all_kexts_info = GetKextLoadInformation()
1208
1209    if "-N" in cmd_options:
1210        kext_name = cmd_options["-N"]
1211        kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True)
1212        if len(kext_name_matches) != 1:
1213            print "Ambiguous match for name: {:s}".format(kext_name)
1214            if len(kext_name_matches) > 0:
1215                print  "Options are:\n\t" + "\n\t".join(kext_name_matches)
1216            return
1217        debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches[0], kext_name))
1218        for x in all_kexts_info:
1219            if kext_name_matches[0] == x[2]:
1220                cur_uuid = x[0].lower()
1221                print "Fetching dSYM for {:s}".format(cur_uuid)
1222                info = dsymForUUID(cur_uuid)
1223                if info and 'DBGSymbolRichExecutable' in info:
1224                    print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
1225                    addDSYM(cur_uuid, info)
1226                    loadDSYM(cur_uuid, int(x[1],16))
1227                else:
1228                    print "Failed to get symbol info for {:s}".format(cur_uuid)
1229                break
1230        kern.symbolicator = None
1231        return
1232
1233    if len(cmd_args) < 1:
1234        raise ArgumentError("No arguments specified.")
1235
1236    uuid = cmd_args[0].lower()
1237
1238    load_all_kexts = False
1239    if uuid == "all":
1240        load_all_kexts = True
1241
1242    if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
1243        raise ArgumentError("Unknown argument {:s}".format(uuid))
1244
1245    for k_info in all_kexts_info:
1246        cur_uuid = k_info[0].lower()
1247        if load_all_kexts or (uuid == cur_uuid):
1248            print "Fetching dSYM for %s" % cur_uuid
1249            info = dsymForUUID(cur_uuid)
1250            if info and 'DBGSymbolRichExecutable' in info:
1251                print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
1252                addDSYM(cur_uuid, info)
1253                loadDSYM(cur_uuid, int(k_info[1],16))
1254            else:
1255                print "Failed to get symbol info for %s" % cur_uuid
1256        #end of for loop
1257    kern.symbolicator = None
1258    return True
1259
1260
1261
1262lldb_alias('showkmod', 'showkmodaddr')
1263lldb_alias('showkext', 'showkmodaddr')
1264lldb_alias('showkextaddr', 'showkmodaddr')
1265
1266@lldb_type_summary(['mount *'])
1267@header("{0: <20s} {1: <20s} {2: <20s} {3: <12s} {4: <12s} {5: <12s} {6: >6s} {7: <30s} {8: <35s}".format('volume(mp)', 'mnt_data', 'mnt_devvp', 'flag', 'kern_flag', 'lflag', 'type', 'mnton', 'mntfrom'))
1268def GetMountSummary(mount):
1269    """ Display a summary of mount on the system
1270    """
1271    out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
1272                  "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
1273                  "{vfs.f_mntonname: <30s} {vfs.f_mntfromname: <35s}").format(mnt=mount, vfs=mount.mnt_vfsstat)
1274    return out_string
1275
1276@lldb_command('showallmounts')
1277def ShowAllMounts(cmd_args=None):
1278    """ Print all mount points
1279    """
1280    mntlist = kern.globals.mountlist
1281    print GetMountSummary.header
1282    for mnt in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
1283        print GetMountSummary(mnt)
1284    return
1285
1286lldb_alias('ShowAllVols', 'showallmounts')
1287
1288@lldb_command('systemlog')
1289def ShowSystemLog(cmd_args=None):
1290    """ Display the kernel's printf ring buffer """
1291    msgbufp = kern.globals.msgbufp
1292    msg_size = int(msgbufp.msg_size)
1293    msg_bufx = int(msgbufp.msg_bufx)
1294    msg_bufr = int(msgbufp.msg_bufr)
1295    msg_bufc = msgbufp.msg_bufc
1296    msg_bufc_data = msg_bufc.GetSBValue().GetPointeeData(0, msg_size)
1297
1298    # the buffer is circular; start at the write pointer to end,
1299    # then from beginning to write pointer
1300    line = ''
1301    err = lldb.SBError()
1302    for i in range(msg_bufx, msg_size) + range(0, msg_bufx) :
1303        err.Clear()
1304        cbyte = msg_bufc_data.GetUnsignedInt8(err, i)
1305        if not err.Success() :
1306            raise ValueError("Failed to read character at offset " + i + ": " + err.GetCString())
1307        c = chr(cbyte)
1308        if c == '\0' :
1309            continue
1310        elif c == '\n' :
1311            print line
1312            line = ''
1313        else :
1314            line += c
1315
1316    if len(line) > 0 :
1317        print line
1318
1319    return
1320
1321@static_var('output','')
1322def _GetVnodePathName(vnode, vnodename):
1323    """ Internal function to get vnode path string from vnode structure.
1324        params:
1325            vnode - core.value
1326            vnodename - str
1327        returns Nothing. The output will be stored in the static variable.
1328    """
1329    if not vnode:
1330        return
1331    if int(vnode.v_flag) & 0x1 and int(hex(vnode.v_mount), 16) !=0:
1332        if int(vnode.v_mount.mnt_vnodecovered):
1333            _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) )
1334    else:
1335        _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name))
1336        _GetVnodePathName.output += "/%s" % vnodename
1337
1338def GetVnodePath(vnode):
1339    """ Get string representation of the vnode
1340        params: vnodeval - value representing vnode * in the kernel
1341        return: str - of format /path/to/something
1342    """
1343    out_str = ''
1344    if vnode:
1345            if (int(vnode.v_flag) & 0x000001) and int(hex(vnode.v_mount), 16) != 0 and (int(vnode.v_mount.mnt_flag) & 0x00004000) :
1346                out_str += "/"
1347            else:
1348                _GetVnodePathName.output = ''
1349                if abs(vnode.v_name) != 0:
1350                    _GetVnodePathName(vnode, str(vnode.v_name))
1351                    out_str += _GetVnodePathName.output
1352                else:
1353                    out_str += 'v_name = NULL'
1354                _GetVnodePathName.output = ''
1355    return out_str
1356
1357
1358@lldb_command('showvnodepath')
1359def ShowVnodePath(cmd_args=None):
1360    """ Prints the path for a vnode
1361        usage: showvnodepath <vnode>
1362    """
1363    if cmd_args != None and len(cmd_args) > 0 :
1364        vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1365        if vnode_val:
1366            print GetVnodePath(vnode_val)
1367    return
1368
1369# Macro: showvnodedev
1370def GetVnodeDevInfo(vnode):
1371    """ Internal function to get information from the device type vnodes
1372        params: vnode - value representing struct vnode *
1373        return: str - formatted output information for block and char vnode types passed as param
1374    """
1375    vnodedev_output = ""
1376    vblk_type = GetEnumValue('vtype::VBLK')
1377    vchr_type = GetEnumValue('vtype::VCHR')
1378    if (vnode.v_type == vblk_type) or (vnode.v_type == vchr_type):
1379        devnode = Cast(vnode.v_data, 'devnode_t *')
1380        devnode_dev = devnode.dn_typeinfo.dev
1381        devnode_major = (devnode_dev >> 24) & 0xff
1382        devnode_minor = devnode_dev & 0x00ffffff
1383
1384        # boilerplate device information for a vnode
1385        vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode)
1386        vnodedev_output += "\n\t type:\t\t"
1387        if (vnode.v_type == vblk_type):
1388            vnodedev_output += "VBLK"
1389        if (vnode.v_type == vchr_type):
1390            vnodedev_output += "VCHR"
1391        vnodedev_output += "\n\t name:\t\t{:<s}".format(vnode.v_name)
1392        vnodedev_output += "\n\t major, minor:\t{:d},{:d}".format(devnode_major, devnode_minor)
1393        vnodedev_output += "\n\t mode\t\t0{:o}".format(unsigned(devnode.dn_mode))
1394        vnodedev_output += "\n\t owner (u,g):\t{:d} {:d}".format(devnode.dn_uid, devnode.dn_gid)
1395
1396        # decode device specific data
1397        vnodedev_output += "\nDevice Specific Information:\t"
1398        if (vnode.v_type == vblk_type):
1399            vnodedev_output += "Sorry, I do not know how to decode block devices yet!"
1400            vnodedev_output += "\nMaybe you can write me!"
1401
1402        if (vnode.v_type == vchr_type):
1403            # Device information; this is scanty
1404            # range check
1405            if (devnode_major > 42) or (devnode_major < 0):
1406                vnodedev_output +=  "Invalid major #\n"
1407            # static assignments in conf
1408            elif (devnode_major == 0):
1409                vnodedev_output += "Console mux device\n"
1410            elif (devnode_major == 2):
1411                vnodedev_output += "Current tty alias\n"
1412            elif (devnode_major == 3):
1413                vnodedev_output += "NULL device\n"
1414            elif (devnode_major == 4):
1415                vnodedev_output += "Old pty slave\n"
1416            elif (devnode_major == 5):
1417                vnodedev_output += "Old pty master\n"
1418            elif (devnode_major == 6):
1419                vnodedev_output += "Kernel log\n"
1420            elif (devnode_major == 12):
1421                vnodedev_output += "Memory devices\n"
1422            # Statically linked dynamic assignments
1423            elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptmx_open')):
1424                vnodedev_output += "Cloning pty master not done\n"
1425                #GetVnodeDevCpty(devnode_major, devnode_minor)
1426            elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptsd_open')):
1427                vnodedev_output += "Cloning pty slave not done\n"
1428                #GetVnodeDevCpty(devnode_major, devnode_minor)
1429            else:
1430                vnodedev_output += "RESERVED SLOT\n"
1431    else:
1432        vnodedev_output += "{:#x} is not a device".format(vnode)
1433    return vnodedev_output
1434
1435@lldb_command('showvnodedev')
1436def ShowVnodeDev(cmd_args=None):
1437    """  Routine to display details of all vnodes of block and character device types
1438         Usage: showvnodedev <address of vnode>
1439    """
1440    if not cmd_args:
1441        print "No arguments passed"
1442        print ShowVnodeDev.__doc__
1443        return False
1444    vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1445    if not vnode_val:
1446        print "unknown arguments:", str(cmd_args)
1447        return False
1448    print GetVnodeDevInfo(vnode_val)
1449
1450# EndMacro: showvnodedev
1451
1452# Macro: showvnodelocks
1453def GetVnodeLock(lockf):
1454    """ Internal function to get information from the given advisory lock
1455        params: lockf - value representing v_lockf member in struct vnode *
1456        return: str - formatted output information for the advisory lock
1457    """
1458    vnode_lock_output = ''
1459    lockf_flags = lockf.lf_flags
1460    lockf_type = lockf.lf_type
1461    if lockf_flags & 0x20:
1462        vnode_lock_output += ("{: <8s}").format('flock')
1463    if lockf_flags & 0x40:
1464        vnode_lock_output += ("{: <8s}").format('posix')
1465    if lockf_flags & 0x80:
1466        vnode_lock_output += ("{: <8s}").format('prov')
1467    if lockf_flags & 0x10:
1468        vnode_lock_output += ("{: <4s}").format('W')
1469    else:
1470        vnode_lock_output += ("{: <4s}").format('.')
1471
1472    # POSIX file vs advisory range locks
1473    if lockf_flags & 0x40:
1474        lockf_proc = Cast(lockf.lf_id, 'proc *')
1475        vnode_lock_output += ("PID {: <18d}").format(lockf_proc.p_pid)
1476    else:
1477        vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id))
1478
1479    # lock type
1480    if lockf_type == 1:
1481        vnode_lock_output += ("{: <12s}").format('shared')
1482    else:
1483        if lockf_type == 3:
1484            vnode_lock_output += ("{: <12s}").format('exclusive')
1485        else:
1486            if lockf_type == 2:
1487                vnode_lock_output += ("{: <12s}").format('unlock')
1488            else:
1489                vnode_lock_output += ("{: <12s}").format('unknown')
1490
1491    # start and stop values
1492    vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start)
1493    vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end)
1494    return vnode_lock_output
1495
1496@header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
1497def GetVnodeLocksSummary(vnode):
1498    """ Internal function to get summary of advisory locks for the given vnode
1499        params: vnode - value representing the vnode object
1500        return: str - formatted output information for the summary of advisory locks
1501    """
1502    out_str = ''
1503    if vnode:
1504            lockf_list = vnode.v_lockf
1505            for lockf_itr in IterateLinkedList(lockf_list, 'lf_next'):
1506                out_str += ("{: <4s}").format('H')
1507                out_str += GetVnodeLock(lockf_itr)
1508                lockf_blocker = lockf_itr.lf_blkhd.tqh_first
1509                while lockf_blocker:
1510                    out_str += ("{: <4s}").format('>')
1511                    out_str += GetVnodeLock(lockf_blocker)
1512                    lockf_blocker = lockf_blocker.lf_block.tqe_next
1513    return out_str
1514
1515@lldb_command('showvnodelocks')
1516def ShowVnodeLocks(cmd_args=None):
1517    """  Routine to display list of advisory record locks for the given vnode address
1518         Usage: showvnodelocks <address of vnode>
1519    """
1520    if not cmd_args:
1521        print "No arguments passed"
1522        print ShowVnodeLocks.__doc__
1523        return False
1524    vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1525    if not vnode_val:
1526        print "unknown arguments:", str(cmd_args)
1527        return False
1528    print GetVnodeLocksSummary.header
1529    print GetVnodeLocksSummary(vnode_val)
1530
1531# EndMacro: showvnodelocks
1532
1533# Macro: showproclocks
1534
1535@lldb_command('showproclocks')
1536def ShowProcLocks(cmd_args=None):
1537    """  Routine to display list of advisory record locks for the given process
1538         Usage: showproclocks <address of proc>
1539    """
1540    if not cmd_args:
1541        print "No arguments passed"
1542        print ShowProcLocks.__doc__
1543        return False
1544    proc = kern.GetValueFromAddress(cmd_args[0], 'proc *')
1545    if not proc:
1546        print "unknown arguments:", str(cmd_args)
1547        return False
1548    out_str = ''
1549    proc_filedesc = proc.p_fd
1550    fd_lastfile = proc_filedesc.fd_lastfile
1551    fd_ofiles = proc_filedesc.fd_ofiles
1552    count = 0
1553    seen = 0
1554    while count <= fd_lastfile:
1555        if fd_ofiles[count]:
1556            fglob = fd_ofiles[count].f_fglob
1557            fo_type = fglob.fg_ops.fo_type
1558            if fo_type == 1:
1559                fg_data = fglob.fg_data
1560                fg_vnode = Cast(fg_data, 'vnode *')
1561                name = fg_vnode.v_name
1562                lockf_itr = fg_vnode.v_lockf
1563                if lockf_itr:
1564                    if not seen:
1565                        print GetVnodeLocksSummary.header
1566                    seen = seen + 1
1567                    out_str += ("\n( fd {:d}, name ").format(count)
1568                    if not name:
1569                        out_str += "(null) )\n"
1570                    else:
1571                        out_str += "{:s} )\n".format(name)
1572                    print out_str
1573                    print GetVnodeLocksSummary(fg_vnode)
1574        count = count + 1
1575    print "\n{0: d} total locks for {1: #018x}".format(seen, proc)
1576
1577# EndMacro: showproclocks
1578
1579@lldb_type_summary(['vnode_t', 'vnode *'])
1580@header("{0: <20s} {1: >8s} {2: >8s} {3: <20s} {4: <6s} {5: <20s} {6: <6s} {7: <35s}".format('vnode', 'usecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'name'))
1581def GetVnodeSummary(vnode):
1582    """ Get a summary of important information out of vnode
1583    """
1584    out_str = ''
1585    format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <35s}"
1586    usecount = int(vnode.v_usecount)
1587    iocount = int(vnode.v_iocount)
1588    v_data_ptr = int(hex(vnode.v_data), 16)
1589    vtype = int(vnode.v_type)
1590    vtype_str = "%d" % vtype
1591    vnode_types = ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX']  # see vnode.h for enum type definition
1592    if vtype >= 0 and vtype < len(vnode_types):
1593        vtype_str = vnode_types[vtype]
1594    parent_ptr = int(hex(vnode.v_parent), 16)
1595    name_ptr = int(hex(vnode.v_name), 16)
1596    name =""
1597    if name_ptr != 0:
1598        name = str(vnode.v_name)
1599    elif int(vnode.v_tag) == 16 :
1600        cnode = Cast(vnode.v_data, 'cnode *')
1601        name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *'))
1602    mapped = '-'
1603    if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0):
1604        # Check to see if vnode is mapped/unmapped
1605        if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0:
1606            mapped = '1'
1607        else:
1608            mapped = '0'
1609    out_str += format_string.format(vnode, usecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, name)
1610    return out_str
1611
1612@lldb_command('showallvnodes')
1613def ShowAllVnodes(cmd_args=None):
1614    """ Display info about all vnodes
1615    """
1616    mntlist = kern.globals.mountlist
1617    print GetVnodeSummary.header
1618    for mntval in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
1619        for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
1620            print GetVnodeSummary(vnodeval)
1621    return
1622
1623@lldb_command('showvnode')
1624def ShowVnode(cmd_args=None):
1625    """ Display info about one vnode
1626        usage: showvnode <vnode>
1627    """
1628    if cmd_args == None or len(cmd_args) < 1:
1629        print  "Please provide valid vnode argument. Type help showvnode for help."
1630        return
1631    vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *')
1632    print GetVnodeSummary.header
1633    print GetVnodeSummary(vnodeval)
1634
1635@lldb_command('showvolvnodes')
1636def ShowVolVnodes(cmd_args=None):
1637    """ Display info about all vnodes of a given mount_t
1638    """
1639    if cmd_args == None or len(cmd_args) < 1:
1640        print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
1641        return
1642    mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
1643    print GetVnodeSummary.header
1644    for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
1645        print GetVnodeSummary(vnodeval)
1646    return
1647
1648@lldb_command('showvolbusyvnodes')
1649def ShowVolBusyVnodes(cmd_args=None):
1650    """ Display info about busy (iocount!=0) vnodes of a given mount_t
1651    """
1652    if cmd_args == None or len(cmd_args) < 1:
1653        print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
1654        return
1655    mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
1656    print GetVnodeSummary.header
1657    for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
1658        if int(vnodeval.v_iocount) != 0:
1659            print GetVnodeSummary(vnodeval)
1660
1661@lldb_command('showallbusyvnodes')
1662def ShowAllBusyVnodes(cmd_args=None):
1663    """ Display info about all busy (iocount!=0) vnodes
1664    """
1665    mntlistval = kern.globals.mountlist
1666    for mntval in IterateTAILQ_HEAD(mntlistval, 'mnt_list'):
1667        ShowVolBusyVnodes([hex(mntval)])
1668
1669@lldb_command('print_vnode')
1670def PrintVnode(cmd_args=None):
1671    """ Prints out the fields of a vnode struct
1672        Usage: print_vnode <vnode>
1673    """
1674    if not cmd_args:
1675        print  "Please provide valid vnode argument. Type help print_vnode for help."
1676        return
1677    ShowVnode(cmd_args)
1678
1679@lldb_command('showworkqvnodes')
1680def ShowWorkqVnodes(cmd_args=None):
1681    """ Print the vnode worker list
1682        Usage: showworkqvnodes <struct mount *>
1683    """
1684    if not cmd_args:
1685        print "Please provide valid mount argument. Type help showworkqvnodes for help."
1686        return
1687
1688    mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
1689    vp = Cast(mp.mnt_workerqueue.tqh_first, 'vnode *')
1690    print GetVnodeSummary.header
1691    while int(vp) != 0:
1692        print GetVnodeSummary(vp)
1693        vp = vp.v_mntvnodes.tqe_next
1694
1695@lldb_command('shownewvnodes')
1696def ShowNewVnodes(cmd_args=None):
1697    """ Print the new vnode list
1698        Usage: shownewvnodes <struct mount *>
1699    """
1700    if not cmd_args:
1701        print "Please provide valid mount argument. Type help shownewvnodes for help."
1702        return
1703    mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
1704    vp = Cast(mp.mnt_newvnodes.tqh_first, 'vnode *')
1705    print GetVnodeSummary.header
1706    while int(vp) != 0:
1707        print GetVnodeSummary(vp)
1708        vp = vp.v_mntvnodes.tqe_next
1709
1710
1711@lldb_command('showprocvnodes')
1712def ShowProcVnodes(cmd_args=None):
1713    """ Routine to print out all the open fds which are vnodes in a process
1714        Usage: showprocvnodes <proc *>
1715    """
1716    if not cmd_args:
1717        print "Please provide valid proc argument. Type help showprocvnodes for help."
1718        return
1719    procptr = kern.GetValueFromAddress(cmd_args[0], 'proc *')
1720    fdptr = Cast(procptr.p_fd, 'filedesc *')
1721    if int(fdptr.fd_cdir) != 0:
1722        print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_cdir))
1723    if int(fdptr.fd_rdir) != 0:
1724        print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir))
1725    count = 0
1726    print '\n' + '{0: <5s} {1: <7s}'.format('fd', 'flags') + GetVnodeSummary.header
1727    # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
1728    fpptr = Cast(fdptr.fd_ofiles, 'fileproc *')
1729    while count < fdptr.fd_nfiles:
1730        fpp = dereference(fpptr)
1731        fproc = Cast(fpp, 'fileproc *')
1732        if int(fproc) != 0:
1733            fglob = dereference(fproc).f_fglob
1734            flags = ""
1735            if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1):
1736                if (fdptr.fd_ofileflags[count] & 1):    flags += 'E'
1737                if (fdptr.fd_ofileflags[count] & 2):    flags += 'F'
1738                if (fdptr.fd_ofileflags[count] & 4):    flags += 'R'
1739                if (fdptr.fd_ofileflags[count] & 8):    flags += 'C'
1740                print '{0: <5d} {1: <7s}'.format(count, flags) + GetVnodeSummary(Cast(fglob.fg_data, 'vnode *'))
1741        count += 1
1742        fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'fileproc *')
1743
1744@lldb_command('showallprocvnodes')
1745def ShowAllProcVnodes(cmd_args=None):
1746    """ Routine to print out all the open fds which are vnodes
1747    """
1748
1749    procptr = Cast(kern.globals.allproc.lh_first, 'proc *')
1750    while procptr and int(procptr) != 0:
1751        print '{:<s}'.format("=" * 106)
1752        print GetProcInfo(procptr)
1753        ShowProcVnodes([int(procptr)])
1754        procptr = procptr.p_list.le_next
1755
1756@xnudebug_test('test_vnode')
1757def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
1758    """ Test the functionality of vnode related commands
1759        returns
1760         - False on failure
1761         - True on success
1762    """
1763    if not isConnected:
1764        print "Target is not connected. Cannot test memstats"
1765        return False
1766    res = lldb.SBCommandReturnObject()
1767    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showallvnodes", res)
1768    result = res.GetOutput()
1769    if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5:
1770        return True
1771    else:
1772        return False
1773
1774# Macro: showallmtx
1775@lldb_type_summary(['_lck_grp_ *'])
1776def GetMutexEntry(mtxg):
1777    """ Summarize a mutex group entry  with important information.
1778        params:
1779        mtxg: value - obj representing a mutex group in kernel
1780        returns:
1781        out_string - summary of the mutex group
1782        """
1783    out_string = ""
1784
1785    if kern.ptrsize == 8:
1786        format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1787    else:
1788        format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1789
1790    if mtxg.lck_grp_mtxcnt:
1791        out_string += format_string.format(mtxg, mtxg.lck_grp_mtxcnt,mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_util_cnt,
1792                                           mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_miss_cnt,
1793                                           mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_wait_cnt, mtxg.lck_grp_name)
1794    return out_string
1795
1796@lldb_command('showallmtx')
1797def ShowAllMtx(cmd_args=None):
1798    """ Routine to print a summary listing of all mutexes
1799    """
1800
1801    if kern.ptrsize == 8:
1802        hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
1803    else:
1804        hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
1805
1806    print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
1807
1808    mtxgrp_queue_head = kern.globals.lck_grp_queue
1809    mtxgrp_ptr_type = GetType('_lck_grp_ *')
1810
1811    for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"):
1812       print GetMutexEntry(mtxgrp_ptr)
1813    return
1814# EndMacro: showallmtx
1815
1816# Macro: showallrwlck
1817@lldb_type_summary(['_lck_grp_ *'])
1818def GetRWLEntry(rwlg):
1819    """ Summarize a reader writer lock group with important information.
1820        params:
1821        rwlg: value - obj representing a reader writer lock group in kernel
1822        returns:
1823        out_string - summary of the reader writer lock group
1824    """
1825    out_string = ""
1826
1827    if kern.ptrsize == 8:
1828        format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1829    else:
1830        format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1831
1832    if rwlg.lck_grp_rwcnt:
1833        out_string += format_string.format(rwlg, rwlg.lck_grp_rwcnt,rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_util_cnt,
1834                                           rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_miss_cnt,
1835                                           rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name)
1836    return out_string
1837
1838#Macro: showlock
1839@lldb_type_summary(['lck_mtx_t *'])
1840@header("===== Mutex Lock Summary =====")
1841def GetMutexLockSummary(mtx):
1842    """ Summarize mutex lock with important information.
1843        params:
1844        mtx: value - obj representing a mutex lock in kernel
1845        returns:
1846        out_str - summary of the mutex lock
1847    """
1848    if not mtx:
1849        return "Invalid lock value: 0x0"
1850
1851    if kern.arch == "x86_64":
1852        out_str = "Lock Type\t\t: MUTEX\n"
1853        mtxd = mtx.lck_mtx_sw.lck_mtxd
1854        out_str += "Owner Thread\t\t: {:#x}\n".format(mtxd.lck_mtxd_owner)
1855        cmd_str = "p/d ((lck_mtx_t*){:#x})->lck_mtx_sw.lck_mtxd.".format(mtx)
1856        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_waiters")
1857        out_str += "Number of Waiters\t: {:s}\n".format(cmd_out.split()[-1])
1858        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_ilocked")
1859        out_str += "ILocked\t\t\t: {:s}\n".format(cmd_out.split()[-1])
1860        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_mlocked")
1861        out_str += "MLocked\t\t\t: {:s}\n".format(cmd_out.split()[-1])
1862        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_promoted")
1863        out_str += "Promoted\t\t: {:s}\n".format(cmd_out.split()[-1])
1864        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_spin")
1865        out_str += "Spin\t\t\t: {:s}\n".format(cmd_out.split()[-1])
1866        return out_str
1867
1868    out_str = "Lock Type\t\t: MUTEX\n"
1869    out_str += "Owner Thread\t\t: {:#x}\n".format(mtx.lck_mtx_hdr.lck_mtxd_data & ~0x3)
1870    out_str += "Number of Waiters\t: {:d}\n".format(mtx.lck_mtx_sw.lck_mtxd.lck_mtxd_waiters)
1871    out_str += "Flags\t\t\t: "
1872    if mtx.lck_mtx_hdr.lck_mtxd_data & 0x1:
1873        out_str += "[Interlock Locked] "
1874    if mtx.lck_mtx_hdr.lck_mtxd_data & 0x2:
1875        out_str += "[Wait Flag]"
1876    if (mtx.lck_mtx_hdr.lck_mtxd_data & 0x3) == 0:
1877        out_str += "None"
1878    return out_str
1879
1880@lldb_type_summary(['lck_spin_t *'])
1881@header("===== SpinLock Summary =====")
1882def GetSpinLockSummary(spinlock):
1883    """ Summarize spinlock with important information.
1884        params:
1885        spinlock: value - obj representing a spinlock in kernel
1886        returns:
1887        out_str - summary of the spinlock
1888    """
1889    if not spinlock:
1890        return "Invalid lock value: 0x0"
1891
1892    out_str = "Lock Type\t\t: SPINLOCK\n"
1893    if kern.arch == "x86_64":
1894        out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
1895        return out_str
1896
1897    out_str += "Owner Thread\t\t: {:#x}\n".format(spinlock.lck_spin_data & ~0x3)
1898    out_str += "Flags\t\t\t: "
1899    if spinlock.lck_spin_data & 0x1:
1900        out_str += "[Interlock Locked] "
1901    if spinlock.lck_spin_data & 0x2:
1902        out_str += "[Wait Flag]"
1903    if (spinlock.lck_spin_data & 0x3) == 0:
1904        out_str += "None"
1905    return out_str
1906
1907@lldb_command('showlock', 'MS')
1908def ShowLock(cmd_args=None, cmd_options={}):
1909    """ Show info about a lock - its state and owner thread details
1910        Usage: showlock <address of a lock>
1911        -M : to consider <addr> as lck_mtx_t
1912        -S : to consider <addr> as lck_spin_t
1913    """
1914    if not cmd_args:
1915        raise ArgumentError("Please specify the address of the lock whose info you want to view.")
1916        return
1917
1918    summary_str = ""
1919    lock = kern.GetValueFromAddress(cmd_args[0], 'uintptr_t*')
1920
1921    if kern.arch == "x86_64" and lock:
1922        if "-M" in cmd_options:
1923            lock_mtx = Cast(lock, 'lck_mtx_t *')
1924            summary_str = GetMutexLockSummary(lock_mtx)
1925        elif "-S" in cmd_options:
1926            lock_spin = Cast(lock, 'lck_spin_t *')
1927            summary_str = GetSpinLockSummary(lock_spin)
1928        else:
1929            summary_str = "Please specify supported lock option(-M/-S)"
1930
1931        print summary_str
1932        return
1933
1934    if lock:
1935        lock_mtx = Cast(lock, 'lck_mtx_t*')
1936        if lock_mtx.lck_mtx_type == 0x22:
1937            summary_str = GetMutexLockSummary(lock_mtx)
1938
1939        lock_spin = Cast(lock, 'lck_spin_t*')
1940        if lock_spin.lck_spin_type == 0x11:
1941            summary_str = GetSpinLockSummary(lock_spin)
1942
1943    if summary_str == "":
1944        summary_str = "Lock Type\t\t: INVALID LOCK"
1945    print summary_str
1946
1947#EndMacro: showlock
1948
1949@lldb_command('showallrwlck')
1950def ShowAllRWLck(cmd_args=None):
1951    """ Routine to print a summary listing of all read/writer locks
1952    """
1953    if kern.ptrsize == 8:
1954        hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
1955    else:
1956        hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
1957
1958    print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
1959
1960    rwlgrp_queue_head = kern.globals.lck_grp_queue
1961    rwlgrp_ptr_type = GetType('_lck_grp_ *')
1962    for rwlgrp_ptr in IterateQueue(rwlgrp_queue_head, rwlgrp_ptr_type, "lck_grp_link"):
1963       print GetRWLEntry(rwlgrp_ptr)
1964    return
1965# EndMacro: showallrwlck
1966
1967#Macro: showbootermemorymap
1968@lldb_command('showbootermemorymap')
1969def ShowBooterMemoryMap(cmd_args=None):
1970    """ Prints out the phys memory map from kernelBootArgs
1971        Supported only on x86_64
1972    """
1973    if kern.arch == 'x86_64':
1974        voffset = unsigned(0xFFFFFF8000000000)
1975    else:
1976        print "showbootermemorymap not supported on this architecture"
1977        return
1978
1979    out_string = ""
1980
1981    # Memory type map
1982    memtype_dict = {
1983            0:  'Reserved',
1984            1:  'LoaderCode',
1985            2:  'LoaderData',
1986            3:  'BS_code',
1987            4:  'BS_data',
1988            5:  'RT_code',
1989            6:  'RT_data',
1990            7:  'Convention',
1991            8:  'Unusable',
1992            9:  'ACPI_recl',
1993            10: 'ACPI_NVS',
1994            11: 'MemMapIO',
1995            12: 'MemPortIO',
1996            13: 'PAL_code'
1997        }
1998
1999    boot_args = kern.globals.kernelBootArgs
2000    msize = boot_args.MemoryMapDescriptorSize
2001    mcount = (boot_args.MemoryMapSize) / unsigned(msize)
2002
2003    out_string += "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
2004
2005    i = 0
2006    while i < mcount:
2007        mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + voffset + unsigned(i*msize), 'EfiMemoryRange *')
2008        mtype = unsigned(mptr.Type)
2009        if mtype in memtype_dict:
2010            out_string += "{0: <12s}".format(memtype_dict[mtype])
2011        else:
2012            out_string += "{0: <12s}".format("UNKNOWN")
2013
2014        if mptr.VirtualStart == 0:
2015            out_string += "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, ' '*19, mptr.Attribute)
2016        else:
2017            out_string += "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, mptr.VirtualStart, mptr.Attribute)
2018        i = i + 1
2019
2020    print out_string
2021#EndMacro: showbootermemorymap
2022
2023