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