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