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