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