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