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