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