xref: /xnu-11417.121.6/tools/lldbmacros/memory.py (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
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    if(page.vmp_object):
3304        m_object_val = _vm_page_unpack_ptr(page.vmp_object)
3305        object = kern.GetValueFromAddress(m_object_val, 'vm_object_t')
3306        if not object:
3307            print("No valid object: {:s}".format(m_object_val))
3308
3309        pager=object.pager
3310        paging_in_progress=object.paging_in_progress
3311        activity_in_progress=object.activity_in_progress
3312
3313    print(ShowVMPage.header)
3314    print("{:>#20x} {:>15d} {:>15d} {:>15d} {:>20d} {:>#20x} {:>#20x} {:>#20x} {:>20d} {:>15d}".format(
3315    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))
3316
3317    if show_object_info:
3318        print("\nPrinting object info for given page",object)
3319        showvmobject(object, 0, 0, 1, 1)
3320
3321    if show_callchain:
3322        print("Searching for Page: ",hex(page))
3323        show_call_chain(hex(page), O)
3324
3325        print("Searching for object: ",hex(m_object_val))
3326        show_call_chain(hex(m_object_val),O)
3327
3328        print("Searching for Pager: ",hex(pager))
3329        show_call_chain(hex(pager),O)
3330
3331@lldb_command("showvmobject", "A:B:PRSTU")
3332def ShowVMObject(cmd_args=None, cmd_options={}):
3333    """Routine to print out a VM object and its shadow chain
3334        usage: showvmobject <vm_object> [-S] [-P]
3335        -S: show VM object shadow chain
3336        -P: show pager info (mapped file, compressed pages, ...)
3337        -U: show UPL info
3338    """
3339    if cmd_args is None or len(cmd_args) == 0:
3340        raise ArgumentError()
3341
3342    show_pager_info = False
3343    show_all_shadows = False
3344    show_upl_info = False
3345
3346    if "-P" in cmd_options:
3347        show_pager_info = True
3348    if "-S" in cmd_options:
3349        show_all_shadows = True
3350    if "-U" in cmd_options:
3351        show_upl_info = True
3352    object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
3353    showvmobject(object, 0, 0, show_pager_info, show_all_shadows, show_upl_info)
3354
3355def PrintUPLSummary(upl, spacing=''):
3356    indented_spacing = spacing + " "*4
3357
3358    page_size = kern.globals.page_size
3359    print(f"{spacing}  {VT.Bold}{'Address (upl_t)':<18} {'Creator (thread)':<18} {'# pages':<10} {'associated UPL'}{VT.EndBold}")
3360
3361    num_pages = math.ceil(upl.u_size / page_size)
3362    associated_upl = f'{upl.associated_upl:#018x}' if upl.associated_upl else ''
3363    print(f'{spacing}  {upl:#018x} {upl.upl_creator:#018x}   {num_pages:<8} {associated_upl}')
3364
3365    first_page_info = True
3366    for page_ind in range(num_pages):
3367        if first_page_info:
3368            print(f"{indented_spacing} {VT.Bold}{'upl_index':<12} {'Address (upl_page_info *)':<28} {'ppnum':<24} {'page (vm_page_t)':<28}{VT.EndBold}")
3369            first_page_info = False
3370
3371        # lite_list is a bitfield marking pages locked by UPL
3372        bits_per_element = sizeof(upl.lite_list[0])
3373        bitfield_number = int(page_ind / bits_per_element)
3374        bit_in_bitfield = (1 << (page_ind % bits_per_element))
3375        if upl.lite_list[bitfield_number] & (bit_in_bitfield):
3376            upl_page_info = upl.page_list[page_ind]
3377            ppnum = upl_page_info.phys_addr
3378            page = _vm_page_get_page_from_phys(ppnum)
3379            page_addr = '' if page is None else f'{unsigned(addressof(page)):<#28x}'
3380
3381            print(f"{indented_spacing} {page_ind:<12} {unsigned(addressof(upl_page_info)):<#28x} {ppnum:<#24x} {page_addr}")
3382
3383def PrintVMObjUPLs(uplq_head):
3384    spacing = " "*19
3385    for upl in IterateQueue(addressof(uplq_head), 'upl_t', 'uplq'):
3386        PrintUPLSummary(upl, spacing)
3387
3388def showvmobject(object, offset=0, size=0, show_pager_info=False, show_all_shadows=False, show_upl_info=False):
3389    page_size = kern.globals.page_size
3390    vnode_pager_ops = kern.globals.vnode_pager_ops
3391    vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
3392    depth = 0
3393    if size == 0 and object != 0 and object.internal:
3394        size = object.vo_un1.vou_size
3395    while object != 0:
3396        depth += 1
3397        if not show_all_shadows and depth != 1 and object.shadow != 0:
3398            offset += unsigned(object.vo_un2.vou_shadow_offset)
3399            object = object.shadow
3400            continue
3401        if object.copy_strategy == 0:
3402            copy_strategy="N"
3403        elif object.copy_strategy == 2:
3404            copy_strategy="D"
3405        elif object.copy_strategy == 4:
3406            copy_strategy="S"
3407        elif object.copy_strategy == 6:
3408            copy_strategy="F";
3409        else:
3410            copy_strategy=str(object.copy_strategy)
3411        if object.internal:
3412            internal = "internal"
3413        else:
3414            internal = "external"
3415        purgeable = "NVED"[int(object.purgable)]
3416        pager_string = ""
3417        if object.phys_contiguous:
3418            pager_string = pager_string + "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2.vou_shadow_offset), unsigned(object.vo_un1.vou_size))
3419        pager = object.pager
3420        if show_pager_info and pager != 0:
3421            if object.internal:
3422                pager_string = pager_string + "-> compressed:{:d} ({:#018x})".format(GetCompressedPagesForObject(object), object.pager)
3423            elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr:
3424                vnode_pager = Cast(pager,'vnode_pager *')
3425                pager_string = pager_string + "-> " + GetVnodePath(vnode_pager.vnode_handle)
3426            else:
3427                pager_string = pager_string + "-> {:s}:{: <#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager)
3428        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))
3429#       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)
3430
3431        if show_upl_info:
3432            PrintVMObjUPLs(object.uplq)
3433
3434        offset += unsigned(object.vo_un2.vou_shadow_offset)
3435        object = object.shadow
3436
3437def 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):
3438    rsize = GetResidentPageCount(map)
3439    print("{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map","pmap","size","#ents","rsize","start","end","pgshift"))
3440    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))
3441    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)
3442
3443def 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):
3444    print("{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map_copy","offset","size","#ents","rsize","start","end","pgshift"))
3445    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))
3446    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)
3447
3448def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree, entry_filter, show_upl_info):
3449    page_size = kern.globals.page_size
3450    if hasattr(kern.globals, 'compressor_object'):
3451        compressor_object = kern.globals.compressor_object
3452    else:
3453        compressor_object = -1;
3454    vme_list_head = maphdr.links
3455    vme_ptr_type = GetType('vm_map_entry *')
3456    print("{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset"))
3457    last_end = unsigned(maphdr.links.start)
3458    skipped_entries = 0
3459    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links", reverse_order):
3460        links = vme.links
3461        vme_start = links.start
3462        vme_end = links.end
3463        vme_start_val = unsigned(vme_start)
3464        vme_end_val = unsigned(vme_end)
3465        if start_vaddr != 0 and end_vaddr != 0:
3466            if vme_start_val > end_vaddr:
3467                break
3468            if unsigned(vme_end) <= start_vaddr:
3469                last_end = vme_end_val
3470                skipped_entries = skipped_entries + 1
3471                continue
3472            if skipped_entries != 0:
3473                print("... skipped {:d} entries ...".format(skipped_entries))
3474                skipped_entries = 0
3475        if entry_filter and not entry_filter(vme):
3476            continue
3477        if vme_start_val != last_end:
3478            print("{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme_start,(vme_start_val - last_end) // page_size))
3479        last_end = vme_end_val
3480        size = vme_end_val - vme_start_val
3481        object = get_vme_object(vme)
3482        object_val = int(object)
3483        if object_val == 0:
3484            object_str = "{: <#018x}".format(object)
3485        elif vme.is_sub_map:
3486            object_str = None
3487
3488            if object_val == kern.globals.bufferhdr_map:
3489                object_str = "BUFFERHDR_MAP"
3490            elif object_val == kern.globals.mb_map:
3491                object_str = "MB_MAP"
3492            elif object_val == kern.globals.bsd_pageable_map:
3493                object_str = "BSD_PAGEABLE_MAP"
3494            elif object_val == kern.globals.ipc_kernel_map:
3495                object_str = "IPC_KERNEL_MAP"
3496            elif object_val == kern.globals.ipc_kernel_copy_map:
3497                object_str = "IPC_KERNEL_COPY_MAP"
3498            elif hasattr(kern.globals, 'io_submap') and object_val == kern.globals.io_submap:
3499                object_str = "IO_SUBMAP"
3500            elif hasattr(kern.globals, 'pgz_submap') and object_val == kern.globals.pgz_submap:
3501                object_str = "ZALLOC:PGZ"
3502            elif hasattr(kern.globals, 'compressor_map') and object_val == kern.globals.compressor_map:
3503                object_str = "COMPRESSOR_MAP"
3504            elif hasattr(kern.globals, 'g_kext_map') and object_val == kern.globals.g_kext_map:
3505                object_str = "G_KEXT_MAP"
3506            elif hasattr(kern.globals, 'vector_upl_submap') and object_val == kern.globals.vector_upl_submap:
3507                object_str = "VECTOR_UPL_SUBMAP"
3508            elif object_val == kern.globals.zone_meta_map:
3509                object_str = "ZALLOC:META"
3510            else:
3511                for i in range(0, int(GetEnumValue('zone_submap_idx_t', 'Z_SUBMAP_IDX_COUNT'))):
3512                    if object_val == kern.globals.zone_submaps[i]:
3513                        object_str = "ZALLOC:{:s}".format(GetEnumName('zone_submap_idx_t', i, 'Z_SUBMAP_IDX_'))
3514                        break
3515            if object_str is None:
3516                object_str = "submap:{: <#018x}".format(object)
3517        else:
3518            if object_val == kern.globals.kernel_object_default:
3519                object_str = "KERNEL_OBJECT"
3520            elif hasattr(kern.globals, 'kernel_object_tagged') and object_val == kern.globals.kernel_object_tagged:
3521                object_str = "KERNEL_OBJECT_TAGGED"
3522            elif object_val == compressor_object:
3523                object_str = "COMPRESSOR_OBJECT"
3524            else:
3525                object_str = "{: <#018x}".format(object)
3526        offset = get_vme_offset(vme)
3527        tag = unsigned(vme.vme_alias)
3528
3529        vme_protection = vme.protection
3530        protection = ""
3531        if vme_protection & 0x1:
3532            protection +="r"
3533        else:
3534            protection += "-"
3535        if vme_protection & 0x2:
3536            protection += "w"
3537        else:
3538            protection += "-"
3539        if vme_protection & 0x4:
3540            protection += "x"
3541        else:
3542            protection += "-"
3543
3544        vme_max_protection = vme.max_protection
3545        max_protection = ""
3546        if vme_max_protection & 0x1:
3547            max_protection += "r"
3548        else:
3549            max_protection += "-"
3550        if vme_max_protection & 0x2:
3551            max_protection += "w"
3552        else:
3553            max_protection += "-"
3554        if vme_max_protection & 0x4:
3555            max_protection += "x"
3556        else:
3557            max_protection += "-"
3558
3559        vme_flags = ""
3560        if vme.is_sub_map:
3561            vme_flags += "s"
3562        if vme.needs_copy:
3563            vme_flags += "n"
3564        if vme.use_pmap:
3565            vme_flags += "p"
3566        if vme.wired_count:
3567            vme_flags += "w"
3568        if vme.used_for_jit:
3569            vme_flags += "j"
3570        if vme.vme_permanent:
3571            vme_flags += "!"
3572        try:
3573            if vme.used_for_tpro:
3574                vme_flags += "t"
3575        except AttributeError:
3576            pass
3577
3578        tagstr = ""
3579        if pmap == kern.globals.kernel_pmap:
3580            xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *')
3581            if xsite and xsite.site.flags & 0x0200:
3582                tagstr = ".{:<3d}".format(xsite.loadTag)
3583        rb_info = ""
3584        if show_rb_tree:
3585            rb_info = "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme.store.entry.rbe_left, vme.store.entry.rbe_right, vme.store.entry.rbe_parent)
3586        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))
3587        if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and object_val != 0:
3588            pass # object is already intialized
3589        else:
3590            object = 0
3591        showvmobject(object, offset, size, show_pager_info, show_all_shadows, show_upl_info)
3592    if start_vaddr != 0 or end_vaddr != 0:
3593        print("...")
3594    elif unsigned(maphdr.links.end) > last_end:
3595        print("{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,maphdr.links.end,(unsigned(maphdr.links.end) - last_end) // page_size))
3596    return None
3597
3598def CountMapTags(map, tagcounts, slow):
3599    page_size = unsigned(kern.globals.page_size)
3600    vme_list_head = map.hdr.links
3601    vme_ptr_type = GetType('vm_map_entry *')
3602    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
3603        object = get_vme_object(vme)
3604        tag = vme.vme_alias
3605        if object == kern.globals.kernel_object_default or (hasattr(kern.globals, 'kernel_object_tagged') and object == kern.globals.kernel_object_tagged):
3606            count = 0
3607            if not slow:
3608                count = unsigned(vme.links.end - vme.links.start) // page_size
3609            else:
3610                addr = unsigned(vme.links.start)
3611                while addr < unsigned(vme.links.end):
3612                    hash_id = _calc_vm_page_hash(object, addr)
3613                    page_list = kern.globals.vm_page_buckets[hash_id].page_list
3614                    page = _vm_page_unpack_ptr(page_list)
3615                    while (page != 0):
3616                        vmpage = kern.GetValueFromAddress(page, 'vm_page_t')
3617                        if (addr == unsigned(vmpage.vmp_offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vmp_object))):
3618                            if (not vmpage.vmp_local) and (vmpage.vmp_wire_count > 0):
3619                                count += 1
3620                            break
3621                        page = _vm_page_unpack_ptr(vmpage.vmp_next_m)
3622                    addr += page_size
3623            tagcounts[tag] += count
3624        elif vme.is_sub_map:
3625            CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow)
3626    return None
3627
3628def CountWiredObject(object, tagcounts):
3629    tagcounts[unsigned(object.wire_tag)] += object.wired_page_count
3630    return None
3631
3632def GetKmodIDName(kmod_id):
3633    kmod_val = kern.globals.kmod
3634    for kmod in IterateLinkedList(kmod_val, 'next'):
3635        if (kmod.id == kmod_id):
3636            return "{:<50s}".format(kmod.name)
3637    return "??"
3638
3639FixedTags = {
3640    0:  "VM_KERN_MEMORY_NONE",
3641    1:  "VM_KERN_MEMORY_OSFMK",
3642    2:  "VM_KERN_MEMORY_BSD",
3643    3:  "VM_KERN_MEMORY_IOKIT",
3644    4:  "VM_KERN_MEMORY_LIBKERN",
3645    5:  "VM_KERN_MEMORY_OSKEXT",
3646    6:  "VM_KERN_MEMORY_KEXT",
3647    7:  "VM_KERN_MEMORY_IPC",
3648    8:  "VM_KERN_MEMORY_STACK",
3649    9:  "VM_KERN_MEMORY_CPU",
3650    10: "VM_KERN_MEMORY_PMAP",
3651    11: "VM_KERN_MEMORY_PTE",
3652    12: "VM_KERN_MEMORY_ZONE",
3653    13: "VM_KERN_MEMORY_KALLOC",
3654    14: "VM_KERN_MEMORY_COMPRESSOR",
3655    15: "VM_KERN_MEMORY_COMPRESSED_DATA",
3656    16: "VM_KERN_MEMORY_PHANTOM_CACHE",
3657    17: "VM_KERN_MEMORY_WAITQ",
3658    18: "VM_KERN_MEMORY_DIAG",
3659    19: "VM_KERN_MEMORY_LOG",
3660    20: "VM_KERN_MEMORY_FILE",
3661    21: "VM_KERN_MEMORY_MBUF",
3662    22: "VM_KERN_MEMORY_UBC",
3663    23: "VM_KERN_MEMORY_SECURITY",
3664    24: "VM_KERN_MEMORY_MLOCK",
3665    25: "VM_KERN_MEMORY_REASON",
3666    26: "VM_KERN_MEMORY_SKYWALK",
3667    27: "VM_KERN_MEMORY_LTABLE",
3668    28: "VM_KERN_MEMORY_HV",
3669    29: "VM_KERN_MEMORY_KALLOC_DATA",
3670    30: "VM_KERN_MEMORY_RETIRED",
3671    31: "VM_KERN_MEMORY_KALLOC_TYPE",
3672    32: "VM_KERN_MEMORY_TRIAGE",
3673    33: "VM_KERN_MEMORY_RECOUNT",
3674    34: "VM_KERN_MEMORY_TAG",
3675    35: "VM_KERN_MEMORY_EXCLAVES",
3676    255:"VM_KERN_MEMORY_ANY",
3677}
3678
3679def GetVMKernName(tag):
3680    """ returns the formatted name for a vmtag and
3681        the sub-tag for kmod tags.
3682    """
3683    if tag in FixedTags:
3684        return (FixedTags[tag], "")
3685    site = kern.globals.vm_allocation_sites[tag]
3686    if site:
3687        if site.flags & 0x007F:
3688            cstr = addressof(site.subtotals[site.subtotalscount])
3689            return ("{:<50s}".format(str(Cast(cstr, 'char *'))), "")
3690        else:
3691            if site.flags & 0x0200:
3692                xsite = Cast(site,'OSKextAccount *')
3693                tagstr = ".{:<3d}".format(xsite.loadTag)
3694                return (GetKmodIDName(xsite.loadTag), tagstr);
3695            else:
3696                return (kern.Symbolicate(site), "")
3697    return ("", "")
3698
3699@SBValueFormatter.converter("vm_kern_tag")
3700def vm_kern_tag_conversion(tag):
3701    s, tagstr = GetVMKernName(tag)
3702
3703    if tagstr != '':
3704        return "{} ({}{})".format(s.strip(), tag, tagstr)
3705    if s != '':
3706        return "{} ({})".format(s.strip(), tag)
3707    return str(tag)
3708
3709@lldb_command("showvmtags", "ASJO")
3710def showvmtags(cmd_args=None, cmd_options={}):
3711    """Routine to print out info about kernel wired page allocations
3712        usage: showvmtags
3713               iterates kernel map and vm objects totaling allocations by tag.
3714        usage: showvmtags -S [-O]
3715               also iterates kernel object pages individually - slow.
3716        usage: showvmtags -A [-O]
3717               show all tags, even tags that have no wired count
3718        usage: showvmtags -J [-O]
3719                Output json
3720
3721        -O: list in increasing size order
3722    """
3723    slow = False
3724    print_json = False
3725    if "-S" in cmd_options:
3726        slow = True
3727    all_tags = False
3728    if "-A" in cmd_options:
3729        all_tags = True
3730    if "-J" in cmd_options:
3731        print_json = True
3732
3733    page_size = unsigned(kern.globals.page_size)
3734    nsites = unsigned(kern.globals.vm_allocation_tag_highest) + 1
3735    tagcounts = [0] * nsites
3736    tagmapped = [0] * nsites
3737
3738    if kern.globals.vm_tag_active_update:
3739        for tag in range(nsites):
3740            site = kern.globals.vm_allocation_sites[tag]
3741            if site:
3742                tagcounts[tag] = unsigned(site.total)
3743                tagmapped[tag] = unsigned(site.mapped)
3744    else:
3745        queue_head = kern.globals.vm_objects_wired
3746        for object in IterateQueue(queue_head, 'struct vm_object *', 'wired_objq'):
3747            if object != kern.globals.kernel_object_default and ((not hasattr(kern.globals, 'kernel_object_tagged')) or object != kern.globals.kernel_object_tagged):
3748                CountWiredObject(object, tagcounts)
3749
3750        CountMapTags(kern.globals.kernel_map, tagcounts, slow)
3751
3752    total = 0
3753    totalmapped = 0
3754    tags = []
3755    for tag in range(nsites):
3756        if all_tags or tagcounts[tag] or tagmapped[tag]:
3757            current = {}
3758            total += tagcounts[tag]
3759            totalmapped += tagmapped[tag]
3760            (sitestr, tagstr) = GetVMKernName(tag)
3761            current["name"] = sitestr
3762            current["size"] = tagcounts[tag]
3763            current["mapped"] = tagmapped[tag]
3764            current["tag"] = tag
3765            current["tagstr"] = tagstr
3766            current["subtotals"] = []
3767
3768            site = kern.globals.vm_allocation_sites[tag]
3769            for sub in range(site.subtotalscount):
3770                alloctag = unsigned(site.subtotals[sub].tag)
3771                amount = unsigned(site.subtotals[sub].total)
3772                subsite = kern.globals.vm_allocation_sites[alloctag]
3773                if alloctag and subsite:
3774                    (sitestr, tagstr) = GetVMKernName(alloctag)
3775                    current["subtotals"].append({
3776                        "amount": amount,
3777                        "flags": int(subsite.flags),
3778                        "tag": alloctag,
3779                        "tagstr": tagstr,
3780                        "sitestr": sitestr,
3781                    })
3782            tags.append(current)
3783
3784    if "-O" in cmd_options:
3785        tags.sort(key = lambda tag: tag['size'])
3786
3787    # Serializing to json here ensure we always catch bugs preventing
3788    # serialization
3789    as_json = json.dumps(tags)
3790    if print_json:
3791        print(as_json)
3792    else:
3793        print(" vm_allocation_tag_highest: {:<7d}  ".format(nsites - 1))
3794        print(" {:<7s}  {:>7s}   {:>7s}  {:<50s}".format("tag.kmod", "size", "mapped", "name"))
3795        for tag in tags:
3796            if not tagstr:
3797                tagstr = ""
3798            print(" {:>3d}{:<4s}  {:>7d}K  {:>7d}K  {:<50s}".format(tag["tag"], tag["tagstr"], tag["size"] // 1024, tag["mapped"] // 1024, tag["name"]))
3799            for sub in tag["subtotals"]:
3800                if ((sub["flags"] & 0x007f) == 0):
3801                    kind_str = "named"
3802                else:
3803                    kind_str = "from"
3804
3805                print(" {:>7s}  {:>7d}K      {:s}  {:>3d}{:<4s} {:<50s}".format(" ", sub["amount"] // 1024, kind_str, sub["tag"], sub["tagstr"], sub["sitestr"]))
3806
3807        print("Total:    {:>7d}K  {:>7d}K".format(total // 1024, totalmapped // 1024))
3808    return None
3809
3810
3811def FindVMEntriesForVnode(task, vn):
3812    """ returns an array of vme that have the vnode set to defined vnode
3813        each entry in array is of format (vme, start_addr, end_address, protection)
3814    """
3815    retval = []
3816    vmmap = task.map
3817    pmap = vmmap.pmap
3818    pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops))
3819    debuglog("pager_ops_addr %s" % hex(pager_ops_addr))
3820
3821    if unsigned(pmap) == 0:
3822        return retval
3823    vme_list_head = vmmap.hdr.links
3824    vme_ptr_type = gettype('vm_map_entry *')
3825    for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'):
3826        #print vme
3827        if unsigned(vme.is_sub_map) == 0 and unsigned(get_vme_object(vme)) != 0:
3828            obj = get_vme_object(vme)
3829        else:
3830            continue
3831
3832        while obj != 0:
3833            if obj.pager != 0:
3834                if obj.internal:
3835                    pass
3836                else:
3837                    vn_pager = Cast(obj.pager, 'vnode_pager *')
3838                    if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn):
3839                        retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection)))
3840            obj = obj.shadow
3841    return retval
3842
3843@lldb_command('showtaskloadinfo')
3844def ShowTaskLoadInfo(cmd_args=None, cmd_options={}):
3845    """ Print the load address and uuid for the process
3846        Usage: (lldb)showtaskloadinfo <task_t>
3847    """
3848    if cmd_args is None or len(cmd_args) == 0:
3849        raise ArgumentError("Insufficient arguments")
3850
3851    t = kern.GetValueFromAddress(cmd_args[0], 'struct task *')
3852    print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
3853    p = GetProcFromTask(t)
3854    if p is None:
3855        print("Task has no associated BSD process.")
3856        return
3857    uuid_out_string = GetUUIDSummary(p.p_uuid)
3858    filepath = GetVnodePath(p.p_textvp)
3859    libname = filepath.split('/')[-1]
3860    mappings = FindVMEntriesForVnode(t, p.p_textvp)
3861    load_addr = 0
3862    end_addr = 0
3863    for m in mappings:
3864        if m[3] == 5:
3865            load_addr = m[1]
3866            end_addr = m[2]
3867    print(print_format.format(load_addr, end_addr,
3868                              libname, uuid_out_string, filepath))
3869
3870@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
3871@lldb_command('vmpagelookup')
3872def VMPageLookup(cmd_args=None):
3873    """ Print the pages in the page bucket corresponding to the provided object and offset.
3874        Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
3875    """
3876    if cmd_args is None or len(cmd_args) != 2:
3877        raise ArgumentError("Please specify an object and offset.")
3878
3879    format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n"
3880
3881    obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
3882    off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
3883
3884    hash_id = _calc_vm_page_hash(obj, off)
3885
3886    page_list = kern.globals.vm_page_buckets[hash_id].page_list
3887    print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list)))
3888
3889    print(VMPageLookup.header)
3890    page = _vm_page_unpack_ptr(page_list)
3891    while page != 0:
3892        pg_t = kern.GetValueFromAddress(page, 'vm_page_t')
3893        print(format_string.format(page, pg_t.vmp_offset, _vm_page_unpack_ptr(pg_t.vmp_object)))
3894        page = _vm_page_unpack_ptr(pg_t.vmp_next_m)
3895
3896
3897def _vm_page_get_phys_page(page):
3898    if kern.arch == 'x86_64':
3899        return page.vmp_phys_page
3900
3901    if page == 0 :
3902        return 0
3903
3904    m = unsigned(page)
3905
3906    if m >= unsigned(kern.globals.vm_pages) and m < unsigned(kern.globals.vm_pages_end) :
3907        return (m - unsigned(kern.globals.vm_pages)) // sizeof('struct vm_page') + unsigned(kern.globals.vm_pages_first_pnum)
3908
3909    target = LazyTarget.GetTarget()
3910    return target.xReadUInt32(unsigned(page) + sizeof('struct vm_page'))
3911
3912
3913@lldb_command('vmpage_get_phys_page')
3914def VmPageGetPhysPage(cmd_args=None):
3915    """ return the physical page for a vm_page_t
3916        usage: vm_page_get_phys_page <vm_page_t>
3917    """
3918    if cmd_args is None or len(cmd_args) != 1:
3919        raise ArgumentError("Missing page argument")
3920
3921    target = LazyTarget.GetTarget()
3922    addr = ArgumentStringToAddress(cmd_args[0])
3923    page = target.xCreateValueFromAddress(None, addr, gettype('struct vm_page'))
3924    phys_page = _vm_page_get_phys_page(value(page.AddressOf()))
3925    print("phys_page = {:#x}".format(phys_page))
3926
3927
3928def _vm_page_get_page_from_phys(pnum):
3929    """ Attempt to return page struct from physical page address"""
3930    if kern.arch == 'x86_64':
3931        return None
3932
3933    pmap_first_pnum     = unsigned(kern.globals.pmap_first_pnum)
3934    vm_pages_first_pnum = unsigned(kern.globals.vm_pages_first_pnum)
3935    vm_pages_count      = unsigned(kern.globals.vm_pages_count)
3936
3937    if pmap_first_pnum <= pnum < vm_pages_first_pnum:
3938        target = LazyTarget.GetTarget()
3939
3940        radix  = kern.globals.vm_pages_radix_root
3941        level  = unsigned(radix) & 0x7
3942        node   = unsigned(radix) - level
3943        index  = pnum - pmap_first_pnum
3944        unpack = kmemory.KMem.get_shared().vm_page_packing.unpack
3945
3946        while node:
3947            key  = (index >> (8 * level)) & 0xff
3948            node = unpack(target.xReadUInt32(node + key * 4))
3949            if node and level == 0:
3950                name = "page_for_pnum_{:#x}".format(pnum)
3951                v = target.xCreateValueFromAddress(name, node, gettype('struct vm_page'))
3952                return value(v)
3953            level -= 1
3954
3955        return None
3956
3957    elif vm_pages_first_pnum <= pnum < vm_pages_first_pnum + vm_pages_count:
3958        return kern.globals.vm_pages[pnum - vm_pages_first_pnum]
3959
3960    return None
3961
3962
3963@lldb_command('vmpage_from_phys_page')
3964def VmPageFromPhysPage(cmd_args=None):
3965    """ return the vm_page_t for a physical page if possible
3966        usage: vm_page_from_phys_page <ppnum_t>
3967    """
3968
3969    if cmd_args is None or len(cmd_args) != 1:
3970        raise ArgumentError("Missing pnum argument")
3971
3972    pnum = ArgumentStringToInt(cmd_args[0])
3973    page = _vm_page_get_page_from_phys(pnum)
3974    if page is None:
3975        print("couldn't find page for pnum = {:#x}".format(pnum))
3976        return
3977
3978    print(xnu_format(
3979        "page = (vm_page_t){:#x}\n"
3980        "\n"
3981        "{:s}",
3982        addressof(page),
3983        str(page)))
3984
3985
3986@lldb_command('vmpage_unpack_ptr')
3987def VmPageUnpackPtr(cmd_args=None):
3988    """ unpack a pointer
3989        usage: vm_page_unpack_ptr <packed_ptr>
3990    """
3991    if cmd_args is None or len(cmd_args) == 0:
3992        print("Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help.")
3993        return
3994
3995    packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long')
3996    unpacked = _vm_page_unpack_ptr(packed)
3997    print("unpacked pointer = 0x%x\n" % unpacked)
3998
3999
4000def _vm_page_unpack_ptr(page: value) -> int:
4001    if kern.ptrsize == 4 :
4002        return int(page)
4003
4004    if page == 0 :
4005        return 0
4006
4007    params = kern.globals.vm_page_packing_params
4008    ptr_shift = params.vmpp_shift
4009    ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask
4010
4011    # when no mask and shift on 64bit systems, we're working with real/non-packed pointers
4012    if ptr_shift == 0 and ptr_mask == 0:
4013        return int(page)
4014
4015    if unsigned(page) & unsigned(ptr_mask):
4016        masked_page = (unsigned(page) & ~ptr_mask)
4017        # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge
4018        vm_pages_addr = unsigned(addressof(kern.globals.vm_pages[0]))
4019        element_size = unsigned(addressof(kern.globals.vm_pages[1])) - vm_pages_addr
4020        return (vm_pages_addr + masked_page * element_size)
4021
4022    kmem = kmemory.KMem.get_shared()
4023    return kmem.vm_page_packing.unpack(unsigned(page))
4024
4025@lldb_command('calcvmpagehash')
4026def CalcVMPageHash(cmd_args=None):
4027    """ Get the page bucket corresponding to the provided object and offset.
4028        Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
4029    """
4030    if cmd_args is None or len(cmd_args) != 2:
4031        raise ArgumentError("Please specify an object and offset.")
4032
4033    obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
4034    off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
4035
4036    hash_id = _calc_vm_page_hash(obj, off)
4037
4038    print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
4039    return None
4040
4041def _calc_vm_page_hash(obj, off):
4042    bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
4043    hash_mask = (int) (kern.globals.vm_page_hash_mask)
4044
4045    one = (obj * bucket_hash) & 0xFFFFFFFF
4046    two = off >> unsigned(kern.globals.page_shift)
4047    three = two ^ bucket_hash
4048    four = one + three
4049    hash_id = four & hash_mask
4050
4051    return hash_id
4052
4053#Macro: showallocatedzoneelement
4054@lldb_command('showallocatedzoneelement', fancy=True)
4055def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}, O=None):
4056    """ Show all the allocated elements in a zone
4057        usage: showzoneallocelements <address of zone>
4058    """
4059    if len(cmd_args) < 1:
4060        raise ArgumentError("Please specify a zone")
4061
4062    zone  = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
4063    array = kern.GetGlobalVariable('zone_array')
4064    index = (unsigned(zone) - array.GetSBValue().GetLoadAddress()) // gettype('struct zone').GetByteSize()
4065    with O.table("{:<8s}  {:<s}".format("Index", "Address")):
4066        i = 1
4067        for elem in kmemory.Zone(index):
4068            print(O.format("{:>8d}  {:#x}", i, elem))
4069            i += 1
4070
4071#EndMacro: showallocatedzoneelement
4072
4073def match_vm_page_attributes(page, matching_attributes):
4074    page_ptr = addressof(page)
4075    unpacked_vm_object = _vm_page_unpack_ptr(page.vmp_object)
4076    matched_attributes = 0
4077    if "vmp_q_state" in matching_attributes and (page.vmp_q_state == matching_attributes["vmp_q_state"]):
4078        matched_attributes += 1
4079    if "vm_object" in matching_attributes and (unsigned(unpacked_vm_object) == unsigned(matching_attributes["vm_object"])):
4080        matched_attributes += 1
4081    if "vmp_offset" in matching_attributes and (unsigned(page.vmp_offset) == unsigned(matching_attributes["vmp_offset"])):
4082        matched_attributes += 1
4083    if "phys_page" in matching_attributes and (unsigned(_vm_page_get_phys_page(page_ptr)) == unsigned(matching_attributes["phys_page"])):
4084        matched_attributes += 1
4085    if "bitfield" in matching_attributes and unsigned(page.__getattr__(matching_attributes["bitfield"])) == 1:
4086        matched_attributes += 1
4087
4088    return matched_attributes
4089
4090#Macro scan_vm_pages
4091@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"))
4092@lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA')
4093def ScanVMPages(cmd_args=None, cmd_options={}):
4094    """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes.
4095        usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone]
4096
4097            scan_vm_pages -A: scan vm pages in the global vm_pages array
4098            scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone
4099            scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue
4100            scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object
4101            scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value
4102            scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number
4103            scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set
4104            scan_vm_pages <-A> -I <start_index>: Start the scan from start_index
4105            scan_vm_pages <-A> -N <npages>: Scan at most npages
4106    """
4107    if (len(cmd_options) < 1):
4108        raise ArgumentError("Please specify at least one matching attribute")
4109
4110    vm_pages = kern.globals.vm_pages
4111    vm_pages_count = kern.globals.vm_pages_count
4112
4113    start_index = 0
4114    npages = vm_pages_count
4115    scan_vmpages_array = False
4116    scan_vmpages_zone = False
4117    attribute_count = 0
4118
4119    if "-A" in cmd_options:
4120        scan_vmpages_array = True
4121
4122    if "-Z" in cmd_options:
4123        scan_vmpages_zone = True
4124
4125    if not scan_vmpages_array and not scan_vmpages_zone:
4126        raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)")
4127
4128    attribute_values = {}
4129    if "-S" in cmd_options:
4130        attribute_values["vmp_q_state"] = kern.GetValueFromAddress(cmd_options["-S"], 'int')
4131        attribute_count += 1
4132
4133    if "-O" in cmd_options:
4134        attribute_values["vm_object"] = kern.GetValueFromAddress(cmd_options["-O"], 'vm_object_t')
4135        attribute_count += 1
4136
4137    if "-F" in cmd_options:
4138        attribute_values["vmp_offset"] = kern.GetValueFromAddress(cmd_options["-F"], 'unsigned long long')
4139        attribute_count += 1
4140
4141    if "-P" in cmd_options:
4142        attribute_values["phys_page"] = kern.GetValueFromAddress(cmd_options["-P"], 'unsigned int')
4143        attribute_count += 1
4144
4145    if "-B" in cmd_options:
4146        valid_vmp_bitfields = [
4147            "vmp_on_specialq",
4148            "vmp_canonical",
4149            "vmp_gobbled",
4150            "vmp_laundry",
4151            "vmp_no_cache",
4152            "vmp_reference",
4153            "vmp_busy",
4154            "vmp_wanted",
4155            "vmp_tabled",
4156            "vmp_hashed",
4157            "vmp_clustered",
4158            "vmp_pmapped",
4159            "vmp_xpmapped",
4160            "vmp_free_when_done",
4161            "vmp_absent",
4162            "vmp_error",
4163            "vmp_dirty",
4164            "vmp_cleaning",
4165            "vmp_precious",
4166            "vmp_overwriting",
4167            "vmp_restart",
4168            "vmp_unusual",
4169            "vmp_cs_validated",
4170            "vmp_cs_tainted",
4171            "vmp_cs_nx",
4172            "vmp_reusable",
4173            "vmp_lopage",
4174            "vmp_written_by_kernel",
4175            "vmp_unused_object_bits"
4176            ]
4177        attribute_values["bitfield"] = cmd_options["-B"]
4178        if attribute_values["bitfield"] in valid_vmp_bitfields:
4179            attribute_count += 1
4180        else:
4181            raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield))
4182
4183    if "-I" in cmd_options:
4184        start_index = kern.GetValueFromAddress(cmd_options["-I"], 'int')
4185        npages = vm_pages_count - start_index
4186
4187    if "-N" in cmd_options:
4188        npages = kern.GetValueFromAddress(cmd_options["-N"], 'int')
4189        if npages == 0:
4190            raise ArgumentError("You specified -N 0, nothing to be scanned")
4191
4192    end_index = start_index + npages - 1
4193    if end_index >= vm_pages_count:
4194        raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count))
4195
4196    header_after_n_lines = 40
4197    format_string = "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}"
4198
4199    found_in_array = 0
4200    if scan_vmpages_array:
4201        print("Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index, end_index, attribute_count))
4202        i = start_index
4203        while i <= end_index:
4204            page = vm_pages[i]
4205            if match_vm_page_attributes(page, attribute_values) == attribute_count:
4206                if found_in_array % header_after_n_lines == 0:
4207                    print(ScanVMPages.header)
4208
4209                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))))
4210                found_in_array += 1
4211
4212            i += 1
4213
4214    found_in_zone = 0
4215    if scan_vmpages_zone:
4216        page_size = kern.GetGlobalVariable('page_size')
4217        print("Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count))
4218
4219        print("Scanning page queues in the vm_pages zone...")
4220        for elem in kmemory.Zone('vm pages'):
4221            page = kern.GetValueFromAddress(elem, 'vm_page_t')
4222
4223            if match_vm_page_attributes(page, attribute_values) == attribute_count:
4224                if found_in_zone % header_after_n_lines == 0:
4225                    print(ScanVMPages.header)
4226
4227                vm_object = _vm_page_unpack_ptr(page.vmp_object)
4228                phys_page = _vm_page_get_phys_page(page)
4229                print(format_string.format("vm_pages zone", elem, page.vmp_q_state, vm_object, page.vmp_offset, phys_page))
4230                found_in_zone += 1
4231
4232    total = found_in_array + found_in_zone
4233    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))
4234
4235#EndMacro scan_vm_pages
4236
4237VM_PAGE_IS_WIRED = 1
4238
4239@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"))
4240@lldb_command('vmobjectwalkpages', 'CSBNQP:O:')
4241def VMObjectWalkPages(cmd_args=None, cmd_options={}):
4242    """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
4243        specifically look for this page, highlighting it in the output or noting if it was not found. For
4244        each page, we confirm that it points to the object. We also keep track of the number of pages we
4245        see and compare this to the object's resident page count field.
4246        Usage:
4247            vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
4248            vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages
4249            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
4250            vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
4251            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)
4252            vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
4253            vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
4254            vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset
4255
4256    """
4257
4258    if (cmd_args is None or len(cmd_args) < 1):
4259        raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
4260
4261    out_string = ""
4262
4263    obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
4264
4265    page = 0
4266    if "-P" in cmd_options:
4267        page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
4268
4269    off = -1
4270    if "-O" in cmd_options:
4271        off = kern.GetValueFromAddress(cmd_options['-O'], 'vm_offset_t')
4272
4273    stop = 0
4274    if "-S" in cmd_options:
4275        if page == 0 and off < 0:
4276            raise ArgumentError("-S can only be passed when a page is specified with -P or -O")
4277        stop = 1
4278
4279    walk_backwards = False
4280    if "-B" in cmd_options:
4281        walk_backwards = True
4282
4283    quiet_mode = False
4284    if "-Q" in cmd_options:
4285        quiet_mode = True
4286
4287    if not quiet_mode:
4288        print(VMObjectWalkPages.header)
4289        format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
4290        first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}\t"
4291        second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
4292        second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
4293        second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
4294        second_bitfield_format_string +=  "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}"
4295
4296    limit = 4096 #arbitrary limit of number of pages to walk
4297    ignore_limit = 0
4298    if "-N" in cmd_options:
4299        ignore_limit = 1
4300
4301    show_compressed = 0
4302    if "-C" in cmd_options:
4303        show_compressed = 1
4304
4305    page_count = 0
4306    res_page_count = unsigned(obj.resident_page_count)
4307    page_found = False
4308    pages_seen = set()
4309
4310    for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr):
4311        page_count += 1
4312        out_string = ""
4313        if (page != 0 and not(page_found) and vmp == page):
4314            out_string += "******"
4315            page_found = True
4316
4317        if (off > 0 and not(page_found) and vmp.vmp_offset == off):
4318            out_string += "******"
4319            page_found = True
4320
4321        if page != 0 or off > 0 or quiet_mode:
4322             if (page_count % 1000) == 0:
4323                print("traversed %d pages ...\n" % (page_count))
4324        else:
4325                phys_page = _vm_page_get_phys_page(vmp)
4326                vmp_fictitious = phys_page in (0xfffffffe, 0xffffffff)
4327                vmp_private = not vmp.vmp_canonical and not vmp_fictitious
4328
4329                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)
4330                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,
4331                                                                   vmp_private, vmp.vmp_reference)
4332
4333                if hasattr(vmp,'slid'):
4334                    vmp_slid = vmp.slid
4335                else:
4336                    vmp_slid = 0
4337                out_string += second_bitfield_format_string.format(vmp.vmp_busy, vmp.vmp_wanted, vmp.vmp_tabled, vmp.vmp_hashed, vmp_fictitious, vmp.vmp_clustered,
4338                                                                    vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent,
4339                                                                    vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting,
4340                                                                    vmp.vmp_restart, vmp.vmp_unusual, 0, 0,
4341                                                                    vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid,
4342                                                                    vmp.vmp_written_by_kernel)
4343
4344        if (vmp in pages_seen):
4345            print(out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n")
4346            return
4347
4348        if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)):
4349            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))))
4350            return
4351
4352        if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0):
4353            print(out_string + " page in wired state with wire_count of 0\n")
4354            print("vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n")
4355            print("stopping...\n")
4356            return
4357
4358        if (hasattr(vmp, 'vmp_unused_page_bits') and (vmp.vmp_unused_page_bits != 0)):
4359            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))
4360            print("stopping...\n")
4361            return
4362
4363        if (hasattr(vmp, 'vmp_unused_object_bits') and (vmp.vmp_unused_object_bits != 0)):
4364            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))
4365            print("stopping...\n")
4366            return
4367
4368        pages_seen.add(vmp)
4369
4370        if False:
4371            hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset)
4372            hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
4373            hash_page = _vm_page_unpack_ptr(hash_page_list)
4374            hash_page_t = 0
4375
4376            while (hash_page != 0):
4377                hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
4378                if hash_page_t == vmp:
4379                    break
4380                hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m)
4381
4382            if (unsigned(vmp) != unsigned(hash_page_t)):
4383                print(out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n")
4384                print(lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset))))
4385                return
4386
4387        if (page_count >= limit and not(ignore_limit)):
4388            print(out_string + "Limit reached (%d pages), stopping..." % (limit))
4389            break
4390
4391        print(out_string)
4392
4393        if page_found and stop:
4394            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)))
4395            return
4396
4397    if (page != 0):
4398        print("page found? : %s\n" % page_found)
4399
4400    if (off > 0):
4401        print("page found? : %s\n" % page_found)
4402
4403    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)))
4404
4405    if show_compressed != 0 and obj.pager != 0 and unsigned(obj.pager.mo_pager_ops) == unsigned(addressof(kern.globals.compressor_pager_ops)):
4406        pager = Cast(obj.pager, 'compressor_pager *')
4407        chunks = pager.cpgr_num_slots // 128
4408        pagesize = kern.globals.page_size
4409
4410        page_idx = 0
4411        while page_idx < pager.cpgr_num_slots:
4412            if chunks != 0:
4413                chunk = pager.cpgr_slots.cpgr_islots[page_idx // 128]
4414                slot = chunk[page_idx % 128]
4415            elif pager.cpgr_num_slots > 2:
4416                slot = pager.cpgr_slots.cpgr_dslots[page_idx]
4417            else:
4418                slot = pager.cpgr_slots.cpgr_eslots[page_idx]
4419
4420            if slot != 0:
4421               print("compressed page for offset: %x slot %x\n" % ((page_idx * pagesize) - obj.paging_offset, slot))
4422            page_idx = page_idx + 1
4423
4424
4425@lldb_command("show_all_apple_protect_pagers")
4426def ShowAllAppleProtectPagers(cmd_args=None):
4427    """Routine to print all apple_protect pagers
4428        usage: show_all_apple_protect_pagers
4429    """
4430    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"))
4431    qhead = kern.globals.apple_protect_pager_queue
4432    qtype = GetType('apple_protect_pager *')
4433    qcnt = kern.globals.apple_protect_pager_count
4434    idx = 0
4435    for pager in IterateQueue(qhead, qtype, "pager_queue"):
4436        idx = idx + 1
4437        show_apple_protect_pager(pager, qcnt, idx)
4438
4439@lldb_command("show_apple_protect_pager")
4440def ShowAppleProtectPager(cmd_args=None):
4441    """Routine to print out info about an apple_protect pager
4442        usage: show_apple_protect_pager <pager>
4443    """
4444    if cmd_args is None or len(cmd_args) == 0:
4445        raise ArgumentError()
4446
4447    pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t')
4448    show_apple_protect_pager(pager, 1, 1)
4449
4450def show_apple_protect_pager(pager, qcnt, idx):
4451    object = pager.backing_object
4452    shadow = object.shadow
4453    while shadow != 0:
4454        object = shadow
4455        shadow = object.shadow
4456    vnode_pager = Cast(object.pager,'vnode_pager *')
4457    filename = GetVnodePath(vnode_pager.vnode_handle)
4458    if hasattr(pager, "ap_pgr_hdr_ref"):
4459        refcnt = pager.ap_pgr_hdr_ref
4460    else:
4461        refcnt = pager.ap_pgr_hdr.mo_ref
4462    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))
4463    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)
4464
4465@lldb_command("show_all_shared_region_pagers")
4466def ShowAllSharedRegionPagers(cmd_args=None):
4467    """Routine to print all shared_region pagers
4468        usage: show_all_shared_region_pagers
4469    """
4470    print("{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "object", "offset", "jop_key", "slide", "slide_info"))
4471    qhead = kern.globals.shared_region_pager_queue
4472    qtype = GetType('shared_region_pager *')
4473    qcnt = kern.globals.shared_region_pager_count
4474    idx = 0
4475    for pager in IterateQueue(qhead, qtype, "srp_queue"):
4476        idx = idx + 1
4477        show_shared_region_pager(pager, qcnt, idx)
4478
4479@lldb_command("show_shared_region_pager")
4480def ShowSharedRegionPager(cmd_args=None):
4481    """Routine to print out info about a shared_region pager
4482        usage: show_shared_region_pager <pager>
4483    """
4484    if cmd_args is None or len(cmd_args) == 0:
4485        raise ArgumentError()
4486
4487    pager = kern.GetValueFromAddress(cmd_args[0], 'shared_region_pager_t')
4488    show_shared_region_pager(pager, 1, 1)
4489
4490def show_shared_region_pager(pager, qcnt, idx):
4491    object = pager.srp_backing_object
4492    shadow = object.shadow
4493    while shadow != 0:
4494        object = shadow
4495        shadow = object.shadow
4496    vnode_pager = Cast(object.pager,'vnode_pager *')
4497    filename = GetVnodePath(vnode_pager.vnode_handle)
4498    if hasattr(pager, 'srp_ref_count'):
4499        ref_count = pager.srp_ref_count
4500    else:
4501        ref_count = pager.srp_header.mo_ref
4502    if hasattr(pager, 'srp_jop_key'):
4503        jop_key = pager.srp_jop_key
4504    else:
4505        jop_key = -1
4506    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))
4507    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)
4508
4509@lldb_command("show_all_dyld_pagers")
4510def ShowAllDyldPagers(cmd_args=None):
4511    """Routine to print all dyld pagers
4512        usage: show_all_dyld_pagers
4513    """
4514    print(ShowDyldPager.header)
4515    qhead = kern.globals.dyld_pager_queue
4516    qtype = GetType('dyld_pager *')
4517    qcnt = kern.globals.dyld_pager_count
4518    idx = 0
4519    for pager in IterateQueue(qhead, qtype, "dyld_pager_queue"):
4520        idx = idx + 1
4521        show_dyld_pager(pager, qcnt, idx)
4522
4523@header("{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "object", "link_info", "link_info_size"))
4524@lldb_command("show_dyld_pager")
4525def ShowDyldPager(cmd_args=None):
4526    """Routine to print out info about a dyld pager
4527        usage: show_dyld_pager <pager>
4528    """
4529    if cmd_args is None or len(cmd_args) == 0:
4530        raise ArgumentError()
4531
4532    print(ShowDyldPager.header)
4533    pager = kern.GetValueFromAddress(cmd_args[0], 'dyld_pager_t')
4534    show_dyld_pager(pager, 1, 1)
4535
4536def show_dyld_pager(pager, qcnt, idx):
4537    object = pager.dyld_backing_object
4538    shadow = object.shadow
4539    while shadow != 0:
4540        object = shadow
4541        shadow = object.shadow
4542    vnode_pager = Cast(object.pager,'vnode_pager *')
4543    filename = GetVnodePath(vnode_pager.vnode_handle)
4544    ref_count = pager.dyld_header.mo_ref
4545    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))
4546    show_dyld_pager_regions(pager)
4547    print("\tvnode:{: <#018x} {:s}\n".format(vnode_pager.vnode_handle, filename))
4548    showvmobject(pager.dyld_backing_object, show_pager_info=True, show_all_shadows=True, show_upl_info=True)
4549
4550def show_dyld_pager_regions(pager):
4551    """Routine to print out region info about a dyld pager
4552    """
4553    print("\tregions:")
4554    print("\t\t{:>3s}/{:<3s} {:<18s} {:<18s} {:<18s}".format("#", "#", "file_offset", "address", "size"))
4555    for idx in range(pager.dyld_num_range):
4556        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]))
4557
4558@lldb_command("show_console_ring")
4559def ShowConsoleRingData(cmd_args=None):
4560    """ Print console ring buffer stats and data
4561    """
4562    cr = kern.globals.console_ring
4563    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))
4564    pending_data = []
4565    for i in range(unsigned(cr.used)):
4566        idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len)
4567        pending_data.append("{:c}".format(cr.buffer[idx]))
4568
4569    if pending_data:
4570        print("Data:")
4571        print("".join(pending_data))
4572
4573def ShowJetsamZprintSnapshot():
4574    """ Helper function for the ShowJetsamSnapshot lldb command to print the last Jetsam zprint snapshot
4575    """
4576    jzs_trigger_band = kern.GetGlobalVariable('jzs_trigger_band')
4577    jzs_gencount = kern.GetGlobalVariable('jzs_gencount')
4578    jzs_names = kern.globals.jzs_names
4579    jzs_info = kern.globals.jzs_info
4580
4581    if (unsigned(jzs_gencount) == ((1 << 64) - 1)):
4582        print("No jetsam zprint snapshot found\n")
4583        return
4584    print("Jetsam zprint snapshot jzs_trigger_band: {:2d}\n".format(jzs_trigger_band))
4585
4586    info_hdr = "{0: >3s} {1: <30s} {2: >6s} {3: >11s}"
4587    info_fmt = "{0: >3d} {1: <30s} {2: >6d} {3: >11d}"
4588
4589    count = kern.GetGlobalVariable('jzs_zone_cnt')
4590
4591    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))
4592    print(info_hdr.format("",    "",   "elem", "cur"))
4593    print(info_hdr.format("#", "name", "size", "inuse"))
4594    print("-----------------------------------------------------------------------------------------------------------")
4595    idx = 0;
4596    while idx < count:
4597        info = dereference(Cast(addressof(jzs_info[idx]), 'mach_zone_info_t *'))
4598        if info.mzi_elem_size > 0:
4599            print(info_fmt.format(idx, jzs_names[idx].mzn_name, info.mzi_elem_size, info.mzi_count))
4600        idx += 1
4601
4602    jzs_meminfo = kern.globals.jzs_meminfo
4603
4604    count = kern.GetGlobalVariable('jzs_meminfo_cnt')
4605    print("\nJetsam zprint snapshot: wired memory info when jetsam last hit band: {:2d}, jzs_gencount: {:6d}\n\n".format(jzs_trigger_band, jzs_gencount))
4606    print(" {:<7s}  {:>7s}   {:>7s}   {:<50s}".format("tag.kmod", "peak", "size", "name"))
4607    print("---------------------------------------------------------------------------------------------------")
4608    idx = 0;
4609    while idx < count:
4610        meminfo = dereference(Cast(addressof(jzs_meminfo[idx]), 'mach_memory_info_t *'))
4611        peak = unsigned(meminfo.peak / 1024)
4612        size = unsigned(meminfo.size / 1024)
4613        if peak > 0:
4614            tag = unsigned(meminfo.tag)
4615            (sitestr, tagstr) = GetVMKernName(tag)
4616            print(" {:>3d}{:<4s}  {:>7d}K  {:>7d}K  {:<50s}".format(tag, tagstr, peak, size, sitestr))
4617        idx += 1
4618
4619    return
4620
4621# Macro: showjetsamsnapshot
4622
4623@lldb_command("showjetsamsnapshot", "DA")
4624def ShowJetsamSnapshot(cmd_args=None, cmd_options={}):
4625    """ Dump entries in the jetsam snapshot table
4626        usage: showjetsamsnapshot [-D] [-A]
4627        Use -D flag to print extra physfootprint details
4628        Use -A flag to print all entries (regardless of valid count)
4629    """
4630
4631    # Not shown are uuid, user_data, cpu_time
4632
4633    global kern
4634
4635    show_footprint_details = False
4636    show_all_entries = False
4637
4638    if "-D" in cmd_options:
4639        show_footprint_details = True
4640
4641    if "-A" in cmd_options:
4642        show_all_entries = True
4643
4644    valid_count = kern.globals.memorystatus_jetsam_snapshot_count
4645    max_count = kern.globals.memorystatus_jetsam_snapshot_max
4646
4647    if show_all_entries:
4648        count = max_count
4649    else:
4650        count = valid_count
4651
4652    print("{:s}".format(valid_count))
4653    print("{:s}".format(max_count))
4654
4655    if int(count) == 0:
4656        print("The jetsam snapshot is empty.")
4657        print("Use -A to force dump all entries (regardless of valid count)")
4658        return
4659
4660    # Dumps the snapshot header info
4661    print(lldb_run_command('p *memorystatus_jetsam_snapshot'))
4662
4663    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}"
4664    if show_footprint_details:
4665        hdr_format += "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}"
4666
4667
4668    if not show_footprint_details:
4669        print(hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax'))
4670        print(hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)'))
4671    else:
4672        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'))
4673        print(hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)'))
4674
4675
4676    entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
4677                   "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
4678                   "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
4679                   "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\
4680                   "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
4681
4682    if show_footprint_details:
4683        entry_format += "{e.jse_internal_pages: >15d} "\
4684                        "{e.jse_internal_compressed_pages: >15d} "\
4685                        "{e.jse_iokit_mapped_pages: >12d} "\
4686                        "{e.jse_purgeable_nonvolatile_pages: >12d} "\
4687                        "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
4688                        "{e.jse_alternate_accounting_pages: >10d} "\
4689                        "{e.jse_alternate_accounting_compressed_pages: >13d} "\
4690                        "{e.jse_page_table_pages: >10d}"
4691
4692    snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries
4693    idx = 0
4694    while idx < count:
4695        current_entry = dereference(Cast(addressof(snapshot_list[idx]), 'jetsam_snapshot_entry *'))
4696        print(entry_format.format(index=idx, e=current_entry))
4697        idx +=1
4698
4699    ShowJetsamZprintSnapshot()
4700    return
4701
4702# EndMacro: showjetsamsnapshot
4703
4704# Macro: showjetsambucket
4705@lldb_command('showjetsamband', 'J')
4706def ShowJetsamBand(cmd_args=[], cmd_options={}):
4707    """ Print the processes in a jetsam band.
4708        Usage: showjetsamband band_number [-J]
4709            -J      : Output pids as json
4710    """
4711    if cmd_args is None or len(cmd_args) != 1:
4712        raise ArgumentError("invalid arguments")
4713
4714    print_json = "-J" in cmd_options
4715
4716    bucket_number = int(cmd_args[0])
4717    buckets = kern.GetGlobalVariable('memstat_bucket')
4718    bucket = value(buckets.GetSBValue().CreateValueFromExpression(None,
4719        'memstat_bucket[%d]' %(bucket_number)))
4720    l = bucket.list
4721
4722    pids = []
4723    if not print_json:
4724        print(GetProcSummary.header)
4725    for i in IterateTAILQ_HEAD(l, "p_memstat_list"):
4726        pids.append(int(i.p_pid))
4727        if not print_json:
4728            print(GetProcSummary(i))
4729
4730    as_json = json.dumps(pids)
4731    if print_json:
4732        print(as_json)
4733
4734# Macro: showvnodecleanblk/showvnodedirtyblk
4735
4736def _GetBufSummary(buf):
4737    """ Get a summary of important information out of a buf_t.
4738    """
4739    initial = "(struct buf) {0: <#0x} ="
4740
4741    # List all of the fields in this buf summary.
4742    entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq,
4743        buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid,
4744        buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp,
4745        buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata,
4746        buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff,
4747        buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr]
4748
4749    # Join an (already decent) string representation of each field
4750    # with newlines and indent the region.
4751    joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n    ")
4752
4753    # Add the total string representation to our title and return it.
4754    out_str = initial.format(int(buf)) + " {\n    " + joined_strs + "\n}\n\n"
4755    return out_str
4756
4757def _ShowVnodeBlocks(dirty=True, cmd_args=None):
4758    """ Display info about all [dirty|clean] blocks in a vnode.
4759    """
4760    if cmd_args is None or len(cmd_args) == 0:
4761        print("Please provide a valid vnode argument.")
4762        return
4763
4764    vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
4765    list_head = vnodeval.v_cleanblkhd;
4766    if dirty:
4767        list_head = vnodeval.v_dirtyblkhd
4768
4769    print("Blocklist for vnode {}:".format(cmd_args[0]))
4770
4771    i = 0
4772    for buf in IterateListEntry(list_head, 'b_hash'):
4773        # For each block (buf_t) in the appropriate list,
4774        # ask for a summary and print it.
4775        print("---->\nblock {}: ".format(i) + _GetBufSummary(buf))
4776        i += 1
4777    return
4778
4779@lldb_command('showvnodecleanblk')
4780def ShowVnodeCleanBlocks(cmd_args=None):
4781    """ Display info about all clean blocks in a vnode.
4782        usage: showvnodecleanblk <address of vnode>
4783    """
4784    _ShowVnodeBlocks(False, cmd_args)
4785
4786@lldb_command('showvnodedirtyblk')
4787def ShowVnodeDirtyBlocks(cmd_args=None):
4788    """ Display info about all dirty blocks in a vnode.
4789        usage: showvnodedirtyblk <address of vnode>
4790    """
4791    _ShowVnodeBlocks(True, cmd_args)
4792
4793# EndMacro: showvnodecleanblk/showvnodedirtyblk
4794
4795
4796@lldb_command("vm_page_lookup_in_map")
4797def VmPageLookupInMap(cmd_args=None):
4798    """Lookup up a page at a virtual address in a VM map
4799        usage: vm_page_lookup_in_map <map> <vaddr>
4800    """
4801    if cmd_args is None or len(cmd_args) < 2:
4802        raise ArgumentError()
4803
4804    map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
4805    vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t')
4806    print("vaddr {:#018x} in map {: <#018x}".format(vaddr, map))
4807    vm_page_lookup_in_map(map, vaddr)
4808
4809def vm_page_lookup_in_map(map, vaddr):
4810    vaddr = unsigned(vaddr)
4811    vme_list_head = map.hdr.links
4812    vme_ptr_type = GetType('vm_map_entry *')
4813    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
4814        if unsigned(vme.links.start) > vaddr:
4815            break
4816        if unsigned(vme.links.end) <= vaddr:
4817            continue
4818        offset_in_vme = vaddr - unsigned(vme.links.start)
4819        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)))
4820        offset_in_object = offset_in_vme + get_vme_offset(vme)
4821        obj_or_submap = get_vme_object(vme)
4822        if vme.is_sub_map:
4823            print("vaddr {:#018x} in map {: <#018x}".format(offset_in_object, obj_or_submap))
4824            return vm_page_lookup_in_map(obj_or_submap, offset_in_object)
4825        else:
4826            return vm_page_lookup_in_object(obj_or_submap, offset_in_object)
4827
4828@lldb_command("vm_page_lookup_in_object")
4829def VmPageLookupInObject(cmd_args=None):
4830    """Lookup up a page at a given offset in a VM object
4831        usage: vm_page_lookup_in_object <object> <offset>
4832    """
4833    if cmd_args is None or len(cmd_args) < 2:
4834        raise ArgumentError()
4835
4836    object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
4837    offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t')
4838    print("offset {:#018x} in object {: <#018x}".format(offset, object))
4839    vm_page_lookup_in_object(object, offset)
4840
4841def vm_page_lookup_in_object(object, offset):
4842    offset = unsigned(offset)
4843    page_size = kern.globals.page_size
4844    trunc_offset = offset & ~(page_size - 1)
4845    print("    offset {:#018x} in VM object {: <#018x}".format(offset, object))
4846    hash_id = _calc_vm_page_hash(object, trunc_offset)
4847    page_list = kern.globals.vm_page_buckets[hash_id].page_list
4848    page = _vm_page_unpack_ptr(page_list)
4849    while page != 0:
4850        m = kern.GetValueFromAddress(page, 'vm_page_t')
4851        m_object_val = _vm_page_unpack_ptr(m.vmp_object)
4852        m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t')
4853        if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset):
4854            page = _vm_page_unpack_ptr(m.vmp_next_m)
4855            continue
4856        print("    resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m)))
4857        return m
4858    if object.pager and object.pager_ready:
4859        offset_in_pager = trunc_offset + unsigned(object.paging_offset)
4860        if not object.internal:
4861            print("    offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager))
4862            return None
4863        pager = Cast(object.pager, 'compressor_pager *')
4864        ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager)
4865        if ret:
4866            return None
4867    if object.shadow and not object.phys_contiguous:
4868        offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset)
4869        vm_page_lookup_in_object(object.shadow, offset_in_shadow)
4870        return None
4871    print("    page is absent and will be zero-filled on demand")
4872    return None
4873
4874@lldb_command("vm_page_lookup_in_compressor_pager")
4875def VmPageLookupInCompressorPager(cmd_args=None):
4876    """Lookup up a page at a given offset in a compressor pager
4877        usage: vm_page_lookup_in_compressor_pager <pager> <offset>
4878    """
4879    if cmd_args is None or len(cmd_args) < 2:
4880        raise ArgumentError()
4881
4882    pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t')
4883    offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t')
4884    print("offset {:#018x} in compressor pager {: <#018x}".format(offset, pager))
4885    vm_page_lookup_in_compressor_pager(pager, offset)
4886
4887def vm_page_lookup_in_compressor_pager(pager, offset):
4888    offset = unsigned(offset)
4889    page_size = unsigned(kern.globals.page_size)
4890    page_num = unsigned(offset // page_size)
4891    if page_num > pager.cpgr_num_slots:
4892        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))
4893        return 0
4894    slots_per_chunk = 512 // sizeof ('compressor_slot_t')
4895    num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) // slots_per_chunk)
4896    if num_chunks > 1:
4897        chunk_idx = unsigned(page_num // slots_per_chunk)
4898        chunk = pager.cpgr_slots.cpgr_islots[chunk_idx]
4899        slot_idx = unsigned(page_num % slots_per_chunk)
4900        slot = GetObjectAtIndexFromArray(chunk, slot_idx)
4901        slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx)
4902    elif pager.cpgr_num_slots > 2:
4903        slot_idx = page_num
4904        slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx)
4905        slot_str = "dslots[{:d}]".format(slot_idx)
4906    else:
4907        slot_idx = page_num
4908        slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx)
4909        slot_str = "eslots[{:d}]".format(slot_idx)
4910    print("      offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot))
4911    if slot == 0:
4912        return 0
4913    slot_value = dereference(slot)
4914    print(" value {:#010x}".format(slot_value))
4915    vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t'))
4916    return 1
4917
4918@lldb_command("vm_page_lookup_in_compressor")
4919def VmPageLookupInCompressor(cmd_args=None):
4920    """Lookup up a page in a given compressor slot
4921        usage: vm_page_lookup_in_compressor <slot>
4922    """
4923    if cmd_args is None or len(cmd_args) == 0:
4924        raise ArgumentError()
4925
4926    slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *')
4927    print("compressor slot {: <#018x}".format(slot))
4928    vm_page_lookup_in_compressor(slot)
4929
4930C_SV_CSEG_ID = ((1 << 22) - 1)
4931
4932def vm_page_lookup_in_compressor(slot_ptr):
4933    slot_ptr = Cast(slot_ptr, 'compressor_slot_t *')
4934    slot_value = dereference(slot_ptr)
4935    slot = Cast(slot_value, 'c_slot_mapping')
4936    print(slot)
4937    print("compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx))
4938    if slot_ptr == 0:
4939        return
4940    if slot.s_cseg == C_SV_CSEG_ID:
4941        sv = kern.globals.c_segment_sv_hash_table
4942        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))
4943        return
4944    if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available):
4945        print("*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available)))
4946        return
4947    c_segments = kern.globals.c_segments
4948    c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1)
4949    c_seg = c_segments_elt.c_seg
4950    c_no_data = 0
4951    if hasattr(c_seg, 'c_state'):
4952        c_state = c_seg.c_state
4953        if c_state == 0:
4954            c_state_str = "C_IS_EMPTY"
4955            c_no_data = 1
4956        elif c_state == 1:
4957            c_state_str = "C_IS_FREE"
4958            c_no_data = 1
4959        elif c_state == 2:
4960            c_state_str = "C_IS_FILLING"
4961        elif c_state == 3:
4962            c_state_str = "C_ON_AGE_Q"
4963        elif c_state == 4:
4964            c_state_str = "C_ON_SWAPOUT_Q"
4965        elif c_state == 5:
4966            c_state_str = "C_ON_SWAPPEDOUT_Q"
4967            c_no_data = 1
4968        elif c_state == 6:
4969            c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q"
4970            c_no_data = 1
4971        elif c_state == 7:
4972            c_state_str = "C_ON_SWAPPEDIN_Q"
4973        elif c_state == 8:
4974            c_state_str = "C_ON_MAJORCOMPACT_Q"
4975        elif c_state == 9:
4976            c_state_str = "C_ON_BAD_Q"
4977            c_no_data = 1
4978        else:
4979            c_state_str = "<unknown>"
4980    else:
4981        c_state = -1
4982        c_state_str = "<no c_state field>"
4983    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))
4984    c_indx = unsigned(slot.s_cindx)
4985    if hasattr(c_seg, 'c_slot_var_array'):
4986        c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len
4987        if c_indx < c_seg_fixed_array_len:
4988            cs = c_seg.c_slot_fixed_array[c_indx]
4989        else:
4990            cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len)
4991    else:
4992        C_SEG_SLOT_ARRAY_SIZE = 64
4993        C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1
4994        cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx // C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK)
4995    print(cs)
4996    kmem = kmemory.KMem.get_shared()
4997    c_slot_unpacked_ptr = kmem.c_slot_packing.unpack(unsigned(cs.c_packed_ptr))
4998    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)))
4999    if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr):
5000        print("*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr))
5001    if c_no_data == 0:
5002        c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset)
5003        c_size = cs.c_size
5004        cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size)
5005        print(cmd)
5006        print(lldb_run_command(cmd))
5007    else:
5008        print("<no compressed data>")
5009
5010# From vm_page.h
5011VM_PAGE_NOT_ON_Q = 0
5012VM_PAGE_IS_WIRED = 1
5013VM_PAGE_USED_BY_COMPRESSOR = 2
5014VM_PAGE_ON_FREE_Q = 3
5015VM_PAGE_ON_FREE_LOCAL_Q = 4
5016VM_PAGE_ON_FREE_LOPAGE_Q = 5
5017VM_PAGE_ON_THROTTLED_Q = 6
5018VM_PAGE_ON_PAGEOUT_Q = 7
5019VM_PAGE_ON_SPECULATIVE_Q = 8
5020VM_PAGE_ON_ACTIVE_LOCAL_Q = 9
5021VM_PAGE_ON_ACTIVE_Q = 10
5022VM_PAGE_ON_INACTIVE_INTERNAL_Q = 11
5023VM_PAGE_ON_INACTIVE_EXTERNAL_Q = 12
5024VM_PAGE_ON_INACTIVE_CLEANED_Q = 13
5025VM_PAGE_ON_SECLUDED_Q = 14
5026
5027@lldb_command('vm_scan_all_pages')
5028def VMScanAllPages(cmd_args=None):
5029    """Scans the vm_pages[] array
5030    """
5031    vm_pages_count = kern.globals.vm_pages_count
5032    vm_pages = kern.globals.vm_pages
5033
5034    free_count = 0
5035    local_free_count = 0
5036    active_count = 0
5037    local_active_count = 0
5038    inactive_count = 0
5039    speculative_count = 0
5040    throttled_count = 0
5041    wired_count = 0
5042    compressor_count = 0
5043    pageable_internal_count = 0
5044    pageable_external_count = 0
5045    secluded_count = 0
5046    secluded_free_count = 0
5047    secluded_inuse_count = 0
5048
5049    i = 0
5050    for i in range(vm_pages_count):
5051        if i % 10000 == 0:
5052            print("{:d}/{:d}...\n".format(i,vm_pages_count))
5053
5054        m = vm_pages[i]
5055        internal = False
5056
5057        m_object_addr = _vm_page_unpack_ptr(m.vmp_object)
5058        if m_object_addr != 0 and (m_object := kern.CreateValueFromAddress(m_object_addr, "struct vm_object")).GetSBValue().IsValid() and m_object.internal:
5059            internal = True
5060
5061        m_vmp_q_state = int(m.vmp_q_state)
5062        if m.vmp_wire_count != 0 and m_vmp_q_state != VM_PAGE_ON_ACTIVE_LOCAL_Q:
5063            wired_count = wired_count + 1
5064            pageable = 0
5065        elif m_vmp_q_state == VM_PAGE_ON_THROTTLED_Q:
5066            throttled_count = throttled_count + 1
5067            pageable = 0
5068        elif m_vmp_q_state == VM_PAGE_ON_ACTIVE_Q:
5069            active_count = active_count + 1
5070            pageable = 1
5071        elif m_vmp_q_state == VM_PAGE_ON_ACTIVE_LOCAL_Q:
5072            local_active_count = local_active_count + 1
5073            pageable = 0
5074        elif m_vmp_q_state in (VM_PAGE_ON_INACTIVE_CLEANED_Q, VM_PAGE_ON_INACTIVE_INTERNAL_Q,
5075                               VM_PAGE_ON_INACTIVE_EXTERNAL_Q):
5076            inactive_count = inactive_count + 1
5077            pageable = 1
5078        elif m_vmp_q_state == VM_PAGE_ON_SPECULATIVE_Q:
5079            speculative_count = speculative_count + 1
5080            pageable = 0
5081        elif m_vmp_q_state == VM_PAGE_ON_FREE_Q:
5082            free_count = free_count + 1
5083            pageable = 0
5084        elif m_vmp_q_state == VM_PAGE_ON_SECLUDED_Q:
5085            secluded_count = secluded_count + 1
5086            if m_object_addr == 0:
5087                secluded_free_count = secluded_free_count + 1
5088            else:
5089                secluded_inuse_count = secluded_inuse_count + 1
5090            pageable = 0
5091        elif m_object_addr == 0 and m.vmp_busy:
5092            local_free_count = local_free_count + 1
5093            pageable = 0
5094        elif m_vmp_q_state == VM_PAGE_USED_BY_COMPRESSOR:
5095            compressor_count = compressor_count + 1
5096            pageable = 0
5097        else:
5098            print("weird page vm_pages[{:d}]?\n".format(i))
5099            pageable = 0
5100
5101        if pageable:
5102            if internal:
5103                pageable_internal_count = pageable_internal_count + 1
5104            else:
5105                pageable_external_count = pageable_external_count + 1
5106
5107    print("vm_pages_count = {:d}\n".format(vm_pages_count))
5108
5109    print("wired_count = {:d}\n".format(wired_count))
5110    print("throttled_count = {:d}\n".format(throttled_count))
5111    print("active_count = {:d}\n".format(active_count))
5112    print("local_active_count = {:d}\n".format(local_active_count))
5113    print("inactive_count = {:d}\n".format(inactive_count))
5114    print("speculative_count = {:d}\n".format(speculative_count))
5115    print("free_count = {:d}\n".format(free_count))
5116    print("local_free_count = {:d}\n".format(local_free_count))
5117    print("compressor_count = {:d}\n".format(compressor_count))
5118
5119    print("pageable_internal_count = {:d}\n".format(pageable_internal_count))
5120    print("pageable_external_count = {:d}\n".format(pageable_external_count))
5121    print("secluded_count = {:d}\n".format(secluded_count))
5122    print("secluded_free_count = {:d}\n".format(secluded_free_count))
5123    print("secluded_inuse_count = {:d}\n".format(secluded_inuse_count))
5124
5125
5126@lldb_command('show_all_vm_named_entries')
5127def ShowAllVMNamedEntries(cmd_args=None):
5128    """ Routine to print a summary listing of all the VM named entries
5129    """
5130
5131    kmem = kmemory.KMem.get_shared()
5132    ikot_named_entry = GetEnumValue('ipc_kotype_t', 'IKOT_NAMED_ENTRY')
5133
5134    port_ty = gettype('struct ipc_port')
5135    ent_ty  = gettype('struct vm_named_entry')
5136
5137    named_entries = (
5138        port
5139        for port
5140        in kmemory.Zone("ipc ports").iter_allocated(port_ty)
5141        if port.xGetScalarByPath(".ip_object.io_bits") & 0x3ff == ikot_named_entry
5142    )
5143
5144    for idx, port in enumerate(named_entries):
5145        ko  = kmem.make_address(port.xGetScalarByName('ip_kobject'))
5146        ent = port.xCreateValueFromAddress(None, ko, ent_ty)
5147        showmemoryentry(value(ent.AddressOf()), idx=idx + 1, port=value(port.AddressOf()))
5148
5149@lldb_command('show_vm_named_entry')
5150def ShowVMNamedEntry(cmd_args=None):
5151    """ Routine to print a VM named entry
5152    """
5153    if cmd_args is None or len(cmd_args) == 0:
5154        raise ArgumentError()
5155
5156    named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t')
5157    showmemoryentry(named_entry)
5158
5159def showmemoryentry(entry, idx=0, port=None):
5160    """  Routine to print out a summary a VM memory entry
5161        params:
5162            entry - core.value : a object of type 'struct vm_named_entry *'
5163        returns:
5164            None
5165    """
5166    show_pager_info = True
5167    show_all_shadows = True
5168
5169    backing = ""
5170    if entry.is_sub_map == 1:
5171        backing += "SUBMAP"
5172    if entry.is_copy == 1:
5173        backing += "COPY"
5174    if entry.is_object == 1:
5175        backing += "OBJECT"
5176    if entry.is_sub_map == 0 and entry.is_copy == 0 and entry.is_object == 0:
5177        backing += "***?***"
5178    prot=""
5179    if entry.protection & 0x1:
5180        prot += "r"
5181    else:
5182        prot += "-"
5183    if entry.protection & 0x2:
5184        prot += "w"
5185    else:
5186        prot += "-"
5187    if entry.protection & 0x4:
5188        prot += "x"
5189    else:
5190        prot += "-"
5191    extra_str = ""
5192    if port is not None:
5193        extra_str += " port={:#016x}".format(port)
5194    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))
5195
5196    if entry.is_sub_map == 1:
5197        showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows)
5198    elif entry.is_copy == 1:
5199        showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows)
5200    elif entry.is_object == 1:
5201        showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows)
5202    else:
5203        print("***** UNKNOWN TYPE *****")
5204    print()
5205
5206@lldb_command("showmaprb")
5207def ShowMapRB(cmd_args=None):
5208    """Routine to print out a VM map's RB tree
5209        usage: showmaprb <vm_map>
5210    """
5211    if cmd_args is None or len(cmd_args) == 0:
5212        raise ArgumentError()
5213
5214
5215    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
5216    print(GetVMMapSummary.header)
5217    print(GetVMMapSummary(map_val))
5218
5219    vme_type = gettype('struct vm_map_entry')
5220    to_entry = vme_type.xContainerOfTransform('store')
5221
5222    print(GetVMEntrySummary.header)
5223    for links in iter_RB_HEAD(map_val.hdr.rb_head_store.GetSBValue(), 'entry'):
5224        print(GetVMEntrySummary(value(to_entry(links).AddressOf())))
5225    return None
5226
5227@lldb_command('show_all_owned_objects', 'T')
5228def ShowAllOwnedObjects(cmd_args=None, cmd_options={}):
5229    """ Routine to print the list of VM objects owned by each task
5230        -T: show only ledger-tagged objects
5231    """
5232    showonlytagged = False
5233    if "-T" in cmd_options:
5234        showonlytagged = True
5235    for task in kern.tasks:
5236        ShowTaskOwnedVmObjects(task, showonlytagged)
5237
5238@lldb_command('show_task_owned_objects', 'T')
5239def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}):
5240    """ Routine to print the list of VM objects owned by the specified task
5241        -T: show only ledger-tagged objects
5242    """
5243    if cmd_args is None or len(cmd_args) == 0:
5244        raise ArgumentError()
5245
5246    showonlytagged = False
5247    if "-T" in cmd_options:
5248        showonlytagged = True
5249    task = kern.GetValueFromAddress(cmd_args[0], 'task *')
5250    ShowTaskOwnedVmObjects(task, showonlytagged)
5251
5252@lldb_command('showdeviceinfo', 'J')
5253def ShowDeviceInfo(cmd_args=None, cmd_options={}):
5254    """ Routine to show basic device information (model, build, ncpus, etc...)
5255        Usage: memstats  [-J]
5256            -J      : Output json
5257    """
5258    print_json = False
5259    if "-J" in cmd_options:
5260        print_json = True
5261    device_info = {}
5262    device_info["build"] =  str(kern.globals.osversion)
5263    device_info["memoryConfig"] = int(kern.globals.max_mem_actual)
5264    device_info["ncpu"] = int(kern.globals.ncpu)
5265    device_info["pagesize"] = int(kern.globals.page_size)
5266    device_info["mlockLimit"] = signed(kern.globals.vm_global_user_wire_limit)
5267    # Serializing to json here ensure we always catch bugs preventing
5268    # serialization
5269    as_json = json.dumps(device_info)
5270
5271
5272    if print_json:
5273        print(as_json)
5274    else:
5275        PrettyPrintDictionary(device_info)
5276
5277def ShowTaskOwnedVmObjects(task, showonlytagged=False):
5278    """  Routine to print out a summary listing of all the entries in a vm_map
5279        params:
5280            task - core.value : a object of type 'task *'
5281        returns:
5282            None
5283    """
5284    taskobjq_total = lambda:None
5285    taskobjq_total.objects = 0
5286    taskobjq_total.vsize = 0
5287    taskobjq_total.rsize = 0
5288    taskobjq_total.wsize = 0
5289    taskobjq_total.csize = 0
5290    vmo_list_head = task.task_objq
5291    vmo_ptr_type = GetType('vm_object *')
5292    idx = 0
5293    for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"):
5294        idx += 1
5295        if not showonlytagged or vmo.vo_ledger_tag != 0:
5296            if taskobjq_total.objects == 0:
5297                print(' \n')
5298                print(GetTaskSummary.header + ' ' + GetProcSummary.header)
5299                print(GetTaskSummary(task) + ' ' + GetProcSummary(GetProcFromTask(task)))
5300                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"))
5301            ShowOwnedVmObject(vmo, idx, 0, taskobjq_total)
5302    if taskobjq_total.objects != 0:
5303        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))
5304    return None
5305
5306def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total):
5307    """  Routine to print out a VM object owned by a task
5308        params:
5309            object - core.value : a object of type 'struct vm_object *'
5310        returns:
5311            None
5312    """
5313    page_size = kern.globals.page_size
5314    if object.purgable == 0:
5315        purgable = "N"
5316    elif object.purgable == 1:
5317        purgable = "V"
5318    elif object.purgable == 2:
5319        purgable = "E"
5320    elif object.purgable == 3:
5321        purgable = "D"
5322    else:
5323        purgable = "?"
5324    if object.pager == 0:
5325        compressed_count = 0
5326    else:
5327        compressor_pager = Cast(object.pager, 'compressor_pager *')
5328        compressed_count = compressor_pager.cpgr_num_slots_occupied
5329
5330    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)))
5331
5332    taskobjq_total.objects += 1
5333    taskobjq_total.vsize += object.vo_un1.vou_size // page_size
5334    taskobjq_total.rsize += object.resident_page_count
5335    taskobjq_total.wsize += object.wired_page_count
5336    taskobjq_total.csize += compressed_count
5337
5338def GetProcPIDForObjectOwner(owner):
5339    """ same as GetProcPIDForTask() but deals with -1 for a disowned object
5340    """
5341    if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
5342        return -1
5343    return GetProcPIDForTask(owner)
5344
5345def GetProcNameForObjectOwner(owner):
5346    """ same as GetProcNameForTask() but deals with -1 for a disowned object
5347    """
5348    if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
5349        return "<disowned>"
5350    return GetProcNameForTask(owner)
5351
5352def GetDescForNamedEntry(mem_entry):
5353    out_str = "\n"
5354    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)
5355    if mem_entry.is_sub_map:
5356        out_str += " is_sub_map"
5357    elif mem_entry.is_copy:
5358        out_str += " is_copy"
5359    elif mem_entry.is_object:
5360        out_str += " is_object"
5361    else:
5362        out_str += " ???"
5363    return out_str
5364
5365# Macro: showdiagmemthresholds
5366def GetDiagThresholdConvertSizeToString(size,human_readable):
5367    if human_readable == 1 :
5368        if(size > (1 << 20)) :
5369            return "{0: >7,.2f}MB".format(size / (1 << 20))
5370        elif(size > (1 << 10)) :
5371            return "{0: >7,.2f}KB".format(size / (1 << 10))
5372        return "{0: >7,.2f}B".format(float(size))
5373    else :
5374            return "{0: >9d}B".format(size )
5375
5376@header("{: >8s} {: >14s}   {: >14s}   {: >10s}   {: >14s}   {: >10s}   {: >10s}  {: <32s}".format(
5377'PID',     'Footprint',
5378'Limit', 'Lim Warned','Threshold', 'Thr Warned','Thr Enabled','Command'))
5379def GetDiagThresholdStatusNode(proc_val,interested_pid,show_all,human_readable):
5380    """ Internal function to get memorystatus information from the given proc
5381        params: proc - value representing struct proc *
5382        return: str - formatted output information for proc object
5383
5384        Options are
5385          -p Define a pid to show information
5386          -a Print all the processes, regardless if threshold is enabled
5387          -r Show data in human readable format
5388    """
5389
5390    if interested_pid != -1 and int(interested_pid) != int(GetProcPID(proc_val)) :
5391        return ""
5392
5393
5394    LF_ENTRY_ACTIVE        = 0x0001 # entry is active if set
5395    LF_WAKE_NEEDED         = 0x0100  # one or more threads are asleep
5396    LF_WAKE_INPROGRESS     = 0x0200  # the wait queue is being processed
5397    LF_REFILL_SCHEDULED    = 0x0400  # a refill timer has been set
5398    LF_REFILL_INPROGRESS   = 0x0800  # the ledger is being refilled
5399    LF_CALLED_BACK         = 0x1000  # callback was called for balance in deficit
5400    LF_WARNED              = 0x2000  # callback was called for balance warning
5401    LF_TRACKING_MAX        = 0x4000  # track max balance. Exclusive w.r.t refill
5402    LF_PANIC_ON_NEGATIVE   = 0x8000  # panic if it goes negative
5403    LF_TRACK_CREDIT_ONLY   = 0x10000 # only update "credit"
5404    LF_DIAG_WARNED         = 0x20000 # callback was called for balance diag
5405    LF_DIAG_DISABLED       = 0x40000 # diagnostics threshold are disabled at the moment
5406
5407    out_str = ''
5408    task_val = GetTaskFromProc(proc_val)
5409    if task_val is None:
5410        return out_str
5411
5412    task_ledgerp = task_val.ledger
5413    ledger_template = kern.globals.task_ledger_template
5414
5415    task_phys_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'phys_footprint')
5416
5417    diagmem_threshold = task_phys_footprint_ledger_entry['diag_threshold_scaled']
5418    if diagmem_threshold == -1 and show_all == 0 and interested_pid == -1 :
5419        return ""
5420
5421    diagmem_threshold_warned = task_phys_footprint_ledger_entry['flags'] & LF_DIAG_WARNED
5422    diagmem_threshold_disabled = task_phys_footprint_ledger_entry['flags'] & LF_DIAG_DISABLED
5423
5424    phys_footprint_limit = task_phys_footprint_ledger_entry['limit']
5425    phys_footprint_limit_warned = task_phys_footprint_ledger_entry['flags'] & LF_WARNED
5426    task_mem_footprint = task_phys_footprint_ledger_entry['balance']
5427
5428
5429    if phys_footprint_limit_warned == 0 :
5430        phys_footprint_limit_warned_str = "Not warned"
5431    else :
5432        phys_footprint_limit_warned_str = "Warned"
5433
5434    if diagmem_threshold_warned == 0 :
5435        diagmem_threshold_warned_str = "Not warned"
5436    else :
5437        diagmem_threshold_warned_str = "Warned"
5438
5439    if diagmem_threshold_disabled == 0 :
5440        diagmem_threshold_disabled_str = "Enabled"
5441    else :
5442        diagmem_threshold_disabled_str = "Disabled"
5443
5444    if diagmem_threshold == -1 :
5445        diagmem_threshold_str = "Not set"
5446    else :
5447        diagmem_threshold_str = GetDiagThresholdConvertSizeToString(diagmem_threshold * (1<<20),human_readable)
5448    #                  PID       FP       LIM      LIMW        THR       THRW    THRD        Name
5449    format_string = '{0: >8d} {1: >14s}   {2: >14s}   {3: >10s}   {4: >14s}   {5: >10s}   {6: >10s}  {7: <32s}'
5450    out_str += format_string.format(
5451        GetProcPID(proc_val),
5452        GetDiagThresholdConvertSizeToString(task_mem_footprint,human_readable),
5453        GetDiagThresholdConvertSizeToString(phys_footprint_limit,human_readable),
5454        phys_footprint_limit_warned_str,
5455        diagmem_threshold_str,
5456        diagmem_threshold_warned_str,
5457        diagmem_threshold_disabled_str,
5458        GetProcName(proc_val)
5459        )
5460    return out_str
5461
5462@lldb_command('showdiagmemthresholds','P:AR')
5463def ShowDiagmemThresholds(cmd_args=None, cmd_options={}):
5464    """  Routine to display each entry in diagmem threshold and its ledger related information
5465         Usage: showdiagmemthresholds
5466        Options are
5467          -P Define a pid to show information
5468          -A Print all the processes, regardless if threshold is enabled
5469          -R Show data in human readable format
5470    """
5471    # If we are focusing only on one PID, lets check
5472    if "-P" in cmd_options:
5473        interested_pid = cmd_options["-P"]
5474    else :
5475        interested_pid = -1
5476
5477
5478    if "-A" in cmd_options:
5479        show_all = 1
5480    else :
5481        show_all = 0
5482
5483    if "-R" in cmd_options:
5484        human_readable = 1
5485    else :
5486        human_readable = 0
5487
5488    bucket_index = 0
5489    bucket_count = 20
5490    print(GetDiagThresholdStatusNode.header)
5491    while bucket_index < bucket_count:
5492        current_bucket = kern.globals.memstat_bucket[bucket_index]
5493        current_list = current_bucket.list
5494        current_proc = Cast(current_list.tqh_first, 'proc *')
5495        while unsigned(current_proc) != 0:
5496            current_line = GetDiagThresholdStatusNode(current_proc,interested_pid,show_all,human_readable)
5497            if current_line != "" :
5498                print(current_line)
5499            current_proc = current_proc.p_memstat_list.tqe_next
5500        bucket_index += 1
5501    print("\n\n")
5502
5503    # EndMacro: showdiagmemthresholds
5504
5505