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