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