xref: /xnu-8019.80.24/tools/lldbmacros/memory.py (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
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
7import shlex
8from utils import *
9import xnudefines
10from process import *
11import macho
12import json
13from ctypes import c_int64
14
15def vm_unpack_pointer(packed, params, type_str = 'void *'):
16    """ Unpack a pointer packed with VM_PACK_POINTER()
17        params:
18            packed       - value : The packed pointer value
19            params       - value : The packing parameters of type vm_packing_params_t
20            type_str     - str   : The type to cast the unpacked pointer into
21        returns:
22            The unpacked pointer
23    """
24    if params.vmpp_base_relative:
25        addr = unsigned(packed) << int(params.vmpp_shift)
26        if addr: addr += int(params.vmpp_base)
27    else:
28        bits  = int(params.vmpp_bits)
29        shift = int(params.vmpp_shift)
30        addr  = c_int64(unsigned(packed) << (64 - bits)).value
31        addr >>= 64 - bits - shift
32    return kern.GetValueFromAddress(addr, type_str)
33
34def GetZPerCPU(root, cpu, element_type = None):
35    """ Iterates over a percpu variable
36        params:
37            root         - value : Value object for per-cpu variable
38            cpu          - int   : the CPU number
39            element_type - str   : Type of element
40        returns:
41            one slot
42    """
43    pagesize = kern.globals.page_size
44    mangle   = 1 << (8 * kern.ptrsize - 1)
45    if element_type is None:
46        element_type = root.GetSBValue().GetType()
47    return kern.GetValueFromAddress((int(root) | mangle) + cpu * pagesize, element_type)
48
49def IterateZPerCPU(root, element_type = None):
50    """ Iterates over a percpu variable
51        params:
52            root         - value : Value object for per-cpu variable
53            element_type - str   : Type of element
54        returns:
55            one slot
56    """
57    for i in range(0, kern.globals.zpercpu_early_count):
58        yield GetZPerCPU(root, i, element_type)
59
60@lldb_command('showzpcpu', "S")
61def ShowZPerCPU(cmd_args=None, cmd_options={}):
62    """ Routine to show per-cpu zone allocated variables
63
64        Usage: showzpcpu [-S] expression [field]
65            -S  : sum the values instead of printing them
66    """
67    if not cmd_args:
68        raise ArgumentError("No arguments passed")
69    pagesize = kern.globals.page_size
70    mangle   = 1 << (8 * kern.ptrsize - 1)
71    sbv = pagesize.GetSBValue()
72    v = sbv.CreateValueFromExpression(None, cmd_args[0])
73    e = value(v)
74    acc = 0
75    for i in range(0, kern.globals.zpercpu_early_count):
76        if len(cmd_args) == 1:
77            t = sbv.CreateValueFromExpression(None, '(%s)%d' % (v.GetTypeName(), (int(e) | mangle) + i * pagesize)).Dereference()
78        else:
79            t = sbv.CreateValueFromExpression(None, '((%s)%d)->%s' % (v.GetTypeName(), (int(e) | mangle) + i * pagesize, cmd_args[1]))
80        if "-S" in cmd_options:
81            acc += value(t)
82        else:
83            print value(t)
84
85    if "-S" in cmd_options:
86        print acc
87
88def ZoneName(zone, zone_security):
89    """ Formats the name for a given zone
90        params:
91            zone             - value : A pointer to a zone
92            zone_security    - value : A pointer to zone security flags
93        returns:
94            the formated name for the zone
95    """
96    names = [ "", "default.", "data.", "kext."]
97    return "{:s}{:s}".format(names[int(zone_security.z_kheap_id)], zone.z_name)
98
99def GetZoneByName(name):
100    """ Internal function to find a zone by name
101    """
102    for i in xrange(1, int(kern.GetGlobalVariable('num_zones'))):
103        z = addressof(kern.globals.zone_array[i])
104        zs = addressof(kern.globals.zone_security_array[i])
105        if ZoneName(z, zs) == name:
106            return z
107    return None
108
109def GetZoneCachedElements(zone):
110    """ Internal function to return cached element addresses in a zone
111    """
112    cached = set()
113    page_size = unsigned(kern.globals.page_size)
114
115    def decode_element(addr):
116        base = unsigned(addr) & -page_size
117        idx  = unsigned(addr) & (page_size - 1)
118        return base + unsigned(zone.z_elem_size) * idx
119
120    if zone.z_pcpu_cache:
121        for cache in IterateZPerCPU(zone.z_pcpu_cache):
122            for i in xrange(0, cache.zc_alloc_cur):
123                cached.add(decode_element(cache.zc_alloc_elems[i].ze_value))
124            for i in xrange(0, cache.zc_free_cur):
125                cached.add(decode_element(cache.zc_free_elems[i].ze_value))
126            for mag in IterateTAILQ_HEAD(cache.zc_depot, 'zm_link', 's'):
127                for i in xrange(0, mag.zm_cur):
128                    cached.add(decode_element(mag.zm_elems[i].ze_value))
129
130    return cached
131
132def IterateZoneElements(zone, minAddr=None, maxAddr=None):
133    """ Internal function to return allocated elements in a zone
134    """
135    cached = GetZoneCachedElements(zone)
136
137    for q in [zone.z_pageq_full, zone.z_pageq_partial]:
138        for meta in ZoneIteratePageQueue(q):
139            for e in meta.iterateElements():
140                if minAddr is not None and e < minAddr:
141                    continue
142                if maxAddr is not None and e > maxAddr:
143                    continue
144                if meta.isElementFree(e):
145                    continue
146                if e in cached:
147                    continue
148                yield e
149
150def PrettyPrintDictionary(d):
151    """ Internal function to pretty print a dictionary with string or integer values
152        params: The dictionary to print
153    """
154    for key, value in d.items():
155        key += ":"
156        if isinstance(value, int):
157            print "{:<30s} {: >10d}".format(key, value)
158        elif isinstance(value, long):
159            print "{:<30s} {: >10d}".format(key, value)
160        else:
161            print "{:<30s} {: >10s}".format(key, value)
162
163# Macro: memstats
164@lldb_command('memstats', 'J')
165def Memstats(cmd_args=None, cmd_options={}):
166    """ 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.
167        usage: memstats -J
168                Output json
169    """
170    print_json = False
171    if "-J" in cmd_options:
172        print_json = True
173
174    memstats = {}
175    try:
176        memstats["memorystatus_level"] = int(kern.globals.memorystatus_level)
177        memstats["memorystatus_available_pages"] = int(kern.globals.memorystatus_available_pages)
178        memstats["inuse_ptepages_count"] = int(kern.globals.inuse_ptepages_count)
179    except ValueError:
180        pass
181    if hasattr(kern.globals, 'compressor_object'):
182        memstats["compressor_page_count"] = int(kern.globals.compressor_object.resident_page_count)
183    memstats["vm_page_throttled_count"] = int(kern.globals.vm_page_throttled_count)
184    memstats["vm_page_active_count"] = int(kern.globals.vm_page_active_count)
185    memstats["vm_page_inactive_count"] = int(kern.globals.vm_page_inactive_count)
186    memstats["vm_page_wire_count"] = int(kern.globals.vm_page_wire_count)
187    memstats["vm_page_free_count"] = int(kern.globals.vm_page_free_count)
188    memstats["vm_page_purgeable_count"] = int(kern.globals.vm_page_purgeable_count)
189    memstats["vm_page_inactive_target"] = int(kern.globals.vm_page_inactive_target)
190    memstats["vm_page_free_target"] = int(kern.globals.vm_page_free_target)
191    memstats["vm_page_free_reserved"] = int(kern.globals.vm_page_free_reserved)
192
193    # Serializing to json here ensure we always catch bugs preventing
194    # serialization
195    as_json = json.dumps(memstats)
196    if print_json:
197        print as_json
198    else:
199        PrettyPrintDictionary(memstats)
200
201@xnudebug_test('test_memstats')
202def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
203    """ Test the functionality of memstats command
204        returns
205         - False on failure
206         - True on success
207    """
208    if not isConnected:
209        print "Target is not connected. Cannot test memstats"
210        return False
211    res = lldb.SBCommandReturnObject()
212    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res)
213    result = res.GetOutput()
214    if result.split(":")[1].strip().find('None') == -1 :
215        return True
216    else:
217        return False
218
219# EndMacro: memstats
220
221# Macro: showmemorystatus
222def CalculateLedgerPeak(phys_footprint_entry):
223    """ Internal function to calculate ledger peak value for the given phys footprint entry
224        params: phys_footprint_entry - value representing struct ledger_entry *
225        return: value - representing the ledger peak for the given phys footprint entry
226    """
227    return max(phys_footprint_entry['balance'], phys_footprint_entry.get('interval_max', 0))
228
229@header("{: >8s} {: >12s} {: >12s} {: >10s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s}  {: <32s}\n".format(
230'pid', 'effective', 'requested', 'state', 'relaunch', 'user_data', 'physical', 'iokit', 'footprint',
231'recent peak', 'lifemax', 'limit', 'command'))
232def GetMemoryStatusNode(proc_val):
233    """ Internal function to get memorystatus information from the given proc
234        params: proc - value representing struct proc *
235        return: str - formatted output information for proc object
236    """
237    out_str = ''
238    task_val = Cast(proc_val.task, 'task *')
239    task_ledgerp = task_val.ledger
240    ledger_template = kern.globals.task_ledger_template
241
242    task_physmem_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'phys_mem')
243    task_iokit_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'iokit_mapped')
244    task_phys_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'phys_footprint')
245    page_size = kern.globals.page_size
246
247    phys_mem_footprint = task_physmem_footprint_ledger_entry['balance'] / page_size
248    iokit_footprint = task_iokit_footprint_ledger_entry['balance'] / page_size
249    phys_footprint = task_phys_footprint_ledger_entry['balance'] / page_size
250    phys_footprint_limit = task_phys_footprint_ledger_entry['limit'] / page_size
251    ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry)
252    phys_footprint_spike = ledger_peak / page_size
253    phys_footprint_lifetime_max = task_phys_footprint_ledger_entry['lifetime_max'] / page_size
254
255    format_string = '{0: >8d} {1: >12d} {2: >12d} {3: #011x} {4: >10d} {5: #011x} {6: >12d} {7: >10d} {8: >13d}'
256    out_str += format_string.format(GetProcPID(proc_val), proc_val.p_memstat_effectivepriority,
257        proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_relaunch_flags,
258        proc_val.p_memstat_userdata, phys_mem_footprint, iokit_footprint, phys_footprint)
259    if phys_footprint != phys_footprint_spike:
260        out_str += "{: >12d}".format(phys_footprint_spike)
261    else:
262        out_str += "{: >12s}".format('-')
263
264    out_str += "{: >10d}  ".format(phys_footprint_lifetime_max)
265    out_str += "{: >10d}  {: <32s}\n".format(phys_footprint_limit, GetProcName(proc_val))
266    return out_str
267
268@lldb_command('showmemorystatus')
269def ShowMemoryStatus(cmd_args=None):
270    """  Routine to display each entry in jetsam list with a summary of pressure statistics
271         Usage: showmemorystatus
272    """
273    bucket_index = 0
274    bucket_count = 20
275    print GetMemoryStatusNode.header
276    print "{: >21s} {: >12s} {: >38s} {: >10s} {: >12s} {: >10s} {: >10s}\n".format("priority", "priority", "(pages)", "(pages)", "(pages)",
277        "(pages)", "(pages)", "(pages)")
278    while bucket_index < bucket_count:
279        current_bucket = kern.globals.memstat_bucket[bucket_index]
280        current_list = current_bucket.list
281        current_proc = Cast(current_list.tqh_first, 'proc *')
282        while unsigned(current_proc) != 0:
283            print GetMemoryStatusNode(current_proc)
284            current_proc = current_proc.p_memstat_list.tqe_next
285        bucket_index += 1
286    print "\n\n"
287    Memstats()
288
289# EndMacro: showmemorystatus
290
291class ZoneMeta(object):
292    """
293    Helper class that helpers walking metadata
294    """
295
296    def __init__(self, addr, isPageIndex = False):
297        global kern
298        pagesize  = kern.globals.page_size
299        zone_info = kern.GetGlobalVariable('zone_info')
300
301        def load_range(var):
302            return (unsigned(var.min_address), unsigned(var.max_address))
303
304        def in_range(x, r):
305            return x >= r[0] and x <= r[1]
306
307        FOREIGN = GetEnumValue('zone_addr_kind_t', 'ZONE_ADDR_FOREIGN')
308        NATIVE  = GetEnumValue('zone_addr_kind_t', 'ZONE_ADDR_NATIVE')
309
310        self.meta_range = load_range(zone_info.zi_meta_range)
311        self.native_range = load_range(zone_info.zi_map_range[NATIVE])
312        self.foreign_range = load_range(zone_info.zi_map_range[FOREIGN])
313        self.addr_base = min(self.foreign_range[0], self.native_range[0])
314
315        addr = unsigned(addr)
316        if isPageIndex:
317            # sign extend
318            addr = value(pagesize.GetSBValue().CreateValueFromExpression(None,
319                '(long)(int)%d * %d' %(addr, pagesize)))
320            addr = unsigned(addr)
321
322        self.address = addr
323
324        if in_range(addr, self.meta_range):
325            self.kind = 'Metadata'
326            addr -= addr % sizeof('struct zone_page_metadata')
327            self.meta_addr = addr
328            self.meta = kern.GetValueFromAddress(addr, "struct zone_page_metadata *")
329
330            self.page_addr = self.addr_base + ((addr - self.meta_range[0]) / sizeof('struct zone_page_metadata') * pagesize)
331        elif in_range(addr, self.native_range) or in_range(addr, self.foreign_range):
332            addr &= ~(pagesize - 1)
333            page_idx = (addr - self.addr_base) / pagesize
334
335            self.kind = 'Element'
336            self.page_addr = addr
337            self.meta_addr = self.meta_range[0] + page_idx * sizeof('struct zone_page_metadata')
338            self.meta = kern.GetValueFromAddress(self.meta_addr, "struct zone_page_metadata *")
339        else:
340            self.kind = 'Unknown'
341            self.meta = None
342            self.page_addr = 0
343            self.meta_addr = 0
344
345        if self.meta:
346            self.zone = addressof(kern.globals.zone_array[self.meta.zm_index])
347            self.zone_security = addressof(kern.globals.zone_security_array[self.meta.zm_index])
348        else:
349            self.zone = None
350            self.zone_security = None
351
352    def isSecondaryPage(self):
353        return self.meta and self.meta.zm_chunk_len >= 0xe
354
355    def getPageCount(self):
356        n = self.meta and self.meta.zm_chunk_len or 0
357        if self.zone and self.zone.z_percpu:
358            n *= kern.globals.zpercpu_early_count
359        return n
360
361    def getAllocAvail(self):
362        if not self.meta: return 0
363        chunk_len = unsigned(self.meta.zm_chunk_len)
364        page_size = unsigned(kern.globals.page_size)
365        return chunk_len * page_size / self.zone.z_elem_size
366
367    def getAllocCount(self):
368        if not self.meta: return 0
369        return self.meta.zm_alloc_size / self.zone.z_elem_size
370
371    def getReal(self):
372        if self.isSecondaryPage():
373            return ZoneMeta(unsigned(self.meta) - sizeof('struct zone_page_metadata') * unsigned(self.meta.zm_page_index))
374
375        return self
376
377    def getElementAddress(self, addr):
378        meta  = self.getReal()
379        esize = meta.zone.z_elem_size
380        start = meta.page_addr
381
382        if esize == 0:
383            return None
384
385        estart = addr - start
386        return unsigned(start + estart - (estart % esize))
387
388    def getInlineBitmapChunkLength(self):
389        if self.zone.z_percpu:
390            return unsigned(self.zone.z_chunk_pages)
391        return unsigned(self.meta.zm_chunk_len)
392
393    def getBitmapSize(self):
394        if not self.meta or self.zone.z_permanent or not self.meta.zm_chunk_len:
395            return 0
396        if self.meta.zm_inline_bitmap:
397            return -4 * self.getInlineBitmapChunkLength()
398        return 8 << (unsigned(self.meta.zm_bitmap) & 0x7);
399
400    def getBitmap(self):
401        if not self.meta or self.zone.z_permanent or not self.meta.zm_chunk_len:
402            return 0
403        if self.meta.zm_inline_bitmap:
404            return unsigned(addressof(self.meta.zm_bitmap))
405        bbase = unsigned(kern.globals.zone_info.zi_bits_range.min_address)
406        index = unsigned(self.meta.zm_bitmap) & ~0x7
407        return bbase + index;
408
409    def getFreeCountSlow(self):
410        if not self.meta or self.zone.z_permanent or not self.meta.zm_chunk_len:
411            return self.getAllocAvail() - self.getAllocCount()
412
413        n = 0
414        if self.meta.zm_inline_bitmap:
415            for i in xrange(0, self.getInlineBitmapChunkLength()):
416                m = kern.GetValueFromAddress(self.meta_addr + i * 16,
417                    'struct zone_page_metadata *');
418                bits = unsigned(m.zm_bitmap)
419                while bits:
420                    n += 1
421                    bits &= bits - 1
422        else:
423            bitmap = kern.GetValueFromAddress(self.getBitmap(), 'uint64_t *')
424            for i in xrange(0, 1 << (unsigned(self.meta.zm_bitmap) & 0x7)):
425                bits = unsigned(bitmap[i])
426                while bits:
427                    n += 1
428                    bits &= bits - 1
429        return n
430
431    def isElementFree(self, addr):
432        meta = self.meta
433
434        if not meta or self.zone.z_permanent or not meta.zm_chunk_len:
435            return True
436
437        start = self.page_addr
438        esize = self.zone.z_elem_size
439        eidx  = (addr - start) / esize
440
441        if meta.zm_inline_bitmap:
442            i = eidx / 32
443            m = unsigned(meta) + sizeof('struct zone_page_metadata') * i
444            bits = kern.GetValueFromAddress(m, meta).zm_bitmap
445            return (bits & (1 << (eidx % 32))) != 0
446
447        else:
448            bitmap = kern.GetValueFromAddress(self.getBitmap(), 'uint64_t *')
449            bits = unsigned(bitmap[eidx / 64])
450            return (bits & (1 << (eidx % 64))) != 0
451
452    def iterateElements(self):
453        if self.meta is None:
454            return
455        esize = self.zone.z_elem_size
456        start = 0
457        end   = unsigned(kern.globals.page_size) * self.meta.zm_chunk_len
458        end  -= end % esize
459
460        for offs in xrange(start, end, esize):
461            yield unsigned(self.page_addr + offs)
462
463@lldb_type_summary(['zone_page_metadata'])
464@header("{:<20s} {:<10s} {:<10s} {:<24s} {:<20s} {:<20s}".format(
465    'METADATA', 'PG_CNT', 'ALLOC_CNT', 'BITMAP', 'ZONE', 'NAME'))
466def GetZoneMetadataSummary(meta):
467    """ Summarize a zone metadata object
468        params: meta - obj representing zone metadata in the kernel
469        returns: str - summary of the zone metadata
470    """
471
472    if type(meta) != ZoneMeta:
473        meta = ZoneMeta(meta)
474
475    out_str = 'Metadata Description:\n' + GetZoneMetadataSummary.header + '\n'
476    if meta.isSecondaryPage():
477        out_str += "{:<#20x} {:<10d} {:<10d} {:<#18x} @{:<4d} {:<#20x} {:s}\n".format(
478                meta.meta_addr, 0, 0, 0, 0, 0, '(fake multipage meta)')
479        meta = meta.getReal()
480    out_str += "{:<#20x} {:<10d} {:<10d} {:<#18x} @{:<4d} {:<#20x} {:s}".format(
481            meta.meta_addr, meta.getPageCount(), meta.getAllocCount(),
482            meta.getBitmap(), meta.getBitmapSize(), meta.zone,
483            ZoneName(meta.zone, meta.zone_security))
484    return out_str
485
486@header("{:<20s} {:<10s} {:<10s} {:<20s} {:<10s}".format(
487    'ADDRESS', 'TYPE', 'STATUS', 'PAGE_ADDR', 'OFFSET'))
488def WhatIs(addr):
489    """ Information about kernel pointer
490    """
491    global kern
492
493    meta = ZoneMeta(addr)
494    estart = None
495
496    if meta.meta is None:
497        out_str = "Address {:#018x} is outside of any zone map ({:#018x}-{:#018x})\n".format(
498                addr, meta.native_range[0], meta.native_range[-1] + 1)
499    else:
500        if meta.kind[0] == 'E': # element
501            page_offset_str = "{:d}/{:d}K".format(
502                    addr - meta.page_addr, kern.globals.page_size / 1024)
503            estart = meta.getElementAddress(addr)
504            if estart is None:
505                status = "Unattributed"
506            elif meta.isElementFree(estart):
507                status = "Free"
508            elif estart in GetZoneCachedElements(meta.zone):
509                status = "Cached"
510            else:
511                status = "Allocated"
512        else:
513            page_offset_str = "-"
514            status = "-"
515        out_str = WhatIs.header + '\n'
516        out_str += "{meta.address:<#20x} {meta.kind:<10s} {status:<10s} {meta.page_addr:<#20x} {:<10s}\n\n".format(
517                page_offset_str, meta=meta, status=status)
518        out_str += GetZoneMetadataSummary(meta) + '\n\n'
519
520    print out_str
521
522    if estart is not None:
523        print "Hexdump:\n"
524
525        meta   = meta.getReal()
526        esize  = meta.zone.z_elem_size
527        start  = meta.page_addr
528        marks  = {unsigned(addr): ">"}
529
530        try:
531            if estart > start:
532                data_array = kern.GetValueFromAddress(estart - 16, "uint8_t *")
533                print_hex_data(data_array[0:16], estart - 16, "")
534        except:
535            pass
536
537        print "------------------------------------------------------------------"
538        try:
539            data_array = kern.GetValueFromAddress(estart, "uint8_t *")
540            print_hex_data(data_array[0:esize], estart, "", marks)
541        except:
542            print "*** unable to read memory ***"
543            pass
544        print "------------------------------------------------------------------"
545
546        try:
547            data_array = kern.GetValueFromAddress(estart + esize, "uint8_t *")
548            print_hex_data(data_array[0:16], estart + esize, "")
549        except:
550            pass
551
552@lldb_command('whatis')
553def WhatIsHelper(cmd_args=None):
554    """ Routine to show information about a kernel pointer
555        Usage: whatis <address>
556    """
557    if not cmd_args:
558        raise ArgumentError("No arguments passed")
559    WhatIs(kern.GetValueFromAddress(cmd_args[0], 'void *'))
560
561# Macro: showzcache
562
563@lldb_type_summary(['zone','zone_t'])
564@header("{:18s}  {:32s}  {:>6s}  {:>6s}  {:>6s}  {:>6s}  {:>6s}  {:>6s}  {:<s}".format(
565    'ZONE', 'NAME', 'WSS', 'CONT', 'USED', 'FREE', 'CACHED', 'RECIRC', 'CPU_CACHES'))
566def GetZoneCacheCPUSummary(zone, zone_security, verbose, O):
567    """ Summarize a zone's cache broken up per cpu
568        params:
569          zone: value - obj representing a zone in kernel
570        returns:
571          str - summary of the zone's per CPU cache contents
572    """
573    format_string  = '{zone:#018x}  {:32s}  '
574    format_string += '{zone.z_elems_free_wss:6d}  {cont:6.2f}  '
575    format_string += '{used:6d}  {zone.z_elems_free:6d}  '
576    format_string += '{cached:6d}  {recirc:6d}  {cpuinfo:s}'
577    cache_elem_count = 0
578    cpu_info = ""
579    mag_capacity = unsigned(kern.GetGlobalVariable('zc_magazine_size'))
580    depot_capacity = kern.GetGlobalVariable('depot_element_count')
581
582    if zone.z_pcpu_cache:
583        if verbose:
584            cpu_info = None
585            for cache in IterateZPerCPU(zone.z_pcpu_cache):
586                if cpu_info is None:
587                    cpu_info = "{ "
588                else:
589                    cpu_info += ", "
590                per_cpu_count = unsigned(cache.zc_alloc_cur)
591                per_cpu_count += unsigned(cache.zc_free_cur)
592                per_cpu_count += unsigned(cache.zc_depot_cur) * mag_capacity
593                cache_elem_count += per_cpu_count
594                cpu_info += "{:3d} /{cache.zc_depot_max:3d}".format(per_cpu_count, cache=cache)
595            cpu_info += " }"
596        else:
597            depot_cur = 0
598            depot_max = 0
599            for cache in IterateZPerCPU(zone.z_pcpu_cache):
600                depot_cur += unsigned(cache.zc_alloc_cur)
601                depot_cur += unsigned(cache.zc_free_cur)
602                cache_elem_count += unsigned(cache.zc_depot_cur) * mag_capacity
603                depot_max += unsigned(cache.zc_depot_max)
604            cache_elem_count += depot_cur
605
606            cpus = unsigned(kern.globals.zpercpu_early_count)
607            cpu_info = "total: {:3d} / {:3d}, avg: {:5.1f} / {:5.1f}".format(
608                    depot_cur, depot_max, float(depot_cur) / cpus, float(depot_max) / cpus)
609
610
611    print O.format(format_string, ZoneName(zone, zone_security),
612            cached=cache_elem_count,
613            used=zone.z_elems_avail - cache_elem_count - zone.z_elems_free,
614            cont=float(zone.z_contention_wma) / 256.,
615            recirc=zone.z_recirc_cur * mag_capacity,
616            zone=zone, cpuinfo = cpu_info)
617
618@lldb_command('showzcache', fancy=True)
619def ZcacheCPUPrint(cmd_args=None, cmd_options={}, O=None):
620    """
621    Routine to print a summary listing of all the kernel zones cache contents
622
623    Usage: showzcache [-V]
624
625    Use -V       to see more detailed output
626    """
627    global kern
628    verbose = "-V" in cmd_options
629    with O.table(GetZoneCacheCPUSummary.header):
630        if len(cmd_args) == 1:
631            zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
632            zone_array = [z[0] for z in kern.zones]
633            zid = zone_array.index(zone)
634            zone_security = kern.zones[zid][1]
635            GetZoneCacheCPUSummary(zone, zone_security, verbose, O);
636        else:
637            for zval, zsval in kern.zones:
638                if zval.z_self:
639                    GetZoneCacheCPUSummary(zval, zsval, verbose, O)
640
641# EndMacro: showzcache
642
643# Macro: zprint
644
645def GetZone(zone_val, zs_val, marks, security_marks):
646    """ Internal function which gets a phython dictionary containing important zone information.
647        params:
648          zone_val: value - obj representing a zone in kernel
649        returns:
650          zone - python dictionary with zone stats
651    """
652    pcpu_scale = 1
653    if zone_val.z_percpu:
654        pcpu_scale = unsigned(kern.globals.zpercpu_early_count)
655    pagesize = kern.globals.page_size
656    zone = {}
657    mag_capacity = unsigned(kern.GetGlobalVariable('zc_magazine_size'))
658    zone["page_count"] = unsigned(zone_val.z_wired_cur) * pcpu_scale
659    zone["allfree_page_count"] = unsigned(zone_val.z_wired_empty)
660
661    cache_elem_count = 0
662    if zone_val.z_pcpu_cache:
663        for cache in IterateZPerCPU(zone_val.z_pcpu_cache):
664            cache_elem_count += unsigned(cache.zc_alloc_cur)
665            cache_elem_count += unsigned(cache.zc_free_cur)
666            cache_elem_count += unsigned(cache.zc_depot_cur) * mag_capacity
667
668    zone["size"] = zone["page_count"] * pagesize
669    zone["submap_idx"] = unsigned(zs_val.z_submap_idx)
670
671    zone["free_size"] = zone_val.z_elems_free * zone_val.z_elem_size * pcpu_scale
672    zone["cached_size"] = cache_elem_count * zone_val.z_elem_size * pcpu_scale
673    zone["used_size"] = zone["size"] - zone["free_size"] - zone["cached_size"]
674
675    zone["element_count"] = zone_val.z_elems_avail - zone_val.z_elems_free - cache_elem_count
676    zone["cache_element_count"] = cache_elem_count
677    zone["free_element_count"] = unsigned(zone_val.z_elems_free)
678
679    if zone_val.z_percpu:
680        zone["allocation_size"] = unsigned(pagesize)
681        zone["allocation_ncpu"] = unsigned(zone_val.z_chunk_pages)
682    else:
683        zone["allocation_size"] = unsigned(zone_val.z_chunk_pages * pagesize)
684        zone["allocation_ncpu"] = 1
685    zone["allocation_count"] = zone["allocation_size"] / zone_val.z_elem_size
686    zone["allocation_waste"] = (zone["allocation_size"] % zone_val.z_elem_size) * zone["allocation_ncpu"]
687
688    if not zone_val.__getattr__("z_self") :
689        zone["destroyed"] = True
690    else:
691        zone["destroyed"] = False
692
693    for mark in marks:
694        if zone_val.__getattr__(mark[0]):
695            zone[mark[0]] = True
696        else:
697            zone[mark[0]] = False
698
699    for mark in security_marks:
700        if zs_val.__getattr__(mark[0]):
701            zone[mark[0]] = True
702        else:
703            zone[mark[0]] = False
704
705    zone["name"] = ZoneName(zone_val, zs_val)
706    if zone_val.exhaustible:
707        zone["exhaustible"] = True
708    else:
709        zone["exhaustible"] = False
710
711    zone["sequester_page_count"] = (unsigned(zone_val.z_va_cur) -
712            unsigned(zone_val.z_wired_cur)) * pcpu_scale
713    zone["page_count_max"] = unsigned(zone_val.z_wired_max) * pcpu_scale
714
715    # Ensure the zone is serializable
716    json.dumps(zone)
717    return zone
718
719
720@lldb_type_summary(['zone','zone_t'])
721@header(("{:<18s}  {:_^47s}  {:_^24s}  {:_^13s}  {:_^28s}\n"+
722"{:<18s}  {:>11s} {:>11s} {:>11s} {:>11s}  {:>8s} {:>7s} {:>7s}  {:>6s} {:>6s}  {:>8s} {:>6s} {:>5s} {:>7s}   {:<22s} {:<20s}").format(
723'', 'SIZE (bytes)', 'ELEMENTS (#)', 'PAGES', 'ALLOC CHUNK CONFIG',
724'ZONE', 'TOTAL', 'ALLOC', 'CACHE', 'FREE', 'ALLOC', 'CACHE', 'FREE', 'COUNT', 'FREE', 'SIZE (P)', 'ELTS', 'WASTE', 'ELT_SZ', 'FLAGS', 'NAME'))
725def GetZoneSummary(zone_val, zs_val, marks, security_marks, stats):
726    """ Summarize a zone with important information. See help zprint for description of each field
727        params:
728          zone_val: value - obj representing a zone in kernel
729        returns:
730          str - summary of the zone
731    """
732    pagesize = kern.globals.page_size
733    out_string = ""
734    zone = GetZone(zone_val, zs_val, marks, security_marks)
735
736    pcpu_scale = 1
737    if zone_val.z_percpu:
738        pcpu_scale = unsigned(kern.globals.zpercpu_early_count)
739
740    format_string  = '{zone:#018x}  {zd[size]:11,d} {zd[used_size]:11,d} {zd[cached_size]:11,d} {zd[free_size]:11,d}  '
741    format_string += '{zd[element_count]:8,d} {zd[cache_element_count]:7,d} {zone.z_elems_free:7,d}  '
742    format_string += '{z_wired_cur:6,d} {z_wired_empty:6,d}  '
743    format_string += '{alloc_size_kb:3,d}K ({zone.z_chunk_pages:d}) '
744    format_string += '{zd[allocation_count]:6,d} {zd[allocation_waste]:5,d} {z_elem_size:7,d}   '
745    format_string += '{markings:<22s} {zone_name:<20s}'
746
747    markings=""
748    if zone["destroyed"]:
749        markings+="I"
750    else:
751        markings+=" "
752
753    for mark in marks:
754        if zone[mark[0]]:
755            markings += mark[1]
756        else:
757            markings+=" "
758    for mark in security_marks:
759        if zone[mark[0]]:
760            markings += mark[1]
761        else:
762            markings+=" "
763
764
765    """ Z_SUBMAP_IDX_READ_ONLY == 1
766    """
767    if zone["submap_idx"] == 1:
768        markings += "%"
769    else:
770        markings+=" "
771
772    alloc_size_kb = zone["allocation_size"] / 1024
773    out_string += format_string.format(zone=zone_val, zd=zone,
774            z_wired_cur=unsigned(zone_val.z_wired_cur) * pcpu_scale,
775            z_wired_empty=unsigned(zone_val.z_wired_empty) * pcpu_scale,
776            z_elem_size=unsigned(zone_val.z_elem_size) * pcpu_scale,
777            alloc_size_kb=alloc_size_kb, markings=markings, zone_name=zone["name"])
778
779    if zone["exhaustible"] :
780            out_string += " (max: {:d})".format(zone["page_count_max"] * pagesize)
781
782    if zone["sequester_page_count"] != 0 :
783            out_string += " (sequester: {:d})".format(zone["sequester_page_count"])
784
785    stats["cur_size"] += zone["size"]
786    stats["used_size"] += zone["used_size"]
787    stats["cached_size"] += zone["cached_size"]
788    stats["free_size"] += zone["free_size"]
789    stats["cur_pages"] += zone["page_count"]
790    stats["free_pages"] += zone["allfree_page_count"]
791    stats["seq_pages"] += zone["sequester_page_count"]
792
793    return out_string
794
795@lldb_command('zprint', "J", fancy=True)
796def Zprint(cmd_args=None, cmd_options={}, O=None):
797    """ Routine to print a summary listing of all the kernel zones
798        usage: zprint -J
799                Output json
800    All columns are printed in decimal
801    Legend:
802        ! - zone uses VA sequestering
803        $ - not encrypted during hibernation
804        % - zone is a read-only zone
805        A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv
806        C - collectable
807        D - destructible
808        E - Per-cpu caching is enabled for this zone
809        F - allows foreign memory (memory not allocated from any zone map)
810        G - currently running GC
811        H - exhaustible
812        I - zone was destroyed and is no longer valid
813        L - zone is being monitored by zleaks
814        M - gzalloc will avoid monitoring this zone
815        N - zone requires alignment (avoids padding this zone for debugging)
816        O - does not allow refill callout to fill zone on noblock allocation
817        R - will be refilled when below low water mark
818        S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv
819        X - expandable
820    """
821    global kern
822
823    marks = [
824            ["collectable",          "C"],
825            ["z_destructible",       "D"],
826            ["expandable",           "X"],
827            ["exhaustible",          "H"],
828            ["z_elems_rsv",          "R"],
829            ["no_callout",           "O"],
830            ["zleak_on",             "L"],
831            ["z_expander",           "A"],
832            ["z_expander_vm_priv",   "S"],
833            ["z_pcpu_cache",         "E"],
834            ["gzalloc_exempt",       "M"],
835            ["alignment_required",   "N"],
836            ]
837    security_marks = [
838            ["z_allows_foreign",     "F"],
839            ["z_va_sequester",       "!"],
840            ["z_noencrypt",          "$"],
841            ]
842
843    stats = {
844        "cur_size": 0, "used_size": 0, "cached_size": 0, "free_size": 0,
845        "cur_pages": 0, "free_pages": 0, "seq_pages": 0
846    }
847
848    print_json = False
849    if "-J" in cmd_options:
850        print_json = True
851
852    if print_json:
853        zones = []
854        for zval, zsval in kern.zones:
855            if zval.z_self:
856                zones.append(GetZone(zval, zsval, marks, security_marks))
857
858        print json.dumps(zones)
859    else:
860        with O.table(GetZoneSummary.header):
861            for zval, zsval in kern.zones:
862                if zval.z_self:
863                    print GetZoneSummary(zval, zsval, marks, security_marks, stats)
864
865            format_string  = '{VT.Bold}{name:19s} {stats[cur_size]:11,d} {stats[used_size]:11,d} {stats[cached_size]:11,d} {stats[free_size]:11,d} '
866            format_string += '                           '
867            format_string += '{stats[cur_pages]:6,d} {stats[free_pages]:6,d}{VT.EndBold}  '
868            format_string += '(sequester: {VT.Bold}{stats[seq_pages]:,d}{VT.EndBold})'
869            print O.format(format_string, name="TOTALS", filler="", stats=stats)
870
871
872@xnudebug_test('test_zprint')
873def TestZprint(kernel_target, config, lldb_obj, isConnected ):
874    """ Test the functionality of zprint command
875        returns
876         - False on failure
877         - True on success
878    """
879    if not isConnected:
880        print "Target is not connected. Cannot test memstats"
881        return False
882    res = lldb.SBCommandReturnObject()
883    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("zprint", res)
884    result = res.GetOutput()
885    if len(result.split("\n")) > 2:
886        return True
887    else:
888        return False
889
890
891# EndMacro: zprint
892# Macro: showtypes
893def PrintTypes(z):
894    kt_cur = cast(z.z_views, "struct kalloc_type_view *")
895    prev_types = {}
896    print '    {0: <24s} {1: <40s} {2: <10s}'.format(
897        "kalloc_type_view", "typename", "signature")
898    while kt_cur:
899        typename = str(kt_cur.kt_zv.zv_name)
900        if "site." in typename:
901            typename = typename.split("site.")[1]
902            sig = str(kt_cur.kt_signature)
903            if typename not in prev_types or prev_types[typename] != sig:
904                print '    {0: <#24x} {1: <40s} {2: <10s}'.format(
905                    kt_cur, typename, sig)
906                prev_types[typename] = sig
907        kt_cur = cast(kt_cur.kt_zv.zv_next, "struct kalloc_type_view *")
908
909def ShowTypesPerSize(size):
910    kalloc_type_zarray = kern.GetGlobalVariable('kalloc_type_zarray')
911    num_kt_sizeclass = kern.GetGlobalVariable('num_kt_sizeclass')
912    for i in range(num_kt_sizeclass):
913        zone = kalloc_type_zarray[i]
914        if zone and zone.z_elem_size == size:
915            while zone:
916                print "Zone: %s (0x%x)" % (zone.z_name, zone)
917                PrintTypes(zone)
918                zone = zone.z_kt_next
919            break
920
921def ShowAllTypes():
922    kalloc_type_zarray = kern.GetGlobalVariable('kalloc_type_zarray')
923    num_kt_sizeclass = kern.GetGlobalVariable('num_kt_sizeclass')
924    for i in range(num_kt_sizeclass):
925        zone = kalloc_type_zarray[i]
926        while zone:
927            print "Zone: %s (0x%x)" % (zone.z_name, zone)
928            PrintTypes(zone)
929            zone = zone.z_kt_next
930
931@lldb_command('showkalloctypes', 'Z:S:')
932def ShowKallocTypes(cmd_args=None, cmd_options={}):
933    """
934    prints kalloc types for a zone or sizeclass
935
936    Usage: showkalloctypes [-Z <zone>] [-S <sizeclass>]
937
938    If no options are provided kalloc types for all zones is printed
939    """
940    if '-Z' in cmd_options:
941        zone = kern.GetValueFromAddress(cmd_options['-Z'], 'struct zone *')
942        if not zone:
943            raise ArgumentError("Invalid zone {:s}".format(cmd_options['-Z']))
944        PrintTypes(zone)
945        return
946    if '-S' in cmd_options:
947        size = unsigned(cmd_options['-S'])
948        if size == 0:
949            raise ArgumentError("Invalid size {:s}".format(cmd_options['-S']))
950        ShowTypesPerSize(size)
951        return
952    ShowAllTypes()
953
954# EndMacro: showkalloctypes
955# Macro: showzchunks
956
957def ZoneIteratePageQueue(page):
958    while page.packed_address:
959        meta = ZoneMeta(page.packed_address, isPageIndex=True)
960        yield meta
961        page = meta.meta.zm_page_next
962
963@header("{: <20s} {: <20s} {: <20s} {: <25s} {: <10s} {: <8s} {: <4s} {: >9s}".format(
964    "Zone", "Metadata", "Page", "Bitmap", "Kind", "Queue", "Pgs", "Allocs"))
965def GetZoneChunk(meta, queue, O=None):
966    format_string  = "{meta.zone: <#20x} "
967    format_string += "{meta.meta_addr: <#20x} {meta.page_addr: <#20x} "
968    format_string += "{bitmap: <#18x} @{bitmap_size:<5d} "
969    format_string += "{kind:<10s} {queue:<8s} {pgs:<1d}/{chunk:<1d}  "
970    format_string += "{alloc_count: >4d}/{avail_count: >4d}"
971
972    pgs = int(meta.zone.z_chunk_pages)
973    chunk = pgs
974    if meta.meta.zm_chunk_len >= 0xe:
975        kind = "secondary"
976        pgs -= int(meta.meta.zm_page_index)
977    else:
978        kind = "primary"
979
980    alloc_count=meta.getAllocCount()
981    avail_count=meta.getAllocAvail()
982    free_count=meta.getFreeCountSlow()
983
984    if alloc_count + free_count != avail_count:
985        format_string += " {VT.Red}bitmap mismatch{VT.Default}"
986
987    return O.format(format_string, meta=meta,
988            alloc_count=alloc_count,
989            avail_count=avail_count,
990            bitmap=meta.getBitmap(),
991            bitmap_size=meta.getBitmapSize(),
992            queue=queue, kind=kind, pgs=pgs, chunk=chunk)
993
994def ShowZChunksImpl(zone, extra_addr=None, cmd_options={}, O=None):
995    verbose = '-V' in cmd_options
996    cached = GetZoneCachedElements(zone)
997
998    def do_content(meta, O, indent=False):
999        with O.table("{:>5s}  {:<20s} {:<10s}".format("#", "Element", "State"), indent=indent):
1000            i = 0
1001            for e in meta.iterateElements():
1002                status = "Allocated"
1003                if meta.isElementFree(e):
1004                    status = "Free"
1005                elif e in cached:
1006                    status = "Cached"
1007                print O.format("{:5d}  {:<#20x} {:10s}", i, e, status)
1008                i += 1
1009
1010    if extra_addr is None:
1011        with O.table(GetZoneChunk.header):
1012            for meta in ZoneIteratePageQueue(zone.z_pageq_full):
1013                print GetZoneChunk(meta, "full", O)
1014                if verbose: do_content(meta, O, indent=True);
1015
1016            for meta in ZoneIteratePageQueue(zone.z_pageq_partial):
1017                print GetZoneChunk(meta, "partial", O)
1018                if verbose: do_content(meta, O, indent=True);
1019
1020            for meta in ZoneIteratePageQueue(zone.z_pageq_empty):
1021                print GetZoneChunk(meta, "empty", O)
1022                if verbose: do_content(meta, O, indent=True);
1023
1024            for meta in ZoneIteratePageQueue(zone.z_pageq_va):
1025                print GetZoneChunk(meta, "va", O)
1026    else:
1027        meta = ZoneMeta(extra_addr, isPageIndex="-I" in cmd_options).getReal()
1028        with O.table(GetZoneChunk.header):
1029            print GetZoneChunk(meta, "N/A", O)
1030        do_content(meta, O)
1031
1032@lldb_command('showzchunks', "IV", fancy=True)
1033def ShowZChunks(cmd_args=None, cmd_options={}, O=None):
1034    """
1035    prints the list of zone chunks, or the content of a given chunk
1036
1037    Usage: showzchunks <zone> [-I] [-V] [address]
1038
1039    Use -I       to interpret [address] as a page index
1040    Use -V       to show the contents of all the chunks
1041
1042    [address]    can by any address belonging to the zone, or metadata
1043    """
1044
1045    if not cmd_args:
1046        return O.error('missing zone argument')
1047
1048    zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
1049
1050    if len(cmd_args) == 1:
1051        ShowZChunksImpl(zone, cmd_options=cmd_options, O=O)
1052    else:
1053        addr = unsigned(kern.GetValueFromAddress(cmd_args[1]))
1054        ShowZChunksImpl(zone, extra_addr=addr, cmd_options=cmd_options, O=O)
1055
1056@lldb_command('showallzchunks', fancy=True)
1057def ShowAllZChunks(cmd_args=None, cmd_options={}, O=None):
1058    """
1059    prints the list of all zone chunks
1060
1061    Usage: showallzchunks
1062    """
1063
1064    for z, zs in kern.zones:
1065        ShowZChunksImpl(z, O=O)
1066
1067# EndMacro: showzchunks
1068# Macro: zstack_showzonesbeinglogged
1069
1070@lldb_command('zstack_showzonesbeinglogged')
1071def ZstackShowZonesBeingLogged(cmd_args=None):
1072    """ Show all zones which have BTLog enabled.
1073    """
1074    global kern
1075    for zval, zsval in kern.zones:
1076        if zval.zlog_btlog:
1077          print "Zone: %s with its BTLog at: 0x%lx" % (ZoneName(zval, zsval),
1078                zval.zlog_btlog)
1079
1080# EndMacro: zstack_showzonesbeinglogged
1081
1082# Macro: zstack
1083
1084@lldb_command('zstack')
1085def Zstack(cmd_args=None):
1086    """ Zone leak debugging: Print the stack trace logged at <index> in the stacks list. If a <count> is supplied, it prints <count> stacks starting at <index>.
1087        Usage: zstack <btlog addr> <index> [<count>]
1088
1089        The suggested usage is to look at stacks with high percentage of refs (maybe > 25%).
1090        The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that.
1091    """
1092    if not cmd_args:
1093        print Zstack.__doc__
1094        return
1095    if int(kern.globals.log_records) == 0:
1096        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
1097        return
1098
1099    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
1100    btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
1101    btrecord_size = unsigned(btlog_ptr.btrecord_size)
1102    btrecords = unsigned(btlog_ptr.btrecords)
1103    btlog_size = unsigned(sizeof('struct btlog'))
1104    depth = unsigned(btlog_ptr.btrecord_btdepth)
1105    zstack_index = ArgumentStringToInt(cmd_args[1])
1106    count = 1
1107    if len(cmd_args) >= 3:
1108        count = ArgumentStringToInt(cmd_args[2])
1109
1110    max_count = ((btrecords_total_size - btlog_size)/btrecord_size)
1111
1112    if (zstack_index + count) > max_count:
1113       count = max_count - zstack_index
1114
1115    while count and (zstack_index != 0xffffff):
1116        zstack_record_offset = zstack_index * btrecord_size
1117        zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
1118        if int(zstack_record.ref_count)!=0:
1119           ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
1120        zstack_index += 1
1121        count -= 1
1122
1123# EndMacro : zstack
1124
1125# Macro: zstack_inorder
1126
1127@lldb_command('zstack_inorder')
1128def ZstackInOrder(cmd_args=None):
1129    """ Zone leak debugging: Print the stack traces starting from head to the tail.
1130        Usage: zstack_inorder <btlog addr>
1131    """
1132    if not cmd_args:
1133        print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>"
1134        return
1135    if int(kern.globals.log_records) == 0:
1136        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
1137        return
1138
1139    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
1140    btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
1141    btrecord_size = unsigned(btlog_ptr.btrecord_size)
1142    btrecords = unsigned(btlog_ptr.btrecords)
1143    btlog_size = unsigned(sizeof('struct btlog'))
1144    depth = unsigned(btlog_ptr.btrecord_btdepth)
1145    zstack_head = unsigned(btlog_ptr.head)
1146    zstack_index = zstack_head
1147    zstack_tail = unsigned(btlog_ptr.tail)
1148    count = ((btrecords_total_size - btlog_size)/btrecord_size)
1149
1150    while count and (zstack_index != 0xffffff):
1151        zstack_record_offset = zstack_index * btrecord_size
1152        zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
1153        ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
1154        zstack_index = zstack_record.next
1155        count -= 1
1156
1157# EndMacro : zstack_inorder
1158
1159# Macro: findoldest
1160
1161@lldb_command('findoldest')
1162def FindOldest(cmd_args=None):
1163    """
1164    """
1165    print "***** DEPRECATED ***** use 'zstack_findleak' macro instead."
1166    return
1167# EndMacro : findoldest
1168
1169# Macro : zstack_findleak
1170
1171@lldb_command('zstack_findleak')
1172def zstack_findleak(cmd_args=None):
1173    """ Zone leak debugging: search the log and print the stack with the most active references
1174        in the stack trace.
1175        Usage: zstack_findleak <btlog address>
1176
1177        This is useful for verifying a suspected stack as being the source of
1178        the leak.
1179    """
1180    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
1181    btrecord_size = unsigned(btlog_ptr.btrecord_size)
1182    btrecords = unsigned(btlog_ptr.btrecords)
1183
1184    cpcs_index = unsigned(btlog_ptr.head)
1185    depth = unsigned(btlog_ptr.btrecord_btdepth)
1186    highref = 0
1187    highref_index = 0
1188    highref_record = 0
1189
1190    while cpcs_index != 0xffffff:
1191        cpcs_record_offset = cpcs_index * btrecord_size
1192        cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
1193        if cpcs_record.ref_count > highref:
1194                highref_record = cpcs_record
1195                highref = cpcs_record.ref_count
1196                highref_index = cpcs_index
1197        cpcs_index = cpcs_record.next
1198    ShowZStackRecord(highref_record, highref_index, depth, unsigned(btlog_ptr.active_element_count))
1199
1200# EndMacro: zstack_findleak
1201
1202# Macro: findelem
1203
1204@lldb_command('findelem')
1205def FindElem(cmd_args=None):
1206    """
1207    """
1208    print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
1209    return
1210# EndMacro: findelem
1211
1212@lldb_command('zstack_findelem')
1213def ZStackFindElem(cmd_args=None):
1214    """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that
1215        refer to the given zone element.
1216        Usage: zstack_findelem <btlog addr> <elem addr>
1217
1218        When the kernel panics due to a corrupted zone element, get the
1219        element address and use this command.  This will show you the stack traces of all logged zalloc and
1220        zfree operations which tells you who touched the element in the recent past.  This also makes
1221        double-frees readily apparent.
1222    """
1223    if not cmd_args:
1224        print ZStackFindElem.__doc__
1225        return
1226    if int(kern.globals.log_records) == 0 or unsigned(kern.globals.corruption_debug_flag) == 0:
1227        print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args."
1228        return
1229
1230    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
1231    target_element = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
1232
1233    btrecord_size = unsigned(btlog_ptr.btrecord_size)
1234    btrecords = unsigned(btlog_ptr.btrecords)
1235    depth = unsigned(btlog_ptr.btrecord_btdepth)
1236
1237    prev_op = -1
1238    scan_items = 0
1239    hashelem = cast(btlog_ptr.elem_linkage_un.element_hash_queue.tqh_first, 'btlog_element_t *')
1240    if (target_element >> 32) != 0:
1241        target_element = target_element ^ 0xFFFFFFFFFFFFFFFF
1242    else:
1243        target_element = target_element ^ 0xFFFFFFFF
1244    while hashelem != 0:
1245        if unsigned(hashelem.elem) == target_element:
1246            recindex = hashelem.recindex
1247            recoffset = recindex * btrecord_size
1248            record = kern.GetValueFromAddress(btrecords + recoffset, 'btlog_record_t *')
1249            out_str = ('-' * 8)
1250            if record.operation == 1:
1251               out_str += "OP: ALLOC. "
1252            else:
1253               out_str += "OP: FREE.  "
1254            out_str += "Stack Index {0: <d} {1: <s}\n".format(recindex, ('-' * 8))
1255            print out_str
1256            print GetBtlogBacktrace(depth, record)
1257            print " \n"
1258            if int(record.operation) == prev_op:
1259                print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
1260                return
1261            prev_op = int(record.operation)
1262            scan_items = 0
1263        hashelem = cast(hashelem.element_hash_link.tqe_next, 'btlog_element_t *')
1264        scan_items += 1
1265        if scan_items % 100 == 0:
1266           print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items)
1267
1268# EndMacro: zstack_findelem
1269
1270@lldb_command('zstack_findtop', 'N:')
1271def ShowZstackTop(cmd_args=None, cmd_options={}):
1272    """ Zone leak debugging: search the log and print the stacks with the most active references
1273        in the stack trace.
1274
1275        Usage: zstack_findtop [-N <n-stacks>] <btlog-addr>
1276    """
1277
1278    if not cmd_args:
1279        raise ArgumentError('Missing required btlog address argument')
1280
1281    n = 5
1282    if '-N' in cmd_options:
1283        n = int(cmd_options['-N'])
1284
1285    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
1286    btrecord_size = unsigned(btlog_ptr.btrecord_size)
1287    btrecords = unsigned(btlog_ptr.btrecords)
1288
1289    cpcs_index = unsigned(btlog_ptr.head)
1290    depth = unsigned(btlog_ptr.btrecord_btdepth)
1291
1292    records = []
1293    while cpcs_index != 0xffffff:
1294        cpcs_record_offset = cpcs_index * btrecord_size
1295        cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
1296        cpcs_record.index = cpcs_index
1297        records.append(cpcs_record)
1298        cpcs_index = cpcs_record.next
1299
1300    recs = sorted(records, key=lambda x: x.ref_count, reverse=True)
1301
1302    for rec in recs[:n]:
1303        ShowZStackRecord(rec, rec.index, depth, unsigned(btlog_ptr.active_element_count))
1304
1305# EndMacro: zstack_findtop
1306
1307# Macro: btlog_find
1308
1309@lldb_command('btlog_find', "AS")
1310def BtlogFind(cmd_args=None, cmd_options={}):
1311    """
1312    """
1313    print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
1314    return
1315
1316#EndMacro: btlog_find
1317
1318#Macro: showzalloc
1319
1320@lldb_command('showzalloc')
1321def ShowZalloc(cmd_args=None):
1322    """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
1323        Usage: showzalloc <index>
1324    """
1325    if not cmd_args:
1326        print ShowZalloc.__doc__
1327        return
1328    if unsigned(kern.globals.zallocations) == 0:
1329        print "zallocations array not initialized!"
1330        return
1331    zallocation = kern.globals.zallocations[ArgumentStringToInt(cmd_args[0])]
1332    print zallocation
1333    ShowZTrace([str(int(zallocation.za_trace_index))])
1334
1335#EndMacro: showzalloc
1336
1337#Macro: showztrace
1338
1339@lldb_command('showztrace')
1340def ShowZTrace(cmd_args=None):
1341    """ Prints the backtrace from the ztraces array at index
1342        Usage: showztrace <trace index>
1343    """
1344    if not cmd_args:
1345        print ShowZTrace.__doc__
1346        return
1347    if unsigned(kern.globals.ztraces) == 0:
1348        print "ztraces array not initialized!"
1349        return
1350    ztrace_addr = kern.globals.ztraces[ArgumentStringToInt(cmd_args[0])]
1351    print ztrace_addr
1352    ShowZstackTraceHelper(ztrace_addr.zt_stack, ztrace_addr.zt_depth)
1353
1354#EndMacro: showztrace
1355
1356#Macro: showztraceaddr
1357
1358@lldb_command('showztraceaddr')
1359def ShowZTraceAddr(cmd_args=None):
1360    """ Prints the struct ztrace passed in.
1361        Usage: showztraceaddr <trace address>
1362    """
1363    if not cmd_args:
1364        print ShowZTraceAddr.__doc__
1365        return
1366    ztrace_ptr = kern.GetValueFromAddress(cmd_args[0], 'struct ztrace *')
1367    print dereference(ztrace_ptr)
1368    ShowZstackTraceHelper(ztrace_ptr.zt_stack, ztrace_ptr.zt_depth)
1369
1370#EndMacro: showztraceaddr
1371
1372#Macro: showzstacktrace
1373
1374@lldb_command('showzstacktrace')
1375def ShowZstackTrace(cmd_args=None):
1376    """ Routine to print a stacktrace stored by OSBacktrace.
1377        Usage: showzstacktrace <saved stacktrace> [size]
1378
1379        size is optional, defaults to 15.
1380    """
1381    if not cmd_args:
1382        print ShowZstackTrace.__doc__
1383        return
1384    void_ptr_type = gettype('void *')
1385    void_double_ptr_type = void_ptr_type.GetPointerType()
1386    trace = kern.GetValueFromAddress(cmd_args[0], void_double_ptr_type)
1387    trace_size = 15
1388    if len(cmd_args) >= 2:
1389        trace_size = ArgumentStringToInt(cmd_args[1])
1390    ShowZstackTraceHelper(trace, trace_size)
1391
1392#EndMacro: showzstacktrace
1393
1394def ShowZstackTraceHelper(stack, depth):
1395    """ Helper routine for printing a zstack.
1396        params:
1397            stack: void *[] - An array of pointers representing the Zstack
1398            depth: int - The depth of the ztrace stack
1399        returns:
1400            None
1401    """
1402    trace_current = 0
1403    while trace_current < depth:
1404        trace_addr = stack[trace_current]
1405        symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr))
1406        if symbol_arr:
1407            symbol_str = str(symbol_arr[0].addr)
1408        else:
1409            symbol_str = ''
1410        print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str)
1411        trace_current += 1
1412
1413#Macro: showtopztrace
1414
1415@lldb_command('showtopztrace')
1416def ShowTopZtrace(cmd_args=None):
1417    """ Shows the ztrace with the biggest size.
1418        (According to top_ztrace, not by iterating through the hash table)
1419    """
1420    top_trace = kern.globals.top_ztrace
1421    print 'Index: {0: <d}'.format((unsigned(top_trace) - unsigned(kern.globals.ztraces)) / sizeof('struct ztrace'))
1422    print dereference(top_trace)
1423    ShowZstackTraceHelper(top_trace.zt_stack, top_trace.zt_depth)
1424
1425#EndMacro: showtopztrace
1426
1427#Macro: showzallocs
1428
1429@lldb_command('showzallocs')
1430def ShowZallocs(cmd_args=None):
1431    """ Prints all allocations in the zallocations table
1432    """
1433    if unsigned(kern.globals.zallocations) == 0:
1434        print "zallocations array not initialized!"
1435        return
1436    print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
1437    current_index = 0
1438    max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
1439    allocation_count = 0
1440    while current_index < max_zallocation:
1441        current_zalloc = kern.globals.zallocations[current_index]
1442        if int(current_zalloc.za_element) != 0:
1443            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))
1444            allocation_count += 1
1445        current_index += 1
1446    print 'Total Allocations: {0: <d}'.format(allocation_count)
1447
1448#EndMacro: showzallocs
1449
1450#Macro: showzallocsfortrace
1451
1452@lldb_command('showzallocsfortrace')
1453def ShowZallocsForTrace(cmd_args=None):
1454    """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
1455        Usage:  showzallocsfortrace <trace index>
1456    """
1457    if not cmd_args:
1458        print ShowZallocsForTrace.__doc__
1459        return
1460    print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
1461    target_index = ArgumentStringToInt(cmd_args[0])
1462    current_index = 0
1463    max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
1464    allocation_count = 0
1465    while current_index < max_zallocation:
1466        current_zalloc = kern.globals.zallocations[current_index]
1467        if unsigned(current_zalloc.za_element) != 0 and (unsigned(current_zalloc.za_trace_index) == unsigned(target_index)):
1468            print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_size)
1469            allocation_count += 1
1470        current_index += 1
1471    print 'Total Allocations: {0: <d}'.format(allocation_count)
1472
1473#EndMacro: showzallocsfortrace
1474
1475#Macro: showztraces
1476
1477@lldb_command('showztraces')
1478def ShowZTraces(cmd_args=None):
1479    """ Prints all traces with size > 0
1480    """
1481    ShowZTracesAbove([0])
1482
1483#EndMacro: showztraces
1484
1485#Macro: showztracesabove
1486
1487@lldb_command('showztracesabove')
1488def ShowZTracesAbove(cmd_args=None):
1489    """ Prints all traces with size greater than X
1490        Usage: showztracesabove <size>
1491    """
1492    if not cmd_args:
1493        print ShowZTracesAbove.__doc__
1494        return
1495    print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
1496    current_index = 0
1497    ztrace_count = 0
1498    max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
1499    while current_index < max_ztrace:
1500        ztrace_current = kern.globals.ztraces[current_index]
1501        if ztrace_current.zt_size > unsigned(cmd_args[0]):
1502            print '{0: <5d} {1: <6d}'.format(current_index, int(ztrace_current.zt_size))
1503            ztrace_count += 1
1504        current_index += 1
1505    print 'Total traces: {0: <d}'.format(ztrace_count)
1506
1507#EndMacro: showztracesabove
1508
1509#Macro: showztracehistogram
1510
1511@lldb_command('showztracehistogram')
1512def ShowZtraceHistogram(cmd_args=None):
1513    """ Prints the histogram of the ztrace table
1514    """
1515    print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
1516    current_index = 0
1517    ztrace_count = 0
1518    max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
1519    while current_index < max_ztrace:
1520        ztrace_current = kern.globals.ztraces[current_index]
1521        if ztrace_current.zt_hit_count != 0:
1522            print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index, ztrace_current.zt_hit_count, ztrace_current.zt_collisions)
1523            ztrace_count += 1
1524        current_index += 1
1525    print 'Total traces: {0: <d}'.format(ztrace_count)
1526
1527#EndMacro: showztracehistogram
1528
1529#Macro: showzallochistogram
1530
1531@lldb_command('showzallochistogram')
1532def ShowZallocHistogram(cmd_args=None):
1533    """ Prints the histogram for the zalloc table
1534    """
1535    print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
1536    current_index = 0
1537    zallocation_count = 0
1538    max_ztrace = unsigned(kern.globals.zleak_alloc_buckets)
1539    while current_index < max_ztrace:
1540        zallocation_current = kern.globals.zallocations[current_index]
1541        if zallocation_current.za_hit_count != 0:
1542            print '{0: <5d} {1: <9d}'.format(current_index, zallocation_current.za_hit_count)
1543            zallocation_count += 1
1544        current_index += 1
1545    print 'Total Allocations: {0: <d}'.format(zallocation_count)
1546
1547#EndMacro: showzallochistogram
1548
1549#Macro: showzstats
1550
1551@lldb_command('showzstats')
1552def ShowZstats(cmd_args=None):
1553    """ Prints the zone leak detection stats
1554    """
1555    print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern.globals.z_alloc_collisions), unsigned(kern.globals.z_trace_collisions))
1556    print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern.globals.z_alloc_overwrites), unsigned(kern.globals.z_trace_overwrites))
1557    print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern.globals.z_alloc_recorded), unsigned(kern.globals.z_trace_recorded))
1558
1559#EndMacro: showzstats
1560
1561#Macro: showpcpu
1562
1563@lldb_command('showpcpu', "N:V", fancy=True)
1564def ShowPCPU(cmd_args=None, cmd_options={}, O=None):
1565    """ Show per-cpu variables
1566    usage: showpcpu [-N <cpu>] [-V] <variable name>
1567
1568    Use -N <cpu> to only dump the value for a given CPU number
1569    Use -V       to dump the values of the variables after their addresses
1570    """
1571
1572    if not cmd_args:
1573        raise ArgumentError("No arguments passed")
1574
1575    cpu = None
1576    ncpu = kern.globals.zpercpu_early_count
1577    pcpu_base = kern.globals.percpu_base
1578
1579    if "-N" in cmd_options:
1580        cpu = unsigned(int(cmd_options["-N"]))
1581        if cpu >= unsigned(ncpu):
1582            raise ArgumentError("Invalid cpu {d}".format(cpu))
1583
1584    var = addressof(kern.GetGlobalVariable('percpu_slot_' + cmd_args[0]))
1585    ty  = var.GetSBValue().GetTypeName()
1586
1587    r = range(0, ncpu)
1588    if cpu is not None:
1589        r = range(cpu, cpu + 1)
1590
1591    def PCPUSlot(pcpu_var, i):
1592        addr = unsigned(pcpu_var) + kern.PERCPU_BASE(i)
1593        return kern.GetValueFromAddress(addr, pcpu_var)
1594
1595    with O.table("{:<4s} {:<20s}".format("CPU", "address")):
1596        for i in r:
1597            print O.format("{:<4d} ({:s}){:#x}", i, ty, PCPUSlot(var, i))
1598
1599    if not "-V" in cmd_options:
1600        return
1601
1602    for i in r:
1603        with O.table("CPU {:d}".format(i)):
1604            print dereference(PCPUSlot(var, i))
1605
1606#EndMacro: showpcpu
1607
1608def GetBtlogBacktrace(depth, zstack_record):
1609    """ Helper routine for getting a BT Log record backtrace stack.
1610        params:
1611            depth:int - The depth of the zstack record
1612            zstack_record:btlog_record_t * - A BTLog record
1613        returns:
1614            str - string with backtrace in it.
1615    """
1616    out_str = ''
1617    frame = 0
1618    if not zstack_record:
1619        return "Zstack record none!"
1620
1621    depth_val = unsigned(depth)
1622    while frame < depth_val:
1623        frame_pc = zstack_record.bt[frame]
1624        if not frame_pc or int(frame_pc) == 0:
1625            break
1626        symbol_arr = kern.SymbolicateFromAddress(frame_pc)
1627        if symbol_arr:
1628            symbol_str = str(symbol_arr[0].addr)
1629        else:
1630            symbol_str = ''
1631        out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str)
1632        frame += 1
1633    return out_str
1634
1635def ShowZStackRecord(zstack_record, zstack_index, btrecord_btdepth, elements_count):
1636    """ Helper routine for printing a single zstack record
1637        params:
1638            zstack_record:btlog_record_t * -  A BTLog record
1639            zstack_index:int - Index for the record in the BTLog table
1640        returns:
1641            None
1642    """
1643    out_str = ('-' * 8)
1644    if zstack_record.operation == 1:
1645        out_str += "ALLOC.  "
1646    else:
1647        out_str += "FREE.   "
1648    out_str += "Stack Index {0: <d} with active refs {1: <d} of {2: <d} {3: <s}\n".format(zstack_index, zstack_record.ref_count, elements_count, ('-' * 8))
1649    print out_str
1650    print GetBtlogBacktrace(btrecord_btdepth, zstack_record)
1651    print " \n"
1652
1653# Macro: showioalloc
1654
1655@lldb_command('showioalloc')
1656def ShowIOAllocations(cmd_args=None):
1657    """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
1658        Routine to display a summary of memory accounting allocated by IOKit allocators.
1659    """
1660    print "Instance allocation  = {0: <#0x} = {1: d}K".format(kern.globals.debug_ivars_size, (kern.globals.debug_ivars_size / 1024))
1661    print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024))
1662    print "IOMalloc allocation  = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024))
1663    print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024))
1664
1665
1666# EndMacro: showioalloc
1667
1668
1669# Macro: showselectmem
1670@lldb_command('showselectmem', "S:")
1671def ShowSelectMem(cmd_args=None, cmd_options={}):
1672    """ Show memory cached by threads on calls to select.
1673
1674        usage: showselectmem [-v]
1675            -v        : print each thread's memory
1676                        (one line per thread with non-zero select memory)
1677            -S {addr} : Find the thread whose thread-local select set
1678                        matches the given address
1679    """
1680    verbose = False
1681    opt_wqs = 0
1682    if config['verbosity'] > vHUMAN:
1683        verbose = True
1684    if "-S" in cmd_options:
1685        opt_wqs = unsigned(kern.GetValueFromAddress(cmd_options["-S"], 'uint64_t *'))
1686        if opt_wqs == 0:
1687            raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options["-S"]))
1688    selmem = 0
1689    if verbose:
1690        print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)')
1691    for t in kern.tasks:
1692        for th in IterateQueue(t.threads, 'thread *', 'task_threads'):
1693            uth = GetBSDThread(th)
1694            wqs = 0
1695            if hasattr(uth, 'uu_allocsize'): # old style
1696                thmem = uth.uu_allocsize
1697                wqs = uth.uu_wqset
1698            elif hasattr(uth, 'uu_wqstate_sz'): # new style
1699                thmem = uth.uu_wqstate_sz
1700                wqs = uth.uu_wqset
1701            else:
1702                print "What kind of uthread is this?!"
1703                return
1704            if opt_wqs and opt_wqs == unsigned(wqs):
1705                print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs, unsigned(th), unsigned(th.thread_id))
1706            if verbose and thmem > 0:
1707                print "{:<#18x} {:<#10x} {:d}".format(unsigned(t), unsigned(th.thread_id), thmem)
1708            selmem += thmem
1709    print '-'*40
1710    print "Total: {:d} bytes ({:d} kbytes)".format(selmem, selmem/1024)
1711# Endmacro: showselectmem
1712
1713
1714# Macro: showtaskvme
1715@lldb_command('showtaskvme', "PS")
1716def ShowTaskVmeHelper(cmd_args=None, cmd_options={}):
1717    """ Display a summary list of the specified vm_map's entries
1718        Usage: showtaskvme <task address>  (ex. showtaskvme 0x00ataskptr00 )
1719        Use -S flag to show VM object shadow chains
1720        Use -P flag to show pager info (mapped file, compressed pages, ...)
1721    """
1722    show_pager_info = False
1723    show_all_shadows = False
1724    if "-P" in cmd_options:
1725        show_pager_info = True
1726    if "-S" in cmd_options:
1727        show_all_shadows = True
1728    task = kern.GetValueFromAddress(cmd_args[0], 'task *')
1729    ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
1730
1731@lldb_command('showallvme', "PS")
1732def ShowAllVME(cmd_args=None, cmd_options={}):
1733    """ Routine to print a summary listing of all the vm map entries
1734        Go Through each task in system and show the vm memory regions
1735        Use -S flag to show VM object shadow chains
1736        Use -P flag to show pager info (mapped file, compressed pages, ...)
1737    """
1738    show_pager_info = False
1739    show_all_shadows = False
1740    if "-P" in cmd_options:
1741        show_pager_info = True
1742    if "-S" in cmd_options:
1743        show_all_shadows = True
1744    for task in kern.tasks:
1745        ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
1746
1747@lldb_command('showallvm')
1748def ShowAllVM(cmd_args=None):
1749    """ Routine to print a summary listing of all the vm maps
1750    """
1751    for task in kern.tasks:
1752        print GetTaskSummary.header + ' ' + GetProcSummary.header
1753        print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
1754        print GetVMMapSummary.header
1755        print GetVMMapSummary(task.map)
1756
1757@lldb_command("showtaskvm")
1758def ShowTaskVM(cmd_args=None):
1759    """ Display info about the specified task's vm_map
1760        syntax: (lldb) showtaskvm <task_ptr>
1761    """
1762    if not cmd_args:
1763        print ShowTaskVM.__doc__
1764        return False
1765    task = kern.GetValueFromAddress(cmd_args[0], 'task *')
1766    if not task:
1767        print "Unknown arguments."
1768        return False
1769    print GetTaskSummary.header + ' ' + GetProcSummary.header
1770    print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
1771    print GetVMMapSummary.header
1772    print GetVMMapSummary(task.map)
1773    return True
1774
1775def GetLedgerEntryBalance(template, ledger, idx):
1776    entry = GetLedgerEntryWithTemplate(template, ledger, idx)
1777    return entry['balance']
1778
1779@lldb_command('showallvmstats')
1780def ShowAllVMStats(cmd_args=None):
1781    """ Print a summary of vm statistics in a table format
1782    """
1783    page_size = kern.globals.page_size
1784    vmstats = lambda:None
1785    vmstats.wired_count = 0
1786    vmstats.resident_count = 0
1787    vmstats.resident_max = 0
1788    vmstats.internal = 0
1789    vmstats.external = 0
1790    vmstats.reusable = 0
1791    vmstats.compressed = 0
1792    vmstats.compressed_peak = 0
1793    vmstats.compressed_lifetime = 0
1794    vmstats.error = ''
1795
1796    hdr_format = "{:>6s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<20s} {:1s}"
1797    print hdr_format.format('#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', 'pid', 'command', '')
1798    print hdr_format.format('', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '', '', '')
1799    entry_format = "{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} {1: >10d} {0: <32s} {s.error}"
1800
1801    ledger_template = kern.globals.task_ledger_template
1802    entry_indices = {}
1803    entry_indices['wired_mem'] = GetLedgerEntryIndex(ledger_template, 'wired_mem')
1804    assert(entry_indices['wired_mem'] != -1)
1805    entry_indices['phys_mem'] = GetLedgerEntryIndex(ledger_template, 'phys_mem')
1806    assert(entry_indices['phys_mem'] != -1)
1807    entry_indices['internal'] = GetLedgerEntryIndex(ledger_template, 'internal')
1808    assert(entry_indices['internal'] != -1)
1809    entry_indices['external'] = GetLedgerEntryIndex(ledger_template, 'external')
1810    assert(entry_indices['external'] != -1)
1811    entry_indices['reusable'] = GetLedgerEntryIndex(ledger_template, 'reusable')
1812    assert(entry_indices['reusable'] != -1)
1813    entry_indices['internal_compressed'] = GetLedgerEntryIndex(ledger_template, 'internal_compressed')
1814    assert(entry_indices['internal_compressed'] != -1)
1815
1816    for task in kern.tasks:
1817        proc = Cast(task.bsd_info, 'proc *')
1818        vmmap = Cast(task.map, '_vm_map *')
1819        page_size = 1 << int(vmmap.hdr.page_shift)
1820        task_ledgerp = task.ledger
1821        vmstats.error = ''
1822        def GetLedgerEntryBalancePages(template, ledger, index):
1823            return GetLedgerEntryBalance(template, ledger, index) / page_size
1824        vmstats.wired_count = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['wired_mem'])
1825        vmstats.resident_count = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['phys_mem'])
1826        vmstats.resident_max = GetLedgerEntryWithTemplate(ledger_template, task_ledgerp, entry_indices['phys_mem'])['lifetime_max'] / page_size
1827        vmstats.internal = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['internal'])
1828        vmstats.external = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['external'])
1829        vmstats.reusable = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['reusable'])
1830        vmstats.compressed = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['internal_compressed'])
1831        vmstats.compressed_peak = GetLedgerEntryWithTemplate(ledger_template, task_ledgerp, entry_indices['internal_compressed'])['lifetime_max'] / page_size
1832        vmstats.compressed_lifetime = GetLedgerEntryWithTemplate(ledger_template, task_ledgerp, entry_indices['internal_compressed'])['credit'] / page_size
1833        vmstats.new_resident_count = vmstats.internal + vmstats.external
1834
1835        if vmstats.internal < 0:
1836            vmstats.error += '*'
1837        if vmstats.external < 0:
1838            vmstats.error += '*'
1839        if vmstats.reusable < 0:
1840            vmstats.error += '*'
1841        if vmstats.compressed < 0:
1842            vmstats.error += '*'
1843        if vmstats.compressed_peak < 0:
1844            vmstats.error += '*'
1845        if vmstats.compressed_lifetime < 0:
1846            vmstats.error += '*'
1847        if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count:
1848            vmstats.error += '*'
1849
1850        print entry_format.format(GetProcName(proc), GetProcPID(proc), p=proc, m=vmmap, vsize=(unsigned(vmmap.size) / page_size), t=task, s=vmstats)
1851
1852
1853def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
1854    """  Routine to print out a summary listing of all the entries in a vm_map
1855        params:
1856            task - core.value : a object of type 'task *'
1857        returns:
1858            None
1859    """
1860    print "vm_map entries for task " + hex(task)
1861    print GetTaskSummary.header
1862    print GetTaskSummary(task)
1863    if not task.map:
1864        print "Task {0: <#020x} has map = 0x0"
1865        return None
1866    print GetVMMapSummary.header
1867    print GetVMMapSummary(task.map)
1868    vme_list_head = task.map.hdr.links
1869    vme_ptr_type = GetType('vm_map_entry *')
1870    print GetVMEntrySummary.header
1871    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
1872        print GetVMEntrySummary(vme, show_pager_info, show_all_shadows)
1873    return None
1874
1875@lldb_command("showmap")
1876def ShowMap(cmd_args=None):
1877    """ Routine to print out info about the specified vm_map
1878        usage: showmap <vm_map>
1879    """
1880    if cmd_args == None or len(cmd_args) < 1:
1881        print "Invalid argument.", ShowMap.__doc__
1882        return
1883    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1884    print GetVMMapSummary.header
1885    print GetVMMapSummary(map_val)
1886
1887@lldb_command("showmapvme")
1888def ShowMapVME(cmd_args=None):
1889    """Routine to print out info about the specified vm_map and its vm entries
1890        usage: showmapvme <vm_map>
1891    """
1892    if cmd_args == None or len(cmd_args) < 1:
1893        print "Invalid argument.", ShowMapVME.__doc__
1894        return
1895    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1896    print GetVMMapSummary.header
1897    print GetVMMapSummary(map_val)
1898    vme_list_head = map_val.hdr.links
1899    vme_ptr_type = GetType('vm_map_entry *')
1900    print GetVMEntrySummary.header
1901    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
1902        print GetVMEntrySummary(vme)
1903    return None
1904
1905def GetResidentPageCount(vmmap):
1906    resident_pages = 0
1907    ledger_template = kern.globals.task_ledger_template
1908    if vmmap.pmap != 0 and vmmap.pmap != kern.globals.kernel_pmap:
1909        idx = GetLedgerEntryIndex(ledger_template, "phys_mem")
1910        phys_mem = GetLedgerEntryBalance(ledger_template, vmmap.pmap.ledger, idx)
1911        resident_pages = phys_mem / kern.globals.page_size
1912    return resident_pages
1913
1914@lldb_type_summary(['_vm_map *', 'vm_map_t'])
1915@header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s} {7: <7s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free", "pgshift"))
1916def GetVMMapSummary(vmmap):
1917    """ Display interesting bits from vm_map struct """
1918    out_string = ""
1919    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x} {7: >7d}"
1920    vm_size = uint64_t(vmmap.size).value
1921    resident_pages = GetResidentPageCount(vmmap)
1922    first_free = 0
1923    if int(vmmap.holelistenabled) == 0: first_free = vmmap.f_s._first_free
1924    out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, first_free, vmmap.hdr.page_shift)
1925    return out_string
1926
1927@lldb_type_summary(['vm_map_entry'])
1928@header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
1929def GetVMEntrySummary(vme):
1930    """ Display vm entry specific information. """
1931    page_size = kern.globals.page_size
1932    out_string = ""
1933    format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
1934    vme_protection = int(vme.protection)
1935    vme_max_protection = int(vme.max_protection)
1936    vme_extra_info_str ="SC-Ds"[int(vme.inheritance)]
1937    if int(vme.is_sub_map) != 0 :
1938        vme_extra_info_str +="s"
1939    elif int(vme.needs_copy) != 0 :
1940        vme_extra_info_str +="n"
1941    num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) / page_size
1942    out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.vme_object.vmo_object, vme.vme_offset)
1943    return out_string
1944
1945# EndMacro: showtaskvme
1946@lldb_command('showmapwired')
1947def ShowMapWired(cmd_args=None):
1948    """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1949    """
1950    if cmd_args == None or len(cmd_args) < 1:
1951        print "Invalid argument", ShowMapWired.__doc__
1952        return
1953    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1954
1955
1956@lldb_type_summary(['kmod_info_t *'])
1957@header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} {7: >20s} {8: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size', 'version', 'name'))
1958def GetKextSummary(kmod):
1959    """ returns a string representation of kext information
1960    """
1961    out_string = ""
1962    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}"
1963    segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod.address), unsigned(kmod.size))
1964    text_segment = macho.get_text_segment(segments)
1965    if not text_segment:
1966        text_segment = segments[0]
1967    out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, text_segment.vmaddr, text_segment.vmsize, kmod.version, kmod.name)
1968    return out_string
1969
1970@lldb_type_summary(['uuid_t'])
1971@header("")
1972def GetUUIDSummary(uuid):
1973    """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1974    """
1975    arr = Cast(addressof(uuid), 'uint8_t *')
1976    data = []
1977    for i in range(16):
1978        data.append(int(arr[i]))
1979    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)
1980
1981@lldb_command('showallkmods')
1982def ShowAllKexts(cmd_args=None):
1983    """Display a summary listing of all loaded kexts (alias: showallkmods)
1984    """
1985    kmod_val = kern.globals.kmod
1986    kextuuidinfo = GetKextLoadInformation(show_progress=(config['verbosity'] > vHUMAN))
1987    print "{: <36s} ".format("UUID") + GetKextSummary.header
1988    for kval in IterateLinkedList(kmod_val, 'next'):
1989        uuid = "........-....-....-....-............"
1990        kaddr = unsigned(kval.address)
1991        found_kext_summary = None
1992        for l in kextuuidinfo :
1993            if kaddr == int(l[3],16):
1994                uuid = l[0]
1995                found_kext_summary = l
1996                break
1997        if found_kext_summary:
1998            _ksummary = GetKextSummary(found_kext_summary[7])
1999        else:
2000            _ksummary = GetKextSummary(kval)
2001        print uuid + " " + _ksummary
2002
2003def GetKmodWithAddr(addr):
2004    """ Go through kmod list and find one with begin_addr as addr
2005        returns: None if not found. else a cvalue of type kmod
2006    """
2007    kmod_val = kern.globals.kmod
2008    for kval in IterateLinkedList(kmod_val, 'next'):
2009        if addr == unsigned(kval.address):
2010                return kval
2011    return None
2012
2013def GetAllSegmentsAndSectionsFromDataInMemory(address, size):
2014    """ reads memory at address and parses mach_header to get segment and section information
2015        returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...])
2016            where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize'
2017            if TEXT segment is not found a dummy segment & section with address, size is returned.
2018    """
2019    cache_hash = "kern.kexts.segments.{}.{}".format(address, size)
2020    cached_result = caching.GetDynamicCacheData(cache_hash,())
2021    if cached_result:
2022        return cached_result
2023
2024    defval = macho.MachOSegment('__TEXT', address, size, 0, size)
2025    if address == 0 or size == 0:
2026        return ([defval], [defval])
2027
2028    ## if int(kern.globals.gLoadedKextSummaries.version) <= 2:
2029    # until we have separate version. we will pay penalty only on arm64 devices
2030    if not kern.arch.startswith('arm64'):
2031        return ([defval], [defval])
2032
2033    restrict_size_to_read = 1536
2034    machoObject = None
2035    while machoObject is None:
2036        err = lldb.SBError()
2037        size_to_read = min(size, restrict_size_to_read)
2038        data = LazyTarget.GetProcess().ReadMemory(address, size_to_read, err)
2039        if not err.Success():
2040            print "Failed to read memory at {} and size {}".format(address, size_to_read)
2041            return ([defval], [defval])
2042        try:
2043            m = macho.MemMacho(data, len(data))
2044            machoObject = m
2045        except Exception as e:
2046            if str(e.message).find('unpack requires a string argument') >= 0:
2047                # this may be due to short read of memory. Lets do double read size.
2048                restrict_size_to_read *= 2
2049                debuglog("Bumping mach header read size to {}".format(restrict_size_to_read))
2050                continue
2051            else:
2052                print "Failed to read MachO for address {} errormessage: {}".format(address, e.message)
2053                return ([defval], [defval])
2054    # end of while loop. We have machoObject defined
2055    segments = machoObject.get_segments_with_name('')
2056    sections = machoObject.get_sections_with_name('')
2057    rval = (segments, sections)
2058    caching.SaveDynamicCacheData(cache_hash, rval)
2059    return rval
2060
2061def GetKextLoadInformation(addr=0, show_progress=False):
2062    """ Extract the kext uuid and load address information from the kernel data structure.
2063        params:
2064            addr - int - optional integer that is the address to search for.
2065        returns:
2066            [] - array with each entry of format
2067                ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name',
2068                  'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj)
2069    """
2070    cached_result = caching.GetDynamicCacheData("kern.kexts.loadinformation", [])
2071    ## if specific addr is provided then ignore caching
2072    if cached_result and not addr:
2073        return cached_result
2074
2075    # because of <rdar://problem/12683084>, we can't find summaries directly
2076    #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
2077    baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10
2078    summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *')
2079    total_summaries = int(kern.globals.gLoadedKextSummaries.numSummaries)
2080    kext_version = int(kern.globals.gLoadedKextSummaries.version)
2081    entry_size = 64 + 16 + 8 + 8 + 8 + 4 + 4
2082    if kext_version >= 2 :
2083        entry_size = int(kern.globals.gLoadedKextSummaries.entry_size)
2084    retval = []
2085    for i in range(total_summaries):
2086        if show_progress:
2087            print "progress: {}/{}".format(i, total_summaries)
2088        tmpaddress = unsigned(summaries_begin) + (i * entry_size)
2089        current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *')
2090        # code to extract macho information
2091        segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext.address), unsigned(current_kext.size))
2092        seginfo = macho.get_text_segment(segments)
2093        if not seginfo:
2094            seginfo = segments[0]
2095        kmod_obj = GetKmodWithAddr(unsigned(current_kext.address))
2096        if addr != 0 :
2097            if addr == unsigned(current_kext.address) or addr == seginfo.vmaddr:
2098                return [(GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj)]
2099        retval.append((GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj))
2100
2101    if not addr:
2102        caching.SaveDynamicCacheData("kern.kexts.loadinformation", retval)
2103    return retval
2104
2105lldb_alias('showallkexts', 'showallkmods')
2106
2107def GetOSKextVersion(version_num):
2108    """ returns a string of format 1.2.3x from the version_num
2109        params: version_num - int
2110        return: str
2111    """
2112    if version_num == -1 :
2113        return "invalid"
2114    (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000)
2115    version = version_num
2116
2117    vers_major = version / MAJ_MULT
2118    version = version - (vers_major * MAJ_MULT)
2119
2120    vers_minor = version / MIN_MULT
2121    version = version - (vers_minor * MIN_MULT)
2122
2123    vers_revision = version / REV_MULT
2124    version = version - (vers_revision * REV_MULT)
2125
2126    vers_stage = version / STAGE_MULT
2127    version = version - (vers_stage * STAGE_MULT)
2128
2129    vers_stage_level = version
2130
2131    out_str = "%d.%d" % (vers_major, vers_minor)
2132    if vers_revision > 0: out_str += ".%d" % vers_revision
2133    if vers_stage == 1 : out_str += "d%d" % vers_stage_level
2134    if vers_stage == 3 : out_str += "a%d" % vers_stage_level
2135    if vers_stage == 5 : out_str += "b%d" % vers_stage_level
2136    if vers_stage == 6 : out_str += "fc%d" % vers_stage_level
2137
2138    return out_str
2139
2140@lldb_command('showallknownkmods')
2141def ShowAllKnownKexts(cmd_args=None):
2142    """ Display a summary listing of all kexts known in the system.
2143        This is particularly useful to find if some kext was unloaded before this crash'ed state.
2144    """
2145    kext_count = int(kern.globals.sKextsByID.count)
2146    index = 0
2147    kext_dictionary = kern.globals.sKextsByID.dictionary
2148    print "%d kexts in sKextsByID:" % kext_count
2149    print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
2150    format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
2151
2152    while index < kext_count:
2153        kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index)
2154        kext_name = str(kext_dict.key.string)
2155        osk = Cast(kext_dict.value, 'OSKext *')
2156        if int(osk.flags.loaded) :
2157            load_addr = "{0: <#020x}".format(osk.kmod_info)
2158            id = "{0: >5d}".format(osk.loadTag)
2159        else:
2160            load_addr = "------"
2161            id = "--"
2162        version_num = unsigned(osk.version)
2163        version = GetOSKextVersion(version_num)
2164        print format_string.format(osk, load_addr, id, version, kext_name)
2165        index += 1
2166
2167    return
2168
2169def FindKmodNameForAddr(addr):
2170    """ Given an address, return the name of the kext containing that address
2171    """
2172    addr = unsigned(addr)
2173    all_kexts_info = GetKextLoadInformation()
2174    for kinfo in all_kexts_info:
2175        segment = macho.get_segment_with_addr(kinfo[4], addr)
2176        if segment:
2177            return kinfo[7].name
2178    return None
2179
2180
2181@lldb_command('addkextaddr')
2182def AddKextAddr(cmd_args=[]):
2183    """ Given an address, load the kext which contains that address
2184        Syntax: (lldb) addkextaddr <addr>
2185    """
2186    if len(cmd_args) < 1:
2187        raise ArgumentError("Insufficient arguments")
2188
2189    addr = ArgumentStringToInt(cmd_args[0])
2190    all_kexts_info = GetKextLoadInformation()
2191    kernel_uuid = str(kern.globals.kernel_uuid_string).lower()
2192    found_kinfo = None
2193    found_segment = None
2194    for kinfo in all_kexts_info:
2195        segment = macho.get_segment_with_addr(kinfo[4], addr)
2196        if segment:
2197            print GetKextSummary.header
2198            print GetKextSummary(kinfo[7]) + " segment: {} offset = {:#0x}".format(segment.name, (addr - segment.vmaddr))
2199            cur_uuid = kinfo[0].lower()
2200            if (kernel_uuid == cur_uuid):
2201                print "(builtin)"
2202            else:
2203                print "Fetching dSYM for %s" % cur_uuid
2204                info = dsymForUUID(cur_uuid)
2205                if info and 'DBGSymbolRichExecutable' in info:
2206                    print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
2207                    addDSYM(cur_uuid, info)
2208                    loadDSYM(cur_uuid, int(kinfo[1],16), kinfo[4])
2209                else:
2210                    print "Failed to get symbol info for %s" % cur_uuid
2211            return
2212
2213
2214@lldb_command('showkmodaddr')
2215def ShowKmodAddr(cmd_args=[]):
2216    """ Given an address, print the offset and name for the kmod containing it
2217        Syntax: (lldb) showkmodaddr <addr>
2218    """
2219    if len(cmd_args) < 1:
2220        raise ArgumentError("Insufficient arguments")
2221
2222    addr = ArgumentStringToInt(cmd_args[0])
2223    all_kexts_info = GetKextLoadInformation()
2224    found_kinfo = None
2225    found_segment = None
2226    for kinfo in all_kexts_info:
2227        s = macho.get_segment_with_addr(kinfo[4], addr)
2228        if s:
2229            found_segment = s
2230            found_kinfo = kinfo
2231            break
2232    if found_kinfo:
2233        print GetKextSummary.header
2234        print GetKextSummary(found_kinfo[7]) + " segment: {} offset = {:#0x}".format(found_segment.name, (addr - found_segment.vmaddr))
2235        return True
2236    return False
2237
2238
2239@lldb_command('addkext','AF:N:')
2240def AddKextSyms(cmd_args=[], cmd_options={}):
2241    """ Add kext symbols into lldb.
2242        This command finds symbols for a uuid and load the required executable
2243        Usage:
2244            addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
2245            addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
2246            addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
2247            addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A
2248            addkext all    : Will load all the kext symbols - SLOW
2249    """
2250
2251
2252    if "-F" in cmd_options:
2253        exec_path = cmd_options["-F"]
2254        exec_full_path = ResolveFSPath(exec_path)
2255        if not os.path.exists(exec_full_path):
2256            raise ArgumentError("Unable to resolve {:s}".format(exec_path))
2257
2258        if not os.path.isfile(exec_full_path):
2259            raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
2260\nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
2261\nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path))
2262
2263        slide_value = None
2264        sections = None
2265        if cmd_args:
2266            slide_value = cmd_args[0]
2267            debuglog("loading slide value from user input {:s}".format(cmd_args[0]))
2268
2269        filespec = lldb.SBFileSpec(exec_full_path, False)
2270        print "target modules add \"{:s}\"".format(exec_full_path)
2271        print lldb_run_command("target modules add \"{:s}\"".format(exec_full_path))
2272        loaded_module = LazyTarget.GetTarget().FindModule(filespec)
2273        if loaded_module.IsValid():
2274            uuid_str = loaded_module.GetUUIDString()
2275            debuglog("added module {:s} with uuid {:s}".format(exec_full_path, uuid_str))
2276            if slide_value is None:
2277                all_kexts_info = GetKextLoadInformation()
2278                for k in all_kexts_info:
2279                    debuglog(k[0])
2280                    if k[0].lower() == uuid_str.lower():
2281                        slide_value = k[1]
2282                        sections = k[4]
2283                        debuglog("found the slide {:s} for uuid {:s}".format(k[1], k[0]))
2284        if slide_value is None:
2285            raise ArgumentError("Unable to find load address for module described at {:s} ".format(exec_full_path))
2286
2287        if not sections:
2288            cmd_str = "target modules load --file \"{:s}\" --slide {:s}".format(exec_full_path, str(slide_value))
2289            debuglog(cmd_str)
2290        else:
2291            cmd_str = "target modules load --file \"{:s}\"".format(exec_full_path)
2292            sections_str = ""
2293            for s in sections:
2294                sections_str += " {:s} {:#0x} ".format(s.name, s.vmaddr)
2295            cmd_str += sections_str
2296            debuglog(cmd_str)
2297
2298        lldb.debugger.HandleCommand(cmd_str)
2299
2300        kern.symbolicator = None
2301        return True
2302
2303    all_kexts_info = GetKextLoadInformation()
2304    kernel_uuid = str(kern.globals.kernel_uuid_string).lower()
2305
2306    if "-N" in cmd_options:
2307        kext_name = cmd_options["-N"]
2308        kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True)
2309        if len(kext_name_matches) != 1 and "-A" not in cmd_options:
2310            print "Ambiguous match for name: {:s}".format(kext_name)
2311            if len(kext_name_matches) > 0:
2312                print  "Options are:\n\t" + "\n\t".join(kext_name_matches)
2313            return
2314        debuglog("matched the kext to name {:s} and uuid {:s}".format(kext_name_matches[0], kext_name))
2315        for cur_knm in kext_name_matches:
2316            for x in all_kexts_info:
2317                if cur_knm == x[2]:
2318                    cur_uuid = x[0].lower()
2319                    if (kernel_uuid == cur_uuid):
2320                        print "(builtin)"
2321                    else:
2322                        print "Fetching dSYM for {:s}".format(cur_uuid)
2323                        info = dsymForUUID(cur_uuid)
2324                        if info and 'DBGSymbolRichExecutable' in info:
2325                            print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
2326                            addDSYM(cur_uuid, info)
2327                            loadDSYM(cur_uuid, int(x[1],16), x[4])
2328                        else:
2329                            print "Failed to get symbol info for {:s}".format(cur_uuid)
2330                    break
2331        kern.symbolicator = None
2332        return
2333
2334    if len(cmd_args) < 1:
2335        raise ArgumentError("No arguments specified.")
2336
2337    uuid = cmd_args[0].lower()
2338
2339    load_all_kexts = False
2340    if uuid == "all":
2341        load_all_kexts = True
2342
2343    if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
2344        raise ArgumentError("Unknown argument {:s}".format(uuid))
2345
2346    for k_info in all_kexts_info:
2347        cur_uuid = k_info[0].lower()
2348        if load_all_kexts or (uuid == cur_uuid):
2349            if (kernel_uuid != cur_uuid):
2350                print "Fetching dSYM for {:s}".format(cur_uuid)
2351                info = dsymForUUID(cur_uuid)
2352                if info and 'DBGSymbolRichExecutable' in info:
2353                    print "Adding dSYM ({:s}) for {:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
2354                    addDSYM(cur_uuid, info)
2355                    loadDSYM(cur_uuid, int(k_info[1],16), k_info[4])
2356                else:
2357                    print "Failed to get symbol info for {:s}".format(cur_uuid)
2358        #end of for loop
2359    kern.symbolicator = None
2360    return True
2361
2362
2363
2364lldb_alias('showkmod', 'showkmodaddr')
2365lldb_alias('showkext', 'showkmodaddr')
2366lldb_alias('showkextaddr', 'showkmodaddr')
2367
2368@lldb_type_summary(['mount *'])
2369@header("{0: <20s} {1: <20s} {2: <20s} {3: <12s} {4: <12s} {5: <12s} {6: >6s} {7: <30s} {8: <35s} {9: <30s}".format('volume(mp)', 'mnt_data', 'mnt_devvp', 'flag', 'kern_flag', 'lflag', 'type', 'mnton', 'mntfrom', 'iosched supported'))
2370def GetMountSummary(mount):
2371    """ Display a summary of mount on the system
2372    """
2373    out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
2374                  "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
2375                  "{vfs.f_mntonname: <30s} {vfs.f_mntfromname: <35s} {iomode: <30s}").format(mnt=mount, vfs=mount.mnt_vfsstat, iomode=('Yes' if (mount.mnt_ioflags & 0x4) else 'No'))
2376    return out_string
2377
2378@lldb_command('showallmounts')
2379def ShowAllMounts(cmd_args=None):
2380    """ Print all mount points
2381    """
2382    mntlist = kern.globals.mountlist
2383    print GetMountSummary.header
2384    for mnt in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
2385        print GetMountSummary(mnt)
2386    return
2387
2388lldb_alias('ShowAllVols', 'showallmounts')
2389
2390@lldb_command('systemlog')
2391def ShowSystemLog(cmd_args=None):
2392    """ Display the kernel's printf ring buffer """
2393    msgbufp = kern.globals.msgbufp
2394    msg_size = int(msgbufp.msg_size)
2395    msg_bufx = int(msgbufp.msg_bufx)
2396    msg_bufr = int(msgbufp.msg_bufr)
2397    msg_bufc = msgbufp.msg_bufc
2398    msg_bufc_data = msg_bufc.GetSBValue().GetPointeeData(0, msg_size)
2399
2400    # the buffer is circular; start at the write pointer to end,
2401    # then from beginning to write pointer
2402    line = ''
2403    err = lldb.SBError()
2404    for i in range(msg_bufx, msg_size) + range(0, msg_bufx) :
2405        err.Clear()
2406        cbyte = msg_bufc_data.GetUnsignedInt8(err, i)
2407        if not err.Success() :
2408            raise ValueError("Failed to read character at offset " + str(i) + ": " + err.GetCString())
2409        c = chr(cbyte)
2410        if c == '\0' :
2411            continue
2412        elif c == '\n' :
2413            print line
2414            line = ''
2415        else :
2416            line += c
2417
2418    if len(line) > 0 :
2419        print line
2420
2421    return
2422
2423@static_var('output','')
2424def _GetVnodePathName(vnode, vnodename):
2425    """ Internal function to get vnode path string from vnode structure.
2426        params:
2427            vnode - core.value
2428            vnodename - str
2429        returns Nothing. The output will be stored in the static variable.
2430    """
2431    if not vnode:
2432        return
2433    if int(vnode.v_flag) & 0x1 and int(hex(vnode.v_mount), 16) !=0:
2434        if int(vnode.v_mount.mnt_vnodecovered):
2435            _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) )
2436    else:
2437        _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name))
2438        _GetVnodePathName.output += "/%s" % vnodename
2439
2440def GetVnodePath(vnode):
2441    """ Get string representation of the vnode
2442        params: vnodeval - value representing vnode * in the kernel
2443        return: str - of format /path/to/something
2444    """
2445    out_str = ''
2446    if vnode:
2447            if (int(vnode.v_flag) & 0x000001) and int(hex(vnode.v_mount), 16) != 0 and (int(vnode.v_mount.mnt_flag) & 0x00004000) :
2448                out_str += "/"
2449            else:
2450                _GetVnodePathName.output = ''
2451                if abs(vnode.v_name) != 0:
2452                    _GetVnodePathName(vnode, str(vnode.v_name))
2453                    out_str += _GetVnodePathName.output
2454                else:
2455                    out_str += 'v_name = NULL'
2456                _GetVnodePathName.output = ''
2457    return out_str
2458
2459
2460@lldb_command('showvnodepath')
2461def ShowVnodePath(cmd_args=None):
2462    """ Prints the path for a vnode
2463        usage: showvnodepath <vnode>
2464    """
2465    if cmd_args != None and len(cmd_args) > 0 :
2466        vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
2467        if vnode_val:
2468            print GetVnodePath(vnode_val)
2469    return
2470
2471# Macro: showvnodedev
2472def GetVnodeDevInfo(vnode):
2473    """ Internal function to get information from the device type vnodes
2474        params: vnode - value representing struct vnode *
2475        return: str - formatted output information for block and char vnode types passed as param
2476    """
2477    vnodedev_output = ""
2478    vblk_type = GetEnumValue('vtype::VBLK')
2479    vchr_type = GetEnumValue('vtype::VCHR')
2480    if (vnode.v_type == vblk_type) or (vnode.v_type == vchr_type):
2481        devnode = Cast(vnode.v_data, 'devnode_t *')
2482        devnode_dev = devnode.dn_typeinfo.dev
2483        devnode_major = (devnode_dev >> 24) & 0xff
2484        devnode_minor = devnode_dev & 0x00ffffff
2485
2486        # boilerplate device information for a vnode
2487        vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode)
2488        vnodedev_output += "\n\t type:\t\t"
2489        if (vnode.v_type == vblk_type):
2490            vnodedev_output += "VBLK"
2491        if (vnode.v_type == vchr_type):
2492            vnodedev_output += "VCHR"
2493        vnodedev_output += "\n\t name:\t\t{:<s}".format(vnode.v_name)
2494        vnodedev_output += "\n\t major, minor:\t{:d},{:d}".format(devnode_major, devnode_minor)
2495        vnodedev_output += "\n\t mode\t\t0{:o}".format(unsigned(devnode.dn_mode))
2496        vnodedev_output += "\n\t owner (u,g):\t{:d} {:d}".format(devnode.dn_uid, devnode.dn_gid)
2497
2498        # decode device specific data
2499        vnodedev_output += "\nDevice Specific Information:\t"
2500        if (vnode.v_type == vblk_type):
2501            vnodedev_output += "Sorry, I do not know how to decode block devices yet!"
2502            vnodedev_output += "\nMaybe you can write me!"
2503
2504        if (vnode.v_type == vchr_type):
2505            # Device information; this is scanty
2506            # range check
2507            if (devnode_major > 42) or (devnode_major < 0):
2508                vnodedev_output +=  "Invalid major #\n"
2509            # static assignments in conf
2510            elif (devnode_major == 0):
2511                vnodedev_output += "Console mux device\n"
2512            elif (devnode_major == 2):
2513                vnodedev_output += "Current tty alias\n"
2514            elif (devnode_major == 3):
2515                vnodedev_output += "NULL device\n"
2516            elif (devnode_major == 4):
2517                vnodedev_output += "Old pty slave\n"
2518            elif (devnode_major == 5):
2519                vnodedev_output += "Old pty master\n"
2520            elif (devnode_major == 6):
2521                vnodedev_output += "Kernel log\n"
2522            elif (devnode_major == 12):
2523                vnodedev_output += "Memory devices\n"
2524            # Statically linked dynamic assignments
2525            elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptmx_open')):
2526                vnodedev_output += "Cloning pty master not done\n"
2527                #GetVnodeDevCpty(devnode_major, devnode_minor)
2528            elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptsd_open')):
2529                vnodedev_output += "Cloning pty slave not done\n"
2530                #GetVnodeDevCpty(devnode_major, devnode_minor)
2531            else:
2532                vnodedev_output += "RESERVED SLOT\n"
2533    else:
2534        vnodedev_output += "{:#x} is not a device".format(vnode)
2535    return vnodedev_output
2536
2537@lldb_command('showvnodedev')
2538def ShowVnodeDev(cmd_args=None):
2539    """  Routine to display details of all vnodes of block and character device types
2540         Usage: showvnodedev <address of vnode>
2541    """
2542    if not cmd_args:
2543        print "No arguments passed"
2544        print ShowVnodeDev.__doc__
2545        return False
2546    vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
2547    if not vnode_val:
2548        print "unknown arguments:", str(cmd_args)
2549        return False
2550    print GetVnodeDevInfo(vnode_val)
2551
2552# EndMacro: showvnodedev
2553
2554# Macro: showvnodelocks
2555def GetVnodeLock(lockf):
2556    """ Internal function to get information from the given advisory lock
2557        params: lockf - value representing v_lockf member in struct vnode *
2558        return: str - formatted output information for the advisory lock
2559    """
2560    vnode_lock_output = ''
2561    lockf_flags = lockf.lf_flags
2562    lockf_type = lockf.lf_type
2563    if lockf_flags & 0x20:
2564        vnode_lock_output += ("{: <8s}").format('flock')
2565    if lockf_flags & 0x40:
2566        vnode_lock_output += ("{: <8s}").format('posix')
2567    if lockf_flags & 0x80:
2568        vnode_lock_output += ("{: <8s}").format('prov')
2569    if lockf_flags & 0x10:
2570        vnode_lock_output += ("{: <4s}").format('W')
2571    if lockf_flags & 0x400:
2572        vnode_lock_output += ("{: <8s}").format('ofd')
2573    else:
2574        vnode_lock_output += ("{: <4s}").format('.')
2575
2576    # POSIX file vs advisory range locks
2577    if lockf_flags & 0x40:
2578        lockf_proc = Cast(lockf.lf_id, 'proc *')
2579        vnode_lock_output += ("PID {: <18d}").format(GetProcPID(lockf_proc))
2580    else:
2581        vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id))
2582
2583    # lock type
2584    if lockf_type == 1:
2585        vnode_lock_output += ("{: <12s}").format('shared')
2586    else:
2587        if lockf_type == 3:
2588            vnode_lock_output += ("{: <12s}").format('exclusive')
2589        else:
2590            if lockf_type == 2:
2591                vnode_lock_output += ("{: <12s}").format('unlock')
2592            else:
2593                vnode_lock_output += ("{: <12s}").format('unknown')
2594
2595    # start and stop values
2596    vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start)
2597    vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end)
2598    return vnode_lock_output
2599
2600@header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
2601def GetVnodeLocksSummary(vnode):
2602    """ Internal function to get summary of advisory locks for the given vnode
2603        params: vnode - value representing the vnode object
2604        return: str - formatted output information for the summary of advisory locks
2605    """
2606    out_str = ''
2607    if vnode:
2608            lockf_list = vnode.v_lockf
2609            for lockf_itr in IterateLinkedList(lockf_list, 'lf_next'):
2610                out_str += ("{: <4s}").format('H')
2611                out_str += GetVnodeLock(lockf_itr)
2612                lockf_blocker = lockf_itr.lf_blkhd.tqh_first
2613                while lockf_blocker:
2614                    out_str += ("{: <4s}").format('>')
2615                    out_str += GetVnodeLock(lockf_blocker)
2616                    lockf_blocker = lockf_blocker.lf_block.tqe_next
2617    return out_str
2618
2619@lldb_command('showvnodelocks')
2620def ShowVnodeLocks(cmd_args=None):
2621    """  Routine to display list of advisory record locks for the given vnode address
2622         Usage: showvnodelocks <address of vnode>
2623    """
2624    if not cmd_args:
2625        print "No arguments passed"
2626        print ShowVnodeLocks.__doc__
2627        return False
2628    vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
2629    if not vnode_val:
2630        print "unknown arguments:", str(cmd_args)
2631        return False
2632    print GetVnodeLocksSummary.header
2633    print GetVnodeLocksSummary(vnode_val)
2634
2635# EndMacro: showvnodelocks
2636
2637# Macro: showproclocks
2638
2639@lldb_command('showproclocks')
2640def ShowProcLocks(cmd_args=None):
2641    """  Routine to display list of advisory record locks for the given process
2642         Usage: showproclocks <address of proc>
2643    """
2644    if not cmd_args:
2645        print "No arguments passed"
2646        print ShowProcLocks.__doc__
2647        return False
2648    proc = kern.GetValueFromAddress(cmd_args[0], 'proc *')
2649    if not proc:
2650        print "unknown arguments:", str(cmd_args)
2651        return False
2652    out_str = ''
2653    proc_filedesc = addressof(proc.p_fd)
2654    fd_ofiles = proc_filedesc.fd_ofiles
2655    seen = 0
2656
2657    for fd in xrange(0, unsigned(proc_filedesc.fd_afterlast)):
2658        if fd_ofiles[fd]:
2659            fglob = fd_ofiles[fd].fp_glob
2660            fo_type = fglob.fg_ops.fo_type
2661            if fo_type == 1:
2662                fg_data = Cast(fglob.fg_data, 'void *')
2663                fg_vnode = Cast(fg_data, 'vnode *')
2664                name = fg_vnode.v_name
2665                lockf_itr = fg_vnode.v_lockf
2666                if lockf_itr:
2667                    if not seen:
2668                        print GetVnodeLocksSummary.header
2669                    seen = seen + 1
2670                    out_str += ("\n( fd {:d}, name ").format(fd)
2671                    if not name:
2672                        out_str += "(null) )\n"
2673                    else:
2674                        out_str += "{:s} )\n".format(name)
2675                    print out_str
2676                    print GetVnodeLocksSummary(fg_vnode)
2677    print "\n{0: d} total locks for {1: #018x}".format(seen, proc)
2678
2679# EndMacro: showproclocks
2680
2681@lldb_type_summary(['vnode_t', 'vnode *'])
2682@header("{0: <20s} {1: >8s} {2: >9s} {3: >8s} {4: <20s} {5: <6s} {6: <20s} {7: <6s} {8: <6s} {9: <35s}".format('vnode', 'usecount', 'kusecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'cs_version', 'name'))
2683def GetVnodeSummary(vnode):
2684    """ Get a summary of important information out of vnode
2685    """
2686    out_str = ''
2687    format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: >8d} {4: <#020x} {5: <6s} {6: <#020x} {7: <6s} {8: <6s} {9: <35s}"
2688    usecount = int(vnode.v_usecount)
2689    kusecount = int(vnode.v_kusecount)
2690    iocount = int(vnode.v_iocount)
2691    v_data_ptr = int(hex(vnode.v_data), 16)
2692    vtype = int(vnode.v_type)
2693    vtype_str = "%d" % vtype
2694    vnode_types = ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX']  # see vnode.h for enum type definition
2695    if vtype >= 0 and vtype < len(vnode_types):
2696        vtype_str = vnode_types[vtype]
2697    parent_ptr = int(hex(vnode.v_parent), 16)
2698    name_ptr = int(hex(vnode.v_name), 16)
2699    name =""
2700    if name_ptr != 0:
2701        name = str(vnode.v_name)
2702    elif int(vnode.v_tag) == 16 :
2703        cnode = Cast(vnode.v_data, 'cnode *')
2704        name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *'))
2705    mapped = '-'
2706    csblob_version = '-'
2707    if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0):
2708        csblob_version = '{: <6d}'.format(vnode.v_un.vu_ubcinfo.cs_add_gen)
2709        # Check to see if vnode is mapped/unmapped
2710        if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0:
2711            mapped = '1'
2712        else:
2713            mapped = '0'
2714    out_str += format_string.format(vnode, usecount, kusecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, csblob_version, name)
2715    return out_str
2716
2717@lldb_command('showallvnodes')
2718def ShowAllVnodes(cmd_args=None):
2719    """ Display info about all vnodes
2720    """
2721    mntlist = kern.globals.mountlist
2722    print GetVnodeSummary.header
2723    for mntval in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
2724        for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2725            print GetVnodeSummary(vnodeval)
2726    return
2727
2728@lldb_command('showvnode')
2729def ShowVnode(cmd_args=None):
2730    """ Display info about one vnode
2731        usage: showvnode <vnode>
2732    """
2733    if cmd_args == None or len(cmd_args) < 1:
2734        print  "Please provide valid vnode argument. Type help showvnode for help."
2735        return
2736    vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *')
2737    print GetVnodeSummary.header
2738    print GetVnodeSummary(vnodeval)
2739
2740@lldb_command('showvolvnodes')
2741def ShowVolVnodes(cmd_args=None):
2742    """ Display info about all vnodes of a given mount_t
2743    """
2744    if cmd_args == None or len(cmd_args) < 1:
2745        print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
2746        return
2747    mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
2748    print GetVnodeSummary.header
2749    for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2750        print GetVnodeSummary(vnodeval)
2751    return
2752
2753@lldb_command('showvolbusyvnodes')
2754def ShowVolBusyVnodes(cmd_args=None):
2755    """ Display info about busy (iocount!=0) vnodes of a given mount_t
2756    """
2757    if cmd_args == None or len(cmd_args) < 1:
2758        print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
2759        return
2760    mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
2761    print GetVnodeSummary.header
2762    for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2763        if int(vnodeval.v_iocount) != 0:
2764            print GetVnodeSummary(vnodeval)
2765
2766@lldb_command('showallbusyvnodes')
2767def ShowAllBusyVnodes(cmd_args=None):
2768    """ Display info about all busy (iocount!=0) vnodes
2769    """
2770    mntlistval = kern.globals.mountlist
2771    for mntval in IterateTAILQ_HEAD(mntlistval, 'mnt_list'):
2772        ShowVolBusyVnodes([hex(mntval)])
2773
2774@lldb_command('print_vnode')
2775def PrintVnode(cmd_args=None):
2776    """ Prints out the fields of a vnode struct
2777        Usage: print_vnode <vnode>
2778    """
2779    if not cmd_args:
2780        print  "Please provide valid vnode argument. Type help print_vnode for help."
2781        return
2782    ShowVnode(cmd_args)
2783
2784@lldb_command('showworkqvnodes')
2785def ShowWorkqVnodes(cmd_args=None):
2786    """ Print the vnode worker list
2787        Usage: showworkqvnodes <struct mount *>
2788    """
2789    if not cmd_args:
2790        print "Please provide valid mount argument. Type help showworkqvnodes for help."
2791        return
2792
2793    mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
2794    vp = Cast(mp.mnt_workerqueue.tqh_first, 'vnode *')
2795    print GetVnodeSummary.header
2796    while int(vp) != 0:
2797        print GetVnodeSummary(vp)
2798        vp = vp.v_mntvnodes.tqe_next
2799
2800@lldb_command('shownewvnodes')
2801def ShowNewVnodes(cmd_args=None):
2802    """ Print the new vnode list
2803        Usage: shownewvnodes <struct mount *>
2804    """
2805    if not cmd_args:
2806        print "Please provide valid mount argument. Type help shownewvnodes for help."
2807        return
2808    mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
2809    vp = Cast(mp.mnt_newvnodes.tqh_first, 'vnode *')
2810    print GetVnodeSummary.header
2811    while int(vp) != 0:
2812        print GetVnodeSummary(vp)
2813        vp = vp.v_mntvnodes.tqe_next
2814
2815
2816@lldb_command('showprocvnodes')
2817def ShowProcVnodes(cmd_args=None):
2818    """ Routine to print out all the open fds which are vnodes in a process
2819        Usage: showprocvnodes <proc *>
2820    """
2821    if not cmd_args:
2822        print "Please provide valid proc argument. Type help showprocvnodes for help."
2823        return
2824    procptr = kern.GetValueFromAddress(cmd_args[0], 'proc *')
2825    fdptr = addressof(procptr.p_fd)
2826    if int(fdptr.fd_cdir) != 0:
2827        print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_cdir))
2828    if int(fdptr.fd_rdir) != 0:
2829        print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir))
2830    count = 0
2831    print '\n' + '{0: <5s} {1: <7s} {2: <20s} '.format('fd', 'flags', 'fileglob') + GetVnodeSummary.header
2832    # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
2833    fpptr = Cast(fdptr.fd_ofiles, 'uint64_t *')
2834    while count < fdptr.fd_nfiles:
2835        fpp = dereference(fpptr)
2836        fproc = kern.GetValueFromAddress(int(fpp), 'fileproc *')
2837        if int(fproc) != 0:
2838            fglob = dereference(fproc).fp_glob
2839            fglob_fg_data = Cast(fglob.fg_data, 'void *')
2840            flags = ""
2841            if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1):
2842                if (fpp.fp_flags & GetEnumValue('fileproc_flags_t', 'FP_CLOEXEC')):    flags += 'E'
2843                if (fpp.fp_flags & GetEnumValue('fileproc_flags_t', 'FP_CLOFORK')):    flags += 'F'
2844                if (fdptr.fd_ofileflags[count] & 4):    flags += 'R'
2845                if (fdptr.fd_ofileflags[count] & 8):    flags += 'C'
2846                print '{0: <5d} {1: <7s} {2: <#020x} '.format(count, flags, fglob) + GetVnodeSummary(Cast(fglob_fg_data, 'vnode *'))
2847        count += 1
2848        fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'uint64_t *')
2849
2850@lldb_command('showallprocvnodes')
2851def ShowAllProcVnodes(cmd_args=None):
2852    """ Routine to print out all the open fds which are vnodes
2853    """
2854
2855    procptr = Cast(kern.globals.allproc.lh_first, 'proc *')
2856    while procptr and int(procptr) != 0:
2857        print '{:<s}'.format("=" * 106)
2858        print GetProcInfo(procptr)
2859        ShowProcVnodes([int(procptr)])
2860        procptr = procptr.p_list.le_next
2861
2862@xnudebug_test('test_vnode')
2863def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
2864    """ Test the functionality of vnode related commands
2865        returns
2866         - False on failure
2867         - True on success
2868    """
2869    if not isConnected:
2870        print "Target is not connected. Cannot test memstats"
2871        return False
2872    res = lldb.SBCommandReturnObject()
2873    lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showallvnodes", res)
2874    result = res.GetOutput()
2875    if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5:
2876        return True
2877    else:
2878        return False
2879
2880# Macro: showallmtx
2881@lldb_type_summary(['_lck_grp_ *'])
2882def GetMutexEntry(mtxg):
2883    """ Summarize a mutex group entry  with important information.
2884        params:
2885        mtxg: value - obj representing a mutex group in kernel
2886        returns:
2887        out_string - summary of the mutex group
2888        """
2889    out_string = ""
2890
2891    if kern.ptrsize == 8:
2892        format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2893    else:
2894        format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2895
2896    if mtxg.lck_grp_mtxcnt:
2897        out_string += format_string.format(mtxg, mtxg.lck_grp_mtxcnt,mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_util_cnt,
2898                                           mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_miss_cnt,
2899                                           mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_wait_cnt, mtxg.lck_grp_name)
2900    return out_string
2901
2902@lldb_command('showallmtx')
2903def ShowAllMtx(cmd_args=None):
2904    """ Routine to print a summary listing of all mutexes
2905    """
2906
2907    if kern.ptrsize == 8:
2908        hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2909    else:
2910        hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2911
2912    print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2913
2914    mtxgrp_queue_head = kern.globals.lck_grp_queue
2915    mtxgrp_ptr_type = GetType('_lck_grp_ *')
2916
2917    for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"):
2918       print GetMutexEntry(mtxgrp_ptr)
2919    return
2920# EndMacro: showallmtx
2921
2922# Macro: showallrwlck
2923@lldb_type_summary(['_lck_grp_ *'])
2924def GetRWLEntry(rwlg):
2925    """ Summarize a reader writer lock group with important information.
2926        params:
2927        rwlg: value - obj representing a reader writer lock group in kernel
2928        returns:
2929        out_string - summary of the reader writer lock group
2930    """
2931    out_string = ""
2932
2933    if kern.ptrsize == 8:
2934        format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2935    else:
2936        format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2937
2938    if rwlg.lck_grp_rwcnt:
2939        out_string += format_string.format(rwlg, rwlg.lck_grp_rwcnt,rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_util_cnt,
2940                                           rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_miss_cnt,
2941                                           rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name)
2942    return out_string
2943
2944#Macro: showlock
2945@lldb_type_summary(['lck_mtx_t *'])
2946@header("===== Mutex Lock Summary =====")
2947def GetMutexLockSummary(mtx):
2948    """ Summarize mutex lock with important information.
2949        params:
2950        mtx: value - obj representing a mutex lock in kernel
2951        returns:
2952        out_str - summary of the mutex lock
2953    """
2954    if not mtx:
2955        return "Invalid lock value: 0x0"
2956
2957    if kern.arch == "x86_64":
2958        out_str = "Lock Type            : MUTEX\n"
2959        if mtx.lck_mtx_tag == 0x07ff1007 :
2960            out_str += "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx.lck_mtx_ptr)
2961            mtx = Cast(mtx.lck_mtx_ptr, 'lck_mtx_t *')
2962
2963        if mtx.lck_mtx_tag == 0x07fe2007 :
2964            out_str += "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx.lck_mtx_tag)
2965
2966        out_str += "Owner Thread        : {mtx.lck_mtx_owner:#x}\n".format(mtx=mtx)
2967        out_str += "Number of Waiters   : {mtx.lck_mtx_waiters:#x}\n".format(mtx=mtx)
2968        out_str += "ILocked             : {mtx.lck_mtx_ilocked:#x}\n".format(mtx=mtx)
2969        out_str += "MLocked             : {mtx.lck_mtx_mlocked:#x}\n".format(mtx=mtx)
2970        out_str += "Promoted            : {mtx.lck_mtx_promoted:#x}\n".format(mtx=mtx)
2971        out_str += "Pri                 : {mtx.lck_mtx_pri:#x}\n".format(mtx=mtx)
2972        out_str += "Spin                : {mtx.lck_mtx_spin:#x}\n".format(mtx=mtx)
2973        out_str += "Ext                 : {mtx.lck_mtx_is_ext:#x}\n".format(mtx=mtx)
2974        if mtx.lck_mtx_pad32 == 0xFFFFFFFF :
2975            out_str += "Canary (valid)      : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx)
2976        else:
2977            out_str += "Canary (INVALID)    : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx)
2978        return out_str
2979
2980    LCK_MTX_TYPE = 0x22
2981    out_str = "Lock Type\t\t: MUTEX\n"
2982    if mtx.lck_mtx_type != LCK_MTX_TYPE:
2983        out_str += "Mutex Invalid"
2984        return out_str
2985    out_str += "Owner Thread\t\t: {:#x}".format(mtx.lck_mtx_data & ~0x3)
2986    if (mtx.lck_mtx_data & ~0x3) == 0xfffffff0:
2987        out_str += " Held as spinlock"
2988    out_str += "\nNumber of Waiters\t: {:d}\n".format(mtx.lck_mtx_waiters)
2989    out_str += "Flags\t\t\t: "
2990    if mtx.lck_mtx_data & 0x1:
2991        out_str += "[Interlock Locked] "
2992    if mtx.lck_mtx_data & 0x2:
2993        out_str += "[Wait Flag]"
2994    return out_str
2995
2996@lldb_type_summary(['lck_spin_t *'])
2997@header("===== SpinLock Summary =====")
2998def GetSpinLockSummary(spinlock):
2999    """ Summarize spinlock with important information.
3000        params:
3001        spinlock: value - obj representing a spinlock in kernel
3002        returns:
3003        out_str - summary of the spinlock
3004    """
3005    if not spinlock:
3006        return "Invalid lock value: 0x0"
3007
3008    out_str = "Lock Type\t\t: SPINLOCK\n"
3009    if kern.arch == "x86_64":
3010        out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
3011        return out_str
3012    LCK_SPIN_TYPE = 0x11
3013    if spinlock.type != LCK_SPIN_TYPE:
3014        out_str += "Spinlock Invalid"
3015        return out_str
3016    lock_data = spinlock.hwlock.lock_data
3017    if lock_data == 1:
3018        out_str += "Invalid state: interlock is locked but no owner\n"
3019        return out_str
3020    out_str += "Owner Thread\t\t: "
3021    if lock_data == 0:
3022        out_str += "None\n"
3023    else:
3024        out_str += "{:#x}\n".format(lock_data & ~0x1)
3025        if (lock_data & 1) == 0:
3026            out_str += "Invalid state: owned but interlock bit is not set\n"
3027    return out_str
3028
3029@lldb_type_summary(['lck_rw_t *'])
3030@header("===== RWLock Summary =====")
3031def GetRWLockSummary(rwlock):
3032    """ Summarize rwlock with important information.
3033        params:
3034        rwlock: value - obj representing a lck_rw_lock in kernel
3035        returns:
3036        out_str - summary of the rwlock
3037    """
3038    if not rwlock:
3039        return "Invalid lock value: 0x0"
3040
3041    out_str = "Lock Type\t\t: RWLOCK\n"
3042    lock_word = rwlock.word
3043    out_str += "Blocking\t\t: "
3044    if lock_word.can_sleep == 0:
3045        out_str += "FALSE\n"
3046    else:
3047        out_str += "TRUE\n"
3048    if lock_word.priv_excl == 0:
3049        out_str += "Recusive\t\t: shared recursive\n"
3050    out_str += "Interlock\t\t: {:#x}\n".format(lock_word.interlock)
3051    out_str += "Writer bits\t\t: "
3052    if lock_word.want_upgrade == 0 and lock_word.want_excl == 0:
3053        out_str += "-\n"
3054    else:
3055        if lock_word.want_upgrade == 1:
3056            out_str += "Read-to-write upgrade requested"
3057            if lock_word.want_excl == 1:
3058                out_str += ","
3059            else:
3060                out_str += "\n"
3061        if lock_word.want_excl == 1:
3062            out_str += "Write ownership requested\n"
3063    out_str += "Write owner\t\t: {:#x}\n".format(rwlock.lck_rw_owner)
3064    out_str += "Reader(s)    \t\t: "
3065    if lock_word.shared_count > 0:
3066        out_str += "{:#d}\n".format(lock_word.shared_count)
3067    else:
3068        out_str += "No readers\n"
3069    if lock_word.r_waiting == 1:
3070        out_str += "Reader(s) blocked\t: TRUE\n"
3071    if lock_word.w_waiting == 1:
3072        out_str += "Writer(s) blocked\t: TRUE\n"
3073    return out_str
3074
3075@lldb_command('showlock', 'MSR')
3076def ShowLock(cmd_args=None, cmd_options={}):
3077    """ Show info about a lock - its state and owner thread details
3078        Usage: showlock <address of a lock>
3079        -M : to consider <addr> as lck_mtx_t
3080        -S : to consider <addr> as lck_spin_t
3081        -R : to consider <addr> as lck_rw_t
3082    """
3083    if not cmd_args:
3084        raise ArgumentError("Please specify the address of the lock whose info you want to view.")
3085        return
3086
3087    summary_str = ""
3088    addr = cmd_args[0]
3089    ## from osfmk/arm/locks.h
3090    if "-M" in cmd_options:
3091        lock_mtx = kern.GetValueFromAddress(addr, 'lck_mtx_t *')
3092        summary_str = GetMutexLockSummary(lock_mtx)
3093    elif "-S" in cmd_options:
3094        lock_spin = kern.GetValueFromAddress(addr, 'lck_spin_t *')
3095        summary_str = GetSpinLockSummary(lock_spin)
3096    elif "-R" in cmd_options:
3097        lock_rw = kern.GetValueFromAddress(addr, 'lck_rw_t *')
3098        summary_str = GetRWLockSummary(lock_rw)
3099    else:
3100        summary_str = "Please specify supported lock option(-M/-S/-R)"
3101
3102    print summary_str
3103
3104#EndMacro: showlock
3105
3106def getThreadRW(thread, debug, elem_find, force_print):
3107    """ Helper routine for finding per thread rw lock:
3108        returns:
3109        String with info
3110    """
3111    out = ""
3112    ## if we are not in debug mode do not access thread.rw_lock_held
3113    if not debug:
3114        if not force_print:
3115            if thread.rwlock_count == 0:
3116                return out
3117        out = "{:<19s} {:>19s} \n".format("Thread", "rwlock_count")
3118        out += "{:<#19x} ".format(thread)
3119        out += "{:>19d} ".format(thread.rwlock_count)
3120        return out
3121
3122    rw_locks_held = thread.rw_lock_held
3123    if not force_print:
3124        if thread.rwlock_count == 0 and rw_locks_held.rwld_locks_acquired == 0:
3125            return out
3126
3127    out = "{:<19s} {:>19s} {:>19s} {:>29s}\n".format("Thread", "rwlock_count", "rwlock_acquired", "RW_Debug_info_missing")
3128    out += "{:<#19x} ".format(thread)
3129    out += "{:>19d} ".format(thread.rwlock_count)
3130    out += "{:>19d} ".format(rw_locks_held.rwld_locks_acquired)
3131
3132    if rw_locks_held.rwld_overflow:
3133        out += "{:>29s}\n".format("TRUE")
3134    else:
3135        out += "{:>29s}\n".format("FALSE")
3136
3137    found = set()
3138    if rw_locks_held.rwld_locks_saved > 0:
3139        lock_entry = rw_locks_held.rwld_locks
3140        num_entry = sizeof(lock_entry) / sizeof(lock_entry[0])
3141        out += "{:>10s} {:<19s} {:>10s} {:>10s} {:>10s} {:<19s}\n".format(" ", "Lock", "Write", "Read", " ", "Caller")
3142        for i in range(num_entry):
3143            entry = lock_entry[i]
3144            if entry.rwlde_lock:
3145                out += "{:>10s} ".format(" ")
3146                found.add(hex(entry.rwlde_lock))
3147                out += "{:<#19x} ".format(entry.rwlde_lock)
3148                write = 0
3149                read = 0
3150                if entry.rwlde_mode_count < 0:
3151                    write = 1
3152                if entry.rwlde_mode_count > 0:
3153                    read = entry.rwlde_mode_count
3154                out += "{:>10d} ".format(write)
3155                out += "{:>10d} ".format(read)
3156                out += "{:>10s} ".format(" ")
3157                caller = vm_unpack_pointer(entry.rwlde_caller_packed, kern.globals.rwlde_caller_packing_params, 'void *')
3158                out += "{:<#19x}\n".format(caller)
3159
3160    if elem_find != 0:
3161        if elem_find in found:
3162            return out
3163        else:
3164            return ""
3165    else:
3166        return out
3167
3168def rwLockDebugDisabled():
3169    ## disLkRWDebug 0x00000010 from locks.h
3170    if (kern.globals.LcksOpts and 0x00000010) == 0x00000010:
3171        return True
3172    else:
3173        return False
3174
3175@lldb_command('showthreadrwlck')
3176def ShowThreadRWLck(cmd_args = None):
3177    """ Routine to print a best effort summary of rwlocks held
3178    """
3179    if not cmd_args:
3180        raise ArgumentError("Please specify the thread pointer")
3181        return
3182    thread = kern.GetValueFromAddress(cmd_args[0], 'thread_t')
3183    if not thread:
3184        raise ArgumentError("Invalid thread pointer")
3185        return
3186
3187    debug = True
3188    if rwLockDebugDisabled():
3189        print "WARNING: Best effort per-thread rwlock tracking is OFF\n"
3190        debug = False
3191
3192    string = getThreadRW(thread, debug, 0, True)
3193    print string
3194
3195    return
3196# EndMacro: showthreadrwlck
3197
3198@lldb_command('showallrwlckheld')
3199def ShowAllRWLckHeld(cmd_args = None):
3200    """ Routine to print a summary listing of all read/writer locks
3201        tracked per thread
3202    """
3203    debug = True
3204    if rwLockDebugDisabled():
3205        print "WARNING: Best effort per-thread rwlock tracking is OFF\n"
3206        debug = False
3207
3208    for t in kern.tasks:
3209        for th in IterateQueue(t.threads, 'thread *', 'task_threads'):
3210            print getThreadRW(th, debug, 0, False)
3211
3212    return
3213# EndMacro: showallrwlckheld
3214
3215@lldb_command('tryfindrwlckholders')
3216def tryFindRwlckHolders(cmd_args = None):
3217    """ Best effort routing to find the current holders of
3218        a rwlock
3219    """
3220    if not cmd_args:
3221        raise ArgumentError("Please specify a rw_lock_t pointer")
3222        return
3223
3224    if rwLockDebugDisabled():
3225        print "WARNING: Best effort per-thread rwlock tracking is OFF\n"
3226        return
3227
3228    print "This is a best effort mechanism, if threads have lock info missing we might not be able to find the lock.\n"
3229    rw_to_find = cmd_args[0]
3230    for t in kern.tasks:
3231        for th in IterateQueue(t.threads, 'thread *', 'task_threads'):
3232            print getThreadRW(th, True, rw_to_find, False)
3233
3234    return
3235# EndMacro: tryfindrwlckholders
3236
3237@lldb_command('showallrwlck')
3238def ShowAllRWLck(cmd_args=None):
3239    """ Routine to print a summary listing of all read/writer locks
3240    """
3241    if kern.ptrsize == 8:
3242        hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
3243    else:
3244        hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
3245
3246    print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
3247
3248    rwlgrp_queue_head = kern.globals.lck_grp_queue
3249    rwlgrp_ptr_type = GetType('_lck_grp_ *')
3250    for rwlgrp_ptr in IterateQueue(rwlgrp_queue_head, rwlgrp_ptr_type, "lck_grp_link"):
3251       print GetRWLEntry(rwlgrp_ptr)
3252    return
3253# EndMacro: showallrwlck
3254
3255#Macro: showbootermemorymap
3256@lldb_command('showbootermemorymap')
3257def ShowBooterMemoryMap(cmd_args=None):
3258    """ Prints out the phys memory map from kernelBootArgs
3259        Supported only on x86_64
3260    """
3261    if kern.arch != 'x86_64':
3262        print "showbootermemorymap not supported on this architecture"
3263        return
3264
3265    out_string = ""
3266
3267    # Memory type map
3268    memtype_dict = {
3269            0:  'Reserved',
3270            1:  'LoaderCode',
3271            2:  'LoaderData',
3272            3:  'BS_code',
3273            4:  'BS_data',
3274            5:  'RT_code',
3275            6:  'RT_data',
3276            7:  'Convention',
3277            8:  'Unusable',
3278            9:  'ACPI_recl',
3279            10: 'ACPI_NVS',
3280            11: 'MemMapIO',
3281            12: 'MemPortIO',
3282            13: 'PAL_code'
3283        }
3284
3285    boot_args = kern.globals.kernelBootArgs
3286    msize = boot_args.MemoryMapDescriptorSize
3287    mcount = (boot_args.MemoryMapSize) / unsigned(msize)
3288
3289    out_string += "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
3290
3291    i = 0
3292    while i < mcount:
3293        mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + kern.VM_MIN_KERNEL_ADDRESS + unsigned(i*msize), 'EfiMemoryRange *')
3294        mtype = unsigned(mptr.Type)
3295        if mtype in memtype_dict:
3296            out_string += "{0: <12s}".format(memtype_dict[mtype])
3297        else:
3298            out_string += "{0: <12s}".format("UNKNOWN")
3299
3300        if mptr.VirtualStart == 0:
3301            out_string += "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, ' '*19, mptr.Attribute)
3302        else:
3303            out_string += "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, mptr.VirtualStart, mptr.Attribute)
3304        i = i + 1
3305
3306    print out_string
3307#EndMacro: showbootermemorymap
3308
3309@lldb_command('show_all_purgeable_objects')
3310def ShowAllPurgeableVmObjects(cmd_args=None):
3311    """ Routine to print a summary listing of all the purgeable vm objects
3312    """
3313    print "\n--------------------    VOLATILE OBJECTS    --------------------\n"
3314    ShowAllPurgeableVolatileVmObjects()
3315    print "\n--------------------  NON-VOLATILE OBJECTS  --------------------\n"
3316    ShowAllPurgeableNonVolatileVmObjects()
3317
3318@lldb_command('show_all_purgeable_nonvolatile_objects')
3319def ShowAllPurgeableNonVolatileVmObjects(cmd_args=None):
3320    """ Routine to print a summary listing of all the vm objects in
3321        the purgeable_nonvolatile_queue
3322    """
3323
3324    nonvolatile_total = lambda:None
3325    nonvolatile_total.objects = 0
3326    nonvolatile_total.vsize = 0
3327    nonvolatile_total.rsize = 0
3328    nonvolatile_total.wsize = 0
3329    nonvolatile_total.csize = 0
3330    nonvolatile_total.disowned_objects = 0
3331    nonvolatile_total.disowned_vsize = 0
3332    nonvolatile_total.disowned_rsize = 0
3333    nonvolatile_total.disowned_wsize = 0
3334    nonvolatile_total.disowned_csize = 0
3335
3336    queue_len = kern.globals.purgeable_nonvolatile_count
3337    queue_head = kern.globals.purgeable_nonvolatile_queue
3338
3339    print 'purgeable_nonvolatile_queue:{: <#018x}  purgeable_volatile_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len)
3340    print 'N:non-volatile  V:volatile  E:empty  D:deny\n'
3341
3342    print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s}   {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
3343    idx = 0
3344    for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
3345        idx += 1
3346        ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total)
3347    print "disowned objects:{:<10d}  [ virtual:{:<10d}  resident:{:<10d}  wired:{:<10d}  compressed:{:<10d} ]\n".format(nonvolatile_total.disowned_objects, nonvolatile_total.disowned_vsize, nonvolatile_total.disowned_rsize, nonvolatile_total.disowned_wsize, nonvolatile_total.disowned_csize)
3348    print "     all objects:{:<10d}  [ virtual:{:<10d}  resident:{:<10d}  wired:{:<10d}  compressed:{:<10d} ]\n".format(nonvolatile_total.objects, nonvolatile_total.vsize, nonvolatile_total.rsize, nonvolatile_total.wsize, nonvolatile_total.csize)
3349
3350
3351def ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total):
3352    """  Routine to print out a summary a VM object in purgeable_nonvolatile_queue
3353        params:
3354            object - core.value : a object of type 'struct vm_object *'
3355        returns:
3356            None
3357    """
3358    page_size = kern.globals.page_size
3359    if object.purgable == 0:
3360        purgable = "N"
3361    elif object.purgable == 1:
3362        purgable = "V"
3363    elif object.purgable == 2:
3364        purgable = "E"
3365    elif object.purgable == 3:
3366        purgable = "D"
3367    else:
3368        purgable = "?"
3369    if object.pager == 0:
3370        compressed_count = 0
3371    else:
3372        compressor_pager = Cast(object.pager, 'compressor_pager *')
3373        compressed_count = compressor_pager.cpgr_num_slots_occupied
3374
3375    print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d}  {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
3376
3377    nonvolatile_total.objects += 1
3378    nonvolatile_total.vsize += object.vo_un1.vou_size/page_size
3379    nonvolatile_total.rsize += object.resident_page_count
3380    nonvolatile_total.wsize += object.wired_page_count
3381    nonvolatile_total.csize += compressed_count
3382    if object.vo_un2.vou_owner == 0:
3383        nonvolatile_total.disowned_objects += 1
3384        nonvolatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
3385        nonvolatile_total.disowned_rsize += object.resident_page_count
3386        nonvolatile_total.disowned_wsize += object.wired_page_count
3387        nonvolatile_total.disowned_csize += compressed_count
3388
3389
3390@lldb_command('show_all_purgeable_volatile_objects')
3391def ShowAllPurgeableVolatileVmObjects(cmd_args=None):
3392    """ Routine to print a summary listing of all the vm objects in
3393        the purgeable queues
3394    """
3395    volatile_total = lambda:None
3396    volatile_total.objects = 0
3397    volatile_total.vsize = 0
3398    volatile_total.rsize = 0
3399    volatile_total.wsize = 0
3400    volatile_total.csize = 0
3401    volatile_total.disowned_objects = 0
3402    volatile_total.disowned_vsize = 0
3403    volatile_total.disowned_rsize = 0
3404    volatile_total.disowned_wsize = 0
3405    volatile_total.disowned_csize = 0
3406
3407    purgeable_queues = kern.globals.purgeable_queues
3408    print "---------- OBSOLETE\n"
3409    ShowPurgeableQueue(purgeable_queues[0], volatile_total)
3410    print "\n\n---------- FIFO\n"
3411    ShowPurgeableQueue(purgeable_queues[1], volatile_total)
3412    print "\n\n---------- LIFO\n"
3413    ShowPurgeableQueue(purgeable_queues[2], volatile_total)
3414
3415    print "disowned objects:{:<10d}  [ virtual:{:<10d}  resident:{:<10d}  wired:{:<10d}  compressed:{:<10d} ]\n".format(volatile_total.disowned_objects, volatile_total.disowned_vsize, volatile_total.disowned_rsize, volatile_total.disowned_wsize, volatile_total.disowned_csize)
3416    print "     all objects:{:<10d}  [ virtual:{:<10d}  resident:{:<10d}  wired:{:<10d}  compressed:{:<10d} ]\n".format(volatile_total.objects, volatile_total.vsize, volatile_total.rsize, volatile_total.wsize, volatile_total.csize)
3417    purgeable_count = kern.globals.vm_page_purgeable_count
3418    purgeable_wired_count = kern.globals.vm_page_purgeable_wired_count
3419    if purgeable_count != volatile_total.rsize or purgeable_wired_count != volatile_total.wsize:
3420        mismatch = "<---------  MISMATCH\n"
3421    else:
3422        mismatch = ""
3423    print "vm_page_purgeable_count:                           resident:{:<10d}  wired:{:<10d}  {:s}\n".format(purgeable_count, purgeable_wired_count, mismatch)
3424
3425
3426def ShowPurgeableQueue(qhead, volatile_total):
3427    print "----- GROUP 0\n"
3428    ShowPurgeableGroup(qhead.objq[0], volatile_total)
3429    print "----- GROUP 1\n"
3430    ShowPurgeableGroup(qhead.objq[1], volatile_total)
3431    print "----- GROUP 2\n"
3432    ShowPurgeableGroup(qhead.objq[2], volatile_total)
3433    print "----- GROUP 3\n"
3434    ShowPurgeableGroup(qhead.objq[3], volatile_total)
3435    print "----- GROUP 4\n"
3436    ShowPurgeableGroup(qhead.objq[4], volatile_total)
3437    print "----- GROUP 5\n"
3438    ShowPurgeableGroup(qhead.objq[5], volatile_total)
3439    print "----- GROUP 6\n"
3440    ShowPurgeableGroup(qhead.objq[6], volatile_total)
3441    print "----- GROUP 7\n"
3442    ShowPurgeableGroup(qhead.objq[7], volatile_total)
3443
3444def ShowPurgeableGroup(qhead, volatile_total):
3445    idx = 0
3446    for object in IterateQueue(qhead, 'struct vm_object *', 'objq'):
3447        if idx == 0:
3448#            print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s}   {:18s} {:>6s} {:<20s} {:18s} {:>6s} {:<20s} {:s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process","volatilizer","pid","process","")
3449            print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s}   {:>3s} {:18s} {:>6s} {:<20s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
3450        idx += 1
3451        ShowPurgeableVolatileVmObject(object, idx, volatile_total)
3452
3453def ShowPurgeableVolatileVmObject(object, idx, volatile_total):
3454    """  Routine to print out a summary a VM object in a purgeable queue
3455        params:
3456            object - core.value : a object of type 'struct vm_object *'
3457        returns:
3458            None
3459    """
3460##   if int(object.vo_un2.vou_owner) != int(object.vo_purgeable_volatilizer):
3461#        diff=" !="
3462##    else:
3463#        diff="  "
3464    page_size = kern.globals.page_size
3465    if object.purgable == 0:
3466        purgable = "N"
3467    elif object.purgable == 1:
3468        purgable = "V"
3469    elif object.purgable == 2:
3470        purgable = "E"
3471    elif object.purgable == 3:
3472        purgable = "D"
3473    else:
3474        purgable = "?"
3475    if object.pager == 0:
3476        compressed_count = 0
3477    else:
3478        compressor_pager = Cast(object.pager, 'compressor_pager *')
3479        compressed_count = compressor_pager.cpgr_num_slots_occupied
3480#    print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {: <#018x} {:>6d} {:<20s}   {: <#018x} {:>6d} {:<20s} {:s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count,object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner),object.vo_purgeable_volatilizer,GetProcPIDForObjectOwner(object.vo_purgeable_volatilizer),GetProcNameForObjectOwner(object.vo_purgeable_volatilizer),diff)
3481    print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d}   {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
3482    volatile_total.objects += 1
3483    volatile_total.vsize += object.vo_un1.vou_size/page_size
3484    volatile_total.rsize += object.resident_page_count
3485    volatile_total.wsize += object.wired_page_count
3486    volatile_total.csize += compressed_count
3487    if object.vo_un2.vou_owner == 0:
3488        volatile_total.disowned_objects += 1
3489        volatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
3490        volatile_total.disowned_rsize += object.resident_page_count
3491        volatile_total.disowned_wsize += object.wired_page_count
3492        volatile_total.disowned_csize += compressed_count
3493
3494
3495def GetCompressedPagesForObject(obj):
3496    """Stuff
3497    """
3498    pager = Cast(obj.pager, 'compressor_pager_t')
3499    return pager.cpgr_num_slots_occupied
3500    """  # commented code below
3501    if pager.cpgr_num_slots > 128:
3502        slots_arr = pager.cpgr_slots.cpgr_islots
3503        num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
3504        index = 0
3505        compressor_slot = 0
3506        compressed_pages = 0
3507        while index < num_indirect_slot_ptr:
3508            compressor_slot = 0
3509            if slots_arr[index]:
3510                while compressor_slot < 128:
3511                    if slots_arr[index][compressor_slot]:
3512                        compressed_pages += 1
3513                    compressor_slot += 1
3514            index += 1
3515    else:
3516        slots_arr = pager.cpgr_slots.cpgr_dslots
3517        compressor_slot = 0
3518        compressed_pages = 0
3519        while compressor_slot < pager.cpgr_num_slots:
3520            if slots_arr[compressor_slot]:
3521                compressed_pages += 1
3522            compressor_slot += 1
3523    return compressed_pages
3524    """
3525
3526def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
3527    """  Routine to print out a summary listing of all the entries in a vm_map
3528        params:
3529            task - core.value : a object of type 'task *'
3530        returns:
3531            None
3532    """
3533    print "vm_map entries for task " + hex(task)
3534    print GetTaskSummary.header
3535    print GetTaskSummary(task)
3536    if not task.map:
3537        print "Task {0: <#020x} has map = 0x0"
3538        return None
3539    showmapvme(task.map, 0, 0, show_pager_info, show_all_shadows, False)
3540
3541@lldb_command("showmapvme", "A:B:F:PRST")
3542def ShowMapVME(cmd_args=None, cmd_options={}):
3543    """Routine to print out info about the specified vm_map and its vm entries
3544        usage: showmapvme <vm_map> [-A start] [-B end] [-S] [-P]
3545        Use -A <start> flag to start at virtual address <start>
3546        Use -B <end> flag to end at virtual address <end>
3547        Use -F <virtaddr> flag to find just the VME containing the given VA
3548        Use -S flag to show VM object shadow chains
3549        Use -P flag to show pager info (mapped file, compressed pages, ...)
3550        Use -R flag to reverse order
3551        Use -T to show red-black tree pointers
3552    """
3553    if cmd_args == None or len(cmd_args) < 1:
3554        print "Invalid argument.", ShowMapVME.__doc__
3555        return
3556    show_pager_info = False
3557    show_all_shadows = False
3558    show_rb_tree = False
3559    start_vaddr = 0
3560    end_vaddr = 0
3561    reverse_order = False
3562    if "-A" in cmd_options:
3563        start_vaddr = unsigned(int(cmd_options['-A'], 16))
3564    if "-B" in cmd_options:
3565        end_vaddr = unsigned(int(cmd_options['-B'], 16))
3566    if "-F" in cmd_options:
3567        start_vaddr = unsigned(int(cmd_options['-F'], 16))
3568        end_vaddr = start_vaddr
3569    if "-P" in cmd_options:
3570        show_pager_info = True
3571    if "-S" in cmd_options:
3572        show_all_shadows = True
3573    if "-R" in cmd_options:
3574        reverse_order = True
3575    if "-T" in cmd_options:
3576        show_rb_tree = True
3577    map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
3578    showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
3579
3580@lldb_command("showmapcopyvme", "A:B:F:PRST")
3581def ShowMapCopyVME(cmd_args=None, cmd_options={}):
3582    """Routine to print out info about the specified vm_map_copy and its vm entries
3583        usage: showmapcopyvme <vm_map_copy> [-A start] [-B end] [-S] [-P]
3584        Use -A <start> flag to start at virtual address <start>
3585        Use -B <end> flag to end at virtual address <end>
3586        Use -F <virtaddr> flag to find just the VME containing the given VA
3587        Use -S flag to show VM object shadow chains
3588        Use -P flag to show pager info (mapped file, compressed pages, ...)
3589        Use -R flag to reverse order
3590        Use -T to show red-black tree pointers
3591    """
3592    if cmd_args == None or len(cmd_args) < 1:
3593        print "Invalid argument.", ShowMapVME.__doc__
3594        return
3595    show_pager_info = False
3596    show_all_shadows = False
3597    show_rb_tree = False
3598    start_vaddr = 0
3599    end_vaddr = 0
3600    reverse_order = False
3601    if "-A" in cmd_options:
3602        start_vaddr = unsigned(int(cmd_options['-A'], 16))
3603    if "-B" in cmd_options:
3604        end_vaddr = unsigned(int(cmd_options['-B'], 16))
3605    if "-F" in cmd_options:
3606        start_vaddr = unsigned(int(cmd_options['-F'], 16))
3607        end_vaddr = start_vaddr
3608    if "-P" in cmd_options:
3609        show_pager_info = True
3610    if "-S" in cmd_options:
3611        show_all_shadows = True
3612    if "-R" in cmd_options:
3613        reverse_order = True
3614    if "-T" in cmd_options:
3615        show_rb_tree = True
3616    map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_copy_t')
3617    showmapcopyvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
3618
3619@lldb_command("showvmobject", "A:B:PRST")
3620def ShowVMObject(cmd_args=None, cmd_options={}):
3621    """Routine to print out a VM object and its shadow chain
3622        usage: showvmobject <vm_object> [-S] [-P]
3623        -S: show VM object shadow chain
3624        -P: show pager info (mapped file, compressed pages, ...)
3625    """
3626    if cmd_args == None or len(cmd_args) < 1:
3627        print "Invalid argument.", ShowMapVME.__doc__
3628        return
3629    show_pager_info = False
3630    show_all_shadows = False
3631    if "-P" in cmd_options:
3632        show_pager_info = True
3633    if "-S" in cmd_options:
3634        show_all_shadows = True
3635    object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
3636    showvmobject(object, 0, 0, show_pager_info, show_all_shadows)
3637
3638def showvmobject(object, offset=0, size=0, show_pager_info=False, show_all_shadows=False):
3639    page_size = kern.globals.page_size
3640    vnode_pager_ops = kern.globals.vnode_pager_ops
3641    vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
3642    depth = 0
3643    if size == 0 and object != 0 and object.internal:
3644        size = object.vo_un1.vou_size
3645    while object != 0:
3646        depth += 1
3647        if show_all_shadows == False and depth != 1 and object.shadow != 0:
3648            offset += unsigned(object.vo_un2.vou_shadow_offset)
3649            object = object.shadow
3650            continue
3651        if object.copy_strategy == 0:
3652            copy_strategy="N"
3653        elif object.copy_strategy == 2:
3654            copy_strategy="D"
3655        elif object.copy_strategy == 4:
3656            copy_strategy="S"
3657
3658        else:
3659            copy_strategy=str(object.copy_strategy)
3660        if object.internal:
3661            internal = "internal"
3662        else:
3663            internal = "external"
3664        purgeable = "NVED"[int(object.purgable)]
3665        pager_string = ""
3666        if object.phys_contiguous:
3667            pager_string = pager_string + "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2.vou_shadow_offset), unsigned(object.vo_un1.vou_size))
3668        pager = object.pager
3669        if show_pager_info and pager != 0:
3670            if object.internal:
3671                pager_string = pager_string + "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
3672            elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr:
3673                vnode_pager = Cast(pager,'vnode_pager *')
3674                pager_string = pager_string + "-> " + GetVnodePath(vnode_pager.vnode_handle)
3675            else:
3676                pager_string = pager_string + "-> {:s}:{: <#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager)
3677        print "{:>18d} {:#018x}:{:#018x} {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} purg:{:1s} {:s} wtag:{:d} ({:d} {:d} {:d}) {:s}".format(depth,offset,offset+size,object,object.ref_count,object.true_share,copy_strategy,purgeable,internal,object.wire_tag,unsigned(object.vo_un1.vou_size)/page_size,object.resident_page_count,object.wired_page_count,pager_string)
3678#       print "        #{:<5d} obj {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} {:s} size:{:<10d} wired:{:<10d} resident:{:<10d} reusable:{:<10d}".format(depth,object,object.ref_count,object.true_share,copy_strategy,internal,object.vo_un1.vou_size/page_size,object.wired_page_count,object.resident_page_count,object.reusable_page_count)
3679        offset += unsigned(object.vo_un2.vou_shadow_offset)
3680        object = object.shadow
3681
3682def showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order=False, show_rb_tree=False):
3683    rsize = GetResidentPageCount(map)
3684    print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map","pmap","size","#ents","rsize","start","end","pgshift")
3685    print "{: <#018x} {: <#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(map,map.pmap,unsigned(map.size),map.hdr.nentries,rsize,map.hdr.links.start,map.hdr.links.end,map.hdr.page_shift)
3686    showmaphdrvme(map.hdr, map.pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
3687
3688def showmapcopyvme(mapcopy, start_vaddr=0, end_vaddr=0, show_pager_info=True, show_all_shadows=True, reverse_order=False, show_rb_tree=False):
3689    print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map_copy","offset","size","#ents","rsize","start","end","pgshift")
3690    print "{: <#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(mapcopy,mapcopy.offset,mapcopy.size,mapcopy.c_u.hdr.nentries,0,mapcopy.c_u.hdr.links.start,mapcopy.c_u.hdr.links.end,mapcopy.c_u.hdr.page_shift)
3691    showmaphdrvme(mapcopy.c_u.hdr, 0, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
3692
3693def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree):
3694    page_size = kern.globals.page_size
3695    vnode_pager_ops = kern.globals.vnode_pager_ops
3696    vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
3697    if hasattr(kern.globals, 'compressor_object'):
3698        compressor_object = kern.globals.compressor_object
3699    else:
3700        compressor_object = -1;
3701    vme_list_head = maphdr.links
3702    vme_ptr_type = GetType('vm_map_entry *')
3703    print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
3704    last_end = unsigned(maphdr.links.start)
3705    skipped_entries = 0
3706    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links", reverse_order):
3707        if start_vaddr != 0 and end_vaddr != 0:
3708            if unsigned(vme.links.start) > end_vaddr:
3709                break
3710            if unsigned(vme.links.end) <= start_vaddr:
3711                last_end = unsigned(vme.links.end)
3712                skipped_entries = skipped_entries + 1
3713                continue
3714            if skipped_entries != 0:
3715                print "... skipped {:d} entries ...".format(skipped_entries)
3716                skipped_entries = 0
3717        if unsigned(vme.links.start) != last_end:
3718            print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme.links.start,(unsigned(vme.links.start) - last_end)/page_size)
3719        last_end = unsigned(vme.links.end)
3720        size = unsigned(vme.links.end) - unsigned(vme.links.start)
3721        object = vme.vme_object.vmo_object
3722        if object == 0:
3723            object_str = "{: <#018x}".format(object)
3724        elif vme.is_sub_map:
3725            if object == kern.globals.bufferhdr_map:
3726                object_str = "BUFFERHDR_MAP"
3727            elif object == kern.globals.mb_map:
3728                object_str = "MB_MAP"
3729            elif object == kern.globals.bsd_pageable_map:
3730                object_str = "BSD_PAGEABLE_MAP"
3731            elif object == kern.globals.ipc_kernel_map:
3732                object_str = "IPC_KERNEL_MAP"
3733            elif object == kern.globals.ipc_kernel_copy_map:
3734                object_str = "IPC_KERNEL_COPY_MAP"
3735            elif object == kern.globals.kalloc_large_map:
3736                object_str = "KALLOC_LARGE_MAP"
3737            elif object == kern.globals.kalloc_large_data_map:
3738                object_str = "KALLOC_LARGE_DATA_MAP"
3739            elif object == kern.globals.kernel_data_map:
3740                object_str = "KERNEL_DATA_MAP"
3741            elif hasattr(kern.globals, 'compressor_map') and object == kern.globals.compressor_map:
3742                object_str = "COMPRESSOR_MAP"
3743            elif hasattr(kern.globals, 'gzalloc_map') and object == kern.globals.gzalloc_map:
3744                object_str = "GZALLOC_MAP"
3745            elif hasattr(kern.globals, 'g_kext_map') and object == kern.globals.g_kext_map:
3746                object_str = "G_KEXT_MAP"
3747            elif hasattr(kern.globals, 'vector_upl_submap') and object == kern.globals.vector_upl_submap:
3748                object_str = "VECTOR_UPL_SUBMAP"
3749            else:
3750                object_str = "submap:{: <#018x}".format(object)
3751        else:
3752            if object == kern.globals.kernel_object:
3753                object_str = "KERNEL_OBJECT"
3754            elif object == kern.globals.vm_submap_object:
3755                object_str = "VM_SUBMAP_OBJECT"
3756            elif object == compressor_object:
3757                object_str = "COMPRESSOR_OBJECT"
3758            else:
3759                object_str = "{: <#018x}".format(object)
3760        offset = unsigned(vme.vme_offset) & ~0xFFF
3761        tag = unsigned(vme.vme_offset & 0xFFF)
3762        protection = ""
3763        if vme.protection & 0x1:
3764            protection +="r"
3765        else:
3766            protection += "-"
3767        if vme.protection & 0x2:
3768            protection += "w"
3769        else:
3770            protection += "-"
3771        if vme.protection & 0x4:
3772            protection += "x"
3773        else:
3774            protection += "-"
3775        max_protection = ""
3776        if vme.max_protection & 0x1:
3777            max_protection +="r"
3778        else:
3779            max_protection += "-"
3780        if vme.max_protection & 0x2:
3781            max_protection += "w"
3782        else:
3783            max_protection += "-"
3784        if vme.max_protection & 0x4:
3785            max_protection += "x"
3786        else:
3787            max_protection += "-"
3788        vme_flags = ""
3789        if vme.is_sub_map:
3790            vme_flags += "s"
3791        if vme.needs_copy:
3792            vme_flags += "n"
3793        if vme.use_pmap:
3794            vme_flags += "p"
3795        if vme.wired_count:
3796            vme_flags += "w"
3797        if vme.used_for_jit:
3798            vme_flags += "j"
3799        tagstr = ""
3800        if pmap == kern.globals.kernel_pmap:
3801            xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *')
3802            if xsite and xsite.site.flags & 0x0200:
3803                tagstr = ".{:<3d}".format(xsite.loadTag)
3804        rb_info = ""
3805        if show_rb_tree:
3806            rb_info = "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme.store.entry.rbe_left, vme.store.entry.rbe_right, vme.store.entry.rbe_parent)
3807        print "{: <#018x} {:#018x}:{:#018x} {:>10d} {:>3d}{:<4s}  {:3s}/{:3s}/{:<8s} {:<18s} {:<#18x} {:s}".format(vme,vme.links.start,vme.links.end,(unsigned(vme.links.end)-unsigned(vme.links.start))/page_size,tag,tagstr,protection,max_protection,vme_flags,object_str,offset, rb_info)
3808        if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and vme.vme_object.vmo_object != 0:
3809            object = vme.vme_object.vmo_object
3810        else:
3811            object = 0
3812        showvmobject(object, offset, size, show_pager_info, show_all_shadows)
3813    if start_vaddr != 0 or end_vaddr != 0:
3814        print "..."
3815    elif unsigned(maphdr.links.end) > last_end:
3816        print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,maphdr.links.end,(unsigned(maphdr.links.end) - last_end)/page_size)
3817    return None
3818
3819def CountMapTags(map, tagcounts, slow):
3820    page_size = unsigned(kern.globals.page_size)
3821    vme_list_head = map.hdr.links
3822    vme_ptr_type = GetType('vm_map_entry *')
3823    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
3824        object = vme.vme_object.vmo_object
3825        tag = vme.vme_offset & 0xFFF
3826        if object == kern.globals.kernel_object:
3827            count = 0
3828            if not slow:
3829                count = unsigned(vme.links.end - vme.links.start) / page_size
3830            else:
3831                addr = unsigned(vme.links.start)
3832                while addr < unsigned(vme.links.end):
3833                    hash_id = _calc_vm_page_hash(object, addr)
3834                    page_list = kern.globals.vm_page_buckets[hash_id].page_list
3835                    page = _vm_page_unpack_ptr(page_list)
3836                    while (page != 0):
3837                        vmpage = kern.GetValueFromAddress(page, 'vm_page_t')
3838                        if (addr == unsigned(vmpage.vmp_offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vmp_object))):
3839                            if (not vmpage.vmp_local) and (vmpage.vmp_wire_count > 0):
3840                                count += 1
3841                            break
3842                        page = _vm_page_unpack_ptr(vmpage.vmp_next_m)
3843                    addr += page_size
3844            tagcounts[tag] += count
3845        elif vme.is_sub_map:
3846            CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow)
3847    return None
3848
3849def CountWiredObject(object, tagcounts):
3850    tagcounts[unsigned(object.wire_tag)] += object.wired_page_count
3851    return None
3852
3853def GetKmodIDName(kmod_id):
3854    kmod_val = kern.globals.kmod
3855    for kmod in IterateLinkedList(kmod_val, 'next'):
3856        if (kmod.id == kmod_id):
3857            return "{:<50s}".format(kmod.name)
3858    return "??"
3859
3860FixedTags = {
3861    0:  "VM_KERN_MEMORY_NONE",
3862    1:  "VM_KERN_MEMORY_OSFMK",
3863    2:  "VM_KERN_MEMORY_BSD",
3864    3:  "VM_KERN_MEMORY_IOKIT",
3865    4:  "VM_KERN_MEMORY_LIBKERN",
3866    5:  "VM_KERN_MEMORY_OSKEXT",
3867    6:  "VM_KERN_MEMORY_KEXT",
3868    7:  "VM_KERN_MEMORY_IPC",
3869    8:  "VM_KERN_MEMORY_STACK",
3870    9:  "VM_KERN_MEMORY_CPU",
3871    10: "VM_KERN_MEMORY_PMAP",
3872    11: "VM_KERN_MEMORY_PTE",
3873    12: "VM_KERN_MEMORY_ZONE",
3874    13: "VM_KERN_MEMORY_KALLOC",
3875    14: "VM_KERN_MEMORY_COMPRESSOR",
3876    15: "VM_KERN_MEMORY_COMPRESSED_DATA",
3877    16: "VM_KERN_MEMORY_PHANTOM_CACHE",
3878    17: "VM_KERN_MEMORY_WAITQ",
3879    18: "VM_KERN_MEMORY_DIAG",
3880    19: "VM_KERN_MEMORY_LOG",
3881    20: "VM_KERN_MEMORY_FILE",
3882    21: "VM_KERN_MEMORY_MBUF",
3883    22: "VM_KERN_MEMORY_UBC",
3884    23: "VM_KERN_MEMORY_SECURITY",
3885    24: "VM_KERN_MEMORY_MLOCK",
3886    25: "VM_KERN_MEMORY_REASON",
3887    26: "VM_KERN_MEMORY_SKYWALK",
3888    27: "VM_KERN_MEMORY_LTABLE",
3889    28: "VM_KERN_MEMORY_HV",
3890    29: "VM_KERN_MEMORY_KALLOC_DATA",
3891    30: "VM_KERN_MEMORY_RETIRED",
3892    31: "VM_KERN_MEMORY_KALLOC_TYPE",
3893    32: "VM_KERN_MEMORY_TRIAGE",
3894    255:"VM_KERN_MEMORY_ANY",
3895}
3896
3897def GetVMKernName(tag):
3898    """ returns the formatted name for a vmtag and
3899        the sub-tag for kmod tags.
3900    """
3901    if ((tag <= 27) or (tag == 255)):
3902        return (FixedTags[tag], "")
3903    site = kern.globals.vm_allocation_sites[tag]
3904    if site:
3905        if site.flags & 0x007F:
3906            cstr = addressof(site.subtotals[site.subtotalscount])
3907            return ("{:<50s}".format(str(Cast(cstr, 'char *'))), "")
3908        else:
3909            if site.flags & 0x0200:
3910                xsite = Cast(site,'OSKextAccount *')
3911                tagstr = ".{:<3d}".format(xsite.loadTag)
3912                return (GetKmodIDName(xsite.loadTag), tagstr);
3913            else:
3914                return (kern.Symbolicate(site), "")
3915    return ("", "")
3916
3917@lldb_command("showvmtags", "ASJO")
3918def showvmtags(cmd_args=None, cmd_options={}):
3919    """Routine to print out info about kernel wired page allocations
3920        usage: showvmtags
3921               iterates kernel map and vm objects totaling allocations by tag.
3922        usage: showvmtags -S [-O]
3923               also iterates kernel object pages individually - slow.
3924        usage: showvmtags -A [-O]
3925               show all tags, even tags that have no wired count
3926        usage: showvmtags -J [-O]
3927                Output json
3928
3929        -O: list in increasing size order
3930    """
3931    slow = False
3932    print_json = False
3933    if "-S" in cmd_options:
3934        slow = True
3935    all_tags = False
3936    if "-A" in cmd_options:
3937        all_tags = True
3938    if "-J" in cmd_options:
3939        print_json = True
3940
3941    page_size = unsigned(kern.globals.page_size)
3942    nsites = unsigned(kern.globals.vm_allocation_tag_highest) + 1
3943    tagcounts = [0] * nsites
3944    tagmapped = [0] * nsites
3945
3946    if kern.globals.vm_tag_active_update:
3947        for tag in range(nsites):
3948            site = kern.globals.vm_allocation_sites[tag]
3949            if site:
3950                tagcounts[tag] = unsigned(site.total)
3951                tagmapped[tag] = unsigned(site.mapped)
3952    else:
3953        queue_head = kern.globals.vm_objects_wired
3954        for object in IterateQueue(queue_head, 'struct vm_object *', 'wired_objq'):
3955            if object != kern.globals.kernel_object:
3956                CountWiredObject(object, tagcounts)
3957
3958        CountMapTags(kern.globals.kernel_map, tagcounts, slow)
3959
3960    total = 0
3961    totalmapped = 0
3962    tags = []
3963    for tag in range(nsites):
3964        if all_tags or tagcounts[tag] or tagmapped[tag]:
3965            current = {}
3966            total += tagcounts[tag]
3967            totalmapped += tagmapped[tag]
3968            (sitestr, tagstr) = GetVMKernName(tag)
3969            current["name"] = sitestr
3970            current["size"] = tagcounts[tag]
3971            current["mapped"] = tagmapped[tag]
3972            current["tag"] = tag
3973            current["tagstr"] = tagstr
3974            current["subtotals"] = []
3975
3976            site = kern.globals.vm_allocation_sites[tag]
3977            for sub in range(site.subtotalscount):
3978                alloctag = unsigned(site.subtotals[sub].tag)
3979                amount = unsigned(site.subtotals[sub].total)
3980                subsite = kern.globals.vm_allocation_sites[alloctag]
3981                if alloctag and subsite:
3982                    (sitestr, tagstr) = GetVMKernName(alloctag)
3983                    current["subtotals"].append({
3984                        "amount": amount,
3985                        "flags": int(subsite.flags),
3986                        "tag": alloctag,
3987                        "tagstr": tagstr,
3988                        "sitestr": sitestr,
3989                    })
3990            tags.append(current)
3991
3992    if "-O" in cmd_options:
3993        tags.sort(key = lambda tag: tag['size'])
3994
3995    # Serializing to json here ensure we always catch bugs preventing
3996    # serialization
3997    as_json = json.dumps(tags)
3998    if print_json:
3999        print as_json
4000    else:
4001        print " vm_allocation_tag_highest: {:<7d}  ".format(nsites - 1)
4002        print " {:<7s}  {:>7s}   {:>7s}  {:<50s}".format("tag.kmod", "size", "mapped", "name")
4003        for tag in tags:
4004            if not tagstr:
4005                tagstr = ""
4006            print " {:>3d}{:<4s}  {:>7d}K  {:>7d}K  {:<50s}".format(tag["tag"], tag["tagstr"], tag["size"] / 1024, tag["mapped"] / 1024, tag["name"])
4007            for sub in tag["subtotals"]:
4008                if ((sub["flags"] & 0x007f) == 0):
4009                    kind_str = "named"
4010                else:
4011                    kind_str = "from"
4012
4013                print " {:>7s}  {:>7d}K      {:s}  {:>3d}{:<4s} {:<50s}".format(" ", sub["amount"] / 1024, kind_str, sub["tag"], sub["tagstr"], sub["sitestr"])
4014
4015        print "Total:    {:>7d}K  {:>7d}K".format(total / 1024, totalmapped / 1024)
4016    return None
4017
4018
4019def FindVMEntriesForVnode(task, vn):
4020    """ returns an array of vme that have the vnode set to defined vnode
4021        each entry in array is of format (vme, start_addr, end_address, protection)
4022    """
4023    retval = []
4024    vmmap = task.map
4025    pmap = vmmap.pmap
4026    pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops))
4027    debuglog("pager_ops_addr %s" % hex(pager_ops_addr))
4028
4029    if unsigned(pmap) == 0:
4030        return retval
4031    vme_list_head = vmmap.hdr.links
4032    vme_ptr_type = gettype('vm_map_entry *')
4033    for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'):
4034        #print vme
4035        if unsigned(vme.is_sub_map) == 0 and unsigned(vme.vme_object.vmo_object) != 0:
4036            obj = vme.vme_object.vmo_object
4037        else:
4038            continue
4039
4040        while obj != 0:
4041            if obj.pager != 0:
4042                if obj.internal:
4043                    pass
4044                else:
4045                    vn_pager = Cast(obj.pager, 'vnode_pager *')
4046                    if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn):
4047                        retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection)))
4048            obj = obj.shadow
4049    return retval
4050
4051@lldb_command('showtaskloadinfo')
4052def ShowTaskLoadInfo(cmd_args=None, cmd_options={}):
4053    """ Print the load address and uuid for the process
4054        Usage: (lldb)showtaskloadinfo <task_t>
4055    """
4056    if not cmd_args:
4057        raise ArgumentError("Insufficient arguments")
4058    t = kern.GetValueFromAddress(cmd_args[0], 'struct task *')
4059    print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
4060    p = Cast(t.bsd_info, 'struct proc *')
4061    uuid = ProcGetUUID(p)
4062    uuid_out_string = "{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=uuid)
4063    filepath = GetVnodePath(p.p_textvp)
4064    libname = filepath.split('/')[-1]
4065    #print "uuid: %s file: %s" % (uuid_out_string, filepath)
4066    mappings = FindVMEntriesForVnode(t, p.p_textvp)
4067    load_addr = 0
4068    end_addr = 0
4069    for m in mappings:
4070        if m[3] == 5:
4071            load_addr = m[1]
4072            end_addr = m[2]
4073            #print "Load address: %s" % hex(m[1])
4074    print print_format.format(load_addr, end_addr, libname, uuid_out_string, filepath)
4075    return None
4076
4077@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
4078@lldb_command('vmpagelookup')
4079def VMPageLookup(cmd_args=None):
4080    """ Print the pages in the page bucket corresponding to the provided object and offset.
4081        Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
4082    """
4083    if cmd_args == None or len(cmd_args) < 2:
4084        raise ArgumentError("Please specify an object and offset.")
4085    format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n"
4086
4087    obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
4088    off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
4089
4090    hash_id = _calc_vm_page_hash(obj, off)
4091
4092    page_list = kern.globals.vm_page_buckets[hash_id].page_list
4093    print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list)))
4094
4095    print VMPageLookup.header
4096    page = _vm_page_unpack_ptr(page_list)
4097    while (page != 0) :
4098        pg_t = kern.GetValueFromAddress(page, 'vm_page_t')
4099        print format_string.format(page, pg_t.vmp_offset, _vm_page_unpack_ptr(pg_t.vmp_object))
4100        page = _vm_page_unpack_ptr(pg_t.vmp_next_m)
4101
4102
4103
4104@lldb_command('vmpage_get_phys_page')
4105def VmPageGetPhysPage(cmd_args=None):
4106    """ return the physical page for a vm_page_t
4107        usage: vm_page_get_phys_page <vm_page_t>
4108    """
4109    if cmd_args == None or len(cmd_args) < 1:
4110        print  "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
4111        return
4112
4113    page = kern.GetValueFromAddress(cmd_args[0], 'vm_page_t')
4114    phys_page = _vm_page_get_phys_page(page)
4115    print("phys_page = 0x%x\n" % phys_page)
4116
4117
4118def _vm_page_get_phys_page(page):
4119    if kern.arch == 'x86_64':
4120        return page.vmp_phys_page
4121
4122    if page == 0 :
4123        return 0
4124
4125    m = unsigned(page)
4126    if m >= unsigned(kern.globals.vm_page_array_beginning_addr) and m < unsigned(kern.globals.vm_page_array_ending_addr) :
4127        return (m - unsigned(kern.globals.vm_page_array_beginning_addr)) / sizeof('struct vm_page') + unsigned(kern.globals.vm_first_phys_ppnum)
4128
4129    page_with_ppnum = Cast(page, 'uint32_t *')
4130    ppnum_offset = sizeof('struct vm_page') / sizeof('uint32_t')
4131    return page_with_ppnum[ppnum_offset]
4132
4133
4134@lldb_command('vmpage_unpack_ptr')
4135def VmPageUnpackPtr(cmd_args=None):
4136    """ unpack a pointer
4137        usage: vm_page_unpack_ptr <packed_ptr>
4138    """
4139    if cmd_args == None or len(cmd_args) < 1:
4140        print  "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
4141        return
4142
4143    packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long')
4144    unpacked = _vm_page_unpack_ptr(packed)
4145    print("unpacked pointer = 0x%x\n" % unpacked)
4146
4147
4148def _vm_page_unpack_ptr(page):
4149    if kern.ptrsize == 4 :
4150        return page
4151
4152    if page == 0 :
4153        return page
4154
4155    params = kern.globals.vm_page_packing_params
4156    ptr_shift = params.vmpp_shift
4157    ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask
4158
4159    # when no mask and shift on 64bit systems, we're working with real/non-packed pointers
4160    if ptr_shift == 0 and ptr_mask == 0:
4161        return page
4162
4163    if unsigned(page) & unsigned(ptr_mask):
4164        masked_page = (unsigned(page) & ~ptr_mask)
4165        # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge
4166        vm_pages_addr = unsigned(addressof(kern.globals.vm_pages[0]))
4167        element_size = unsigned(addressof(kern.globals.vm_pages[1])) - vm_pages_addr
4168        return (vm_pages_addr + masked_page * element_size)
4169    return unsigned(vm_unpack_pointer(page, params))
4170
4171@lldb_command('calcvmpagehash')
4172def CalcVMPageHash(cmd_args=None):
4173    """ Get the page bucket corresponding to the provided object and offset.
4174        Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
4175    """
4176    if cmd_args == None or len(cmd_args) < 2:
4177        raise ArgumentError("Please specify an object and offset.")
4178
4179    obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
4180    off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
4181
4182    hash_id = _calc_vm_page_hash(obj, off)
4183
4184    print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
4185    return None
4186
4187def _calc_vm_page_hash(obj, off):
4188    bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
4189    hash_mask = (int) (kern.globals.vm_page_hash_mask)
4190
4191    one = (obj * bucket_hash) & 0xFFFFFFFF
4192    two = off >> unsigned(kern.globals.page_shift)
4193    three = two ^ bucket_hash
4194    four = one + three
4195    hash_id = four & hash_mask
4196
4197    return hash_id
4198
4199#Macro: showallocatedzoneelement
4200@lldb_command('showallocatedzoneelement')
4201def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}):
4202    """ Show all the allocated elements in a zone
4203        usage: showzoneallocelements <address of zone>
4204    """
4205    if len(cmd_args) < 1:
4206        raise ArgumentError("Please specify a zone")
4207
4208    zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
4209    elements = FindAllocatedElementsInZone(zone)
4210    i = 1
4211    for elem in elements:
4212        print "{0: >10d}/{1:<10d} element: {2: <#20x}".format(i, len(elements), elem)
4213        i += 1
4214
4215#EndMacro: showallocatedzoneelement
4216
4217def FindAllocatedElementsInZone(zone):
4218    elements = []
4219
4220    if not zone.z_self or zone.z_permanent:
4221        return elements
4222
4223    for head in [zone.z_pageq_partial, zone.z_pageq_full]:
4224        for meta in ZoneIteratePageQueue(head):
4225            for elem in meta.iterateElements():
4226                if not meta.isElementFree(elem):
4227                    elements.append(elem)
4228
4229    return elements
4230
4231def match_vm_page_attributes(page, matching_attributes):
4232    page_ptr = addressof(page)
4233    unpacked_vm_object = _vm_page_unpack_ptr(page.vmp_object)
4234    matched_attributes = 0
4235    if "vmp_q_state" in matching_attributes and (page.vmp_q_state == matching_attributes["vmp_q_state"]):
4236        matched_attributes += 1
4237    if "vm_object" in matching_attributes and (unsigned(unpacked_vm_object) == unsigned(matching_attributes["vm_object"])):
4238        matched_attributes += 1
4239    if "vmp_offset" in matching_attributes and (unsigned(page.vmp_offset) == unsigned(matching_attributes["vmp_offset"])):
4240        matched_attributes += 1
4241    if "phys_page" in matching_attributes and (unsigned(_vm_page_get_phys_page(page_ptr)) == unsigned(matching_attributes["phys_page"])):
4242        matched_attributes += 1
4243    if "bitfield" in matching_attributes and unsigned(page.__getattr__(matching_attributes["bitfield"])) == 1:
4244        matched_attributes += 1
4245
4246    return matched_attributes
4247
4248#Macro scan_vm_pages
4249@header("{0: >26s}{1: >20s}{2: >10s}{3: >20s}{4: >20s}{5: >16s}".format("vm_pages_index/zone", "vm_page", "q_state", "vm_object", "offset", "ppn", "bitfield", "from_zone_map"))
4250@lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA')
4251def ScanVMPages(cmd_args=None, cmd_options={}):
4252    """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes.
4253        usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone]
4254
4255            scan_vm_pages -A: scan vm pages in the global vm_pages array
4256            scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone
4257            scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue
4258            scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object
4259            scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value
4260            scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number
4261            scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set
4262            scan_vm_pages <-A> -I <start_index>: Start the scan from start_index
4263            scan_vm_pages <-A> -N <npages>: Scan at most npages
4264    """
4265    if (len(cmd_options) < 1):
4266        raise ArgumentError("Please specify at least one matching attribute")
4267
4268    vm_pages = kern.globals.vm_pages
4269    vm_pages_count = kern.globals.vm_pages_count
4270
4271    start_index = 0
4272    npages = vm_pages_count
4273    scan_vmpages_array = False
4274    scan_vmpages_zone = False
4275    attribute_count = 0
4276
4277    if "-A" in cmd_options:
4278        scan_vmpages_array = True
4279
4280    if "-Z" in cmd_options:
4281        scan_vmpages_zone = True
4282
4283    if scan_vmpages_array == False and scan_vmpages_zone == False:
4284        raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)")
4285
4286    attribute_values = {}
4287    if "-S" in cmd_options:
4288        attribute_values["vmp_q_state"] = kern.GetValueFromAddress(cmd_options["-S"], 'int')
4289        attribute_count += 1
4290
4291    if "-O" in cmd_options:
4292        attribute_values["vm_object"] = kern.GetValueFromAddress(cmd_options["-O"], 'vm_object_t')
4293        attribute_count += 1
4294
4295    if "-F" in cmd_options:
4296        attribute_values["vmp_offset"] = kern.GetValueFromAddress(cmd_options["-F"], 'unsigned long long')
4297        attribute_count += 1
4298
4299    if "-P" in cmd_options:
4300        attribute_values["phys_page"] = kern.GetValueFromAddress(cmd_options["-P"], 'unsigned int')
4301        attribute_count += 1
4302
4303    if "-B" in cmd_options:
4304        valid_vmp_bitfields = [
4305            "vmp_in_background",
4306            "vmp_on_backgroundq",
4307            "vmp_gobbled",
4308            "vmp_laundry",
4309            "vmp_no_cache",
4310            "vmp_private",
4311            "vmp_reference",
4312            "vmp_busy",
4313            "vmp_wanted",
4314            "vmp_tabled",
4315            "vmp_hashed",
4316            "vmp_fictitious",
4317            "vmp_clustered",
4318            "vmp_pmapped",
4319            "vmp_xpmapped",
4320            "vmp_free_when_done",
4321            "vmp_absent",
4322            "vmp_error",
4323            "vmp_dirty",
4324            "vmp_cleaning",
4325            "vmp_precious",
4326            "vmp_overwriting",
4327            "vmp_restart",
4328            "vmp_unusual",
4329            "vmp_cs_validated",
4330            "vmp_cs_tainted",
4331            "vmp_cs_nx",
4332            "vmp_reusable",
4333            "vmp_lopage",
4334            "vmp_written_by_kernel",
4335            "vmp_unused_object_bits"
4336            ]
4337        attribute_values["bitfield"] = cmd_options["-B"]
4338        if attribute_values["bitfield"] in valid_vmp_bitfields:
4339            attribute_count += 1
4340        else:
4341            raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield))
4342
4343    if "-I" in cmd_options:
4344        start_index = kern.GetValueFromAddress(cmd_options["-I"], 'int')
4345        npages = vm_pages_count - start_index
4346
4347    if "-N" in cmd_options:
4348        npages = kern.GetValueFromAddress(cmd_options["-N"], 'int')
4349        if npages == 0:
4350            raise ArgumentError("You specified -N 0, nothing to be scanned")
4351
4352    end_index = start_index + npages - 1
4353    if end_index >= vm_pages_count:
4354        raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count))
4355
4356    header_after_n_lines = 40
4357    format_string = "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}"
4358
4359    found_in_array = 0
4360    if scan_vmpages_array:
4361        print "Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index, end_index, attribute_count)
4362        i = start_index
4363        while i <= end_index:
4364            page = vm_pages[i]
4365            if match_vm_page_attributes(page, attribute_values) == attribute_count:
4366                if found_in_array % header_after_n_lines == 0:
4367                    print ScanVMPages.header
4368
4369                print format_string.format(str(i), addressof(page), page.vmp_q_state, _vm_page_unpack_ptr(page.vmp_object), page.vmp_offset, _vm_page_get_phys_page(addressof(page)))
4370                found_in_array += 1
4371
4372            i += 1
4373
4374    found_in_zone = 0
4375    if scan_vmpages_zone:
4376        page_size = kern.GetGlobalVariable('page_size')
4377        print "Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count)
4378
4379        zone = GetZoneByName("vm pages")
4380        if zone is None:
4381            print "Cannot find vm_pages zone, skip the scan"
4382        else:
4383            print "Scanning page queues in the vm_pages zone..."
4384            elements = FindAllocatedElementsInZone(zone)
4385            for elem in elements:
4386                page = kern.GetValueFromAddress(elem, 'vm_page_t')
4387
4388                if match_vm_page_attributes(page, attribute_values) == attribute_count:
4389                    if found_in_zone % header_after_n_lines == 0:
4390                        print ScanVMPages.header
4391
4392                    vm_object = _vm_page_unpack_ptr(page.vmp_object)
4393                    phys_page = _vm_page_get_phys_page(page)
4394                    print format_string.format("vm_pages zone", elem, page.vmp_q_state, vm_object, page.vmp_offset, phys_page)
4395                    found_in_zone += 1
4396
4397    total = found_in_array + found_in_zone
4398    print "Found {0:d} vm pages ({1:d} in array, {2:d} in zone) matching the requested {3:d} attribute(s)".format(total, found_in_array, found_in_zone, attribute_count)
4399
4400#EndMacro scan_vm_pages
4401
4402VM_PAGE_IS_WIRED = 1
4403
4404@header("{0: <10s} of {1: <10s} {2: <20s} {3: <20s} {4: <20s} {5: <10s} {6: <5s}\t {7: <28s}\t{8: <50s}".format("index", "total", "vm_page_t", "offset", "next", "phys_page", "wire#", "first bitfield", "second bitfield"))
4405@lldb_command('vmobjectwalkpages', 'CSBNQP:O:')
4406def VMObjectWalkPages(cmd_args=None, cmd_options={}):
4407    """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
4408        specifically look for this page, highlighting it in the output or noting if it was not found. For
4409        each page, we confirm that it points to the object. We also keep track of the number of pages we
4410        see and compare this to the object's resident page count field.
4411        Usage:
4412            vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
4413            vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages
4414            vmobjectwalkpages <vm_object_t> -B : Walk and print all the pages for a given object (up to 4K pages by default), traversing the memq backwards
4415            vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
4416            vmobjectwalkpages <vm_object_t> -Q : Walk all pages for a given object, looking for known signs of corruption (i.e. q_state == VM_PAGE_IS_WIRED && wire_count == 0)
4417            vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
4418            vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
4419            vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset
4420
4421    """
4422
4423    if (cmd_args == None or len(cmd_args) < 1):
4424        raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
4425
4426    out_string = ""
4427
4428    obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
4429
4430    page = 0
4431    if "-P" in cmd_options:
4432        page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
4433
4434    off = -1
4435    if "-O" in cmd_options:
4436        off = kern.GetValueFromAddress(cmd_options['-O'], 'vm_offset_t')
4437
4438    stop = 0
4439    if "-S" in cmd_options:
4440        if page == 0 and off < 0:
4441            raise ArgumentError("-S can only be passed when a page is specified with -P or -O")
4442        stop = 1
4443
4444    walk_backwards = False
4445    if "-B" in cmd_options:
4446        walk_backwards = True
4447
4448    quiet_mode = False
4449    if "-Q" in cmd_options:
4450        quiet_mode = True
4451
4452    if not quiet_mode:
4453        print VMObjectWalkPages.header
4454        format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
4455        first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
4456        second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
4457        second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
4458        second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
4459        second_bitfield_format_string +=  "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
4460
4461    limit = 4096 #arbitrary limit of number of pages to walk
4462    ignore_limit = 0
4463    if "-N" in cmd_options:
4464        ignore_limit = 1
4465
4466    show_compressed = 0
4467    if "-C" in cmd_options:
4468        show_compressed = 1
4469
4470    page_count = 0
4471    res_page_count = unsigned(obj.resident_page_count)
4472    page_found = False
4473    pages_seen = set()
4474
4475    for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr):
4476        page_count += 1
4477        out_string = ""
4478        if (page != 0 and not(page_found) and vmp == page):
4479            out_string += "******"
4480            page_found = True
4481
4482        if (off > 0 and not(page_found) and vmp.vmp_offset == off):
4483            out_string += "******"
4484            page_found = True
4485
4486        if page != 0 or off > 0 or quiet_mode:
4487             if (page_count % 1000) == 0:
4488                print "traversed %d pages ...\n" % (page_count)
4489        else:
4490                out_string += format_string.format(page_count, res_page_count, vmp, vmp.vmp_offset, _vm_page_unpack_ptr(vmp.vmp_listq.next), _vm_page_get_phys_page(vmp), vmp.vmp_wire_count)
4491                out_string += first_bitfield_format_string.format(vmp.vmp_q_state, vmp.vmp_in_background, vmp.vmp_on_backgroundq, vmp.vmp_gobbled, vmp.vmp_laundry, vmp.vmp_no_cache,
4492                                                                   vmp.vmp_private, vmp.vmp_reference)
4493
4494                if hasattr(vmp,'slid'):
4495                    vmp_slid = vmp.slid
4496                else:
4497                    vmp_slid = 0
4498                out_string += second_bitfield_format_string.format(vmp.vmp_busy, vmp.vmp_wanted, vmp.vmp_tabled, vmp.vmp_hashed, vmp.vmp_fictitious, vmp.vmp_clustered,
4499                                                                    vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent,
4500                                                                    vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting,
4501                                                                    vmp.vmp_restart, vmp.vmp_unusual, 0, 0,
4502                                                                    vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid,
4503                                                                    vmp.vmp_written_by_kernel)
4504
4505        if (vmp in pages_seen):
4506            print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n"
4507            return
4508
4509        if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)):
4510            print out_string + " vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) +  " points to different vm_object_t: " + "{0: <#020x}".format(unsigned(_vm_page_unpack_ptr(vmp.vmp_object)))
4511            return
4512
4513        if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0):
4514            print out_string + " page in wired state with wire_count of 0\n"
4515            print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n"
4516            print "stopping...\n"
4517            return
4518
4519        if (hasattr(vmp, 'vmp_unused_page_bits') and (vmp.vmp_unused_page_bits != 0)):
4520            print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused__pageq_bits: %d\n" % (vmp.vmp_unused_page_bits)
4521            print "stopping...\n"
4522            return
4523
4524        if (hasattr(vmp, 'vmp_unused_object_bits') and (vmp.vmp_unused_object_bits != 0)):
4525            print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused_object_bits : %d\n" % (vmp.vmp_unused_object_bits)
4526            print "stopping...\n"
4527            return
4528
4529        pages_seen.add(vmp)
4530
4531        if False:
4532            hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset)
4533            hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
4534            hash_page = _vm_page_unpack_ptr(hash_page_list)
4535            hash_page_t = 0
4536
4537            while (hash_page != 0):
4538                hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
4539                if hash_page_t == vmp:
4540                    break
4541                hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m)
4542
4543            if (unsigned(vmp) != unsigned(hash_page_t)):
4544                print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n"
4545                print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset)))
4546                return
4547
4548        if (page_count >= limit and not(ignore_limit)):
4549            print out_string + "Limit reached (%d pages), stopping..." % (limit)
4550            break
4551
4552        print out_string
4553
4554        if page_found and stop:
4555            print("Object reports resident page count of: %d we stopped after traversing %d and finding the requested page.\n" % (unsigned(obj.res_page_count), unsigned(page_count)))
4556            return
4557
4558    if (page != 0):
4559        print("page found? : %s\n" % page_found)
4560
4561    if (off > 0):
4562        print("page found? : %s\n" % page_found)
4563
4564    print("Object reports resident page count of %d, we saw %d pages when we walked the resident list.\n" % (unsigned(obj.resident_page_count), unsigned(page_count)))
4565
4566    if show_compressed != 0 and obj.pager != 0 and unsigned(obj.pager.mo_pager_ops) == unsigned(addressof(kern.globals.compressor_pager_ops)):
4567        pager = Cast(obj.pager, 'compressor_pager *')
4568        chunks = pager.cpgr_num_slots / 128
4569        pagesize = kern.globals.page_size
4570
4571        page_idx = 0
4572        while page_idx < pager.cpgr_num_slots:
4573            if chunks != 0:
4574                chunk = pager.cpgr_slots.cpgr_islots[page_idx / 128]
4575                slot = chunk[page_idx % 128]
4576            elif pager.cpgr_num_slots > 2:
4577                slot = pager.cpgr_slots.cpgr_dslots[page_idx]
4578            else:
4579                slot = pager.cpgr_slots.cpgr_eslots[page_idx]
4580
4581            if slot != 0:
4582               print("compressed page for offset: %x slot %x\n" % ((page_idx * pagesize) - obj.paging_offset, slot))
4583            page_idx = page_idx + 1
4584
4585
4586@lldb_command("show_all_apple_protect_pagers")
4587def ShowAllAppleProtectPagers(cmd_args=None):
4588    """Routine to print all apple_protect pagers
4589        usage: show_all_apple_protect_pagers
4590    """
4591    print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "cached", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
4592    qhead = kern.globals.apple_protect_pager_queue
4593    qtype = GetType('apple_protect_pager *')
4594    qcnt = kern.globals.apple_protect_pager_count
4595    idx = 0
4596    for pager in IterateQueue(qhead, qtype, "pager_queue"):
4597        idx = idx + 1
4598        show_apple_protect_pager(pager, qcnt, idx)
4599
4600@lldb_command("show_apple_protect_pager")
4601def ShowAppleProtectPager(cmd_args=None):
4602    """Routine to print out info about an apple_protect pager
4603        usage: show_apple_protect_pager <pager>
4604    """
4605    if cmd_args == None or len(cmd_args) < 1:
4606        print "Invalid argument.", ShowAppleProtectPager.__doc__
4607        return
4608    pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t')
4609    show_apple_protect_pager(pager, 1, 1)
4610
4611def show_apple_protect_pager(pager, qcnt, idx):
4612    object = pager.backing_object
4613    shadow = object.shadow
4614    while shadow != 0:
4615        object = shadow
4616        shadow = object.shadow
4617    vnode_pager = Cast(object.pager,'vnode_pager *')
4618    filename = GetVnodePath(vnode_pager.vnode_handle)
4619    if hasattr(pager, "ap_pgr_hdr_ref"):
4620        refcnt = pager.ap_pgr_hdr_ref
4621    else:
4622        refcnt = pager.ap_pgr_hdr.mo_ref
4623    print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {:>6d} {: <#018x} {:#018x} {:#018x} {:#018x} {:#018x}\n\tcrypt_info:{: <#018x} <decrypt:{: <#018x} end:{:#018x} ops:{: <#018x} refs:{:<d}>\n\tvnode:{: <#018x} {:s}\n".format(idx, qcnt, pager, refcnt, pager.is_ready, pager.is_mapped, pager.is_cached, pager.backing_object, pager.backing_offset, pager.crypto_backing_offset, pager.crypto_start, pager.crypto_end, pager.crypt_info, pager.crypt_info.page_decrypt, pager.crypt_info.crypt_end, pager.crypt_info.crypt_ops, pager.crypt_info.crypt_refcnt, vnode_pager.vnode_handle, filename)
4624    showvmobject(pager.backing_object, pager.backing_offset, pager.crypto_end - pager.crypto_start, 1, 1)
4625
4626@lldb_command("show_all_shared_region_pagers")
4627def ShowAllSharedRegionPagers(cmd_args=None):
4628    """Routine to print all shared_region pagers
4629        usage: show_all_shared_region_pagers
4630    """
4631    print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "object", "offset", "jop_key", "slide", "slide_info")
4632    qhead = kern.globals.shared_region_pager_queue
4633    qtype = GetType('shared_region_pager *')
4634    qcnt = kern.globals.shared_region_pager_count
4635    idx = 0
4636    for pager in IterateQueue(qhead, qtype, "srp_queue"):
4637        idx = idx + 1
4638        show_shared_region_pager(pager, qcnt, idx)
4639
4640@lldb_command("show_shared_region_pager")
4641def ShowSharedRegionPager(cmd_args=None):
4642    """Routine to print out info about a shared_region pager
4643        usage: show_shared_region_pager <pager>
4644    """
4645    if cmd_args == None or len(cmd_args) < 1:
4646        print "Invalid argument.", ShowSharedRegionPager.__doc__
4647        return
4648    pager = kern.GetValueFromAddress(cmd_args[0], 'shared_region_pager_t')
4649    show_shared_region_pager(pager, 1, 1)
4650
4651def show_shared_region_pager(pager, qcnt, idx):
4652    object = pager.srp_backing_object
4653    shadow = object.shadow
4654    while shadow != 0:
4655        object = shadow
4656        shadow = object.shadow
4657    vnode_pager = Cast(object.pager,'vnode_pager *')
4658    filename = GetVnodePath(vnode_pager.vnode_handle)
4659    if hasattr(pager, 'srp_ref_count'):
4660        ref_count = pager.srp_ref_count
4661    else:
4662        ref_count = pager.srp_header.mo_ref
4663    if hasattr(pager, 'srp_jop_key'):
4664        jop_key = pager.srp_jop_key
4665    else:
4666        jop_key = -1
4667    print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {: <#018x} {:#018x} {:#018x} {:#018x}\n\tvnode:{: <#018x} {:s}\n".format(idx, qcnt, pager, ref_count, pager.srp_is_ready, pager.srp_is_mapped, pager.srp_backing_object, pager.srp_backing_offset, jop_key, pager.srp_slide_info.si_slide, pager.srp_slide_info, vnode_pager.vnode_handle, filename)
4668    showvmobject(pager.srp_backing_object, pager.srp_backing_offset, pager.srp_slide_info.si_end - pager.srp_slide_info.si_start, 1, 1)
4669
4670@lldb_command("show_console_ring")
4671def ShowConsoleRingData(cmd_args=None):
4672    """ Print console ring buffer stats and data
4673    """
4674    cr = kern.globals.console_ring
4675    print "console_ring = {:#018x}  buffer = {:#018x}  length = {:<5d}  used = {:<5d}  read_ptr = {:#018x}  write_ptr = {:#018x}".format(addressof(cr), cr.buffer, cr.len, cr.used, cr.read_ptr, cr.write_ptr)
4676    pending_data = []
4677    for i in range(unsigned(cr.used)):
4678        idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len)
4679        pending_data.append("{:c}".format(cr.buffer[idx]))
4680
4681    if pending_data:
4682        print "Data:"
4683        print "".join(pending_data)
4684
4685# Macro: showjetsamsnapshot
4686
4687@lldb_command("showjetsamsnapshot", "DA")
4688def ShowJetsamSnapshot(cmd_args=None, cmd_options={}):
4689    """ Dump entries in the jetsam snapshot table
4690        usage: showjetsamsnapshot [-D] [-A]
4691        Use -D flag to print extra physfootprint details
4692        Use -A flag to print all entries (regardless of valid count)
4693    """
4694
4695    # Not shown are uuid, user_data, cpu_time
4696
4697    global kern
4698
4699    show_footprint_details = False
4700    show_all_entries = False
4701
4702    if "-D" in cmd_options:
4703        show_footprint_details = True
4704
4705    if "-A" in cmd_options:
4706        show_all_entries = True
4707
4708    valid_count = kern.globals.memorystatus_jetsam_snapshot_count
4709    max_count = kern.globals.memorystatus_jetsam_snapshot_max
4710
4711    if (show_all_entries == True):
4712        count = max_count
4713    else:
4714        count = valid_count
4715
4716    print "{:s}".format(valid_count)
4717    print "{:s}".format(max_count)
4718
4719    if int(count) == 0:
4720        print "The jetsam snapshot is empty."
4721        print "Use -A to force dump all entries (regardless of valid count)"
4722        return
4723
4724    # Dumps the snapshot header info
4725    print lldb_run_command('p *memorystatus_jetsam_snapshot')
4726
4727    hdr_format = "{0: >32s} {1: >5s} {2: >4s} {3: >6s} {4: >6s} {5: >20s} {6: >20s} {7: >20s} {8: >5s} {9: >10s} {10: >6s} {11: >6s} {12: >10s} {13: >15s} {14: >15s} {15: >15s}"
4728    if (show_footprint_details == True):
4729        hdr_format += "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}"
4730
4731
4732    if (show_footprint_details == False):
4733        print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax')
4734        print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)')
4735    else:
4736        print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax', '|| internal', 'internal_comp', 'iokit_mapped', 'purge_nonvol', 'purge_nonvol_comp', 'alt_acct', 'alt_acct_comp', 'page_table')
4737        print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
4738
4739
4740    entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
4741                   "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
4742                   "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
4743                   "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\
4744                   "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
4745
4746    if (show_footprint_details == True):
4747        entry_format += "{e.jse_internal_pages: >15d} "\
4748                        "{e.jse_internal_compressed_pages: >15d} "\
4749                        "{e.jse_iokit_mapped_pages: >12d} "\
4750                        "{e.jse_purgeable_nonvolatile_pages: >12d} "\
4751                        "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
4752                        "{e.jse_alternate_accounting_pages: >10d} "\
4753                        "{e.jse_alternate_accounting_compressed_pages: >13d} "\
4754                        "{e.jse_page_table_pages: >10d}"
4755
4756    snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries
4757    idx = 0
4758    while idx < count:
4759        current_entry = dereference(Cast(addressof(snapshot_list[idx]), 'jetsam_snapshot_entry *'))
4760        print entry_format.format(index=idx, e=current_entry)
4761        idx +=1
4762    return
4763
4764# EndMacro: showjetsamsnapshot
4765
4766# Macro: showvnodecleanblk/showvnodedirtyblk
4767
4768def _GetBufSummary(buf):
4769    """ Get a summary of important information out of a buf_t.
4770    """
4771    initial = "(struct buf) {0: <#0x} ="
4772
4773    # List all of the fields in this buf summary.
4774    entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq,
4775        buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid,
4776        buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp,
4777        buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata,
4778        buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff,
4779        buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr]
4780
4781    # Join an (already decent) string representation of each field
4782    # with newlines and indent the region.
4783    joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n    ")
4784
4785    # Add the total string representation to our title and return it.
4786    out_str = initial.format(int(buf)) + " {\n    " + joined_strs + "\n}\n\n"
4787    return out_str
4788
4789def _ShowVnodeBlocks(dirty=True, cmd_args=None):
4790    """ Display info about all [dirty|clean] blocks in a vnode.
4791    """
4792    if cmd_args == None or len(cmd_args) < 1:
4793        print "Please provide a valid vnode argument."
4794        return
4795
4796    vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
4797    list_head = vnodeval.v_cleanblkhd;
4798    if dirty:
4799        list_head = vnodeval.v_dirtyblkhd
4800
4801    print "Blocklist for vnode {}:".format(cmd_args[0])
4802
4803    i = 0
4804    for buf in IterateListEntry(list_head, 'struct buf *', 'b_hash'):
4805        # For each block (buf_t) in the appropriate list,
4806        # ask for a summary and print it.
4807        print "---->\nblock {}: ".format(i) + _GetBufSummary(buf)
4808        i += 1
4809    return
4810
4811@lldb_command('showvnodecleanblk')
4812def ShowVnodeCleanBlocks(cmd_args=None):
4813    """ Display info about all clean blocks in a vnode.
4814        usage: showvnodecleanblk <address of vnode>
4815    """
4816    _ShowVnodeBlocks(False, cmd_args)
4817
4818@lldb_command('showvnodedirtyblk')
4819def ShowVnodeDirtyBlocks(cmd_args=None):
4820    """ Display info about all dirty blocks in a vnode.
4821        usage: showvnodedirtyblk <address of vnode>
4822    """
4823    _ShowVnodeBlocks(True, cmd_args)
4824
4825# EndMacro: showvnodecleanblk/showvnodedirtyblk
4826
4827
4828@lldb_command("vm_page_lookup_in_map")
4829def VmPageLookupInMap(cmd_args=None):
4830    """Lookup up a page at a virtual address in a VM map
4831        usage: vm_page_lookup_in_map <map> <vaddr>
4832    """
4833    if cmd_args == None or len(cmd_args) < 2:
4834        print "Invalid argument.", VmPageLookupInMap.__doc__
4835        return
4836    map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
4837    vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t')
4838    print "vaddr {:#018x} in map {: <#018x}".format(vaddr, map)
4839    vm_page_lookup_in_map(map, vaddr)
4840
4841def vm_page_lookup_in_map(map, vaddr):
4842    vaddr = unsigned(vaddr)
4843    vme_list_head = map.hdr.links
4844    vme_ptr_type = GetType('vm_map_entry *')
4845    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
4846        if unsigned(vme.links.start) > vaddr:
4847            break
4848        if unsigned(vme.links.end) <= vaddr:
4849            continue
4850        offset_in_vme = vaddr - unsigned(vme.links.start)
4851        print "  offset {:#018x} in map entry {: <#018x} [{:#018x}:{:#018x}] object {: <#018x} offset {:#018x}".format(offset_in_vme, vme, unsigned(vme.links.start), unsigned(vme.links.end), vme.vme_object.vmo_object, unsigned(vme.vme_offset) & ~0xFFF)
4852        offset_in_object = offset_in_vme + (unsigned(vme.vme_offset) & ~0xFFF)
4853        if vme.is_sub_map:
4854            print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object, vme.vme_object.vmo_submap)
4855            vm_page_lookup_in_map(vme.vme_object.vmo_submap, offset_in_object)
4856        else:
4857            vm_page_lookup_in_object(vme.vme_object.vmo_object, offset_in_object)
4858
4859@lldb_command("vm_page_lookup_in_object")
4860def VmPageLookupInObject(cmd_args=None):
4861    """Lookup up a page at a given offset in a VM object
4862        usage: vm_page_lookup_in_object <object> <offset>
4863    """
4864    if cmd_args == None or len(cmd_args) < 2:
4865        print "Invalid argument.", VmPageLookupInObject.__doc__
4866        return
4867    object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
4868    offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t')
4869    print "offset {:#018x} in object {: <#018x}".format(offset, object)
4870    vm_page_lookup_in_object(object, offset)
4871
4872def vm_page_lookup_in_object(object, offset):
4873    offset = unsigned(offset)
4874    page_size = kern.globals.page_size
4875    trunc_offset = offset & ~(page_size - 1)
4876    print "    offset {:#018x} in VM object {: <#018x}".format(offset, object)
4877    hash_id = _calc_vm_page_hash(object, trunc_offset)
4878    page_list = kern.globals.vm_page_buckets[hash_id].page_list
4879    page = _vm_page_unpack_ptr(page_list)
4880    while page != 0:
4881        m = kern.GetValueFromAddress(page, 'vm_page_t')
4882        m_object_val = _vm_page_unpack_ptr(m.vmp_object)
4883        m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t')
4884        if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset):
4885            page = _vm_page_unpack_ptr(m.vmp_next_m)
4886            continue
4887        print "    resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m))
4888        return
4889    if object.pager and object.pager_ready:
4890        offset_in_pager = trunc_offset + unsigned(object.paging_offset)
4891        if not object.internal:
4892            print "    offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager)
4893            return
4894        pager = Cast(object.pager, 'compressor_pager *')
4895        ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager)
4896        if ret:
4897            return
4898    if object.shadow and not object.phys_contiguous:
4899        offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset)
4900        vm_page_lookup_in_object(object.shadow, offset_in_shadow)
4901        return
4902    print "    page is absent and will be zero-filled on demand"
4903    return
4904
4905@lldb_command("vm_page_lookup_in_compressor_pager")
4906def VmPageLookupInCompressorPager(cmd_args=None):
4907    """Lookup up a page at a given offset in a compressor pager
4908        usage: vm_page_lookup_in_compressor_pager <pager> <offset>
4909    """
4910    if cmd_args == None or len(cmd_args) < 2:
4911        print "Invalid argument.", VmPageLookupInCompressorPager.__doc__
4912        return
4913    pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t')
4914    offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t')
4915    print "offset {:#018x} in compressor pager {: <#018x}".format(offset, pager)
4916    vm_page_lookup_in_compressor_pager(pager, offset)
4917
4918def vm_page_lookup_in_compressor_pager(pager, offset):
4919    offset = unsigned(offset)
4920    page_size = unsigned(kern.globals.page_size)
4921    page_num = unsigned(offset / page_size)
4922    if page_num > pager.cpgr_num_slots:
4923        print "      *** ERROR: vm_page_lookup_in_compressor_pager({: <#018x},{:#018x}): page_num {:#x} > num_slots {:#x}".format(pager, offset, page_num, pager.cpgr_num_slots)
4924        return 0
4925    slots_per_chunk = 512 / sizeof ('compressor_slot_t')
4926    num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) / slots_per_chunk)
4927    if num_chunks > 1:
4928        chunk_idx = unsigned(page_num / slots_per_chunk)
4929        chunk = pager.cpgr_slots.cpgr_islots[chunk_idx]
4930        slot_idx = unsigned(page_num % slots_per_chunk)
4931        slot = GetObjectAtIndexFromArray(chunk, slot_idx)
4932        slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx)
4933    elif pager.cpgr_num_slots > 2:
4934        slot_idx = page_num
4935        slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx)
4936        slot_str = "dslots[{:d}]".format(slot_idx)
4937    else:
4938        slot_idx = page_num
4939        slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx)
4940        slot_str = "eslots[{:d}]".format(slot_idx)
4941    print "      offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot)
4942    if slot == 0:
4943        return 0
4944    slot_value = dereference(slot)
4945    print " value {:#010x}".format(slot_value)
4946    vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t'))
4947    return 1
4948
4949@lldb_command("vm_page_lookup_in_compressor")
4950def VmPageLookupInCompressor(cmd_args=None):
4951    """Lookup up a page in a given compressor slot
4952        usage: vm_page_lookup_in_compressor <slot>
4953    """
4954    if cmd_args == None or len(cmd_args) < 1:
4955        print "Invalid argument.", VmPageLookupInCompressor.__doc__
4956        return
4957    slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *')
4958    print "compressor slot {: <#018x}".format(slot)
4959    vm_page_lookup_in_compressor(slot)
4960
4961C_SV_CSEG_ID = ((1 << 22) - 1)
4962
4963def vm_page_lookup_in_compressor(slot_ptr):
4964    slot_ptr = Cast(slot_ptr, 'compressor_slot_t *')
4965    slot_value = dereference(slot_ptr)
4966    slot = Cast(slot_value, 'c_slot_mapping')
4967    print slot
4968    print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx)
4969    if slot_ptr == 0:
4970        return
4971    if slot.s_cseg == C_SV_CSEG_ID:
4972        sv = kern.globals.c_segment_sv_hash_table
4973        print "single value[{:#d}]: ref {:d} value {:#010x}".format(slot.s_cindx, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_ref, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_data)
4974        return
4975    if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available):
4976        print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available))
4977        return
4978    c_segments = kern.globals.c_segments
4979    c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1)
4980    c_seg = c_segments_elt.c_seg
4981    c_no_data = 0
4982    if hasattr(c_seg, 'c_state'):
4983        c_state = c_seg.c_state
4984        if c_state == 0:
4985            c_state_str = "C_IS_EMPTY"
4986            c_no_data = 1
4987        elif c_state == 1:
4988            c_state_str = "C_IS_FREE"
4989            c_no_data = 1
4990        elif c_state == 2:
4991            c_state_str = "C_IS_FILLING"
4992        elif c_state == 3:
4993            c_state_str = "C_ON_AGE_Q"
4994        elif c_state == 4:
4995            c_state_str = "C_ON_SWAPOUT_Q"
4996        elif c_state == 5:
4997            c_state_str = "C_ON_SWAPPEDOUT_Q"
4998            c_no_data = 1
4999        elif c_state == 6:
5000            c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q"
5001            c_no_data = 1
5002        elif c_state == 7:
5003            c_state_str = "C_ON_SWAPPEDIN_Q"
5004        elif c_state == 8:
5005            c_state_str = "C_ON_MAJORCOMPACT_Q"
5006        elif c_state == 9:
5007            c_state_str = "C_ON_BAD_Q"
5008            c_no_data = 1
5009        else:
5010            c_state_str = "<unknown>"
5011    else:
5012        c_state = -1
5013        c_state_str = "<no c_state field>"
5014    print "c_segments[{:d}] {: <#018x} c_seg {: <#018x} c_state {:#x}={:s}".format(slot.s_cseg-1, c_segments_elt, c_seg, c_state, c_state_str)
5015    c_indx = unsigned(slot.s_cindx)
5016    if hasattr(c_seg, 'c_slot_var_array'):
5017        c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len
5018        if c_indx < c_seg_fixed_array_len:
5019            cs = c_seg.c_slot_fixed_array[c_indx]
5020        else:
5021            cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len)
5022    else:
5023        C_SEG_SLOT_ARRAY_SIZE = 64
5024        C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1
5025        cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx / C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK)
5026    print cs
5027    c_slot_unpacked_ptr = vm_unpack_ptr(cs.c_packed_ptr, kern.globals.c_slot_packing_params)
5028    print "c_slot {: <#018x} c_offset {:#x} c_size {:#x} c_packed_ptr {:#x} (unpacked: {: <#018x})".format(cs, cs.c_offset, cs.c_size, cs.c_packed_ptr, unsigned(c_slot_unpacked_ptr))
5029    if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr):
5030        print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr)
5031    if c_no_data == 0:
5032        c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset)
5033        c_size = cs.c_size
5034        cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size)
5035        print cmd
5036        print lldb_run_command(cmd)
5037    else:
5038        print "<no compressed data>"
5039
5040@lldb_command('vm_scan_all_pages')
5041def VMScanAllPages(cmd_args=None):
5042    """Scans the vm_pages[] array
5043    """
5044    vm_pages_count = kern.globals.vm_pages_count
5045    vm_pages = kern.globals.vm_pages
5046
5047    free_count = 0
5048    local_free_count = 0
5049    active_count = 0
5050    local_active_count = 0
5051    inactive_count = 0
5052    speculative_count = 0
5053    throttled_count = 0
5054    wired_count = 0
5055    compressor_count = 0
5056    pageable_internal_count = 0
5057    pageable_external_count = 0
5058    secluded_count = 0
5059    secluded_free_count = 0
5060    secluded_inuse_count = 0
5061
5062    i = 0
5063    while i < vm_pages_count:
5064
5065        if i % 10000 == 0:
5066            print "{:d}/{:d}...\n".format(i,vm_pages_count)
5067
5068        m = vm_pages[i]
5069
5070        internal = 0
5071        external = 0
5072        m_object_val = _vm_page_unpack_ptr(m.vmp_object)
5073
5074        if m_object:
5075            if m_object.internal:
5076                internal = 1
5077            else:
5078                external = 1
5079
5080        if m.vmp_wire_count != 0 and m.vmp_local == 0:
5081            wired_count = wired_count + 1
5082            pageable = 0
5083        elif m.vmp_throttled:
5084            throttled_count = throttled_count + 1
5085            pageable = 0
5086        elif m.vmp_active:
5087            active_count = active_count + 1
5088            pageable = 1
5089        elif m.vmp_local:
5090            local_active_count = local_active_count + 1
5091            pageable = 0
5092        elif m.vmp_inactive:
5093            inactive_count = inactive_count + 1
5094            pageable = 1
5095        elif m.vmp_speculative:
5096            speculative_count = speculative_count + 1
5097            pageable = 0
5098        elif m.vmp_free:
5099            free_count = free_count + 1
5100            pageable = 0
5101        elif m.vmp_secluded:
5102            secluded_count = secluded_count + 1
5103            if m_object == 0:
5104                secluded_free_count = secluded_free_count + 1
5105            else:
5106                secluded_inuse_count = secluded_inuse_count + 1
5107            pageable = 0
5108        elif m_object == 0 and m.vmp_busy:
5109            local_free_count = local_free_count + 1
5110            pageable = 0
5111        elif m.vmp_compressor:
5112            compressor_count = compressor_count + 1
5113            pageable = 0
5114        else:
5115            print "weird page vm_pages[{:d}]?\n".format(i)
5116            pageable = 0
5117
5118        if pageable:
5119            if internal:
5120                pageable_internal_count = pageable_internal_count + 1
5121            else:
5122                pageable_external_count = pageable_external_count + 1
5123        i = i + 1
5124
5125    print "vm_pages_count = {:d}\n".format(vm_pages_count)
5126
5127    print "wired_count = {:d}\n".format(wired_count)
5128    print "throttled_count = {:d}\n".format(throttled_count)
5129    print "active_count = {:d}\n".format(active_count)
5130    print "local_active_count = {:d}\n".format(local_active_count)
5131    print "inactive_count = {:d}\n".format(inactive_count)
5132    print "speculative_count = {:d}\n".format(speculative_count)
5133    print "free_count = {:d}\n".format(free_count)
5134    print "local_free_count = {:d}\n".format(local_free_count)
5135    print "compressor_count = {:d}\n".format(compressor_count)
5136
5137    print "pageable_internal_count = {:d}\n".format(pageable_internal_count)
5138    print "pageable_external_count = {:d}\n".format(pageable_external_count)
5139    print "secluded_count = {:d}\n".format(secluded_count)
5140    print "secluded_free_count = {:d}\n".format(secluded_free_count)
5141    print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count)
5142
5143
5144@lldb_command('show_all_vm_named_entries')
5145def ShowAllVMNamedEntries(cmd_args=None):
5146    """ Routine to print a summary listing of all the VM named entries
5147    """
5148    queue_len = kern.globals.vm_named_entry_count
5149    queue_head = kern.globals.vm_named_entry_list
5150
5151    print 'vm_named_entry_list:{: <#018x}  vm_named_entry_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len)
5152
5153#    print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s}   {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
5154    idx = 0
5155    for entry in IterateQueue(queue_head, 'struct vm_named_entry *', 'named_entry_list'):
5156        idx += 1
5157        showmemoryentry(entry, idx, queue_len)
5158
5159@lldb_command('show_vm_named_entry')
5160def ShowVMNamedEntry(cmd_args=None):
5161    """ Routine to print a VM named entry
5162    """
5163    if cmd_args == None or len(cmd_args) < 1:
5164        print "Invalid argument.", ShowMapVMNamedEntry.__doc__
5165        return
5166    named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t')
5167    showmemoryentry(named_entry, 0, 0)
5168
5169def showmemoryentry(entry, idx=0, queue_len=0):
5170    """  Routine to print out a summary a VM memory entry
5171        params:
5172            entry - core.value : a object of type 'struct vm_named_entry *'
5173        returns:
5174            None
5175    """
5176    show_pager_info = True
5177    show_all_shadows = True
5178
5179    backing = ""
5180    if entry.is_sub_map == 1:
5181        backing += "SUBMAP"
5182    if entry.is_copy == 1:
5183        backing += "COPY"
5184    if entry.is_object == 1:
5185        backing += "OBJECT"
5186    if entry.is_sub_map == 0 and entry.is_copy == 0 and entry.is_object == 0:
5187        backing += "***?***"
5188    prot=""
5189    if entry.protection & 0x1:
5190        prot += "r"
5191    else:
5192        prot += "-"
5193    if entry.protection & 0x2:
5194        prot += "w"
5195    else:
5196        prot += "-"
5197    if entry.protection & 0x4:
5198        prot += "x"
5199    else:
5200        prot += "-"
5201    extra_str = ""
5202    if hasattr(entry, 'named_entry_alias'):
5203        extra_str += " alias={:d}".format(entry.named_entry_alias)
5204    if hasattr(entry, 'named_entry_port'):
5205        extra_str += " port={:#016x}".format(entry.named_entry_port)
5206    print "{:d}/{:d} {: <#018x} ref={:d} prot={:d}/{:s} type={:s} backing={: <#018x} offset={:#016x} dataoffset={:#016x} size={:#016x}{:s}\n".format(idx,queue_len,entry,entry.ref_count,entry.protection,prot,backing,entry.backing.copy,entry.offset,entry.data_offset,entry.size,extra_str)
5207    if entry.is_sub_map == 1:
5208        showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows)
5209    elif entry.is_copy == 1:
5210        showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
5211    elif entry.is_object == 1:
5212        showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
5213    else:
5214        print "***** UNKNOWN TYPE *****"
5215    print " \n"
5216
5217
5218def IterateRBTreeEntry2(element, element_type, field_name1, field_name2):
5219    """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
5220            element      - value : Value object for rbh_root
5221            element_type - str   : Type of the link element
5222            field_name   - str   : Name of the field in link element's structure
5223        returns:
5224            A generator does not return. It is used for iterating
5225            value  : an object thats of type (element_type) head->sle_next. Always a pointer object
5226    """
5227    elt = element.__getattr__('rbh_root')
5228    if type(element_type) == str:
5229        element_type = gettype(element_type)
5230    charp_type = gettype('char *');
5231
5232    # Walk to find min
5233    parent = elt
5234    while unsigned(elt) != 0:
5235        parent = elt
5236        elt = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
5237    elt = parent
5238
5239    # Now elt is min
5240    while unsigned(elt) != 0:
5241        yield elt
5242        # implementation cribbed from RB_NEXT in libkern/tree.h
5243        right = cast(elt.__getattr__(field_name1).__getattr__(fieldname2).__getattr__('rbe_right'), element_type)
5244        if unsigned(right) != 0:
5245            elt = right
5246            left = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
5247            while unsigned(left) != 0:
5248                elt = left
5249                left = cast(elt.__getattr__(field_name1).__getattr(__field_name2).__getattr__('rbe_left'), element_type)
5250        else:
5251
5252            # avoid using GetValueFromAddress
5253            addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
5254            parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
5255            parent = cast(parent, element_type)
5256
5257            if unsigned(parent) != 0:
5258                left = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
5259            if (unsigned(parent) != 0) and (unsigned(elt) == unsigned(left)):
5260                elt = parent
5261            else:
5262                if unsigned(parent) != 0:
5263                    right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
5264                while unsigned(parent) != 0 and (unsigned(elt) == unsigned(right)):
5265                    elt = parent
5266
5267                    # avoid using GetValueFromAddress
5268                    addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
5269                    parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
5270                    parent = cast(parent, element_type)
5271
5272                    right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
5273
5274                # avoid using GetValueFromAddress
5275                addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
5276                elt = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
5277                elt = cast(elt, element_type)
5278
5279
5280@lldb_command("showmaprb")
5281def ShowMapRB(cmd_args=None):
5282    """Routine to print out a VM map's RB tree
5283        usage: showmaprb <vm_map>
5284    """
5285    if cmd_args == None or len(cmd_args) < 1:
5286        print "Invalid argument.", ShowMapRB.__doc__
5287        return
5288    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
5289    print GetVMMapSummary.header
5290    print GetVMMapSummary(map_val)
5291    vme_rb_root = map_val.hdr.rb_head_store
5292    vme_ptr_type = GetType('struct vm_map_entry *')
5293    print GetVMEntrySummary.header
5294    for vme in IterateRBTreeEntry2(vme_rb_root, 'struct vm_map_entry *', 'store', 'entry'):
5295        print GetVMEntrySummary(vme)
5296    return None
5297
5298@lldb_command('show_all_owned_objects', 'T')
5299def ShowAllOwnedObjects(cmd_args=None, cmd_options={}):
5300    """ Routine to print the list of VM objects owned by each task
5301        -T: show only ledger-tagged objects
5302    """
5303    showonlytagged = False
5304    if "-T" in cmd_options:
5305        showonlytagged = True
5306    for task in kern.tasks:
5307        ShowTaskOwnedVmObjects(task, showonlytagged)
5308
5309@lldb_command('show_task_owned_objects', 'T')
5310def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}):
5311    """ Routine to print the list of VM objects owned by the specified task
5312        -T: show only ledger-tagged objects
5313    """
5314    showonlytagged = False
5315    if "-T" in cmd_options:
5316        showonlytagged = True
5317    task = kern.GetValueFromAddress(cmd_args[0], 'task *')
5318    ShowTaskOwnedVmObjects(task, showonlytagged)
5319
5320@lldb_command('showdeviceinfo', 'J')
5321def ShowDeviceInfo(cmd_args=None, cmd_options={}):
5322    """ Routine to show basic device information (model, build, ncpus, etc...)
5323        Usage: memstats  [-J]
5324            -J      : Output json
5325    """
5326    print_json = False
5327    if "-J" in cmd_options:
5328        print_json = True
5329    device_info = {}
5330    device_info["build"] =  str(kern.globals.osversion)
5331    device_info["memoryConfig"] = int(kern.globals.max_mem_actual)
5332    device_info["ncpu"] = int(kern.globals.ncpu)
5333    device_info["pagesize"] = int(kern.globals.page_size)
5334    device_info["mlockLimit"] = long(kern.globals.vm_global_user_wire_limit)
5335    # Serializing to json here ensure we always catch bugs preventing
5336    # serialization
5337    as_json = json.dumps(device_info)
5338
5339
5340    if print_json:
5341        print as_json
5342    else:
5343        PrettyPrintDictionary(device_info)
5344
5345def ShowTaskOwnedVmObjects(task, showonlytagged=False):
5346    """  Routine to print out a summary listing of all the entries in a vm_map
5347        params:
5348            task - core.value : a object of type 'task *'
5349        returns:
5350            None
5351    """
5352    taskobjq_total = lambda:None
5353    taskobjq_total.objects = 0
5354    taskobjq_total.vsize = 0
5355    taskobjq_total.rsize = 0
5356    taskobjq_total.wsize = 0
5357    taskobjq_total.csize = 0
5358    vmo_list_head = task.task_objq
5359    vmo_ptr_type = GetType('vm_object *')
5360    idx = 0
5361    for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"):
5362        idx += 1
5363        if not showonlytagged or vmo.vo_ledger_tag != 0:
5364            if taskobjq_total.objects == 0:
5365                print ' \n'
5366                print GetTaskSummary.header + ' ' + GetProcSummary.header
5367                print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
5368                print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>2s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tg","owner","pid","process")
5369            ShowOwnedVmObject(vmo, idx, 0, taskobjq_total)
5370    if taskobjq_total.objects != 0:
5371        print "           total:{:<10d}  [ virtual:{:<10d}  resident:{:<10d}  wired:{:<10d}  compressed:{:<10d} ]\n".format(taskobjq_total.objects, taskobjq_total.vsize, taskobjq_total.rsize, taskobjq_total.wsize, taskobjq_total.csize)
5372    return None
5373
5374def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total):
5375    """  Routine to print out a VM object owned by a task
5376        params:
5377            object - core.value : a object of type 'struct vm_object *'
5378        returns:
5379            None
5380    """
5381    page_size = kern.globals.page_size
5382    if object.purgable == 0:
5383        purgable = "N"
5384    elif object.purgable == 1:
5385        purgable = "V"
5386    elif object.purgable == 2:
5387        purgable = "E"
5388    elif object.purgable == 3:
5389        purgable = "D"
5390    else:
5391        purgable = "?"
5392    if object.pager == 0:
5393        compressed_count = 0
5394    else:
5395        compressor_pager = Cast(object.pager, 'compressor_pager *')
5396        compressed_count = compressor_pager.cpgr_num_slots_occupied
5397
5398    print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>2d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
5399
5400    taskobjq_total.objects += 1
5401    taskobjq_total.vsize += object.vo_un1.vou_size/page_size
5402    taskobjq_total.rsize += object.resident_page_count
5403    taskobjq_total.wsize += object.wired_page_count
5404    taskobjq_total.csize += compressed_count
5405
5406def GetProcPIDForObjectOwner(owner):
5407    """ same as GetProcPIDForTask() but deals with -1 for a disowned object
5408    """
5409    if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
5410        return -1
5411    return GetProcPIDForTask(owner)
5412
5413def GetProcNameForObjectOwner(owner):
5414    """ same as GetProcNameForTask() but deals with -1 for a disowned object
5415    """
5416    if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
5417        return "<disowned>"
5418    return GetProcNameForTask(owner)
5419
5420def GetDescForNamedEntry(mem_entry):
5421    out_str = "\n"
5422    out_str += "\t\tmem_entry {:#08x} ref:{:d} offset:{:#08x} size:{:#08x} prot{:d} backing {:#08x}".format(mem_entry, mem_entry.ref_count, mem_entry.offset, mem_entry.size, mem_entry.protection, mem_entry.backing.copy)
5423    if mem_entry.is_sub_map:
5424        out_str += " is_sub_map"
5425    elif mem_entry.is_copy:
5426        out_str += " is_copy"
5427    elif mem_entry.is_object:
5428        out_str += " is_object"
5429    else:
5430        out_str += " ???"
5431    return out_str
5432