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