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