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