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