]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/memory.py
xnu-7195.81.3.tar.gz
[apple/xnu.git] / tools / lldbmacros / memory.py
old mode 100644 (file)
new mode 100755 (executable)
index 2844727..be557e7
 
 """ Please make sure you read the README file COMPLETELY BEFORE reading anything below.
 
 """ Please make sure you read the README file COMPLETELY BEFORE reading anything below.
-    It is very critical that you read coding guidelines in Section E in README file. 
+    It is very critical that you read coding guidelines in Section E in README file.
 """
 from xnu import *
 """
 from xnu import *
-import sys, shlex
+import sys
+import shlex
 from utils import *
 import xnudefines
 from process import *
 from utils import *
 import xnudefines
 from process import *
+import macho
+import json
+from ctypes import c_int64
+
+def vm_unpack_pointer(packed, params, type_str = 'void *'):
+    """ Unpack a pointer packed with VM_PACK_POINTER()
+        params:
+            packed       - value : The packed pointer value
+            params       - value : The packing parameters of type vm_packing_params_t
+            type_str     - str   : The type to cast the unpacked pointer into
+        returns:
+            The unpacked pointer
+    """
+    if params.vmpp_base_relative:
+        addr = unsigned(packed) << int(params.vmpp_shift)
+        if addr: addr += int(params.vmpp_base)
+    else:
+        bits  = int(params.vmpp_bits)
+        shift = int(params.vmpp_shift)
+        addr  = c_int64(unsigned(packed) << (64 - bits)).value
+        addr >>= 64 - bits - shift
+    return kern.GetValueFromAddress(addr, type_str)
+
+def IterateZPerCPU(root, element_type):
+    """ Iterates over a percpu variable
+        params:
+            root         - value : Value object for per-cpu variable
+            element_type - str   : Type of element
+        returns:
+            one slot
+    """
+    pagesize = kern.globals.page_size
+    mangle   = 1 << (8 * kern.ptrsize - 1)
+    for i in range(0, kern.globals.zpercpu_early_count):
+        yield kern.GetValueFromAddress((int(root) | mangle) + i * pagesize, element_type)
+
+@lldb_command('showzpcpu', "S")
+def ShowZPerCPU(cmd_args=None, cmd_options={}):
+    """ Routine to show per-cpu zone allocated variables
+
+        Usage: showzpcpu [-S] expression [field]
+            -S  : sum the values instead of printing them
+    """
+    if not cmd_args:
+        raise ArgumentError("No arguments passed")
+    pagesize = kern.globals.page_size
+    mangle   = 1 << (8 * kern.ptrsize - 1)
+    sbv = pagesize.GetSBValue()
+    v = sbv.CreateValueFromExpression(None, cmd_args[0])
+    e = value(v)
+    acc = 0
+    for i in range(0, kern.globals.zpercpu_early_count):
+        if len(cmd_args) == 1:
+            t = sbv.CreateValueFromExpression(None, '(%s)%d' % (v.GetTypeName(), (int(e) | mangle) + i * pagesize)).Dereference()
+        else:
+            t = sbv.CreateValueFromExpression(None, '((%s)%d)->%s' % (v.GetTypeName(), (int(e) | mangle) + i * pagesize, cmd_args[1]))
+        if "-S" in cmd_options:
+            acc += value(t)
+        else:
+            print value(t)
+
+    if "-S" in cmd_options:
+        print acc
+
+def ZoneName(zone):
+    """ Formats the name for a given zone
+        params:
+            zone         - value : A pointer to a zone
+        returns:
+            the formated name for the zone
+    """
+    names = [ "", "default.", "data.", "kext."]
+    return "{:s}{:s}".format(names[int(zone.kalloc_heap)], zone.z_name)
+
+def PrettyPrintDictionary(d):
+    """ Internal function to pretty print a dictionary with string or integer values
+        params: The dictionary to print
+    """
+    for key, value in d.items():
+        key += ":"
+        if isinstance(value, int):
+            print "{:<30s} {: >10d}".format(key, value)
+        else:
+            print "{:<30s} {: >10s}".format(key, value)
 
 # Macro: memstats
 
 # Macro: memstats
-@lldb_command('memstats')
-def Memstats(cmd_args=None):
+@lldb_command('memstats', 'J')
+def Memstats(cmd_args=None, cmd_options={}):
     """ 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.
     """ 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.
+        usage: memstats -J
+                Output json
     """
     """
+    print_json = False
+    if "-J" in cmd_options:
+        print_json = True
+
+    memstats = {}
     try:
     try:
-        print "memorystatus_level: {: >10d}".format(kern.globals.memorystatus_level)
-    except ValueError:
-        pass
-    try:
-        print "memorystatus_available_pages: {: >10d}".format(kern.globals.memorystatus_available_pages)
+        memstats["memorystatus_level"] = int(kern.globals.memorystatus_level)
+        memstats["memorystatus_available_pages"] = int(kern.globals.memorystatus_available_pages)
+        memstats["inuse_ptepages_count"] = int(kern.globals.inuse_ptepages_count)
     except ValueError:
         pass
     except ValueError:
         pass
-    print "vm_page_throttled_count: {: >10d}".format(kern.globals.vm_page_throttled_count)
-    print "vm_page_active_count:    {: >10d}".format(kern.globals.vm_page_active_count)
-    print "vm_page_inactive_count:  {: >10d}".format(kern.globals.vm_page_inactive_count)
-    print "vm_page_wire_count:      {: >10d}".format(kern.globals.vm_page_wire_count)
-    print "vm_page_free_count:      {: >10d}".format(kern.globals.vm_page_free_count)
-    print "vm_page_purgeable_count: {: >10d}".format(kern.globals.vm_page_purgeable_count)
-    print "vm_page_inactive_target: {: >10d}".format(kern.globals.vm_page_inactive_target)
-    print "vm_page_free_target:     {: >10d}".format(kern.globals.vm_page_free_target)
-    print "inuse_ptepages_count:    {: >10d}".format(kern.globals.inuse_ptepages_count)
-    print "vm_page_free_reserved:   {: >10d}".format(kern.globals.vm_page_free_reserved)
+    if hasattr(kern.globals, 'compressor_object'):
+        memstats["compressor_page_count"] = int(kern.globals.compressor_object.resident_page_count)
+    memstats["vm_page_throttled_count"] = int(kern.globals.vm_page_throttled_count)
+    memstats["vm_page_active_count"] = int(kern.globals.vm_page_active_count)
+    memstats["vm_page_inactive_count"] = int(kern.globals.vm_page_inactive_count)
+    memstats["vm_page_wire_count"] = int(kern.globals.vm_page_wire_count)
+    memstats["vm_page_free_count"] = int(kern.globals.vm_page_free_count)
+    memstats["vm_page_purgeable_count"] = int(kern.globals.vm_page_purgeable_count)
+    memstats["vm_page_inactive_target"] = int(kern.globals.vm_page_inactive_target)
+    memstats["vm_page_free_target"] = int(kern.globals.vm_page_free_target)
+    memstats["vm_page_free_reserved"] = int(kern.globals.vm_page_free_reserved)
+
+    if print_json:
+        print json.dumps(memstats)
+    else:
+        PrettyPrintDictionary(memstats)
 
 @xnudebug_test('test_memstats')
 def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
     """ Test the functionality of memstats command
 
 @xnudebug_test('test_memstats')
 def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
     """ Test the functionality of memstats command
-        returns 
+        returns
          - False on failure
          - False on failure
-         - True on success 
+         - True on success
     """
     if not isConnected:
         print "Target is not connected. Cannot test memstats"
     """
     if not isConnected:
         print "Target is not connected. Cannot test memstats"
@@ -45,9 +141,9 @@ def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
     res = lldb.SBCommandReturnObject()
     lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res)
     result = res.GetOutput()
     res = lldb.SBCommandReturnObject()
     lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res)
     result = res.GetOutput()
-    if result.split(":")[1].strip().find('None') == -1 : 
+    if result.split(":")[1].strip().find('None') == -1 :
         return True
         return True
-    else: 
+    else:
         return False
 
 # EndMacro: memstats
         return False
 
 # EndMacro: memstats
@@ -55,20 +151,18 @@ def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
 # Macro: showmemorystatus
 def CalculateLedgerPeak(phys_footprint_entry):
     """ Internal function to calculate ledger peak value for the given phys footprint entry
 # Macro: showmemorystatus
 def CalculateLedgerPeak(phys_footprint_entry):
     """ Internal function to calculate ledger peak value for the given phys footprint entry
-        params: phys_footprint_entry - value representing struct ledger_entry * 
+        params: phys_footprint_entry - value representing struct ledger_entry *
         return: value - representing the ledger peak for the given phys footprint entry
     """
     now = kern.globals.sched_tick / 20
         return: value - representing the ledger peak for the given phys footprint entry
     """
     now = kern.globals.sched_tick / 20
-    ledger_peak = phys_footprint_entry.le_credit - phys_footprint_entry.le_debit
-    if (now - phys_footprint_entry._le.le_peaks[0].le_time <= 1) and (phys_footprint_entry._le.le_peaks[0].le_max > ledger_peak):
-        ledger_peak = phys_footprint_entry._le.le_peaks[0].le_max
-    if (now - phys_footprint_entry._le.le_peaks[1].le_time <= 1) and (phys_footprint_entry._le.le_peaks[1].le_max > ledger_peak):
-        ledger_peak = phys_footprint_entry._le.le_peaks[1].le_max
+    ledger_peak = long(phys_footprint_entry.le_credit) - long(phys_footprint_entry.le_debit)
+    if hasattr(phys_footprint_entry._le._le_max, 'le_interval_max') and (long(phys_footprint_entry._le._le_max.le_interval_max) > ledger_peak):
+        ledger_peak = long(phys_footprint_entry._le._le_max.le_interval_max)
     return ledger_peak
 
     return ledger_peak
 
-@header("{: >8s} {: >22s} {: >22s} {: >11s} {: >11s} {: >12s} {: >10s} {: >13s} {: ^10s} {: >8s}  {: <20s}\n".format(
-'pid', 'effective priority', 'requested priority', 'state', 'user_data', 'physical', 'iokit', 'footprint',
-'spike', 'limit', 'command'))
+@header("{: >8s} {: >12s} {: >12s} {: >10s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s}  {: <32s}\n".format(
+'pid', 'effective', 'requested', 'state', 'relaunch', 'user_data', 'physical', 'iokit', 'footprint',
+'recent peak', 'lifemax', 'limit', 'command'))
 def GetMemoryStatusNode(proc_val):
     """ Internal function to get memorystatus information from the given proc
         params: proc - value representing struct proc *
 def GetMemoryStatusNode(proc_val):
     """ Internal function to get memorystatus information from the given proc
         params: proc - value representing struct proc *
@@ -79,27 +173,30 @@ def GetMemoryStatusNode(proc_val):
     task_ledgerp = task_val.ledger
 
     task_physmem_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_mem]
     task_ledgerp = task_val.ledger
 
     task_physmem_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_mem]
-    task_iokit_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.iokit_mem]
+    task_iokit_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.iokit_mapped]
     task_phys_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_footprint]
     page_size = kern.globals.page_size
     task_phys_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_footprint]
     page_size = kern.globals.page_size
-    
-    phys_mem_footprint = (task_physmem_footprint_ledger_entry.le_credit - task_physmem_footprint_ledger_entry.le_debit) / page_size
-    iokit_footprint = (task_iokit_footprint_ledger_entry.le_credit - task_iokit_footprint_ledger_entry.le_debit) / page_size
-    phys_footprint = (task_phys_footprint_ledger_entry.le_credit - task_phys_footprint_ledger_entry.le_debit) / page_size
-    phys_footprint_limit = task_phys_footprint_ledger_entry.le_limit / page_size
+
+    phys_mem_footprint = (long(task_physmem_footprint_ledger_entry.le_credit) - long(task_physmem_footprint_ledger_entry.le_debit)) / page_size
+    iokit_footprint = (long(task_iokit_footprint_ledger_entry.le_credit) - long(task_iokit_footprint_ledger_entry.le_debit)) / page_size
+    phys_footprint = (long(task_phys_footprint_ledger_entry.le_credit) - long(task_phys_footprint_ledger_entry.le_debit)) / page_size
+    phys_footprint_limit = long(task_phys_footprint_ledger_entry.le_limit) / page_size
     ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry)
     phys_footprint_spike = ledger_peak / page_size
     ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry)
     phys_footprint_spike = ledger_peak / page_size
+    phys_footprint_lifetime_max = long(task_phys_footprint_ledger_entry._le._le_max.le_lifetime_max) / page_size
 
 
-    format_string = '{0: >8d} {1: >22d} {2: >22d} {3: #011x} {4: #011x} {5: >12d} {6: >10d} {7: >13d}'
+    format_string = '{0: >8d} {1: >12d} {2: >12d} {3: #011x} {4: >10d} {5: #011x} {6: >12d} {7: >10d} {8: >13d}'
     out_str += format_string.format(proc_val.p_pid, proc_val.p_memstat_effectivepriority,
     out_str += format_string.format(proc_val.p_pid, proc_val.p_memstat_effectivepriority,
-        proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_userdata,
-        phys_mem_footprint, iokit_footprint, phys_footprint)
+        proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_relaunch_flags, 
+        proc_val.p_memstat_userdata, phys_mem_footprint, iokit_footprint, phys_footprint)
     if phys_footprint != phys_footprint_spike:
     if phys_footprint != phys_footprint_spike:
-        out_str += "{: ^12d}".format(phys_footprint_spike)
+        out_str += "{: >12d}".format(phys_footprint_spike)
     else:
     else:
-        out_str += "{: ^12s}".format('-')
-    out_str += "{: 8d}  {: <20s}\n".format(phys_footprint_limit, proc_val.p_comm)
-    return out_str        
+        out_str += "{: >12s}".format('-')
+
+    out_str += "{: >10d}  ".format(phys_footprint_lifetime_max)
+    out_str += "{: >10d}  {: <32s}\n".format(phys_footprint_limit, GetProcName(proc_val))
+    return out_str
 
 @lldb_command('showmemorystatus')
 def ShowMemoryStatus(cmd_args=None):
 
 @lldb_command('showmemorystatus')
 def ShowMemoryStatus(cmd_args=None):
@@ -109,8 +206,8 @@ def ShowMemoryStatus(cmd_args=None):
     bucket_index = 0
     bucket_count = 20
     print GetMemoryStatusNode.header
     bucket_index = 0
     bucket_count = 20
     print GetMemoryStatusNode.header
-    print "{: >91s} {: >10s} {: >13s} {: ^10s} {: >8s}\n".format("(pages)", "(pages)", "(pages)",
-        "(pages)", "(pages)")
+    print "{: >21s} {: >12s} {: >38s} {: >10s} {: >12s} {: >10s} {: >10s}\n".format("priority", "priority", "(pages)", "(pages)", "(pages)",
+        "(pages)", "(pages)", "(pages)")
     while bucket_index < bucket_count:
         current_bucket = kern.globals.memstat_bucket[bucket_index]
         current_list = current_bucket.list
     while bucket_index < bucket_count:
         current_bucket = kern.globals.memstat_bucket[bucket_index]
         current_list = current_bucket.list
@@ -121,96 +218,495 @@ def ShowMemoryStatus(cmd_args=None):
         bucket_index += 1
     print "\n\n"
     Memstats()
         bucket_index += 1
     print "\n\n"
     Memstats()
-    
+
 # EndMacro: showmemorystatus
 
 # EndMacro: showmemorystatus
 
+class ZoneMeta(object):
+    """
+    Helper class that helpers walking metadata
+    """
+
+    @classmethod
+    def _looksForeign(cls, addr):
+        if addr & (kern.globals.page_size - 1):
+            return False
+        try:
+            meta = kern.GetValueFromAddress(addr, "struct zone_page_metadata *")
+            return meta.zm_foreign_cookie[0] == 0x123456789abcdef
+        except:
+            return False
+
+    def __init__(self, addr, isPageIndex = False):
+        global kern
+        pagesize  = kern.globals.page_size
+        zone_info = kern.GetGlobalVariable('zone_info')
+
+        self.zone_map_min   = unsigned(zone_info.zi_map_range.min_address)
+        self.zone_map_max   = unsigned(zone_info.zi_map_range.max_address)
+        self.zone_meta_min  = unsigned(zone_info.zi_meta_range.min_address)
+        self.zone_meta_max  = unsigned(zone_info.zi_meta_range.max_address)
+
+        addr = unsigned(addr)
+        if isPageIndex:
+            # sign extend
+            addr = value(pagesize.GetSBValue().CreateValueFromExpression(None,
+                '(long)(int)%d * %d' %(addr, pagesize)))
+            addr = unsigned(addr)
+
+        self.address = addr
+
+        if self.zone_meta_min <= addr and addr < self.zone_meta_max:
+            self.kind = 'Metadata'
+            addr -= (addr - self.zone_meta_min) % sizeof('struct zone_page_metadata')
+            self.meta_addr = addr
+            self.meta = kern.GetValueFromAddress(addr, "struct zone_page_metadata *")
+
+            self.page_addr = self.zone_map_min + ((addr - self.zone_meta_min) / sizeof('struct zone_page_metadata') * pagesize)
+            self.first_offset = 0
+        elif self.zone_map_min <= addr and addr < self.zone_map_max:
+            addr &= ~(pagesize - 1)
+            page_idx = (addr - self.zone_map_min) / pagesize
+
+            self.kind = 'Element'
+            self.page_addr = addr
+            self.meta_addr = self.zone_meta_min + page_idx * sizeof('struct zone_page_metadata')
+            self.meta = kern.GetValueFromAddress(self.meta_addr, "struct zone_page_metadata *")
+            self.first_offset = 0
+        elif ZoneMeta._looksForeign(addr):
+            self.kind = 'Element (F)'
+            addr &= ~(pagesize - 1)
+            self.page_addr = addr
+            self.meta_addr = addr
+            self.meta = kern.GetValueFromAddress(addr, "struct zone_page_metadata *")
+            self.first_offset = 32 # ZONE_FOREIGN_PAGE_FIRST_OFFSET in zalloc.c
+        else:
+            self.kind = 'Unknown'
+            self.meta = None
+            self.page_addr = 0
+            self.meta_addr = 0
+            self.first_offset = 0
+
+    def isSecondaryPage(self):
+        return self.meta and self.meta.zm_secondary_page
+
+    def getPageCount(self):
+        return self.meta and self.meta.zm_page_count or 0
+
+    def getAllocCount(self):
+        return self.meta and self.meta.zm_alloc_count or 0
+
+    def getReal(self):
+        if self.isSecondaryPage():
+            return ZoneMeta(self.meta - self.meta.zm_page_count)
+
+        return self
+
+    def getFreeList(self):
+        if self.meta and self.meta.zm_freelist_offs != unsigned(0xffff):
+            return kern.GetValueFromAddress(self.page_addr + self.meta.zm_freelist_offs, 'vm_offset_t *')
+        return 0
+
+    def iterateFreeList(self):
+        cur = self.getFreeList()
+        while cur:
+            yield cur
+
+            cur = dereference(cast(cur, 'vm_offset_t *'))
+            cur = unsigned(cur) ^ unsigned(kern.globals.zp_nopoison_cookie)
+            cur = kern.GetValueFromAddress(cur, 'vm_offset_t *')
+
+    def iterateElements(self):
+        if self.meta is None:
+            return
+        esize = self.getZone().z_elem_size
+        offs  = self.first_offset
+        end   = kern.globals.page_size
+        if not self.meta.zm_percpu:
+            end *= self.meta.zm_page_count
+
+        while offs + esize <= end:
+            yield kern.GetValueFromAddress(self.page_addr + offs, 'void *')
+            offs += esize
+
+    def getZone(self):
+        if self.meta:
+            return kern.globals.zone_array[self.meta.zm_index]
+        return None
+
+@lldb_type_summary(['zone_page_metadata'])
+@header("{:<18s} {:<18s} {:>8s} {:>8s} {:<18s} {:<20s}".format('ZONE_METADATA', 'FREELIST', 'PG_CNT', 'ALLOC_CNT', 'ZONE', 'NAME'))
+def GetZoneMetadataSummary(meta):
+    """ Summarize a zone metadata object
+        params: meta - obj representing zone metadata in the kernel
+        returns: str - summary of the zone metadata
+    """
+
+    if type(meta) != ZoneMeta:
+        meta = ZoneMeta(meta)
+
+    out_str = 'Metadata Description:\n' + GetZoneMetadataSummary.header + '\n'
+    if meta.isSecondaryPage():
+        out_str += "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}\n".format(
+                meta.meta_addr, 0, 0, 0, 0, '(fake multipage meta)')
+        meta = meta.getReal()
+    zinfo = meta.getZone()
+    out_str += "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}".format(
+            meta.meta_addr, meta.getFreeList(), meta.getPageCount(), meta.getAllocCount(),
+            addressof(zinfo), ZoneName(zinfo))
+    return out_str
+
+@header("{:<18s} {:>10s}  {:>18s}  {:>18s} {:<10s}".format(
+    'ADDRESS', 'TYPE', 'METADATA', 'PAGE_ADDR', 'OFFSET'))
+def WhatIs(addr):
+    """ Information about kernel pointer
+    """
+    global kern
+
+    meta = ZoneMeta(addr)
+
+    if meta.meta is None:
+        out_str = "Address {:#018x} is outside of any zone map ({:#018x}-{:#018x})\n".format(
+                addr, meta.zone_map_min, meta.zone_map_max)
+    else:
+        if meta.kind[0] == 'E': # element
+            page_offset_str = "{:d}/{:d}K".format(
+                    addr - meta.page_addr, kern.globals.page_size / 1024)
+        else:
+            page_offset_str = "-"
+        out_str = WhatIs.header + '\n'
+        out_str += "{meta.address:#018x} {meta.kind:>10s}  {meta.meta_addr:#018x}  {meta.page_addr:#018x} {:<10s}\n\n".format(
+                page_offset_str, meta=meta)
+        out_str += GetZoneMetadataSummary(meta) + '\n\n'
+
+    print out_str
+
+    if meta.kind[0] == 'E':
+        print "Hexdump:\n"
+
+        meta  = meta.getReal()
+        esize = meta.getZone().z_elem_size
+        start = meta.page_addr
+
+        estart = addr - (start - meta.first_offset)
+        estart = start + estart - (estart % esize)
+
+        try:
+            if estart > start:
+                data_array = kern.GetValueFromAddress(estart - 16, "uint8_t *")
+                print_hex_data(data_array[0:16], estart - 16, "")
+                print "------------------------------------------------------------------"
+        except:
+            pass
+
+        try:
+            data_array = kern.GetValueFromAddress(estart, "uint8_t *")
+            print_hex_data(data_array[0:esize], estart, "")
+        except:
+            pass
+
+        try:
+            print "------------------------------------------------------------------"
+            data_array = kern.GetValueFromAddress(estart + esize, "uint8_t *")
+            print_hex_data(data_array[0:16], estart + esize, "")
+        except:
+            pass
+
+@lldb_command('whatis')
+def WhatIsHelper(cmd_args=None):
+    """ Routine to show information about a kernel pointer
+        Usage: whatis <address>
+    """
+    if not cmd_args:
+        raise ArgumentError("No arguments passed")
+    WhatIs(kern.GetValueFromAddress(cmd_args[0], 'void *'))
+
+# Macro: showzcache
+
+@lldb_type_summary(['zone','zone_t'])
+@header("{:<18s}  {:>5s}   {:>10s}  {:>12s} {:>12s} {:>9s} {:>9s} {:>9s} {:>9s} {:>9s}  {:<20s}".format(
+'ZONE', 'ELTS', 'D FULL/EMPTY', 'ALLOCS', 'FREES', 'D_SWAP', 'D_FILL', 'D_DRAIN', 'D_GC', 'D_FAIL', 'NAME'))
+
+def GetZoneCacheSummary(zone, O):
+    """ Summarize a zone's cache with important information.
+        params:
+          zone: value - obj representing a zone in kernel
+        returns:
+          str - summary of the zone's cache contents
+    """
+    format_string = '{:#018x}  {:>5d}    {:>4d} / {:>4d}  {:>12,d} {:>12,d} {:>9,d} {:>9,d} {:>9,d} {:>9,d} {:>9,d}  {:<20s}'
+    mag_capacity = kern.GetGlobalVariable('magazine_element_count')
+    depot_capacity = kern.GetGlobalVariable('depot_element_count')
+
+    cache_elem_count = 0
+    allocs = 0
+    frees  = 0
+
+    if zone.__getattr__('cpu_cache_enabled') :
+        for cache in IterateZPerCPU(zone.zcache.zcc_pcpu, 'struct zcc_per_cpu_cache *'):
+            cache_elem_count += cache.current.zcc_magazine_index
+            cache_elem_count += cache.previous.zcc_magazine_index
+            allocs += cache.zcc_allocs
+            frees  += cache.zcc_frees
+
+        depot = zone.zcache.zcc_depot
+        cache_elem_count += depot.zcc_depot_index * mag_capacity
+        print O.format(format_string, zone, cache_elem_count,
+                depot.zcc_depot_index, depot_capacity - depot.zcc_depot_index,
+                allocs, frees, depot.zcc_swap, depot.zcc_fill, depot.zcc_drain,
+                depot.zcc_gc, depot.zcc_fail, ZoneName(zone))
+
+@lldb_command('showzcache', fancy=True)
+def ZcachePrint(cmd_args=None, cmd_options={}, O=None):
+    """ Routine to print a summary listing of all the kernel zones cache contents
+    All columns are printed in decimal
+    """
+    global kern
+    with O.table(GetZoneCacheSummary.header):
+        for zval in kern.zones:
+            if zval.__getattr__('cpu_cache_enabled') :
+                GetZoneCacheSummary(zval, O)
+
+# EndMacro: showzcache
+
+# Macro: showzcachecpu
+
+@lldb_type_summary(['zone','zone_t'])
+@header("{:18s}  {:32s}  {:<10s}  {:<10s}".format(
+'ZONE', 'NAME', 'CACHE_ELTS', 'CPU_INFO'))
+
+def GetZoneCacheCPUSummary(zone, O):
+    """ Summarize a zone's cache broken up per cpu
+        params:
+          zone: value - obj representing a zone in kernel
+        returns:
+          str - summary of the zone's per CPU cache contents
+    """
+    format_string = '{:#018x}  {:32s}  {:10d}  {cpuinfo:s}'
+    cache_elem_count = 0
+    cpu_info = ""
+    per_cpu_count = 0
+    mag_capacity = kern.GetGlobalVariable('magazine_element_count')
+    depot_capacity = kern.GetGlobalVariable('depot_element_count')
+
+    if zone.__getattr__('cpu_cache_enabled') :
+        i = 0
+        for cache in IterateZPerCPU(zone.zcache.zcc_pcpu, 'struct zcc_per_cpu_cache *'):
+            if i is not 0:
+                cpu_info += ", "
+            per_cpu_count = cache.current.zcc_magazine_index
+            per_cpu_count += cache.previous.zcc_magazine_index
+            cache_elem_count += per_cpu_count
+            cpu_info += "CPU {:d}: {:5}".format(i,per_cpu_count)
+            i += 1
+        cache_elem_count += zone.zcache.zcc_depot.zcc_depot_index * mag_capacity
+
+    print O.format(format_string, zone, ZoneName(zone), cache_elem_count,cpuinfo = cpu_info)
+
+@lldb_command('showzcachecpu', fancy=True)
+def ZcacheCPUPrint(cmd_args=None, cmd_options={}, O=None):
+    """ Routine to print a summary listing of all the kernel zones cache contents
+    All columns are printed in decimal
+    """
+    global kern
+    with O.table(GetZoneCacheCPUSummary.header):
+        for zval in kern.zones:
+            if zval.__getattr__('cpu_cache_enabled'):
+                GetZoneCacheCPUSummary(zval, O)
+
+# EndMacro: showzcachecpu
+
 # Macro: zprint
 
 # Macro: zprint
 
+def GetZone(zone_val, marks):
+    """ Internal function which gets a phython dictionary containing important zone information.
+        params:
+          zone_val: value - obj representing a zone in kernel
+        returns:
+          zone - python dictionary with zone stats
+    """
+    pagesize = kern.globals.page_size
+    zone = {}
+    zone["free_size"] = zone_val.countfree * zone_val.pcpu_elem_size
+    mag_capacity = kern.GetGlobalVariable('magazine_element_count')
+    zone["page_count"] = unsigned(zone_val.page_count)
+    zone["allfree_page_count"] = unsigned(zone_val.allfree_page_count)
+
+    zone["size"] = zone_val.page_count * pagesize
+    zone["used_size"] = zone["size"] - zone["free_size"]
+    zone["element_count"] = zone_val.countavail - zone_val.countfree
+
+    if zone_val.percpu:
+        zone["allocation_size"] = unsigned(pagesize)
+        zone["allocation_ncpu"] = unsigned(zone_val.alloc_pages)
+    else:
+        zone["allocation_size"] = unsigned(zone_val.alloc_pages * pagesize)
+        zone["allocation_ncpu"] = 1
+    zone["allocation_count"] = zone["allocation_size"] / zone_val.z_elem_size
+    zone["allocation_waste"] = (zone["allocation_size"] % zone_val.z_elem_size) * zone["allocation_ncpu"]
+    
+    if not zone_val.__getattr__("z_self") :
+        zone["destroyed"] = True
+    else:
+        zone["destroyed"] = False
+
+    for mark in marks:
+        if zone_val.__getattr__(mark[0]):
+            zone[mark[0]] = True
+        else:
+            zone[mark[0]] = False
+
+    cache_elem_count = 0
+    if zone_val.__getattr__('cpu_cache_enabled') :
+        for cache in IterateZPerCPU(zone_val.zcache.zcc_pcpu, 'struct zcc_per_cpu_cache *'):
+            cache_elem_count += cache.current.zcc_magazine_index
+            cache_elem_count += cache.previous.zcc_magazine_index
+        cache_elem_count += zone_val.zcache.zcc_depot.zcc_depot_index * mag_capacity
+    zone["cache_element_count"] = cache_elem_count
+    zone["name"] = ZoneName(zone_val)
+    if zone_val.exhaustible:
+        zone["exhaustible"] = True
+    else:
+        zone["exhaustible"] = False
+
+    zone["sequester_page_count"] = unsigned(zone_val.sequester_page_count)
+    zone["page_count_max"] = unsigned(zone_val.page_count_max)
+
+    return zone
+
+
 @lldb_type_summary(['zone','zone_t'])
 @lldb_type_summary(['zone','zone_t'])
-@header("{:^18s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s}({:>6s} {:>6s} {:>6s}) {:^14s} {:<20s}".format(
-'ZONE', 'TOT_SZ', 'PAGE_COUNT', 'ALLOC_ELTS', 'FREE_ELTS', 'FREE_SZ', 'ELT_SZ', 'ALLOC', 'ELTS', 'PGS', 'SLK', 'FLAGS', 'NAME'))
-def GetZoneSummary(zone):
+@header(("{:<18s}  {:_^35s}  {:_^24s}  {:_^13s}  {:_^28s}\n"+
+"{:<18s}  {:>11s} {:>11s} {:>11s}  {:>8s} {:>7s} {:>7s}  {:>6s} {:>6s}  {:>8s} {:>6s} {:>5s} {:>7s}   {:<18s} {:<20s}").format(
+'', 'SIZE (bytes)', 'ELEMENTS (#)', 'PAGES', 'ALLOC CHUNK CONFIG',
+'ZONE', 'TOTAL', 'ALLOC', 'FREE', 'ALLOC', 'FREE', 'CACHE', 'COUNT', 'FREE', 'SIZE (P)', 'ELTS', 'WASTE', 'ELT_SZ', 'FLAGS', 'NAME'))
+def GetZoneSummary(zone_val, marks, stats):
     """ Summarize a zone with important information. See help zprint for description of each field
     """ Summarize a zone with important information. See help zprint for description of each field
-        params: 
-          zone: value - obj representing a zone in kernel
-        returns: 
+        params:
+          zone_val: value - obj representing a zone in kernel
+        returns:
           str - summary of the zone
     """
           str - summary of the zone
     """
+    pagesize = kern.globals.page_size
     out_string = ""
     out_string = ""
-    format_string = '{:#018x} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:6d} {:6d} {:6d}  {markings} {name:s} ' 
-    pagesize = 4096
-    
-    free_elements = (zone.cur_size / zone.elem_size) - zone.count
-    free_size = free_elements * zone.elem_size
-    
-    alloc_count = zone.alloc_size / zone.elem_size
-    alloc_pages = zone.alloc_size / pagesize
-    alloc_slack = zone.alloc_size % zone.elem_size
-    marks = [
-            ["collectable",        "C"],
-            ["expandable",         "X"],
-            ["noencrypt",          "$"],
-            ["caller_acct",        "@"],
-            ["exhaustible",        "H"],
-            ["allows_foreign",     "F"],
-            ["async_prio_refill",  "R"],
-            ["no_callout",         "O"],
-            ["zleak_on",           "L"],
-            ["doing_alloc",        "A"],
-            ["waiting",            "W"],
-            ["doing_gc",           "G"]
-            ]
-    if kern.arch == 'x86_64':
-        marks.append(["gzalloc_exempt",     "M"])
-        marks.append(["alignment_required", "N"])
-        
+    zone = GetZone(zone_val, marks)
+
+    format_string  = '{zone:#018x}  {cur_size:11,d} {used_size:11,d} {free_size:11,d}  '
+    format_string += '{count_elts:8,d} {zone.countfree:7,d} {cache_elem_count:7,d}  '
+    format_string += '{zone.page_count:6,d} {zone.allfree_page_count:6,d}  '
+    format_string += '{alloc_size_kb:3,d}K ({zone.alloc_pages:d}) {alloc_count:6,d} {alloc_waste:5,d} {zone.pcpu_elem_size:7,d}   '
+    format_string += '{markings:<18s} {zone_name:<20s}'
+
     markings=""
     markings=""
+    if zone["destroyed"]:
+        markings+="I"
+        
     for mark in marks:
     for mark in marks:
-        if zone.__getattr__(mark[0]) :
-            markings+=mark[1]
+        if zone[mark[0]]:
+            markings += mark[1]
         else:
             markings+=" "
         else:
             markings+=" "
-    out_string += format_string.format(zone, zone.cur_size, zone.page_count,
-                    zone.count, free_elements, free_size,
-                    zone.elem_size, zone.alloc_size, alloc_count,
-                    alloc_pages, alloc_slack, name = zone.zone_name, markings=markings)
-    
-    if zone.exhaustible :
-            out_string += "(max: {:d})".format(zone.max_size)
-            
+
+    alloc_size_kb = zone["allocation_size"] / 1024
+    out_string += format_string.format(zone=zone_val, free_size=zone["free_size"], used_size=zone["used_size"],
+            cur_size=zone["size"], count_elts=zone["element_count"], cache_elem_count=zone["cache_element_count"],
+            alloc_count=zone["allocation_count"], alloc_size_kb=alloc_size_kb, alloc_waste=zone["allocation_waste"],
+            markings=markings, zone_name=zone["name"])
+
+    if zone["exhaustible"] :
+            out_string += " (max: {:d})".format(zone["page_count_max"] * pagesize)
+
+    if zone["sequester_page_count"] != 0 :
+            out_string += " (sequester: {:d})".format(zone["sequester_page_count"])
+
+    stats["cur_size"] += zone["size"]
+    stats["used_size"] += zone["used_size"]
+    stats["free_size"] += zone["free_size"]
+    stats["cur_pages"] += zone["page_count"]
+    stats["free_pages"] += zone["allfree_page_count"]
+    stats["seq_pages"] += zone["sequester_page_count"]
+
     return out_string
 
     return out_string
 
-@lldb_command('zprint')
-def Zprint(cmd_args=None):
+@lldb_command('zprint', "J", fancy=True)
+def Zprint(cmd_args=None, cmd_options={}, O=None):
     """ Routine to print a summary listing of all the kernel zones
     """ Routine to print a summary listing of all the kernel zones
+        usage: zprint -J
+                Output json
     All columns are printed in decimal
     Legend:
         C - collectable
     All columns are printed in decimal
     Legend:
         C - collectable
+        D - destructible
         X - expandable
         $ - not encrypted during hibernation
         X - expandable
         $ - not encrypted during hibernation
-        @ - allocs and frees are accounted to caller process for KPRVT
         H - exhaustible
         H - exhaustible
-        F - allows foreign memory (memory not allocated from zone_map)
+        F - allows foreign memory (memory not allocated from any zone map)
         M - gzalloc will avoid monitoring this zone
         R - will be refilled when below low water mark
         O - does not allow refill callout to fill zone on noblock allocation
         N - zone requires alignment (avoids padding this zone for debugging)
         M - gzalloc will avoid monitoring this zone
         R - will be refilled when below low water mark
         O - does not allow refill callout to fill zone on noblock allocation
         N - zone requires alignment (avoids padding this zone for debugging)
-        A - currently trying to allocate more backing memory from kernel_memory_allocate
+        A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv
+        S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv
         W - another thread is waiting for more memory
         W - another thread is waiting for more memory
+        E - Per-cpu caching is enabled for this zone
         L - zone is being monitored by zleaks
         G - currently running GC
         L - zone is being monitored by zleaks
         G - currently running GC
+        I - zone was destroyed and is no longer valid
     """
     global kern
     """
     global kern
-    print GetZoneSummary.header
-    for zval in kern.zones:
-        print GetZoneSummary(zval)
+
+    marks = [
+            ["collectable",          "C"],
+            ["destructible",         "D"],
+            ["expandable",           "X"],
+            ["noencrypt",            "$"],
+            ["exhaustible",          "H"],
+            ["allows_foreign",       "F"],
+            ["prio_refill_count",    "R"],
+            ["no_callout",           "O"],
+            ["zleak_on",             "L"],
+            ["expanding_no_vm_priv", "A"],
+            ["expanding_vm_priv",    "S"],
+            ["waiting",              "W"],
+            ["cpu_cache_enabled",    "E"],
+            ["gzalloc_exempt",       "M"],
+            ["alignment_required",   "N"],
+            ["va_sequester",         "!"]
+            ]
+    stats = {
+        "cur_size": 0, "used_size": 0, "free_size": 0,
+        "cur_pages": 0, "free_pages": 0, "seq_pages": 0
+    }
+
+    print_json = False
+    if "-J" in cmd_options:
+        print_json = True
+
+    if print_json:
+        zones = []
+        for zval in kern.zones:
+            if zval.z_self:
+                zones.append(GetZone(zval, marks))
+
+        print json.dumps(zones)
+    else:
+        with O.table(GetZoneSummary.header):
+            for zval in kern.zones:
+                if zval.z_self:
+                    print GetZoneSummary(zval, marks, stats)
+
+            format_string  = '{VT.Bold}{name:19s} {stats[cur_size]:11,d} {stats[used_size]:11,d} {stats[free_size]:11,d} '
+            format_string += '                           '
+            format_string += '{stats[cur_pages]:6,d} {stats[free_pages]:6,d}{VT.EndBold}  '
+            format_string += '(sequester: {VT.Bold}{stats[seq_pages]:,d}{VT.EndBold})'
+            print O.format(format_string, name="TOTALS", filler="", stats=stats)
+
 
 @xnudebug_test('test_zprint')
 def TestZprint(kernel_target, config, lldb_obj, isConnected ):
     """ Test the functionality of zprint command
 
 @xnudebug_test('test_zprint')
 def TestZprint(kernel_target, config, lldb_obj, isConnected ):
     """ Test the functionality of zprint command
-        returns 
+        returns
          - False on failure
          - False on failure
-         - True on success 
+         - True on success
     """
     if not isConnected:
         print "Target is not connected. Cannot test memstats"
     """
     if not isConnected:
         print "Target is not connected. Cannot test memstats"
@@ -220,7 +716,7 @@ def TestZprint(kernel_target, config, lldb_obj, isConnected ):
     result = res.GetOutput()
     if len(result.split("\n")) > 2:
         return True
     result = res.GetOutput()
     if len(result.split("\n")) > 2:
         return True
-    else: 
+    else:
         return False
 
 
         return False
 
 
@@ -236,10 +732,14 @@ def ShowZfreeListHeader(zone):
         returns:
             None
     """
         returns:
             None
     """
+
+    scaled_factor = (unsigned(kern.globals.zp_factor) +
+            (unsigned(zone.z_elem_size) >> unsigned(kern.globals.zp_scale)))
+
     out_str = ""
     out_str += "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
     out_str += "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
     out_str = ""
     out_str += "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
     out_str += "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
-                zone.elem_size, zone.count, kern.globals.zp_nopoison_cookie, kern.globals.zp_poisoned_cookie, zone.zp_count, kern.globals.zp_factor)
+                zone.z_elem_size, zone.countavail - zone.countfree, kern.globals.zp_nopoison_cookie, kern.globals.zp_poisoned_cookie, zone.zp_count, scaled_factor)
     out_str += "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
                 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
     print out_str
     out_str += "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
                 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
     print out_str
@@ -257,7 +757,9 @@ def ShowZfreeListChain(zone, zfirst, zlimit):
     while ShowZfreeList.elts_found < zlimit:
         ShowZfreeList.elts_found += 1
         znext = dereference(Cast(current, 'vm_offset_t *'))
     while ShowZfreeList.elts_found < zlimit:
         ShowZfreeList.elts_found += 1
         znext = dereference(Cast(current, 'vm_offset_t *'))
-        backup_ptr = kern.GetValueFromAddress((unsigned(Cast(current, 'vm_offset_t')) + unsigned(zone.elem_size) - sizeof('vm_offset_t')), 'vm_offset_t *')
+        znext = (unsigned(znext) ^ unsigned(kern.globals.zp_nopoison_cookie))
+        znext = kern.GetValueFromAddress(znext, 'vm_offset_t *')
+        backup_ptr = kern.GetValueFromAddress((unsigned(Cast(current, 'vm_offset_t')) + unsigned(zone.z_elem_size) - sizeof('vm_offset_t')), 'vm_offset_t *')
         backup_val = dereference(backup_ptr)
         n_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_nopoison_cookie))
         p_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_poisoned_cookie))
         backup_val = dereference(backup_ptr)
         n_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_nopoison_cookie))
         p_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_poisoned_cookie))
@@ -269,11 +771,18 @@ def ShowZfreeListChain(zone, zfirst, zlimit):
             if n_unobfuscated != unsigned(znext):
                 poison_str = "INVALID"
         print "{0: <7d} 0x{1:0>16x} 0x{2:0>16x} 0x{3:0>16x} 0x{4:0>16x} 0x{5:0>16x} {6: <14s}\n".format(
             if n_unobfuscated != unsigned(znext):
                 poison_str = "INVALID"
         print "{0: <7d} 0x{1:0>16x} 0x{2:0>16x} 0x{3:0>16x} 0x{4:0>16x} 0x{5:0>16x} {6: <14s}\n".format(
-              ShowZfreeList.elts_found, unsigned(current), unsigned(znext), unsigned(backup_val), n_unobfuscated, p_unobfuscated, poison_str)
+              ShowZfreeList.elts_found, unsigned(current), unsigned(znext),
+              unsigned(backup_val), n_unobfuscated, p_unobfuscated, poison_str)
         if unsigned(znext) == 0:
             break
         current = Cast(znext, 'void *')
 
         if unsigned(znext) == 0:
             break
         current = Cast(znext, 'void *')
 
+def ZoneIteratePageQueue(page):
+    while page.packed_address:
+        meta = ZoneMeta(page.packed_address, isPageIndex=True)
+        yield meta
+        page = meta.meta.zm_page_next
+
 @static_var('elts_found',0)
 @static_var('last_poisoned',0)
 @lldb_command('showzfreelist')
 @static_var('elts_found',0)
 @static_var('last_poisoned',0)
 @lldb_command('showzfreelist')
@@ -295,31 +804,14 @@ def ShowZfreeList(cmd_args=None):
         zlimit = ArgumentStringToInt(cmd_args[1])
     ShowZfreeListHeader(zone)
 
         zlimit = ArgumentStringToInt(cmd_args[1])
     ShowZfreeListHeader(zone)
 
-    if unsigned(zone.use_page_list) == 1:
-        if unsigned(zone.allows_foreign) == 1:
-            for free_page_meta in IterateQueue(zone.pages.any_free_foreign, 'struct zone_page_metadata *', 'pages'):
-                if ShowZfreeList.elts_found == zlimit:
-                    break
-                zfirst = Cast(free_page_meta.elements, 'void *')
-                if unsigned(zfirst) != 0:
-                    ShowZfreeListChain(zone, zfirst, zlimit)
-        for free_page_meta in IterateQueue(zone.pages.intermediate, 'struct zone_page_metadata *', 'pages'):
-            if ShowZfreeList.elts_found == zlimit:
-                break
-            zfirst = Cast(free_page_meta.elements, 'void *')
-            if unsigned(zfirst) != 0:
-                ShowZfreeListChain(zone, zfirst, zlimit)
-        for free_page_meta in IterateQueue(zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
+    for head in [zone.pages_any_free_foreign, zone.pages_intermediate, zone.pages_all_free]:
+        for free_page_meta in ZoneIteratePageQueue(head):
             if ShowZfreeList.elts_found == zlimit:
                 break
             if ShowZfreeList.elts_found == zlimit:
                 break
-            zfirst = Cast(free_page_meta.elements, 'void *')
-            if unsigned(zfirst) != 0:
+            zfirst = free_page_meta.getFreeList()
+            if zfirst != 0:
                 ShowZfreeListChain(zone, zfirst, zlimit)
                 ShowZfreeListChain(zone, zfirst, zlimit)
-    else:
-        zfirst = Cast(zone.free_elements, 'void *')
-        if unsigned(zfirst) != 0:
-            ShowZfreeListChain(zone, zfirst, zlimit)
-    
+
     if ShowZfreeList.elts_found == zlimit:
         print "Stopped at {0: <d} elements!".format(zlimit)
     else:
     if ShowZfreeList.elts_found == zlimit:
         print "Stopped at {0: <d} elements!".format(zlimit)
     else:
@@ -327,20 +819,28 @@ def ShowZfreeList(cmd_args=None):
 
 # EndMacro: showzfreelist
 
 
 # EndMacro: showzfreelist
 
+# Macro: zstack_showzonesbeinglogged
+
+@lldb_command('zstack_showzonesbeinglogged')
+def ZstackShowZonesBeingLogged(cmd_args=None):
+    """ Show all zones which have BTLog enabled.
+    """
+    global kern
+    for zval in kern.zones:
+        if zval.zlog_btlog:
+          print "Zone: %s with its BTLog at: 0x%lx" % (ZoneName(zval), zval.zlog_btlog)
+
+# EndMacro: zstack_showzonesbeinglogged
+
 # Macro: zstack
 
 @lldb_command('zstack')
 def Zstack(cmd_args=None):
 # Macro: zstack
 
 @lldb_command('zstack')
 def Zstack(cmd_args=None):
-    """ Zone leak debugging: Print the stack trace of log element at <index>. If a <count> is supplied, it prints <count> log elements starting at <index>.
-        Usage: zstack <index> [<count>]
+    """ Zone leak debugging: Print the stack trace logged at <index> in the stacks list. If a <count> is supplied, it prints <count> stacks starting at <index>.
+        Usage: zstack <btlog addr> <index> [<count>]
 
 
-        The suggested usage is to look at indexes below zcurrent and look for common stack traces.
-        The stack trace that occurs the most is probably the cause of the leak.  Find the pc of the
-        function calling into zalloc and use the countpcs command to find out how often that pc occurs in the log.
-        The pc occuring in a high percentage of records is most likely the source of the leak.
-        
-        The findoldest command is also useful for leak debugging since it identifies the oldest record
-        in the log, which may indicate the leaker.
+        The suggested usage is to look at stacks with high percentage of refs (maybe > 25%).
+        The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that.
     """
     if not cmd_args:
         print Zstack.__doc__
     """
     if not cmd_args:
         print Zstack.__doc__
@@ -348,97 +848,125 @@ def Zstack(cmd_args=None):
     if int(kern.globals.log_records) == 0:
         print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
         return
     if int(kern.globals.log_records) == 0:
         print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
         return
-    if int(kern.globals.zlog_btlog) == 0:
-        print "Zone logging enabled, but zone has not been initialized yet."
-        return
 
 
+    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
+    btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
+    btrecord_size = unsigned(btlog_ptr.btrecord_size)
+    btrecords = unsigned(btlog_ptr.btrecords)
+    btlog_size = unsigned(sizeof('struct btlog'))
+    depth = unsigned(btlog_ptr.btrecord_btdepth)
+    zstack_index = ArgumentStringToInt(cmd_args[1])
     count = 1
     count = 1
-    if len(cmd_args) >= 2:
-        count = ArgumentStringToInt(cmd_args[1])
-    zstack_index = unsigned(cmd_args[0])
+    if len(cmd_args) >= 3:
+        count = ArgumentStringToInt(cmd_args[2])
+
+    max_count = ((btrecords_total_size - btlog_size)/btrecord_size)
+
+    if (zstack_index + count) > max_count:
+       count = max_count - zstack_index
+
     while count and (zstack_index != 0xffffff):
     while count and (zstack_index != 0xffffff):
-        zstack_record_offset = zstack_index * unsigned(kern.globals.zlog_btlog.btrecord_size)
-        zstack_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + zstack_record_offset, 'btlog_record_t *')
-        ShowZStackRecord(zstack_record, zstack_index)
-        zstack_index = zstack_record.next
+        zstack_record_offset = zstack_index * btrecord_size
+        zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
+        if int(zstack_record.ref_count)!=0:
+           ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
+        zstack_index += 1
         count -= 1
 
 # EndMacro : zstack
 
         count -= 1
 
 # EndMacro : zstack
 
-# Macro: findoldest
+# Macro: zstack_inorder
 
 
-@lldb_command('findoldest')
-def FindOldest(cmd_args=None):
-    """ Zone leak debugging: find and print the oldest record in the log.
-        
-        Once it prints a stack trace, find the pc of the caller above all the zalloc, kalloc and
-        IOKit layers.  Then use the countpcs command to see how often this caller has allocated
-        memory.  A caller with a high percentage of records in the log is probably the leaker.
+@lldb_command('zstack_inorder')
+def ZstackInOrder(cmd_args=None):
+    """ Zone leak debugging: Print the stack traces starting from head to the tail.
+        Usage: zstack_inorder <btlog addr>
     """
     """
-    if int(kern.globals.log_records) == 0:
-        print FindOldest.__doc__
+    if not cmd_args:
+        print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>"
         return
         return
-    if int(kern.globals.zlog_btlog) == 0:
-        print "Zone logging enabled, but zone has not been initialized yet."
+    if int(kern.globals.log_records) == 0:
+        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
         return
         return
-    index = kern.globals.zlog_btlog.head
-    if unsigned(index) != 0xffffff:
-        print "Oldest record is at log index: {0: <d}".format(index)
-        Zstack([index])
-    else:
-        print "No Records Present"
 
 
+    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
+    btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
+    btrecord_size = unsigned(btlog_ptr.btrecord_size)
+    btrecords = unsigned(btlog_ptr.btrecords)
+    btlog_size = unsigned(sizeof('struct btlog'))
+    depth = unsigned(btlog_ptr.btrecord_btdepth)
+    zstack_head = unsigned(btlog_ptr.head)
+    zstack_index = zstack_head
+    zstack_tail = unsigned(btlog_ptr.tail)
+    count = ((btrecords_total_size - btlog_size)/btrecord_size)
+
+    while count and (zstack_index != 0xffffff):
+        zstack_record_offset = zstack_index * btrecord_size
+        zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
+        ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
+        zstack_index = zstack_record.next
+        count -= 1
+
+# EndMacro : zstack_inorder
+
+# Macro: findoldest
+
+@lldb_command('findoldest')
+def FindOldest(cmd_args=None):
+    """
+    """
+    print "***** DEPRECATED ***** use 'zstack_findleak' macro instead."
+    return
 # EndMacro : findoldest
 
 # EndMacro : findoldest
 
-# Macro: countpcs
+# Macro : zstack_findleak
 
 
-@lldb_command('countpcs')
-def Countpcs(cmd_args=None):
-    """ Zone leak debugging: search the log and print a count of all log entries that contain the given <pc>
+@lldb_command('zstack_findleak')
+def zstack_findleak(cmd_args=None):
+    """ Zone leak debugging: search the log and print the stack with the most active references
         in the stack trace.
         in the stack trace.
-        Usage: countpcs <pc>
+        Usage: zstack_findleak <btlog address>
 
 
-        This is useful for verifying a suspected <pc> as being the source of
-        the leak.  If a high percentage of the log entries contain the given <pc>, then it's most
-        likely the source of the leak.  Note that this command can take several minutes to run.
+        This is useful for verifying a suspected stack as being the source of
+        the leak.
     """
     """
-    if not cmd_args:
-        print Countpcs.__doc__
-        return
-    if int(kern.globals.log_records) == 0:
-        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
-        return
-    if int(kern.globals.zlog_btlog) == 0:
-        print "Zone logging enabled, but zone has not been initialized yet."
-        return
-    
-    cpcs_index = unsigned(kern.globals.zlog_btlog.head)
-    target_pc = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *'))
-    found = 0
-    depth = unsigned(kern.globals.zlog_btlog.btrecord_btdepth)
+    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
+    btrecord_size = unsigned(btlog_ptr.btrecord_size)
+    btrecords = unsigned(btlog_ptr.btrecords)
+
+    cpcs_index = unsigned(btlog_ptr.head)
+    depth = unsigned(btlog_ptr.btrecord_btdepth)
+    highref = 0
+    highref_index = 0
+    highref_record = 0
 
     while cpcs_index != 0xffffff:
 
     while cpcs_index != 0xffffff:
-        cpcs_record_offset = cpcs_index * unsigned(kern.globals.zlog_btlog.btrecord_size)
-        cpcs_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + cpcs_record_offset, 'btlog_record_t *')
-        frame = 0
-        while frame < depth:
-            frame_pc = unsigned(cpcs_record.bt[frame])
-            if frame_pc == target_pc:
-                found += 1
-                break
-            frame += 1
+        cpcs_record_offset = cpcs_index * btrecord_size
+        cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
+        if cpcs_record.ref_count > highref:
+                highref_record = cpcs_record
+                highref = cpcs_record.ref_count
+                highref_index = cpcs_index
         cpcs_index = cpcs_record.next
         cpcs_index = cpcs_record.next
-    print "Occured {0: <d} times in log ({1: <d}{2: <s} of records)".format(found, (found * 100)/unsigned(kern.globals.zlog_btlog.activecount), '%')
+    ShowZStackRecord(highref_record, highref_index, depth, unsigned(btlog_ptr.active_element_count))
 
 
-# EndMacro: countpcs
+# EndMacro: zstack_findleak
 
 # Macro: findelem
 
 @lldb_command('findelem')
 def FindElem(cmd_args=None):
 
 # Macro: findelem
 
 @lldb_command('findelem')
 def FindElem(cmd_args=None):
-    """ Zone corruption debugging: search the log and print out the stack traces for all log entries that
-        refer to the given zone element.  
-        Usage: findelem <elem addr>
+    """
+    """
+    print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
+    return
+# EndMacro: findelem
+
+@lldb_command('zstack_findelem')
+def ZStackFindElem(cmd_args=None):
+    """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that
+        refer to the given zone element.
+        Usage: zstack_findelem <btlog addr> <elem addr>
 
         When the kernel panics due to a corrupted zone element, get the
         element address and use this command.  This will show you the stack traces of all logged zalloc and
 
         When the kernel panics due to a corrupted zone element, get the
         element address and use this command.  This will show you the stack traces of all logged zalloc and
@@ -446,66 +974,97 @@ def FindElem(cmd_args=None):
         double-frees readily apparent.
     """
     if not cmd_args:
         double-frees readily apparent.
     """
     if not cmd_args:
-        print FindElem.__doc__
-        return
-    if int(kern.globals.log_records) == 0:
-        print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
+        print ZStackFindElem.__doc__
         return
         return
-    if int(kern.globals.zlog_btlog) == 0:
-        print "Zone logging enabled, but zone has not been initialized yet."
+    if int(kern.globals.log_records) == 0 or unsigned(kern.globals.corruption_debug_flag) == 0:
+        print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args."
         return
         return
-  
-    target_element = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *'))
-    index = unsigned(kern.globals.zlog_btlog.head)
-    prev_op = -1
 
 
-    while index != 0xffffff:
-        findelem_record_offset = index * unsigned(kern.globals.zlog_btlog.btrecord_size)
-        findelem_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + findelem_record_offset, 'btlog_record_t *')
-        if unsigned(findelem_record.element) == target_element:
-            Zstack([index])
-            if int(findelem_record.operation) == prev_op:
+    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
+    target_element = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
+
+    btrecord_size = unsigned(btlog_ptr.btrecord_size)
+    btrecords = unsigned(btlog_ptr.btrecords)
+    depth = unsigned(btlog_ptr.btrecord_btdepth)
+
+    prev_op = -1
+    scan_items = 0
+    hashelem = cast(btlog_ptr.elem_linkage_un.element_hash_queue.tqh_first, 'btlog_element_t *')
+    if (target_element >> 32) != 0:
+        target_element = target_element ^ 0xFFFFFFFFFFFFFFFF
+    else:
+        target_element = target_element ^ 0xFFFFFFFF
+    while hashelem != 0:
+        if unsigned(hashelem.elem) == target_element:
+            recindex = hashelem.recindex
+            recoffset = recindex * btrecord_size
+            record = kern.GetValueFromAddress(btrecords + recoffset, 'btlog_record_t *')
+            out_str = ('-' * 8)
+            if record.operation == 1:
+               out_str += "OP: ALLOC. "
+            else:
+               out_str += "OP: FREE.  "
+            out_str += "Stack Index {0: <d} {1: <s}\n".format(recindex, ('-' * 8))
+            print out_str
+            print GetBtlogBacktrace(depth, record)
+            print " \n"
+            if int(record.operation) == prev_op:
                 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
                 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
-                prev_op = int(findelem_record.operation)
-        index = findelem_record.next
+                return
+            prev_op = int(record.operation)
+            scan_items = 0
+        hashelem = cast(hashelem.element_hash_link.tqe_next, 'btlog_element_t *')
+        scan_items += 1
+        if scan_items % 100 == 0:
+           print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items)
+
+# EndMacro: zstack_findelem
+
+@lldb_command('zstack_findtop', 'N:')
+def ShowZstackTop(cmd_args=None, cmd_options={}):
+    """ Zone leak debugging: search the log and print the stacks with the most active references
+        in the stack trace.
 
 
-# EndMacro: findelem
+        Usage: zstack_findtop [-N <n-stacks>] <btlog-addr>
+    """
+
+    if not cmd_args:
+        raise ArgumentError('Missing required btlog address argument')
+
+    n = 5
+    if '-N' in cmd_options:
+        n = int(cmd_options['-N'])
+
+    btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
+    btrecord_size = unsigned(btlog_ptr.btrecord_size)
+    btrecords = unsigned(btlog_ptr.btrecords)
+
+    cpcs_index = unsigned(btlog_ptr.head)
+    depth = unsigned(btlog_ptr.btrecord_btdepth)
+
+    records = []
+    while cpcs_index != 0xffffff:
+        cpcs_record_offset = cpcs_index * btrecord_size
+        cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
+        cpcs_record.index = cpcs_index
+        records.append(cpcs_record)
+        cpcs_index = cpcs_record.next
+
+    recs = sorted(records, key=lambda x: x.ref_count, reverse=True)
+
+    for rec in recs[:n]:
+        ShowZStackRecord(rec, rec.index, depth, unsigned(btlog_ptr.active_element_count))
+
+# EndMacro: zstack_findtop
 
 # Macro: btlog_find
 
 
 # Macro: btlog_find
 
-@lldb_command('btlog_find', "A")
+@lldb_command('btlog_find', "AS")
 def BtlogFind(cmd_args=None, cmd_options={}):
 def BtlogFind(cmd_args=None, cmd_options={}):
-    """ Search the btlog_t for entries corresponding to the given element.
-        Use -A flag to print all entries.
-        Usage: btlog_find <btlog_t> <element>
-        Usage: btlog_find <btlog_t> -A 
-        Note: Backtraces will be in chronological order, with oldest entries aged out in FIFO order as needed.
     """
     """
-    if not cmd_args:
-        raise ArgumentError("Need a btlog_t parameter")
-    btlog = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
-    printall = False
-    target_elem = 0xffffff
-
-    if "-A" in cmd_options:
-        printall = True
-    else:
-        if not printall and len(cmd_args) < 2:
-            raise ArgumentError("<element> is missing in args. Need a search pointer.")
-        target_elem = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
-    
-    index = unsigned(btlog.head)
-    progress = 0
-    record_size = unsigned(btlog.btrecord_size)
-    while index != 0xffffff:
-        record_offset = index * record_size
-        record = kern.GetValueFromAddress(unsigned(btlog.btrecords) + record_offset, 'btlog_record_t *')
-        if unsigned(record.element) == target_elem or printall:
-            print '{0: <s} OP {1: <d} {2: <#0x} {3: <s}\n'.format(('-' * 8), record.operation, target_elem, ('-' * 8))
-            ShowBtlogBacktrace(btlog.btrecord_btdepth, record)
-        index = record.next
-        progress += 1
-        if (progress % 1000) == 0: print '{0: <d} entries searched!\n'.format(progress)
+    """
+    print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
+    return
 
 #EndMacro: btlog_find
 
 
 #EndMacro: btlog_find
 
@@ -582,14 +1141,14 @@ def ShowZstackTrace(cmd_args=None):
     if len(cmd_args) >= 2:
         trace_size = ArgumentStringToInt(cmd_args[1])
     ShowZstackTraceHelper(trace, trace_size)
     if len(cmd_args) >= 2:
         trace_size = ArgumentStringToInt(cmd_args[1])
     ShowZstackTraceHelper(trace, trace_size)
-    
+
 #EndMacro: showzstacktrace
 
 def ShowZstackTraceHelper(stack, depth):
     """ Helper routine for printing a zstack.
         params:
             stack: void *[] - An array of pointers representing the Zstack
 #EndMacro: showzstacktrace
 
 def ShowZstackTraceHelper(stack, depth):
     """ Helper routine for printing a zstack.
         params:
             stack: void *[] - An array of pointers representing the Zstack
-            depth: int - The depth of the ztrace stack 
+            depth: int - The depth of the ztrace stack
         returns:
             None
     """
         returns:
             None
     """
@@ -608,7 +1167,7 @@ def ShowZstackTraceHelper(stack, depth):
 
 @lldb_command('showtopztrace')
 def ShowTopZtrace(cmd_args=None):
 
 @lldb_command('showtopztrace')
 def ShowTopZtrace(cmd_args=None):
-    """ Shows the ztrace with the biggest size. 
+    """ Shows the ztrace with the biggest size.
         (According to top_ztrace, not by iterating through the hash table)
     """
     top_trace = kern.globals.top_ztrace
         (According to top_ztrace, not by iterating through the hash table)
     """
     top_trace = kern.globals.top_ztrace
@@ -627,7 +1186,7 @@ def ShowZallocs(cmd_args=None):
     if unsigned(kern.globals.zallocations) == 0:
         print "zallocations array not initialized!"
         return
     if unsigned(kern.globals.zallocations) == 0:
         print "zallocations array not initialized!"
         return
-    print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE') 
+    print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
     current_index = 0
     max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
     allocation_count = 0
     current_index = 0
     max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
     allocation_count = 0
@@ -651,7 +1210,7 @@ def ShowZallocsForTrace(cmd_args=None):
     if not cmd_args:
         print ShowZallocsForTrace.__doc__
         return
     if not cmd_args:
         print ShowZallocsForTrace.__doc__
         return
-    print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE') 
+    print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
     target_index = ArgumentStringToInt(cmd_args[0])
     current_index = 0
     max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
     target_index = ArgumentStringToInt(cmd_args[0])
     current_index = 0
     max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
@@ -752,19 +1311,68 @@ def ShowZstats(cmd_args=None):
 
 #EndMacro: showzstats
 
 
 #EndMacro: showzstats
 
-def ShowBtlogBacktrace(depth, zstack_record):
-    """ Helper routine for printing a BT Log record backtrace stack.
+#Macro: showpcpu
+
+@lldb_command('showpcpu', "N:V", fancy=True)
+def ShowPCPU(cmd_args=None, cmd_options={}, O=None):
+    """ Show per-cpu variables
+    usage: showpcpu [-N <cpu>] [-V] <variable name>
+
+    Use -N <cpu> to only dump the value for a given CPU number
+    Use -V       to dump the values of the variables after their addresses
+    """
+
+    if not cmd_args:
+        raise ArgumentError("No arguments passed")
+
+    cpu = None
+    ncpu = kern.globals.zpercpu_early_count
+    pcpu_base = kern.globals.percpu_base
+
+    if "-N" in cmd_options:
+        cpu = unsigned(int(cmd_options["-N"]))
+        if cpu >= unsigned(ncpu):
+            raise ArgumentError("Invalid cpu {d}".format(cpu))
+
+    var = addressof(kern.GetGlobalVariable('percpu_slot_' + cmd_args[0]))
+    ty  = var.GetSBValue().GetTypeName()
+
+    r = range(0, ncpu)
+    if cpu:
+        r = range(cpu, cpu + 1)
+
+    def PCPUSlot(pcpu_var, i):
+        if i == 0:
+            return pcpu_var
+        addr = unsigned(pcpu_var) + unsigned(pcpu_base.start) + (i - 1) * unsigned(pcpu_base.size)
+        return kern.GetValueFromAddress(addr, pcpu_var)
+
+    with O.table("{:<4s} {:<20s}".format("CPU", "address")):
+        for i in r:
+            print O.format("{:<4d} ({:s}){:#x}", i, ty, PCPUSlot(var, i))
+
+    if not "-V" in cmd_options:
+        return
+
+    for i in r:
+        with O.table("CPU {:d}".format(i)):
+            print dereference(PCPUSlot(var, i))
+
+#EndMacro: showpcpu
+
+def GetBtlogBacktrace(depth, zstack_record):
+    """ Helper routine for getting a BT Log record backtrace stack.
         params:
             depth:int - The depth of the zstack record
             zstack_record:btlog_record_t * - A BTLog record
         returns:
         params:
             depth:int - The depth of the zstack record
             zstack_record:btlog_record_t * - A BTLog record
         returns:
-            None
+            str - string with backtrace in it.
     """
     out_str = ''
     frame = 0
     if not zstack_record:
     """
     out_str = ''
     frame = 0
     if not zstack_record:
-        print "Zstack record none!"
-        return
+        return "Zstack record none!"
+
     depth_val = unsigned(depth)
     while frame < depth_val:
         frame_pc = zstack_record.bt[frame]
     depth_val = unsigned(depth)
     while frame < depth_val:
         frame_pc = zstack_record.bt[frame]
@@ -777,9 +1385,9 @@ def ShowBtlogBacktrace(depth, zstack_record):
             symbol_str = ''
         out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str)
         frame += 1
             symbol_str = ''
         out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str)
         frame += 1
-    print out_str
+    return out_str
 
 
-def ShowZStackRecord(zstack_record, zstack_index):
+def ShowZStackRecord(zstack_record, zstack_index, btrecord_btdepth, elements_count):
     """ Helper routine for printing a single zstack record
         params:
             zstack_record:btlog_record_t * -  A BTLog record
     """ Helper routine for printing a single zstack record
         params:
             zstack_record:btlog_record_t * -  A BTLog record
@@ -789,12 +1397,13 @@ def ShowZStackRecord(zstack_record, zstack_index):
     """
     out_str = ('-' * 8)
     if zstack_record.operation == 1:
     """
     out_str = ('-' * 8)
     if zstack_record.operation == 1:
-        out_str += "ALLOC  "
+        out_str += "ALLOC.  "
     else:
     else:
-        out_str += "FREE   "
-    out_str += "{0: <#0x} : Index {1: <d} {2: <s}\n".format(zstack_record.element, zstack_index, ('-' * 8))
+        out_str += "FREE.   "
+    out_str += "Stack Index {0: <d} with active refs {1: <d} of {2: <d} {3: <s}\n".format(zstack_index, zstack_record.ref_count, elements_count, ('-' * 8))
     print out_str
     print out_str
-    ShowBtlogBacktrace(kern.globals.zlog_btlog.btrecord_btdepth, zstack_record)
+    print GetBtlogBacktrace(btrecord_btdepth, zstack_record)
+    print " \n"
 
 # Macro: showioalloc
 
 
 # Macro: showioalloc
 
@@ -807,29 +1416,88 @@ def ShowIOAllocations(cmd_args=None):
     print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024))
     print "IOMalloc allocation  = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024))
     print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024))
     print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024))
     print "IOMalloc allocation  = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024))
     print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024))
-    
-    
-# EndMacro: showioalloc    
 
 
 
 
+# EndMacro: showioalloc
+
+
+# Macro: showselectmem
+@lldb_command('showselectmem', "S:")
+def ShowSelectMem(cmd_args=None, cmd_options={}):
+    """ Show memory cached by threads on calls to select.
+
+        usage: showselectmem [-v]
+            -v        : print each thread's memory
+                        (one line per thread with non-zero select memory)
+            -S {addr} : Find the thread whose thread-local select set
+                        matches the given address
+    """
+    verbose = False
+    opt_wqs = 0
+    if config['verbosity'] > vHUMAN:
+        verbose = True
+    if "-S" in cmd_options:
+        opt_wqs = unsigned(kern.GetValueFromAddress(cmd_options["-S"], 'uint64_t *'))
+        if opt_wqs == 0:
+            raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options["-S"]))
+    selmem = 0
+    if verbose:
+        print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)')
+    for t in kern.tasks:
+        for th in IterateQueue(t.threads, 'thread *', 'task_threads'):
+            uth = Cast(th.uthread, 'uthread *');
+            wqs = 0
+            if hasattr(uth, 'uu_allocsize'): # old style
+                thmem = uth.uu_allocsize
+                wqs = uth.uu_wqset
+            elif hasattr(uth, 'uu_wqstate_sz'): # new style
+                thmem = uth.uu_wqstate_sz
+                wqs = uth.uu_wqset
+            else:
+                print "What kind of uthread is this?!"
+                return
+            if opt_wqs and opt_wqs == unsigned(wqs):
+                print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs, unsigned(th), unsigned(th.thread_id))
+            if verbose and thmem > 0:
+                print "{:<#18x} {:<#10x} {:d}".format(unsigned(t), unsigned(th.thread_id), thmem)
+            selmem += thmem
+    print '-'*40
+    print "Total: {:d} bytes ({:d} kbytes)".format(selmem, selmem/1024)
+# Endmacro: showselectmem
+
+
 # Macro: showtaskvme
 # Macro: showtaskvme
-@lldb_command('showtaskvme')
-def ShowTaskVmeHelper(cmd_args=None):
+@lldb_command('showtaskvme', "PS")
+def ShowTaskVmeHelper(cmd_args=None, cmd_options={}):
     """ Display a summary list of the specified vm_map's entries
         Usage: showtaskvme <task address>  (ex. showtaskvme 0x00ataskptr00 )
     """ Display a summary list of the specified vm_map's entries
         Usage: showtaskvme <task address>  (ex. showtaskvme 0x00ataskptr00 )
+        Use -S flag to show VM object shadow chains
+        Use -P flag to show pager info (mapped file, compressed pages, ...)
     """
     """
+    show_pager_info = False
+    show_all_shadows = False
+    if "-P" in cmd_options:
+        show_pager_info = True
+    if "-S" in cmd_options:
+        show_all_shadows = True
     task = kern.GetValueFromAddress(cmd_args[0], 'task *')
     task = kern.GetValueFromAddress(cmd_args[0], 'task *')
-    ShowTaskVMEntries(task)
+    ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
 
 
-@lldb_command('showallvme')
-def ShowAllVME(cmd_args=None):
+@lldb_command('showallvme', "PS")
+def ShowAllVME(cmd_args=None, cmd_options={}):
     """ Routine to print a summary listing of all the vm map entries
     """ Routine to print a summary listing of all the vm map entries
-        Go Through each task in system and show the vm info
+        Go Through each task in system and show the vm memory regions
+        Use -S flag to show VM object shadow chains
+        Use -P flag to show pager info (mapped file, compressed pages, ...)
     """
     """
+    show_pager_info = False
+    show_all_shadows = False
+    if "-P" in cmd_options:
+        show_pager_info = True
+    if "-S" in cmd_options:
+        show_all_shadows = True
     for task in kern.tasks:
     for task in kern.tasks:
-        ShowTaskVMEntries(task)
+        ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
 
 @lldb_command('showallvm')
 def ShowAllVM(cmd_args=None):
 
 @lldb_command('showallvm')
 def ShowAllVM(cmd_args=None):
@@ -863,6 +1531,7 @@ def ShowTaskVM(cmd_args=None):
 def ShowAllVMStats(cmd_args=None):
     """ Print a summary of vm statistics in a table format
     """
 def ShowAllVMStats(cmd_args=None):
     """ Print a summary of vm statistics in a table format
     """
+    page_size = kern.globals.page_size
     vmstats = lambda:None
     vmstats.wired_count = 0
     vmstats.resident_count = 0
     vmstats = lambda:None
     vmstats.wired_count = 0
     vmstats.resident_count = 0
@@ -875,10 +1544,10 @@ def ShowAllVMStats(cmd_args=None):
     vmstats.compressed_lifetime = 0
     vmstats.error = ''
 
     vmstats.compressed_lifetime = 0
     vmstats.error = ''
 
-    hdr_format = "{0: >10s} {1: <20s} {2: >6s} {3: >10s} {4: >10s} {5: >10s} {6: >10s} {7: >10s} {8: >10s} {9: >10s} {10: >10s} {11: >10s} {12: >10s} {13: >10s} {14:}"
-    print hdr_format.format('pid', 'command', '#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', '')
-    print hdr_format.format('', '', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '')
-    entry_format = "{p.p_pid: >10d} {p.p_comm: <20s} {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} {s.error}"
+    hdr_format = "{:>6s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<20s} {:1s}"
+    print hdr_format.format('#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', 'pid', 'command', '')
+    print hdr_format.format('', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '', '', '')
+    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} {p.p_pid: >10d} {0: <32s} {s.error}"
 
     for task in kern.tasks:
         proc = Cast(task.bsd_info, 'proc *')
 
     for task in kern.tasks:
         proc = Cast(task.bsd_info, 'proc *')
@@ -910,12 +1579,12 @@ def ShowAllVMStats(cmd_args=None):
         if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count:
             vmstats.error += '*'
 
         if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count:
             vmstats.error += '*'
 
-        print entry_format.format(p=proc, m=vmmap, vsize=(unsigned(vmmap.size) >> 12), t=task, s=vmstats)
-        
+        print entry_format.format(GetProcName(proc), p=proc, m=vmmap, vsize=(unsigned(vmmap.size) / page_size), t=task, s=vmstats)
+
 
 
-def ShowTaskVMEntries(task):
+def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
     """  Routine to print out a summary listing of all the entries in a vm_map
     """  Routine to print out a summary listing of all the entries in a vm_map
-        params: 
+        params:
             task - core.value : a object of type 'task *'
         returns:
             None
             task - core.value : a object of type 'task *'
         returns:
             None
@@ -932,7 +1601,7 @@ def ShowTaskVMEntries(task):
     vme_ptr_type = GetType('vm_map_entry *')
     print GetVMEntrySummary.header
     for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
     vme_ptr_type = GetType('vm_map_entry *')
     print GetVMEntrySummary.header
     for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
-        print GetVMEntrySummary(vme)
+        print GetVMEntrySummary(vme, show_pager_info, show_all_shadows)
     return None
 
 @lldb_command("showmap")
     return None
 
 @lldb_command("showmap")
@@ -953,7 +1622,7 @@ def ShowMapVME(cmd_args=None):
         usage: showmapvme <vm_map>
     """
     if cmd_args == None or len(cmd_args) < 1:
         usage: showmapvme <vm_map>
     """
     if cmd_args == None or len(cmd_args) < 1:
-        print "Invalid argument.", ShowMap.__doc__
+        print "Invalid argument.", ShowMapVME.__doc__
         return
     map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
     print GetVMMapSummary.header
         return
     map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
     print GetVMMapSummary.header
@@ -966,32 +1635,35 @@ def ShowMapVME(cmd_args=None):
     return None
 
 @lldb_type_summary(['_vm_map *', 'vm_map_t'])
     return None
 
 @lldb_type_summary(['_vm_map *', 'vm_map_t'])
-@header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free"))
+@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"))
 def GetVMMapSummary(vmmap):
     """ Display interesting bits from vm_map struct """
     out_string = ""
 def GetVMMapSummary(vmmap):
     """ Display interesting bits from vm_map struct """
     out_string = ""
-    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}"
+    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x} {7: >7d}"
     vm_size = uint64_t(vmmap.size).value
     resident_pages = 0
     if vmmap.pmap != 0: resident_pages = int(vmmap.pmap.stats.resident_count)
     vm_size = uint64_t(vmmap.size).value
     resident_pages = 0
     if vmmap.pmap != 0: resident_pages = int(vmmap.pmap.stats.resident_count)
-    out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, vmmap.first_free)
+    first_free = 0
+    if int(vmmap.holelistenabled) == 0: first_free = vmmap.f_s._first_free
+    out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, first_free, vmmap.hdr.page_shift)
     return out_string
 
 @lldb_type_summary(['vm_map_entry'])
 @header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
 def GetVMEntrySummary(vme):
     """ Display vm entry specific information. """
     return out_string
 
 @lldb_type_summary(['vm_map_entry'])
 @header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
 def GetVMEntrySummary(vme):
     """ Display vm entry specific information. """
+    page_size = kern.globals.page_size
     out_string = ""
     format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
     vme_protection = int(vme.protection)
     vme_max_protection = int(vme.max_protection)
     vme_extra_info_str ="SC-Ds"[int(vme.inheritance)]
     out_string = ""
     format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
     vme_protection = int(vme.protection)
     vme_max_protection = int(vme.max_protection)
     vme_extra_info_str ="SC-Ds"[int(vme.inheritance)]
-    if int(vme.is_sub_map) != 0 : 
+    if int(vme.is_sub_map) != 0 :
         vme_extra_info_str +="s"
     elif int(vme.needs_copy) != 0 :
         vme_extra_info_str +="n"
         vme_extra_info_str +="s"
     elif int(vme.needs_copy) != 0 :
         vme_extra_info_str +="n"
-    num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) >> 12
-    out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.object.vm_object, vme.offset)
+    num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) / page_size
+    out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.vme_object.vmo_object, vme.vme_offset)
     return out_string
 
 # EndMacro: showtaskvme
     return out_string
 
 # EndMacro: showtaskvme
@@ -1003,20 +1675,24 @@ def ShowMapWired(cmd_args=None):
         print "Invalid argument", ShowMapWired.__doc__
         return
     map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
         print "Invalid argument", ShowMapWired.__doc__
         return
     map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
-    
+
 
 @lldb_type_summary(['kmod_info_t *'])
 
 @lldb_type_summary(['kmod_info_t *'])
-@header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: >20s} {6: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'version', 'name'))
+@header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} {7: >20s} {8: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size', 'version', 'name'))
 def GetKextSummary(kmod):
 def GetKextSummary(kmod):
-    """ returns a string representation of kext information 
+    """ returns a string representation of kext information
     """
     out_string = ""
     """
     out_string = ""
-    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: >20s} {6: <30s}"
-    out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, kmod.version, kmod.name)
+    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}"
+    segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod.address), unsigned(kmod.size))
+    text_segment = macho.get_text_segment(segments)
+    if not text_segment:
+        text_segment = segments[0]
+    out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, text_segment.vmaddr, text_segment.vmsize, kmod.version, kmod.name)
     return out_string
 
 @lldb_type_summary(['uuid_t'])
     return out_string
 
 @lldb_type_summary(['uuid_t'])
-@header("")    
+@header("")  
 def GetUUIDSummary(uuid):
     """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
     """
 def GetUUIDSummary(uuid):
     """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
     """
@@ -1031,25 +1707,96 @@ def ShowAllKexts(cmd_args=None):
     """Display a summary listing of all loaded kexts (alias: showallkmods)
     """
     kmod_val = kern.globals.kmod
     """Display a summary listing of all loaded kexts (alias: showallkmods)
     """
     kmod_val = kern.globals.kmod
+    kextuuidinfo = GetKextLoadInformation(show_progress=(config['verbosity'] > vHUMAN))
     print "{: <36s} ".format("UUID") + GetKextSummary.header
     print "{: <36s} ".format("UUID") + GetKextSummary.header
-    kextuuidinfo = GetKextLoadInformation()
     for kval in IterateLinkedList(kmod_val, 'next'):
         uuid = "........-....-....-....-............"
         kaddr = unsigned(kval.address)
     for kval in IterateLinkedList(kmod_val, 'next'):
         uuid = "........-....-....-....-............"
         kaddr = unsigned(kval.address)
+        found_kext_summary = None
         for l in kextuuidinfo :
         for l in kextuuidinfo :
-            if kaddr == int(l[1],16):
+            if kaddr == int(l[3],16):
                 uuid = l[0]
                 uuid = l[0]
+                found_kext_summary = l
                 break
                 break
-        print uuid + " " + GetKextSummary(kval) 
+        if found_kext_summary:
+            _ksummary = GetKextSummary(found_kext_summary[7])
+        else:
+            _ksummary = GetKextSummary(kval)
+        print uuid + " " + _ksummary
 
 
-def GetKextLoadInformation(addr=0):
+def GetKmodWithAddr(addr):
+    """ Go through kmod list and find one with begin_addr as addr
+        returns: None if not found. else a cvalue of type kmod
+    """
+    kmod_val = kern.globals.kmod
+    for kval in IterateLinkedList(kmod_val, 'next'):
+        if addr == unsigned(kval.address):
+                return kval
+    return None
+
+def GetAllSegmentsAndSectionsFromDataInMemory(address, size):
+    """ reads memory at address and parses mach_header to get segment and section information
+        returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...])
+            where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize'
+            if TEXT segment is not found a dummy segment & section with address, size is returned.
+    """
+    cache_hash = "kern.kexts.segments.{}.{}".format(address, size)
+    cached_result = caching.GetDynamicCacheData(cache_hash,())
+    if cached_result:
+        return cached_result
+
+    defval = macho.MachOSegment('__TEXT', address, size, 0, size)
+    if address == 0 or size == 0:
+        return ([defval], [defval])
+
+    ## if int(kern.globals.gLoadedKextSummaries.version) <= 2:
+    # until we have separate version. we will pay penalty only on arm64 devices
+    if not kern.arch.startswith('arm64'):
+        return ([defval], [defval])
+
+    restrict_size_to_read = 1536
+    machoObject = None
+    while machoObject is None:
+        err = lldb.SBError()
+        size_to_read = min(size, restrict_size_to_read)
+        data = LazyTarget.GetProcess().ReadMemory(address, size_to_read, err)
+        if not err.Success():
+            print "Failed to read memory at {} and size {}".format(address, size_to_read)
+            return ([defval], [defval])
+        try:
+            m = macho.MemMacho(data, len(data))
+            machoObject = m
+        except Exception as e:
+            if str(e.message).find('unpack requires a string argument') >= 0:
+                # this may be due to short read of memory. Lets do double read size.
+                restrict_size_to_read *= 2
+                debuglog("Bumping mach header read size to {}".format(restrict_size_to_read))
+                continue
+            else:
+                print "Failed to read MachO for address {} errormessage: {}".format(address, e.message)
+                return ([defval], [defval])
+    # end of while loop. We have machoObject defined
+    segments = machoObject.get_segments_with_name('')
+    sections = machoObject.get_sections_with_name('')
+    rval = (segments, sections)
+    caching.SaveDynamicCacheData(cache_hash, rval)
+    return rval
+
+def GetKextLoadInformation(addr=0, show_progress=False):
     """ Extract the kext uuid and load address information from the kernel data structure.
         params:
             addr - int - optional integer that is the address to search for.
     """ Extract the kext uuid and load address information from the kernel data structure.
         params:
             addr - int - optional integer that is the address to search for.
-        returns: 
-            [] - array with each entry of format ( 'UUID', 'Hex Load Address')
+        returns:
+            [] - array with each entry of format
+                ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name',
+                  'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj)
     """
     """
-    # because of <rdar://problem/12683084>, we can't find summaries directly 
+    cached_result = caching.GetDynamicCacheData("kern.kexts.loadinformation", [])
+    ## if specific addr is provided then ignore caching
+    if cached_result and not addr:
+        return cached_result
+
+    # because of <rdar://problem/12683084>, we can't find summaries directly
     #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
     baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10
     summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *')
     #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
     baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10
     summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *')
@@ -1060,14 +1807,23 @@ def GetKextLoadInformation(addr=0):
         entry_size = int(kern.globals.gLoadedKextSummaries.entry_size)
     retval = []
     for i in range(total_summaries):
         entry_size = int(kern.globals.gLoadedKextSummaries.entry_size)
     retval = []
     for i in range(total_summaries):
+        if show_progress:
+            print "progress: {}/{}".format(i, total_summaries)
         tmpaddress = unsigned(summaries_begin) + (i * entry_size)
         current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *')
         tmpaddress = unsigned(summaries_begin) + (i * entry_size)
         current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *')
+        # code to extract macho information
+        segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext.address), unsigned(current_kext.size))
+        seginfo = macho.get_text_segment(segments)
+        if not seginfo:
+            seginfo = segments[0]
+        kmod_obj = GetKmodWithAddr(unsigned(current_kext.address))
         if addr != 0 :
         if addr != 0 :
-            if addr == unsigned(current_kext.address):
-                retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) ))
-        else:
-            retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) )) 
-        
+            if addr == unsigned(current_kext.address) or addr == seginfo.vmaddr:
+                return [(GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj)]
+        retval.append((GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj))
+
+    if not addr:
+        caching.SaveDynamicCacheData("kern.kexts.loadinformation", retval)
     return retval
 
 lldb_alias('showallkexts', 'showallkmods')
     return retval
 
 lldb_alias('showallkexts', 'showallkmods')
@@ -1075,34 +1831,34 @@ lldb_alias('showallkexts', 'showallkmods')
 def GetOSKextVersion(version_num):
     """ returns a string of format 1.2.3x from the version_num
         params: version_num - int
 def GetOSKextVersion(version_num):
     """ returns a string of format 1.2.3x from the version_num
         params: version_num - int
-        return: str 
+        return: str
     """
     if version_num == -1 :
         return "invalid"
     (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000)
     version = version_num
     """
     if version_num == -1 :
         return "invalid"
     (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000)
     version = version_num
-    
+
     vers_major = version / MAJ_MULT
     version = version - (vers_major * MAJ_MULT)
     vers_major = version / MAJ_MULT
     version = version - (vers_major * MAJ_MULT)
-    
+
     vers_minor = version / MIN_MULT
     version = version - (vers_minor * MIN_MULT)
     vers_minor = version / MIN_MULT
     version = version - (vers_minor * MIN_MULT)
-    
+
     vers_revision = version / REV_MULT
     version = version - (vers_revision * REV_MULT)
     vers_revision = version / REV_MULT
     version = version - (vers_revision * REV_MULT)
-    
+
     vers_stage = version / STAGE_MULT
     version = version - (vers_stage * STAGE_MULT)
     vers_stage = version / STAGE_MULT
     version = version - (vers_stage * STAGE_MULT)
-    
-    vers_stage_level = version 
-    
+
+    vers_stage_level = version
+
     out_str = "%d.%d" % (vers_major, vers_minor)
     if vers_revision > 0: out_str += ".%d" % vers_revision
     if vers_stage == 1 : out_str += "d%d" % vers_stage_level
     if vers_stage == 3 : out_str += "a%d" % vers_stage_level
     if vers_stage == 5 : out_str += "b%d" % vers_stage_level
     if vers_stage == 6 : out_str += "fc%d" % vers_stage_level
     out_str = "%d.%d" % (vers_major, vers_minor)
     if vers_revision > 0: out_str += ".%d" % vers_revision
     if vers_stage == 1 : out_str += "d%d" % vers_stage_level
     if vers_stage == 3 : out_str += "a%d" % vers_stage_level
     if vers_stage == 5 : out_str += "b%d" % vers_stage_level
     if vers_stage == 6 : out_str += "fc%d" % vers_stage_level
-    
+
     return out_str
 
 @lldb_command('showallknownkmods')
     return out_str
 
 @lldb_command('showallknownkmods')
@@ -1116,7 +1872,7 @@ def ShowAllKnownKexts(cmd_args=None):
     print "%d kexts in sKextsByID:" % kext_count
     print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
     format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
     print "%d kexts in sKextsByID:" % kext_count
     print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
     format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
-    
+
     while index < kext_count:
         kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index)
         kext_name = str(kext_dict.key.string)
     while index < kext_count:
         kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index)
         kext_name = str(kext_dict.key.string)
@@ -1131,37 +1887,91 @@ def ShowAllKnownKexts(cmd_args=None):
         version = GetOSKextVersion(version_num)
         print format_string.format(osk, load_addr, id, version, kext_name)
         index += 1
         version = GetOSKextVersion(version_num)
         print format_string.format(osk, load_addr, id, version, kext_name)
         index += 1
-    
+
     return
 
     return
 
+def FindKmodNameForAddr(addr):
+    """ Given an address, return the name of the kext containing that address
+    """
+    addr = unsigned(addr)
+    all_kexts_info = GetKextLoadInformation()
+    for kinfo in all_kexts_info:
+        segment = macho.get_segment_with_addr(kinfo[4], addr)
+        if segment:
+            return kinfo[7].name
+    return None
+
+
+@lldb_command('addkextaddr')
+def AddKextAddr(cmd_args=[]):
+    """ Given an address, load the kext which contains that address
+        Syntax: (lldb) addkextaddr <addr>
+    """
+    if len(cmd_args) < 1:
+        raise ArgumentError("Insufficient arguments")
+
+    addr = ArgumentStringToInt(cmd_args[0])
+    all_kexts_info = GetKextLoadInformation()
+    kernel_uuid = str(kern.globals.kernel_uuid_string).lower()
+    found_kinfo = None
+    found_segment = None
+    for kinfo in all_kexts_info:
+        segment = macho.get_segment_with_addr(kinfo[4], addr)
+        if segment:
+            print GetKextSummary.header
+            print GetKextSummary(kinfo[7]) + " segment: {} offset = {:#0x}".format(segment.name, (addr - segment.vmaddr))
+            cur_uuid = kinfo[0].lower()
+            if (kernel_uuid == cur_uuid):
+                print "(builtin)"
+            else:
+                print "Fetching dSYM for %s" % cur_uuid
+                info = dsymForUUID(cur_uuid)
+                if info and 'DBGSymbolRichExecutable' in info:
+                    print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
+                    addDSYM(cur_uuid, info)
+                    loadDSYM(cur_uuid, int(kinfo[1],16), kinfo[4])
+                else:
+                    print "Failed to get symbol info for %s" % cur_uuid
+            return
+
+
 @lldb_command('showkmodaddr')
 def ShowKmodAddr(cmd_args=[]):
 @lldb_command('showkmodaddr')
 def ShowKmodAddr(cmd_args=[]):
-    """ Given an address, print the offset and name for the kmod containing it 
+    """ Given an address, print the offset and name for the kmod containing it
         Syntax: (lldb) showkmodaddr <addr>
     """
     if len(cmd_args) < 1:
         raise ArgumentError("Insufficient arguments")
 
     addr = ArgumentStringToInt(cmd_args[0])
         Syntax: (lldb) showkmodaddr <addr>
     """
     if len(cmd_args) < 1:
         raise ArgumentError("Insufficient arguments")
 
     addr = ArgumentStringToInt(cmd_args[0])
-    kmod_val = kern.globals.kmod
-    for kval in IterateLinkedList(kmod_val, 'next'):
-        if addr >= unsigned(kval.address) and addr <= (unsigned(kval.address) + unsigned(kval.size)):
-            print GetKextSummary.header
-            print GetKextSummary(kval) + " offset = {0: #0x}".format((addr - unsigned(kval.address)))
-            return True
+    all_kexts_info = GetKextLoadInformation()
+    found_kinfo = None
+    found_segment = None
+    for kinfo in all_kexts_info:
+        s = macho.get_segment_with_addr(kinfo[4], addr)
+        if s:
+            found_segment = s
+            found_kinfo = kinfo
+            break
+    if found_kinfo:
+        print GetKextSummary.header
+        print GetKextSummary(found_kinfo[7]) + " segment: {} offset = {:#0x}".format(found_segment.name, (addr - found_segment.vmaddr))
+        return True
     return False
 
     return False
 
-@lldb_command('addkext','F:N:')
+
+@lldb_command('addkext','AF:N:')
 def AddKextSyms(cmd_args=[], cmd_options={}):
     """ Add kext symbols into lldb.
         This command finds symbols for a uuid and load the required executable
 def AddKextSyms(cmd_args=[], cmd_options={}):
     """ Add kext symbols into lldb.
         This command finds symbols for a uuid and load the required executable
-        Usage: 
+        Usage:
             addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
             addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
             addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
             addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
             addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
             addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
-            addkext all    : Will load all the kext symbols - SLOW 
+            addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A
+            addkext all    : Will load all the kext symbols - SLOW
     """
     """
-    
+
 
     if "-F" in cmd_options:
         exec_path = cmd_options["-F"]
 
     if "-F" in cmd_options:
         exec_path = cmd_options["-F"]
@@ -1173,10 +1983,9 @@ def AddKextSyms(cmd_args=[], cmd_options={}):
             raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
 \nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
 \nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path))
             raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
 \nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
 \nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path))
-        if not os.access(exec_full_path, os.X_OK):
-            raise ArgumentError("Path is {:s} not an executable file".format(exec_path))
 
         slide_value = None
 
         slide_value = None
+        sections = None
         if cmd_args:
             slide_value = cmd_args[0]
             debuglog("loading slide value from user input %s" % cmd_args[0])
         if cmd_args:
             slide_value = cmd_args[0]
             debuglog("loading slide value from user input %s" % cmd_args[0])
@@ -1194,39 +2003,55 @@ def AddKextSyms(cmd_args=[], cmd_options={}):
                     debuglog(k[0])
                     if k[0].lower() == uuid_str.lower():
                         slide_value = k[1]
                     debuglog(k[0])
                     if k[0].lower() == uuid_str.lower():
                         slide_value = k[1]
+                        sections = k[4]
                         debuglog("found the slide %s for uuid %s" % (k[1], k[0]))
                         debuglog("found the slide %s for uuid %s" % (k[1], k[0]))
-        
         if slide_value is None:
             raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path)
         if slide_value is None:
             raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path)
-        load_cmd = "target modules load --file %s --slide %s" % (exec_full_path, str(slide_value))
-        print load_cmd
-        print lldb_run_command(load_cmd)  
+
+        if not sections:
+            cmd_str = "target modules load --file %s --slide %s" % ( exec_full_path, str(slide_value))
+            debuglog(cmd_str)
+        else:
+            cmd_str = "target modules load --file {}   ".format(exec_full_path)
+            sections_str = ""
+            for s in sections:
+                sections_str += " {} {:#0x} ".format(s.name, s.vmaddr)
+            cmd_str += sections_str
+            debuglog(cmd_str)
+
+        lldb.debugger.HandleCommand(cmd_str)
+
         kern.symbolicator = None
         return True
 
     all_kexts_info = GetKextLoadInformation()
         kern.symbolicator = None
         return True
 
     all_kexts_info = GetKextLoadInformation()
-    
+    kernel_uuid = str(kern.globals.kernel_uuid_string).lower()
+
     if "-N" in cmd_options:
         kext_name = cmd_options["-N"]
         kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True)
     if "-N" in cmd_options:
         kext_name = cmd_options["-N"]
         kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True)
-        if len(kext_name_matches) != 1:
+        if len(kext_name_matches) != 1 and "-A" not in cmd_options:
             print "Ambiguous match for name: {:s}".format(kext_name)
             if len(kext_name_matches) > 0:
                 print  "Options are:\n\t" + "\n\t".join(kext_name_matches)
             return
         debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches[0], kext_name))
             print "Ambiguous match for name: {:s}".format(kext_name)
             if len(kext_name_matches) > 0:
                 print  "Options are:\n\t" + "\n\t".join(kext_name_matches)
             return
         debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches[0], kext_name))
-        for x in all_kexts_info:
-            if kext_name_matches[0] == x[2]:
-                cur_uuid = x[0].lower()
-                print "Fetching dSYM for {:s}".format(cur_uuid)
-                info = dsymForUUID(cur_uuid)
-                if info and 'DBGSymbolRichExecutable' in info:
-                    print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
-                    addDSYM(cur_uuid, info)
-                    loadDSYM(cur_uuid, int(x[1],16))
-                else:
-                    print "Failed to get symbol info for {:s}".format(cur_uuid)
-                break
+        for cur_knm in kext_name_matches:
+            for x in all_kexts_info:
+                if cur_knm == x[2]:
+                    cur_uuid = x[0].lower()
+                    if (kernel_uuid == cur_uuid):
+                        print "(builtin)"
+                    else:
+                        print "Fetching dSYM for {:s}".format(cur_uuid)
+                        info = dsymForUUID(cur_uuid)
+                        if info and 'DBGSymbolRichExecutable' in info:
+                            print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
+                            addDSYM(cur_uuid, info)
+                            loadDSYM(cur_uuid, int(x[1],16), x[4])
+                        else:
+                            print "Failed to get symbol info for {:s}".format(cur_uuid)
+                    break
         kern.symbolicator = None
         return
 
         kern.symbolicator = None
         return
 
@@ -1238,39 +2063,40 @@ def AddKextSyms(cmd_args=[], cmd_options={}):
     load_all_kexts = False
     if uuid == "all":
         load_all_kexts = True
     load_all_kexts = False
     if uuid == "all":
         load_all_kexts = True
-    
+
     if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
         raise ArgumentError("Unknown argument {:s}".format(uuid))
 
     for k_info in all_kexts_info:
         cur_uuid = k_info[0].lower()
         if load_all_kexts or (uuid == cur_uuid):
     if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
         raise ArgumentError("Unknown argument {:s}".format(uuid))
 
     for k_info in all_kexts_info:
         cur_uuid = k_info[0].lower()
         if load_all_kexts or (uuid == cur_uuid):
-            print "Fetching dSYM for %s" % cur_uuid
-            info = dsymForUUID(cur_uuid)
-            if info and 'DBGSymbolRichExecutable' in info:
-                print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
-                addDSYM(cur_uuid, info)
-                loadDSYM(cur_uuid, int(k_info[1],16))
-            else:
-                print "Failed to get symbol info for %s" % cur_uuid
+            if (kernel_uuid != cur_uuid):
+                print "Fetching dSYM for %s" % cur_uuid
+                info = dsymForUUID(cur_uuid)
+                if info and 'DBGSymbolRichExecutable' in info:
+                    print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
+                    addDSYM(cur_uuid, info)
+                    loadDSYM(cur_uuid, int(k_info[1],16), k_info[4])
+                else:
+                    print "Failed to get symbol info for %s" % cur_uuid
         #end of for loop
     kern.symbolicator = None
     return True
 
         #end of for loop
     kern.symbolicator = None
     return True
 
-    
+
 
 lldb_alias('showkmod', 'showkmodaddr')
 lldb_alias('showkext', 'showkmodaddr')
 lldb_alias('showkextaddr', 'showkmodaddr')
 
 @lldb_type_summary(['mount *'])
 
 lldb_alias('showkmod', 'showkmodaddr')
 lldb_alias('showkext', 'showkmodaddr')
 lldb_alias('showkextaddr', 'showkmodaddr')
 
 @lldb_type_summary(['mount *'])
-@header("{0: <20s} {1: <20s} {2: <20s} {3: <12s} {4: <12s} {5: <12s} {6: >6s} {7: <30s} {8: <35s}".format('volume(mp)', 'mnt_data', 'mnt_devvp', 'flag', 'kern_flag', 'lflag', 'type', 'mnton', 'mntfrom'))
+@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'))
 def GetMountSummary(mount):
 def GetMountSummary(mount):
-    """ Display a summary of mount on the system 
+    """ Display a summary of mount on the system
     """
     out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
                   "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
     """
     out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
                   "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
-                  "{vfs.f_mntonname: <30s} {vfs.f_mntfromname: <35s}").format(mnt=mount, vfs=mount.mnt_vfsstat)
+                  "{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'))
     return out_string
 
 @lldb_command('showallmounts')
     return out_string
 
 @lldb_command('showallmounts')
@@ -1303,7 +2129,7 @@ def ShowSystemLog(cmd_args=None):
         err.Clear()
         cbyte = msg_bufc_data.GetUnsignedInt8(err, i)
         if not err.Success() :
         err.Clear()
         cbyte = msg_bufc_data.GetUnsignedInt8(err, i)
         if not err.Success() :
-            raise ValueError("Failed to read character at offset " + i + ": " + err.GetCString())
+            raise ValueError("Failed to read character at offset " + str(i) + ": " + err.GetCString())
         c = chr(cbyte)
         if c == '\0' :  
             continue
         c = chr(cbyte)
         if c == '\0' :  
             continue
@@ -1333,7 +2159,7 @@ def _GetVnodePathName(vnode, vnodename):
             _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) )
     else:
         _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name))
             _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) )
     else:
         _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name))
-        _GetVnodePathName.output += "/%s" % vnodename 
+        _GetVnodePathName.output += "/%s" % vnodename
 
 def GetVnodePath(vnode):
     """ Get string representation of the vnode
 
 def GetVnodePath(vnode):
     """ Get string representation of the vnode
@@ -1381,7 +2207,7 @@ def GetVnodeDevInfo(vnode):
         devnode_major = (devnode_dev >> 24) & 0xff
         devnode_minor = devnode_dev & 0x00ffffff
 
         devnode_major = (devnode_dev >> 24) & 0xff
         devnode_minor = devnode_dev & 0x00ffffff
 
-        # boilerplate device information for a vnode 
+        # boilerplate device information for a vnode
         vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode)
         vnodedev_output += "\n\t type:\t\t"
         if (vnode.v_type == vblk_type):
         vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode)
         vnodedev_output += "\n\t type:\t\t"
         if (vnode.v_type == vblk_type):
@@ -1466,6 +2292,8 @@ def GetVnodeLock(lockf):
         vnode_lock_output += ("{: <8s}").format('prov')
     if lockf_flags & 0x10:
         vnode_lock_output += ("{: <4s}").format('W')
         vnode_lock_output += ("{: <8s}").format('prov')
     if lockf_flags & 0x10:
         vnode_lock_output += ("{: <4s}").format('W')
+    if lockf_flags & 0x400:
+        vnode_lock_output += ("{: <8s}").format('ofd')
     else:
         vnode_lock_output += ("{: <4s}").format('.')
 
     else:
         vnode_lock_output += ("{: <4s}").format('.')
 
@@ -1475,7 +2303,7 @@ def GetVnodeLock(lockf):
         vnode_lock_output += ("PID {: <18d}").format(lockf_proc.p_pid)
     else:
         vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id))
         vnode_lock_output += ("PID {: <18d}").format(lockf_proc.p_pid)
     else:
         vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id))
-        
+
     # lock type
     if lockf_type == 1:
         vnode_lock_output += ("{: <12s}").format('shared')
     # lock type
     if lockf_type == 1:
         vnode_lock_output += ("{: <12s}").format('shared')
@@ -1487,7 +2315,7 @@ def GetVnodeLock(lockf):
                 vnode_lock_output += ("{: <12s}").format('unlock')
             else:
                 vnode_lock_output += ("{: <12s}").format('unknown')
                 vnode_lock_output += ("{: <12s}").format('unlock')
             else:
                 vnode_lock_output += ("{: <12s}").format('unknown')
-    
+
     # start and stop values
     vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start)
     vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end)
     # start and stop values
     vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start)
     vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end)
@@ -1509,7 +2337,7 @@ def GetVnodeLocksSummary(vnode):
                 while lockf_blocker:
                     out_str += ("{: <4s}").format('>')
                     out_str += GetVnodeLock(lockf_blocker)
                 while lockf_blocker:
                     out_str += ("{: <4s}").format('>')
                     out_str += GetVnodeLock(lockf_blocker)
-                    lockf_blocker = lockf_blocker.lf_block.tqe_next    
+                    lockf_blocker = lockf_blocker.lf_block.tqe_next
     return out_str
 
 @lldb_command('showvnodelocks')
     return out_str
 
 @lldb_command('showvnodelocks')
@@ -1531,7 +2359,7 @@ def ShowVnodeLocks(cmd_args=None):
 # EndMacro: showvnodelocks
 
 # Macro: showproclocks
 # EndMacro: showvnodelocks
 
 # Macro: showproclocks
-            
+
 @lldb_command('showproclocks')
 def ShowProcLocks(cmd_args=None):
     """  Routine to display list of advisory record locks for the given process
 @lldb_command('showproclocks')
 def ShowProcLocks(cmd_args=None):
     """  Routine to display list of advisory record locks for the given process
@@ -1553,7 +2381,7 @@ def ShowProcLocks(cmd_args=None):
     seen = 0
     while count <= fd_lastfile:
         if fd_ofiles[count]:
     seen = 0
     while count <= fd_lastfile:
         if fd_ofiles[count]:
-            fglob = fd_ofiles[count].f_fglob
+            fglob = fd_ofiles[count].fp_glob
             fo_type = fglob.fg_ops.fo_type
             if fo_type == 1:
                 fg_data = fglob.fg_data
             fo_type = fglob.fg_ops.fo_type
             if fo_type == 1:
                 fg_data = fglob.fg_data
@@ -1577,13 +2405,14 @@ def ShowProcLocks(cmd_args=None):
 # EndMacro: showproclocks
 
 @lldb_type_summary(['vnode_t', 'vnode *'])
 # EndMacro: showproclocks
 
 @lldb_type_summary(['vnode_t', 'vnode *'])
-@header("{0: <20s} {1: >8s} {2: >8s} {3: <20s} {4: <6s} {5: <20s} {6: <6s} {7: <35s}".format('vnode', 'usecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'name'))
+@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'))
 def GetVnodeSummary(vnode):
     """ Get a summary of important information out of vnode
     """
     out_str = ''
 def GetVnodeSummary(vnode):
     """ Get a summary of important information out of vnode
     """
     out_str = ''
-    format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <35s}"
+    format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: >8d} {4: <#020x} {5: <6s} {6: <#020x} {7: <6s} {8: <6s} {9: <35s}"
     usecount = int(vnode.v_usecount)
     usecount = int(vnode.v_usecount)
+    kusecount = int(vnode.v_kusecount)
     iocount = int(vnode.v_iocount)
     v_data_ptr = int(hex(vnode.v_data), 16)
     vtype = int(vnode.v_type)
     iocount = int(vnode.v_iocount)
     v_data_ptr = int(hex(vnode.v_data), 16)
     vtype = int(vnode.v_type)
@@ -1600,13 +2429,15 @@ def GetVnodeSummary(vnode):
         cnode = Cast(vnode.v_data, 'cnode *')
         name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *'))
     mapped = '-'
         cnode = Cast(vnode.v_data, 'cnode *')
         name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *'))
     mapped = '-'
+    csblob_version = '-'
     if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0):
     if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0):
-        # Check to see if vnode is mapped/unmapped 
+        csblob_version = '{: <6d}'.format(vnode.v_un.vu_ubcinfo.cs_add_gen)
+        # Check to see if vnode is mapped/unmapped
         if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0:
             mapped = '1'
         else:
             mapped = '0'
         if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0:
             mapped = '1'
         else:
             mapped = '0'
-    out_str += format_string.format(vnode, usecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, name)
+    out_str += format_string.format(vnode, usecount, kusecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, csblob_version, name)
     return out_str
 
 @lldb_command('showallvnodes')
     return out_str
 
 @lldb_command('showallvnodes')
@@ -1631,7 +2462,7 @@ def ShowVnode(cmd_args=None):
     vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *')
     print GetVnodeSummary.header
     print GetVnodeSummary(vnodeval)
     vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *')
     print GetVnodeSummary.header
     print GetVnodeSummary(vnodeval)
-   
+
 @lldb_command('showvolvnodes')
 def ShowVolVnodes(cmd_args=None):
     """ Display info about all vnodes of a given mount_t
 @lldb_command('showvolvnodes')
 def ShowVolVnodes(cmd_args=None):
     """ Display info about all vnodes of a given mount_t
@@ -1723,23 +2554,23 @@ def ShowProcVnodes(cmd_args=None):
     if int(fdptr.fd_rdir) != 0:
         print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir))
     count = 0
     if int(fdptr.fd_rdir) != 0:
         print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir))
     count = 0
-    print '\n' + '{0: <5s} {1: <7s}'.format('fd', 'flags') + GetVnodeSummary.header 
+    print '\n' + '{0: <5s} {1: <7s} {2: <20s} '.format('fd', 'flags', 'fileglob') + GetVnodeSummary.header
     # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
     # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
-    fpptr = Cast(fdptr.fd_ofiles, 'fileproc *')
+    fpptr = Cast(fdptr.fd_ofiles, 'uint64_t *')
     while count < fdptr.fd_nfiles:
         fpp = dereference(fpptr)
     while count < fdptr.fd_nfiles:
         fpp = dereference(fpptr)
-        fproc = Cast(fpp, 'fileproc *')
+        fproc = kern.GetValueFromAddress(int(fpp), 'fileproc *')
         if int(fproc) != 0:
         if int(fproc) != 0:
-            fglob = dereference(fproc).f_fglob
+            fglob = dereference(fproc).fp_glob
             flags = ""
             if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1):
                 if (fdptr.fd_ofileflags[count] & 1):    flags += 'E'
                 if (fdptr.fd_ofileflags[count] & 2):    flags += 'F'
                 if (fdptr.fd_ofileflags[count] & 4):    flags += 'R'
                 if (fdptr.fd_ofileflags[count] & 8):    flags += 'C'
             flags = ""
             if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1):
                 if (fdptr.fd_ofileflags[count] & 1):    flags += 'E'
                 if (fdptr.fd_ofileflags[count] & 2):    flags += 'F'
                 if (fdptr.fd_ofileflags[count] & 4):    flags += 'R'
                 if (fdptr.fd_ofileflags[count] & 8):    flags += 'C'
-                print '{0: <5d} {1: <7s}'.format(count, flags) + GetVnodeSummary(Cast(fglob.fg_data, 'vnode *'))
+                print '{0: <5d} {1: <7s} {2: <#020x} '.format(count, flags, fglob) + GetVnodeSummary(Cast(fglob.fg_data, 'vnode *'))
         count += 1
         count += 1
-        fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'fileproc *')
+        fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'uint64_t *')
 
 @lldb_command('showallprocvnodes')
 def ShowAllProcVnodes(cmd_args=None):
 
 @lldb_command('showallprocvnodes')
 def ShowAllProcVnodes(cmd_args=None):
@@ -1756,9 +2587,9 @@ def ShowAllProcVnodes(cmd_args=None):
 @xnudebug_test('test_vnode')
 def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
     """ Test the functionality of vnode related commands
 @xnudebug_test('test_vnode')
 def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
     """ Test the functionality of vnode related commands
-        returns 
+        returns
          - False on failure
          - False on failure
-         - True on success 
+         - True on success
     """
     if not isConnected:
         print "Target is not connected. Cannot test memstats"
     """
     if not isConnected:
         print "Target is not connected. Cannot test memstats"
@@ -1768,7 +2599,7 @@ def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
     result = res.GetOutput()
     if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5:
         return True
     result = res.GetOutput()
     if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5:
         return True
-    else: 
+    else:
         return False
 
 # Macro: showallmtx
         return False
 
 # Macro: showallmtx
@@ -1802,13 +2633,13 @@ def ShowAllMtx(cmd_args=None):
         hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
     else:
         hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
         hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
     else:
         hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
-    
-    print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')    
+
+    print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
 
     mtxgrp_queue_head = kern.globals.lck_grp_queue
 
     mtxgrp_queue_head = kern.globals.lck_grp_queue
-    mtxgrp_ptr_type = GetType('_lck_grp_ *')   
-    
-    for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"): 
+    mtxgrp_ptr_type = GetType('_lck_grp_ *')
+
+    for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"):
        print GetMutexEntry(mtxgrp_ptr)
     return
 # EndMacro: showallmtx
        print GetMutexEntry(mtxgrp_ptr)
     return
 # EndMacro: showallmtx
@@ -1849,32 +2680,38 @@ def GetMutexLockSummary(mtx):
         return "Invalid lock value: 0x0"
 
     if kern.arch == "x86_64":
         return "Invalid lock value: 0x0"
 
     if kern.arch == "x86_64":
-        out_str = "Lock Type\t\t: MUTEX\n"
-        mtxd = mtx.lck_mtx_sw.lck_mtxd
-        out_str += "Owner Thread\t\t: {:#x}\n".format(mtxd.lck_mtxd_owner)
-        cmd_str = "p/d ((lck_mtx_t*){:#x})->lck_mtx_sw.lck_mtxd.".format(mtx)
-        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_waiters")
-        out_str += "Number of Waiters\t: {:s}\n".format(cmd_out.split()[-1])
-        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_ilocked")
-        out_str += "ILocked\t\t\t: {:s}\n".format(cmd_out.split()[-1])
-        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_mlocked")
-        out_str += "MLocked\t\t\t: {:s}\n".format(cmd_out.split()[-1])
-        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_promoted")
-        out_str += "Promoted\t\t: {:s}\n".format(cmd_out.split()[-1])
-        cmd_out = lldb_run_command(cmd_str + "lck_mtxd_spin")
-        out_str += "Spin\t\t\t: {:s}\n".format(cmd_out.split()[-1])
+        out_str = "Lock Type            : MUTEX\n"
+        if mtx.lck_mtx_tag == 0x07ff1007 :
+            out_str += "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx.lck_mtx_ptr)
+            mtx = Cast(mtx.lck_mtx_ptr, 'lck_mtx_t *')
+
+        if mtx.lck_mtx_tag == 0x07fe2007 :
+            out_str += "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx.lck_mtx_tag)
+
+        out_str += "Owner Thread        : {mtx.lck_mtx_owner:#x}\n".format(mtx=mtx)
+        out_str += "Number of Waiters   : {mtx.lck_mtx_waiters:#x}\n".format(mtx=mtx)
+        out_str += "ILocked             : {mtx.lck_mtx_ilocked:#x}\n".format(mtx=mtx)
+        out_str += "MLocked             : {mtx.lck_mtx_mlocked:#x}\n".format(mtx=mtx)
+        out_str += "Promoted            : {mtx.lck_mtx_promoted:#x}\n".format(mtx=mtx)
+        out_str += "Pri                 : {mtx.lck_mtx_pri:#x}\n".format(mtx=mtx)
+        out_str += "Spin                : {mtx.lck_mtx_spin:#x}\n".format(mtx=mtx)
+        out_str += "Ext                 : {mtx.lck_mtx_is_ext:#x}\n".format(mtx=mtx)
+        if mtx.lck_mtx_pad32 == 0xFFFFFFFF :
+            out_str += "Canary (valid)      : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx)
+        else:
+            out_str += "Canary (INVALID)    : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx)
         return out_str
 
     out_str = "Lock Type\t\t: MUTEX\n"
         return out_str
 
     out_str = "Lock Type\t\t: MUTEX\n"
-    out_str += "Owner Thread\t\t: {:#x}\n".format(mtx.lck_mtx_hdr.lck_mtxd_data & ~0x3)
-    out_str += "Number of Waiters\t: {:d}\n".format(mtx.lck_mtx_sw.lck_mtxd.lck_mtxd_waiters)
+    out_str += "Owner Thread\t\t: {:#x}".format(mtx.lck_mtx_data & ~0x3)
+    if (mtx.lck_mtx_data & ~0x3) == 0xfffffff0:
+        out_str += " Held as spinlock"
+    out_str += "\nNumber of Waiters\t: {:d}\n".format(mtx.lck_mtx_waiters)
     out_str += "Flags\t\t\t: "
     out_str += "Flags\t\t\t: "
-    if mtx.lck_mtx_hdr.lck_mtxd_data & 0x1:
+    if mtx.lck_mtx_data & 0x1:
         out_str += "[Interlock Locked] "
         out_str += "[Interlock Locked] "
-    if mtx.lck_mtx_hdr.lck_mtxd_data & 0x2:
+    if mtx.lck_mtx_data & 0x2:
         out_str += "[Wait Flag]"
         out_str += "[Wait Flag]"
-    if (mtx.lck_mtx_hdr.lck_mtxd_data & 0x3) == 0:
-        out_str += "None"
     return out_str
 
 @lldb_type_summary(['lck_spin_t *'])
     return out_str
 
 @lldb_type_summary(['lck_spin_t *'])
@@ -1894,14 +2731,17 @@ def GetSpinLockSummary(spinlock):
         out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
         return out_str 
 
         out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
         return out_str 
 
-    out_str += "Owner Thread\t\t: {:#x}\n".format(spinlock.lck_spin_data & ~0x3)
-    out_str += "Flags\t\t\t: "
-    if spinlock.lck_spin_data & 0x1:
-        out_str += "[Interlock Locked] "
-    if spinlock.lck_spin_data & 0x2:
-        out_str += "[Wait Flag]"
-    if (spinlock.lck_spin_data & 0x3) == 0:
-        out_str += "None" 
+    lock_data = spinlock.hwlock.lock_data
+    if lock_data == 1:
+        out_str += "Invalid state: interlock is locked but no owner\n"
+        return out_str
+    out_str += "Owner Thread\t\t: "
+    if lock_data == 0:
+        out_str += "None\n"
+    else:
+        out_str += "{:#x}\n".format(lock_data & ~0x1)
+        if (lock_data & 1) == 0:
+            out_str += "Invalid state: owned but interlock bit is not set\n"
     return out_str
 
 @lldb_command('showlock', 'MS')
     return out_str
 
 @lldb_command('showlock', 'MS')
@@ -1916,33 +2756,34 @@ def ShowLock(cmd_args=None, cmd_options={}):
         return
 
     summary_str = ""
         return
 
     summary_str = ""
-    lock = kern.GetValueFromAddress(cmd_args[0], 'uintptr_t*')
-
-    if kern.arch == "x86_64" and lock:
+    addr = cmd_args[0]
+    # from osfmk/arm/locks.h
+    LCK_SPIN_TYPE = 0x11
+    LCK_MTX_TYPE = 0x22
+    if kern.arch == "x86_64":
         if "-M" in cmd_options:
         if "-M" in cmd_options:
-            lock_mtx = Cast(lock, 'lck_mtx_t *')
+            lock_mtx = kern.GetValueFromAddress(addr, 'lck_mtx_t *')
             summary_str = GetMutexLockSummary(lock_mtx)
         elif "-S" in cmd_options:
             summary_str = GetMutexLockSummary(lock_mtx)
         elif "-S" in cmd_options:
-            lock_spin = Cast(lock, 'lck_spin_t *')
+            lock_spin = kern.GetValueFromAddress(addr, 'lck_spin_t *')
             summary_str = GetSpinLockSummary(lock_spin)
         else:
             summary_str = "Please specify supported lock option(-M/-S)"
 
         print summary_str
             summary_str = GetSpinLockSummary(lock_spin)
         else:
             summary_str = "Please specify supported lock option(-M/-S)"
 
         print summary_str
-        return
-
-    if lock:
-        lock_mtx = Cast(lock, 'lck_mtx_t*')
-        if lock_mtx.lck_mtx_type == 0x22:
-            summary_str = GetMutexLockSummary(lock_mtx)
-
-        lock_spin = Cast(lock, 'lck_spin_t*')
-        if lock_spin.lck_spin_type == 0x11:
-            summary_str = GetSpinLockSummary(lock_spin)
-
-    if summary_str == "":
-        summary_str = "Lock Type\t\t: INVALID LOCK" 
-    print summary_str
+    else:
+        lock = kern.GetValueFromAddress(addr, 'uintptr_t *')
+        if lock:
+            lock_mtx = Cast(lock, 'lck_mtx_t*')
+            if lock_mtx.lck_mtx_type == LCK_MTX_TYPE:
+                summary_str = GetMutexLockSummary(lock_mtx)
+
+            lock_spin = Cast(lock, 'lck_spin_t*')
+            if lock_spin.type == LCK_SPIN_TYPE:
+                summary_str = GetSpinLockSummary(lock_spin)
+        if summary_str == "":
+            summary_str = "Lock Type\t\t: INVALID LOCK"
+        print summary_str
 
 #EndMacro: showlock
 
 
 #EndMacro: showlock
 
@@ -1970,12 +2811,10 @@ def ShowBooterMemoryMap(cmd_args=None):
     """ Prints out the phys memory map from kernelBootArgs
         Supported only on x86_64
     """
     """ Prints out the phys memory map from kernelBootArgs
         Supported only on x86_64
     """
-    if kern.arch == 'x86_64':
-        voffset = unsigned(0xFFFFFF8000000000)
-    else:
+    if kern.arch != 'x86_64':
         print "showbootermemorymap not supported on this architecture"
         return
         print "showbootermemorymap not supported on this architecture"
         return
-    
+
     out_string = ""
     
     # Memory type map
     out_string = ""
     
     # Memory type map
@@ -2004,7 +2843,7 @@ def ShowBooterMemoryMap(cmd_args=None):
     
     i = 0
     while i < mcount:
     
     i = 0
     while i < mcount:
-        mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + voffset + unsigned(i*msize), 'EfiMemoryRange *')
+        mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + kern.VM_MIN_KERNEL_ADDRESS + unsigned(i*msize), 'EfiMemoryRange *')
         mtype = unsigned(mptr.Type)
         if mtype in memtype_dict:
             out_string += "{0: <12s}".format(memtype_dict[mtype])
         mtype = unsigned(mptr.Type)
         if mtype in memtype_dict:
             out_string += "{0: <12s}".format(memtype_dict[mtype])
@@ -2020,3 +2859,2108 @@ def ShowBooterMemoryMap(cmd_args=None):
     print out_string
 #EndMacro: showbootermemorymap
 
     print out_string
 #EndMacro: showbootermemorymap
 
+@lldb_command('show_all_purgeable_objects')
+def ShowAllPurgeableVmObjects(cmd_args=None):
+    """ Routine to print a summary listing of all the purgeable vm objects
+    """
+    print "\n--------------------    VOLATILE OBJECTS    --------------------\n"
+    ShowAllPurgeableVolatileVmObjects()
+    print "\n--------------------  NON-VOLATILE OBJECTS  --------------------\n"
+    ShowAllPurgeableNonVolatileVmObjects()
+
+@lldb_command('show_all_purgeable_nonvolatile_objects')
+def ShowAllPurgeableNonVolatileVmObjects(cmd_args=None):
+    """ Routine to print a summary listing of all the vm objects in
+        the purgeable_nonvolatile_queue
+    """
+
+    nonvolatile_total = lambda:None
+    nonvolatile_total.objects = 0
+    nonvolatile_total.vsize = 0
+    nonvolatile_total.rsize = 0
+    nonvolatile_total.wsize = 0
+    nonvolatile_total.csize = 0
+    nonvolatile_total.disowned_objects = 0
+    nonvolatile_total.disowned_vsize = 0
+    nonvolatile_total.disowned_rsize = 0
+    nonvolatile_total.disowned_wsize = 0
+    nonvolatile_total.disowned_csize = 0
+
+    queue_len = kern.globals.purgeable_nonvolatile_count
+    queue_head = kern.globals.purgeable_nonvolatile_queue
+
+    print 'purgeable_nonvolatile_queue:{: <#018x}  purgeable_volatile_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len)
+    print 'N:non-volatile  V:volatile  E:empty  D:deny\n'
+
+    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")
+    idx = 0
+    for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
+        idx += 1
+        ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total)
+    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)
+    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)
+
+
+def ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total):
+    """  Routine to print out a summary a VM object in purgeable_nonvolatile_queue
+        params: 
+            object - core.value : a object of type 'struct vm_object *'
+        returns:
+            None
+    """
+    page_size = kern.globals.page_size
+    if object.purgable == 0:
+        purgable = "N"
+    elif object.purgable == 1:
+        purgable = "V"
+    elif object.purgable == 2:
+        purgable = "E"
+    elif object.purgable == 3:
+        purgable = "D"
+    else:
+        purgable = "?"
+    if object.pager == 0:
+        compressed_count = 0
+    else:
+        compressor_pager = Cast(object.pager, 'compressor_pager *')
+        compressed_count = compressor_pager.cpgr_num_slots_occupied
+
+    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))
+
+    nonvolatile_total.objects += 1
+    nonvolatile_total.vsize += object.vo_un1.vou_size/page_size
+    nonvolatile_total.rsize += object.resident_page_count
+    nonvolatile_total.wsize += object.wired_page_count
+    nonvolatile_total.csize += compressed_count
+    if object.vo_un2.vou_owner == 0:
+        nonvolatile_total.disowned_objects += 1
+        nonvolatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
+        nonvolatile_total.disowned_rsize += object.resident_page_count
+        nonvolatile_total.disowned_wsize += object.wired_page_count
+        nonvolatile_total.disowned_csize += compressed_count
+
+
+@lldb_command('show_all_purgeable_volatile_objects')
+def ShowAllPurgeableVolatileVmObjects(cmd_args=None):
+    """ Routine to print a summary listing of all the vm objects in
+        the purgeable queues
+    """
+    volatile_total = lambda:None
+    volatile_total.objects = 0
+    volatile_total.vsize = 0
+    volatile_total.rsize = 0
+    volatile_total.wsize = 0
+    volatile_total.csize = 0
+    volatile_total.disowned_objects = 0
+    volatile_total.disowned_vsize = 0
+    volatile_total.disowned_rsize = 0
+    volatile_total.disowned_wsize = 0
+    volatile_total.disowned_csize = 0
+
+    purgeable_queues = kern.globals.purgeable_queues
+    print "---------- OBSOLETE\n"
+    ShowPurgeableQueue(purgeable_queues[0], volatile_total)
+    print "\n\n---------- FIFO\n"
+    ShowPurgeableQueue(purgeable_queues[1], volatile_total)
+    print "\n\n---------- LIFO\n"
+    ShowPurgeableQueue(purgeable_queues[2], volatile_total)
+
+    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)
+    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)
+    purgeable_count = kern.globals.vm_page_purgeable_count
+    purgeable_wired_count = kern.globals.vm_page_purgeable_wired_count
+    if purgeable_count != volatile_total.rsize or purgeable_wired_count != volatile_total.wsize:
+        mismatch = "<---------  MISMATCH\n"
+    else:
+        mismatch = ""
+    print "vm_page_purgeable_count:                           resident:{:<10d}  wired:{:<10d}  {:s}\n".format(purgeable_count, purgeable_wired_count, mismatch)
+
+
+def ShowPurgeableQueue(qhead, volatile_total):
+    print "----- GROUP 0\n"
+    ShowPurgeableGroup(qhead.objq[0], volatile_total)
+    print "----- GROUP 1\n"
+    ShowPurgeableGroup(qhead.objq[1], volatile_total)
+    print "----- GROUP 2\n"
+    ShowPurgeableGroup(qhead.objq[2], volatile_total)
+    print "----- GROUP 3\n"
+    ShowPurgeableGroup(qhead.objq[3], volatile_total)
+    print "----- GROUP 4\n"
+    ShowPurgeableGroup(qhead.objq[4], volatile_total)
+    print "----- GROUP 5\n"
+    ShowPurgeableGroup(qhead.objq[5], volatile_total)
+    print "----- GROUP 6\n"
+    ShowPurgeableGroup(qhead.objq[6], volatile_total)
+    print "----- GROUP 7\n"
+    ShowPurgeableGroup(qhead.objq[7], volatile_total)
+
+def ShowPurgeableGroup(qhead, volatile_total):
+    idx = 0
+    for object in IterateQueue(qhead, 'struct vm_object *', 'objq'):
+        if idx == 0:
+#            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","")
+            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")
+        idx += 1
+        ShowPurgeableVolatileVmObject(object, idx, volatile_total)
+
+def ShowPurgeableVolatileVmObject(object, idx, volatile_total):
+    """  Routine to print out a summary a VM object in a purgeable queue
+        params: 
+            object - core.value : a object of type 'struct vm_object *'
+        returns:
+            None
+    """
+##   if int(object.vo_un2.vou_owner) != int(object.vo_purgeable_volatilizer):
+#        diff=" !="
+##    else:
+#        diff="  "
+    page_size = kern.globals.page_size
+    if object.purgable == 0:
+        purgable = "N"
+    elif object.purgable == 1:
+        purgable = "V"
+    elif object.purgable == 2:
+        purgable = "E"
+    elif object.purgable == 3:
+        purgable = "D"
+    else:
+        purgable = "?"
+    if object.pager == 0:
+        compressed_count = 0
+    else:
+        compressor_pager = Cast(object.pager, 'compressor_pager *')
+        compressed_count = compressor_pager.cpgr_num_slots_occupied
+#    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)
+    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))
+    volatile_total.objects += 1
+    volatile_total.vsize += object.vo_un1.vou_size/page_size
+    volatile_total.rsize += object.resident_page_count
+    volatile_total.wsize += object.wired_page_count
+    volatile_total.csize += compressed_count
+    if object.vo_un2.vou_owner == 0:
+        volatile_total.disowned_objects += 1
+        volatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
+        volatile_total.disowned_rsize += object.resident_page_count
+        volatile_total.disowned_wsize += object.wired_page_count
+        volatile_total.disowned_csize += compressed_count
+
+
+def GetCompressedPagesForObject(obj):
+    """Stuff
+    """
+    pager = Cast(obj.pager, 'compressor_pager_t')
+    return pager.cpgr_num_slots_occupied
+    """  # commented code below
+    if pager.cpgr_num_slots > 128:
+        slots_arr = pager.cpgr_slots.cpgr_islots
+        num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
+        index = 0
+        compressor_slot = 0
+        compressed_pages = 0
+        while index < num_indirect_slot_ptr:
+            compressor_slot = 0
+            if slots_arr[index]:
+                while compressor_slot < 128:
+                    if slots_arr[index][compressor_slot]:
+                        compressed_pages += 1
+                    compressor_slot += 1
+            index += 1
+    else:
+        slots_arr = pager.cpgr_slots.cpgr_dslots
+        compressor_slot = 0
+        compressed_pages = 0
+        while compressor_slot < pager.cpgr_num_slots:
+            if slots_arr[compressor_slot]:
+                compressed_pages += 1
+            compressor_slot += 1
+    return compressed_pages
+    """
+
+def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
+    """  Routine to print out a summary listing of all the entries in a vm_map
+        params: 
+            task - core.value : a object of type 'task *'
+        returns:
+            None
+    """
+    print "vm_map entries for task " + hex(task)
+    print GetTaskSummary.header
+    print GetTaskSummary(task)
+    if not task.map:
+        print "Task {0: <#020x} has map = 0x0"
+        return None
+    showmapvme(task.map, 0, 0, show_pager_info, show_all_shadows, False)
+
+@lldb_command("showmapvme", "A:B:F:PRST")
+def ShowMapVME(cmd_args=None, cmd_options={}):
+    """Routine to print out info about the specified vm_map and its vm entries
+        usage: showmapvme <vm_map> [-A start] [-B end] [-S] [-P]
+        Use -A <start> flag to start at virtual address <start>
+        Use -B <end> flag to end at virtual address <end>
+        Use -F <virtaddr> flag to find just the VME containing the given VA
+        Use -S flag to show VM object shadow chains
+        Use -P flag to show pager info (mapped file, compressed pages, ...)
+        Use -R flag to reverse order
+        Use -T to show red-black tree pointers
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Invalid argument.", ShowMapVME.__doc__
+        return
+    show_pager_info = False
+    show_all_shadows = False
+    show_rb_tree = False
+    start_vaddr = 0
+    end_vaddr = 0
+    reverse_order = False
+    if "-A" in cmd_options:
+        start_vaddr = unsigned(int(cmd_options['-A'], 16))
+    if "-B" in cmd_options:
+        end_vaddr = unsigned(int(cmd_options['-B'], 16))
+    if "-F" in cmd_options:
+        start_vaddr = unsigned(int(cmd_options['-F'], 16))
+        end_vaddr = start_vaddr
+    if "-P" in cmd_options:
+        show_pager_info = True
+    if "-S" in cmd_options:
+        show_all_shadows = True
+    if "-R" in cmd_options:
+        reverse_order = True
+    if "-T" in cmd_options:
+        show_rb_tree = True
+    map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
+    showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+@lldb_command("showmapcopyvme", "A:B:F:PRST")
+def ShowMapCopyVME(cmd_args=None, cmd_options={}):
+    """Routine to print out info about the specified vm_map_copy and its vm entries
+        usage: showmapcopyvme <vm_map_copy> [-A start] [-B end] [-S] [-P]
+        Use -A <start> flag to start at virtual address <start>
+        Use -B <end> flag to end at virtual address <end>
+        Use -F <virtaddr> flag to find just the VME containing the given VA
+        Use -S flag to show VM object shadow chains
+        Use -P flag to show pager info (mapped file, compressed pages, ...)
+        Use -R flag to reverse order
+        Use -T to show red-black tree pointers
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Invalid argument.", ShowMapVME.__doc__
+        return
+    show_pager_info = False
+    show_all_shadows = False
+    show_rb_tree = False
+    start_vaddr = 0
+    end_vaddr = 0
+    reverse_order = False
+    if "-A" in cmd_options:
+        start_vaddr = unsigned(int(cmd_options['-A'], 16))
+    if "-B" in cmd_options:
+        end_vaddr = unsigned(int(cmd_options['-B'], 16))
+    if "-F" in cmd_options:
+        start_vaddr = unsigned(int(cmd_options['-F'], 16))
+        end_vaddr = start_vaddr
+    if "-P" in cmd_options:
+        show_pager_info = True
+    if "-S" in cmd_options:
+        show_all_shadows = True
+    if "-R" in cmd_options:
+        reverse_order = True
+    if "-T" in cmd_options:
+        show_rb_tree = True
+    map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_copy_t')
+    showmapcopyvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+@lldb_command("showvmobject", "A:B:PRST")
+def ShowVMObject(cmd_args=None, cmd_options={}):
+    """Routine to print out a VM object and its shadow chain
+        usage: showvmobject <vm_object> [-S] [-P]
+        -S: show VM object shadow chain
+        -P: show pager info (mapped file, compressed pages, ...)
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Invalid argument.", ShowMapVME.__doc__
+        return
+    show_pager_info = False
+    show_all_shadows = False
+    if "-P" in cmd_options:
+        show_pager_info = True
+    if "-S" in cmd_options:
+        show_all_shadows = True
+    object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
+    showvmobject(object, 0, 0, show_pager_info, show_all_shadows)
+
+def showvmobject(object, offset=0, size=0, show_pager_info=False, show_all_shadows=False):
+    page_size = kern.globals.page_size
+    vnode_pager_ops = kern.globals.vnode_pager_ops
+    vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
+    depth = 0
+    if size == 0 and object != 0 and object.internal:
+        size = object.vo_un1.vou_size
+    while object != 0:
+        depth += 1
+        if show_all_shadows == False and depth != 1 and object.shadow != 0:
+            offset += unsigned(object.vo_un2.vou_shadow_offset)
+            object = object.shadow
+            continue
+        if object.copy_strategy == 0:
+            copy_strategy="N"
+        elif object.copy_strategy == 2:
+            copy_strategy="D"
+        elif object.copy_strategy == 4:
+            copy_strategy="S"
+
+        else:
+            copy_strategy=str(object.copy_strategy)
+        if object.internal:
+            internal = "internal"
+        else:
+            internal = "external"
+        purgeable = "NVED"[int(object.purgable)]
+        pager_string = ""
+        if object.phys_contiguous:
+            pager_string = pager_string + "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2.vou_shadow_offset), unsigned(object.vo_un1.vou_size))
+        pager = object.pager
+        if show_pager_info and pager != 0:
+            if object.internal:
+                pager_string = pager_string + "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
+            elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr:
+                vnode_pager = Cast(pager,'vnode_pager *')
+                pager_string = pager_string + "-> " + GetVnodePath(vnode_pager.vnode_handle)
+            else:
+                pager_string = pager_string + "-> {:s}:{: <#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager)
+        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)
+#       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)
+        offset += unsigned(object.vo_un2.vou_shadow_offset)
+        object = object.shadow
+
+def showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order=False, show_rb_tree=False):
+    rsize = 0
+    if map.pmap != 0:
+        rsize = int(map.pmap.stats.resident_count)
+    print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map","pmap","size","#ents","rsize","start","end","pgshift")
+    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)
+    showmaphdrvme(map.hdr, map.pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+def showmapcopyvme(mapcopy, start_vaddr=0, end_vaddr=0, show_pager_info=True, show_all_shadows=True, reverse_order=False, show_rb_tree=False):
+    print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map_copy","offset","size","#ents","rsize","start","end","pgshift")
+    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)
+    showmaphdrvme(mapcopy.c_u.hdr, 0, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree):
+    page_size = kern.globals.page_size
+    vnode_pager_ops = kern.globals.vnode_pager_ops
+    vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
+    if hasattr(kern.globals, 'compressor_object'):
+        compressor_object = kern.globals.compressor_object
+    else:
+        compressor_object = -1;
+    vme_list_head = maphdr.links
+    vme_ptr_type = GetType('vm_map_entry *')
+    print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
+    last_end = unsigned(maphdr.links.start)
+    skipped_entries = 0
+    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links", reverse_order):
+        if start_vaddr != 0 and end_vaddr != 0:
+            if unsigned(vme.links.start) > end_vaddr:
+                break
+            if unsigned(vme.links.end) <= start_vaddr:
+                last_end = unsigned(vme.links.end)
+                skipped_entries = skipped_entries + 1
+                continue
+            if skipped_entries != 0:
+                print "... skipped {:d} entries ...".format(skipped_entries)
+                skipped_entries = 0
+        if unsigned(vme.links.start) != last_end:
+            print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme.links.start,(unsigned(vme.links.start) - last_end)/page_size)
+        last_end = unsigned(vme.links.end)
+        size = unsigned(vme.links.end) - unsigned(vme.links.start)
+        object = vme.vme_object.vmo_object
+        if object == 0:
+            object_str = "{: <#018x}".format(object)
+        elif vme.is_sub_map:
+            if object == kern.globals.bufferhdr_map:
+                object_str = "BUFFERHDR_MAP"
+            elif object == kern.globals.mb_map:
+                object_str = "MB_MAP"
+            elif object == kern.globals.bsd_pageable_map:
+                object_str = "BSD_PAGEABLE_MAP"
+            elif object == kern.globals.ipc_kernel_map:
+                object_str = "IPC_KERNEL_MAP"
+            elif object == kern.globals.ipc_kernel_copy_map:
+                object_str = "IPC_KERNEL_COPY_MAP"
+            elif object == kern.globals.kalloc_map:
+                object_str = "KALLOC_MAP"
+            elif hasattr(kern.globals, 'compressor_map') and object == kern.globals.compressor_map:
+                object_str = "COMPRESSOR_MAP"
+            elif hasattr(kern.globals, 'gzalloc_map') and object == kern.globals.gzalloc_map:
+                object_str = "GZALLOC_MAP"
+            elif hasattr(kern.globals, 'g_kext_map') and object == kern.globals.g_kext_map:
+                object_str = "G_KEXT_MAP"
+            elif hasattr(kern.globals, 'vector_upl_submap') and object == kern.globals.vector_upl_submap:
+                object_str = "VECTOR_UPL_SUBMAP"
+            else:
+                object_str = "submap:{: <#018x}".format(object)
+        else:
+            if object == kern.globals.kernel_object:
+                object_str = "KERNEL_OBJECT"
+            elif object == kern.globals.vm_submap_object:
+                object_str = "VM_SUBMAP_OBJECT"
+            elif object == compressor_object:
+                object_str = "COMPRESSOR_OBJECT"
+            else:
+                object_str = "{: <#018x}".format(object)
+        offset = unsigned(vme.vme_offset) & ~0xFFF
+        tag = unsigned(vme.vme_offset & 0xFFF)
+        protection = ""
+        if vme.protection & 0x1:
+            protection +="r"
+        else:
+            protection += "-"
+        if vme.protection & 0x2:
+            protection += "w"
+        else:
+            protection += "-"
+        if vme.protection & 0x4:
+            protection += "x"
+        else:
+            protection += "-"
+        max_protection = ""
+        if vme.max_protection & 0x1:
+            max_protection +="r"
+        else:
+            max_protection += "-"
+        if vme.max_protection & 0x2:
+            max_protection += "w"
+        else:
+            max_protection += "-"
+        if vme.max_protection & 0x4:
+            max_protection += "x"
+        else:
+            max_protection += "-"
+        vme_flags = ""
+        if vme.is_sub_map:
+            vme_flags += "s"
+        if vme.needs_copy:
+            vme_flags += "n"
+        if vme.use_pmap:
+            vme_flags += "p"
+        if vme.wired_count:
+            vme_flags += "w"
+        if vme.used_for_jit:
+            vme_flags += "j"
+        tagstr = ""
+        if pmap == kern.globals.kernel_pmap:
+            xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *')
+            if xsite and xsite.site.flags & 0x0200:
+                tagstr = ".{:<3d}".format(xsite.loadTag)
+        rb_info = ""
+        if show_rb_tree:
+            rb_info = "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme.store.entry.rbe_left, vme.store.entry.rbe_right, vme.store.entry.rbe_parent)
+        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)
+        if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and vme.vme_object.vmo_object != 0:
+            object = vme.vme_object.vmo_object
+        else:
+            object = 0
+        showvmobject(object, offset, size, show_pager_info, show_all_shadows)
+    if start_vaddr != 0 or end_vaddr != 0:
+        print "..."
+    elif unsigned(maphdr.links.end) > last_end:
+        print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,maphdr.links.end,(unsigned(maphdr.links.end) - last_end)/page_size)
+    return None
+
+def CountMapTags(map, tagcounts, slow):
+    page_size = unsigned(kern.globals.page_size)
+    vme_list_head = map.hdr.links
+    vme_ptr_type = GetType('vm_map_entry *')
+    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
+        object = vme.vme_object.vmo_object
+        tag = vme.vme_offset & 0xFFF
+        if object == kern.globals.kernel_object:
+            count = 0
+            if not slow:
+                count = unsigned(vme.links.end - vme.links.start) / page_size
+            else:
+                addr = unsigned(vme.links.start)
+                while addr < unsigned(vme.links.end):
+                    hash_id = _calc_vm_page_hash(object, addr)
+                    page_list = kern.globals.vm_page_buckets[hash_id].page_list
+                    page = _vm_page_unpack_ptr(page_list)
+                    while (page != 0):
+                        vmpage = kern.GetValueFromAddress(page, 'vm_page_t')
+                        if (addr == unsigned(vmpage.vmp_offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vmp_object))):
+                            if (not vmpage.vmp_local) and (vmpage.vmp_wire_count > 0):
+                                count += 1
+                            break
+                        page = _vm_page_unpack_ptr(vmpage.vmp_next_m)
+                    addr += page_size
+            tagcounts[tag] += count
+        elif vme.is_sub_map:
+            CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow)
+    return None
+
+def CountWiredObject(object, tagcounts):
+    tagcounts[unsigned(object.wire_tag)] += object.wired_page_count
+    return None
+
+def GetKmodIDName(kmod_id):
+    kmod_val = kern.globals.kmod
+    for kmod in IterateLinkedList(kmod_val, 'next'):
+        if (kmod.id == kmod_id):
+            return "{:<50s}".format(kmod.name)
+    return "??"
+
+FixedTags = {
+    0:  "VM_KERN_MEMORY_NONE",
+    1:  "VM_KERN_MEMORY_OSFMK",
+    2:  "VM_KERN_MEMORY_BSD",
+    3:  "VM_KERN_MEMORY_IOKIT",
+    4:  "VM_KERN_MEMORY_LIBKERN",
+    5:  "VM_KERN_MEMORY_OSKEXT",
+    6:  "VM_KERN_MEMORY_KEXT",
+    7:  "VM_KERN_MEMORY_IPC",
+    8:  "VM_KERN_MEMORY_STACK",
+    9:  "VM_KERN_MEMORY_CPU",
+    10: "VM_KERN_MEMORY_PMAP",
+    11: "VM_KERN_MEMORY_PTE",
+    12: "VM_KERN_MEMORY_ZONE",
+    13: "VM_KERN_MEMORY_KALLOC",
+    14: "VM_KERN_MEMORY_COMPRESSOR",
+    15: "VM_KERN_MEMORY_COMPRESSED_DATA",
+    16: "VM_KERN_MEMORY_PHANTOM_CACHE",
+    17: "VM_KERN_MEMORY_WAITQ",
+    18: "VM_KERN_MEMORY_DIAG",
+    19: "VM_KERN_MEMORY_LOG",
+    20: "VM_KERN_MEMORY_FILE",
+    21: "VM_KERN_MEMORY_MBUF",
+    22: "VM_KERN_MEMORY_UBC",
+    23: "VM_KERN_MEMORY_SECURITY",
+    24: "VM_KERN_MEMORY_MLOCK",
+    25: "VM_KERN_MEMORY_REASON",
+    26: "VM_KERN_MEMORY_SKYWALK",
+    27: "VM_KERN_MEMORY_LTABLE",
+    28: "VM_KERN_MEMORY_HV",
+    255:"VM_KERN_MEMORY_ANY",
+}
+
+def GetVMKernName(tag):
+    """ returns the formatted name for a vmtag and
+        the sub-tag for kmod tags.
+    """
+    if ((tag <= 27) or (tag == 255)):
+        return (FixedTags[tag], "")
+    site = kern.globals.vm_allocation_sites[tag]
+    if site:
+        if site.flags & 0x007F:
+            cstr = addressof(site.subtotals[site.subtotalscount])
+            return ("{:<50s}".format(str(Cast(cstr, 'char *'))), "")
+        else:
+            if site.flags & 0x0200:
+                xsite = Cast(site,'OSKextAccount *')
+                tagstr = ".{:<3d}".format(xsite.loadTag)
+                return (GetKmodIDName(xsite.loadTag), tagstr);
+            else:
+                return (kern.Symbolicate(site), "")
+    return ("", "")
+
+@lldb_command("showvmtags", "ASJ")
+def showvmtags(cmd_args=None, cmd_options={}):
+    """Routine to print out info about kernel wired page allocations
+        usage: showvmtags
+               iterates kernel map and vm objects totaling allocations by tag.
+        usage: showvmtags -S
+               also iterates kernel object pages individually - slow.
+        usage: showvmtags -A
+               show all tags, even tags that have no wired count
+        usage: showvmtags -J
+                Output json
+    """
+    slow = False
+    print_json = False
+    if "-S" in cmd_options:
+        slow = True
+    all_tags = False
+    if "-A" in cmd_options:
+        all_tags = True
+    if "-J" in cmd_options:
+        print_json = True
+
+    page_size = unsigned(kern.globals.page_size)
+    nsites = unsigned(kern.globals.vm_allocation_tag_highest) + 1
+    tagcounts = [0] * nsites
+    tagpeaks = [0] * nsites
+    tagmapped = [0] * nsites
+
+    if kern.globals.vm_tag_active_update:
+        for tag in range(nsites):
+            site = kern.globals.vm_allocation_sites[tag]
+            if site:
+                tagcounts[tag] = unsigned(site.total)
+                tagmapped[tag] = unsigned(site.mapped)
+                tagpeaks[tag] = unsigned(site.peak)
+    else:
+        queue_head = kern.globals.vm_objects_wired
+        for object in IterateQueue(queue_head, 'struct vm_object *', 'wired_objq'):
+            if object != kern.globals.kernel_object:
+                CountWiredObject(object, tagcounts)
+
+        CountMapTags(kern.globals.kernel_map, tagcounts, slow)
+
+    total = 0
+    totalmapped = 0
+    tags = []
+    for tag in range(nsites):
+        if all_tags or tagcounts[tag] or tagmapped[tag]:
+            current = {}
+            total += tagcounts[tag]
+            totalmapped += tagmapped[tag]
+            (sitestr, tagstr) = GetVMKernName(tag)
+            current["name"] = sitestr
+            current["size"] = tagcounts[tag]
+            current["mapped"] = tagmapped[tag]
+            current["peak"] = tagpeaks[tag]
+            current["tag"] = tag
+            current["tagstr"] = tagstr
+            current["subtotals"] = []
+
+            site = kern.globals.vm_allocation_sites[tag]
+            for sub in range(site.subtotalscount):
+                alloctag = unsigned(site.subtotals[sub].tag)
+                amount = unsigned(site.subtotals[sub].total)
+                subsite = kern.globals.vm_allocation_sites[alloctag]
+                if alloctag and subsite:
+                    (sitestr, tagstr) = GetVMKernName(alloctag)
+                    current["subtotals"].append({
+                        "amount": amount,
+                        "flags": int(subsite.flags),
+                        "tag": alloctag,
+                        "tagstr": tagstr,
+                        "sitestr": sitestr,
+                    })
+            tags.append(current)
+
+    if print_json:
+        print json.dumps(tags)
+    else:
+        print " vm_allocation_tag_highest: {:<7d}  ".format(nsites - 1)
+        print " {:<7s}  {:>7s}   {:>7s}   {:>7s}  {:<50s}".format("tag.kmod", "peak", "size", "mapped", "name")
+        for tag in tags:
+            if not tagstr:
+                tagstr = ""
+            print " {:>3d}{:<4s}  {:>7d}K  {:>7d}K  {:>7d}K  {:<50s}".format(tag["tag"], tag["tagstr"], tag["peak"] / 1024, tag["size"] / 1024, tag["mapped"] / 1024, tag["name"])
+            for sub in tag["subtotals"]:
+                if ((sub["flags"] & 0x007f) == 0):
+                    kind_str = "named"
+                else:
+                    kind_str = "from"
+
+                print " {:>7s}  {:>7s}   {:>7s}   {:>7d}K      {:s} {:>3d}{:<4s} {:<50s}".format(" ", " ", " ", sub["amount"] / 1024, kind_str, sub["tag"], sub["tagstr"], sub["sitestr"])
+
+        print "Total:              {:>7d}K  {:>7d}K".format(total / 1024, totalmapped / 1024)
+    return None
+
+
+def FindVMEntriesForVnode(task, vn):
+    """ returns an array of vme that have the vnode set to defined vnode
+        each entry in array is of format (vme, start_addr, end_address, protection)
+    """
+    retval = []
+    vmmap = task.map
+    pmap = vmmap.pmap
+    pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops))
+    debuglog("pager_ops_addr %s" % hex(pager_ops_addr))
+
+    if unsigned(pmap) == 0:
+        return retval
+    vme_list_head = vmmap.hdr.links
+    vme_ptr_type = gettype('vm_map_entry *')
+    for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'):
+        #print vme
+        if unsigned(vme.is_sub_map) == 0 and unsigned(vme.vme_object.vmo_object) != 0:
+            obj = vme.vme_object.vmo_object
+        else:
+            continue
+
+        while obj != 0:
+            if obj.pager != 0:
+                if obj.internal:
+                    pass
+                else:
+                    vn_pager = Cast(obj.pager, 'vnode_pager *')
+                    if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn):
+                        retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection)))
+            obj = obj.shadow
+    return retval
+
+@lldb_command('showtaskloadinfo')
+def ShowTaskLoadInfo(cmd_args=None, cmd_options={}):
+    """ Print the load address and uuid for the process
+        Usage: (lldb)showtaskloadinfo <task_t>
+    """
+    if not cmd_args:
+        raise ArgumentError("Insufficient arguments")
+    t = kern.GetValueFromAddress(cmd_args[0], 'struct task *')
+    print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
+    p = Cast(t.bsd_info, 'struct proc *')
+    uuid = p.p_uuid
+    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)
+    filepath = GetVnodePath(p.p_textvp)
+    libname = filepath.split('/')[-1]
+    #print "uuid: %s file: %s" % (uuid_out_string, filepath)
+    mappings = FindVMEntriesForVnode(t, p.p_textvp)
+    load_addr = 0
+    end_addr = 0
+    for m in mappings:
+        if m[3] == 5:
+            load_addr = m[1]
+            end_addr = m[2]
+            #print "Load address: %s" % hex(m[1])
+    print print_format.format(load_addr, end_addr, libname, uuid_out_string, filepath)
+    return None
+
+@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
+@lldb_command('vmpagelookup')
+def VMPageLookup(cmd_args=None):
+    """ Print the pages in the page bucket corresponding to the provided object and offset.
+        Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        raise ArgumentError("Please specify an object and offset.")
+    format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n"
+
+    obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
+    off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
+
+    hash_id = _calc_vm_page_hash(obj, off)
+
+    page_list = kern.globals.vm_page_buckets[hash_id].page_list
+    print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list)))
+
+    print VMPageLookup.header
+    page = _vm_page_unpack_ptr(page_list)
+    while (page != 0) :
+        pg_t = kern.GetValueFromAddress(page, 'vm_page_t')
+        print format_string.format(page, pg_t.vmp_offset, _vm_page_unpack_ptr(pg_t.vmp_object))
+        page = _vm_page_unpack_ptr(pg_t.vmp_next_m)
+
+
+
+@lldb_command('vmpage_get_phys_page')
+def VmPageGetPhysPage(cmd_args=None):
+    """ return the physical page for a vm_page_t
+        usage: vm_page_get_phys_page <vm_page_t>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print  "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
+        return
+
+    page = kern.GetValueFromAddress(cmd_args[0], 'vm_page_t')
+    phys_page = _vm_page_get_phys_page(page)
+    print("phys_page = 0x%x\n" % phys_page)
+
+
+def _vm_page_get_phys_page(page):
+    if kern.arch == 'x86_64':
+        return page.vmp_phys_page
+
+    if page == 0 :
+        return 0
+
+    m = unsigned(page)
+    if m >= unsigned(kern.globals.vm_page_array_beginning_addr) and m < unsigned(kern.globals.vm_page_array_ending_addr) :
+        return (m - unsigned(kern.globals.vm_page_array_beginning_addr)) / sizeof('struct vm_page') + unsigned(kern.globals.vm_first_phys_ppnum)
+
+    page_with_ppnum = Cast(page, 'uint32_t *')
+    ppnum_offset = sizeof('struct vm_page') / sizeof('uint32_t')
+    return page_with_ppnum[ppnum_offset]
+
+
+@lldb_command('vmpage_unpack_ptr')
+def VmPageUnpackPtr(cmd_args=None):
+    """ unpack a pointer
+        usage: vm_page_unpack_ptr <packed_ptr>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print  "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
+        return
+
+    packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long')
+    unpacked = _vm_page_unpack_ptr(packed)
+    print("unpacked pointer = 0x%x\n" % unpacked)
+
+
+def _vm_page_unpack_ptr(page):
+    if kern.ptrsize == 4 :
+        return page
+
+    if page == 0 :
+        return page
+
+    params = kern.globals.vm_page_packing_params
+    ptr_shift = params.vmpp_shift
+    ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask
+
+    # when no mask and shift on 64bit systems, we're working with real/non-packed pointers
+    if ptr_shift == 0 and ptr_mask == 0:
+        return page
+
+    if unsigned(page) & unsigned(ptr_mask):
+        masked_page = (unsigned(page) & ~ptr_mask)
+        # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge
+        vm_pages_addr = unsigned(addressof(kern.globals.vm_pages[0]))
+        element_size = unsigned(addressof(kern.globals.vm_pages[1])) - vm_pages_addr
+        return (vm_pages_addr + masked_page * element_size)
+    return unsigned(vm_unpack_pointer(page, params))
+
+@lldb_command('calcvmpagehash')
+def CalcVMPageHash(cmd_args=None):
+    """ Get the page bucket corresponding to the provided object and offset.
+        Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        raise ArgumentError("Please specify an object and offset.")
+
+    obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
+    off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
+
+    hash_id = _calc_vm_page_hash(obj, off)
+
+    print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
+    return None
+
+def _calc_vm_page_hash(obj, off):
+    bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
+    hash_mask = (int) (kern.globals.vm_page_hash_mask)
+
+    one = (obj * bucket_hash) & 0xFFFFFFFF
+    two = off >> unsigned(kern.globals.page_shift)
+    three = two ^ bucket_hash
+    four = one + three
+    hash_id = four & hash_mask
+
+    return hash_id
+
+#Macro: showallocatedzoneelement
+@lldb_command('showallocatedzoneelement')
+def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}):
+    """ Show all the allocated elements in a zone
+        usage: showzoneallocelements <address of zone>
+    """
+    if len(cmd_args) < 1:
+        raise ArgumentError("Please specify a zone")
+
+    zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
+    elements = FindAllocatedElementsInZone(zone)
+    i = 1
+    for elem in elements:
+        print "{0: >10d}/{1:<10d} element: {2: <#20x}".format(i, len(elements), elem)
+        i += 1
+
+#EndMacro: showallocatedzoneelement
+
+def FindAllocatedElementsInZone(zone):
+    elements = []
+
+    if not zone.z_self or zone.permanent:
+        return elements
+
+    for head in [zone.pages_any_free_foreign, zone.pages_all_used_foreign,
+            zone.pages_intermediate, zone.pages_all_used]:
+
+        for meta in ZoneIteratePageQueue(head):
+            free_elements = set(meta.iterateFreeList())
+
+            for elem in meta.iterateElements():
+                if elem in free_elements:
+                    continue
+
+                if elem not in free_elements:
+                    elements.append(elem)
+                elem += zone.z_elem_size
+
+    return elements
+
+def match_vm_page_attributes(page, matching_attributes):
+    page_ptr = addressof(page)
+    unpacked_vm_object = _vm_page_unpack_ptr(page.vmp_object)
+    matched_attributes = 0
+    if "vmp_q_state" in matching_attributes and (page.vmp_q_state == matching_attributes["vmp_q_state"]):
+        matched_attributes += 1
+    if "vm_object" in matching_attributes and (unsigned(unpacked_vm_object) == unsigned(matching_attributes["vm_object"])):
+        matched_attributes += 1
+    if "vmp_offset" in matching_attributes and (unsigned(page.vmp_offset) == unsigned(matching_attributes["vmp_offset"])):
+        matched_attributes += 1
+    if "phys_page" in matching_attributes and (unsigned(_vm_page_get_phys_page(page_ptr)) == unsigned(matching_attributes["phys_page"])):
+        matched_attributes += 1
+    if "bitfield" in matching_attributes and unsigned(page.__getattr__(matching_attributes["bitfield"])) == 1:
+        matched_attributes += 1
+
+    return matched_attributes
+
+#Macro scan_vm_pages
+@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"))
+@lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA')
+def ScanVMPages(cmd_args=None, cmd_options={}):
+    """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes.
+        usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone]
+
+            scan_vm_pages -A: scan vm pages in the global vm_pages array
+            scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone
+            scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue
+            scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object
+            scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value
+            scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number
+            scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set
+            scan_vm_pages <-A> -I <start_index>: Start the scan from start_index
+            scan_vm_pages <-A> -N <npages>: Scan at most npages
+    """
+    if (len(cmd_options) < 1):
+        raise ArgumentError("Please specify at least one matching attribute")
+
+    vm_pages = kern.globals.vm_pages
+    vm_pages_count = kern.globals.vm_pages_count
+
+    start_index = 0
+    npages = vm_pages_count
+    scan_vmpages_array = False
+    scan_vmpages_zone = False
+    attribute_count = 0
+
+    if "-A" in cmd_options:
+        scan_vmpages_array = True
+
+    if "-Z" in cmd_options:
+        scan_vmpages_zone = True
+
+    if scan_vmpages_array == False and scan_vmpages_zone == False:
+        raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)")
+
+    attribute_values = {}
+    if "-S" in cmd_options:
+        attribute_values["vmp_q_state"] = kern.GetValueFromAddress(cmd_options["-S"], 'int')
+        attribute_count += 1
+
+    if "-O" in cmd_options:
+        attribute_values["vm_object"] = kern.GetValueFromAddress(cmd_options["-O"], 'vm_object_t')
+        attribute_count += 1
+
+    if "-F" in cmd_options:
+        attribute_values["vmp_offset"] = kern.GetValueFromAddress(cmd_options["-F"], 'unsigned long long')
+        attribute_count += 1
+
+    if "-P" in cmd_options:
+        attribute_values["phys_page"] = kern.GetValueFromAddress(cmd_options["-P"], 'unsigned int')
+        attribute_count += 1
+
+    if "-B" in cmd_options:
+        valid_vmp_bitfields = [
+            "vmp_in_background",
+            "vmp_on_backgroundq",
+            "vmp_gobbled",
+            "vmp_laundry",
+            "vmp_no_cache",
+            "vmp_private",
+            "vmp_reference",
+            "vmp_busy",
+            "vmp_wanted",
+            "vmp_tabled",
+            "vmp_hashed",
+            "vmp_fictitious",
+            "vmp_clustered",
+            "vmp_pmapped",
+            "vmp_xpmapped",
+            "vmp_free_when_done",
+            "vmp_absent",
+            "vmp_error",
+            "vmp_dirty",
+            "vmp_cleaning",
+            "vmp_precious",
+            "vmp_overwriting",
+            "vmp_restart",
+            "vmp_unusual",
+            "vmp_cs_validated",
+            "vmp_cs_tainted",
+            "vmp_cs_nx",
+            "vmp_reusable",
+            "vmp_lopage",
+            "vmp_written_by_kernel",
+            "vmp_unused_object_bits"
+            ]
+        attribute_values["bitfield"] = cmd_options["-B"]
+        if attribute_values["bitfield"] in valid_vmp_bitfields:
+            attribute_count += 1
+        else:
+            raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield))
+
+    if "-I" in cmd_options:
+        start_index = kern.GetValueFromAddress(cmd_options["-I"], 'int')
+        npages = vm_pages_count - start_index
+
+    if "-N" in cmd_options:
+        npages = kern.GetValueFromAddress(cmd_options["-N"], 'int')
+        if npages == 0:
+            raise ArgumentError("You specified -N 0, nothing to be scanned")
+
+    end_index = start_index + npages - 1
+    if end_index >= vm_pages_count:
+        raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count))
+
+    header_after_n_lines = 40
+    format_string = "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}"
+
+    found_in_array = 0
+    if scan_vmpages_array:
+        print "Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index, end_index, attribute_count)
+        i = start_index
+        while i <= end_index:
+            page = vm_pages[i]
+            if match_vm_page_attributes(page, attribute_values) == attribute_count:
+                if found_in_array % header_after_n_lines == 0:
+                    print ScanVMPages.header
+
+                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)))
+                found_in_array += 1
+
+            i += 1
+
+    found_in_zone = 0
+    if scan_vmpages_zone:
+        page_size = kern.GetGlobalVariable('page_size')
+        num_zones = kern.GetGlobalVariable('num_zones')
+        zone_array = kern.GetGlobalVariable('zone_array')
+        print "Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count)
+        i = 0
+        while i < num_zones:
+            zone = zone_array[i]
+            if str(zone.z_name) == "vm pages":
+                break;
+            i += 1
+
+        if i == num_zones:
+            print "Cannot find vm_pages zone, skip the scan"
+        else:
+            print "Scanning page queues in the vm_pages zone..."
+            elements = FindAllocatedElementsInZone(zone)
+            for elem in elements:
+                page = kern.GetValueFromAddress(elem, 'vm_page_t')
+
+                if match_vm_page_attributes(page, attribute_values) == attribute_count:
+                    if found_in_zone % header_after_n_lines == 0:
+                        print ScanVMPages.header
+
+                    vm_object = _vm_page_unpack_ptr(page.vmp_object)
+                    phys_page = _vm_page_get_phys_page(page)
+                    print format_string.format("vm_pages zone", elem, page.vmp_q_state, vm_object, page.vmp_offset, phys_page)
+                    found_in_zone += 1
+
+    total = found_in_array + found_in_zone
+    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)
+
+#EndMacro scan_vm_pages
+
+VM_PAGE_IS_WIRED = 1
+
+@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"))
+@lldb_command('vmobjectwalkpages', 'CSBNQP:O:')
+def VMObjectWalkPages(cmd_args=None, cmd_options={}):
+    """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
+        specifically look for this page, highlighting it in the output or noting if it was not found. For
+        each page, we confirm that it points to the object. We also keep track of the number of pages we
+        see and compare this to the object's resident page count field.
+        Usage:
+            vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
+            vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages
+            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
+            vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
+            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)
+            vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
+            vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
+            vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset
+
+    """
+
+    if (cmd_args == None or len(cmd_args) < 1):
+        raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
+
+    out_string = ""
+
+    obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
+
+    page = 0
+    if "-P" in cmd_options:
+        page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
+
+    off = -1
+    if "-O" in cmd_options:
+        off = kern.GetValueFromAddress(cmd_options['-O'], 'vm_offset_t')
+
+    stop = 0
+    if "-S" in cmd_options:
+        if page == 0 and off < 0:
+            raise ArgumentError("-S can only be passed when a page is specified with -P or -O")
+        stop = 1
+
+    walk_backwards = False
+    if "-B" in cmd_options:
+        walk_backwards = True
+
+    quiet_mode = False
+    if "-Q" in cmd_options:
+        quiet_mode = True
+
+    if not quiet_mode:
+        print VMObjectWalkPages.header
+        format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
+        first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
+        second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
+        second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
+        second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
+        second_bitfield_format_string +=  "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
+
+    limit = 4096 #arbitrary limit of number of pages to walk
+    ignore_limit = 0
+    if "-N" in cmd_options:
+        ignore_limit = 1
+
+    show_compressed = 0
+    if "-C" in cmd_options:
+        show_compressed = 1
+
+    page_count = 0
+    res_page_count = unsigned(obj.resident_page_count)
+    page_found = False
+    pages_seen = set()
+
+    for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr):
+        page_count += 1
+        out_string = ""
+        if (page != 0 and not(page_found) and vmp == page):
+            out_string += "******"
+            page_found = True
+
+        if (off > 0 and not(page_found) and vmp.vmp_offset == off):
+            out_string += "******"
+            page_found = True
+
+        if page != 0 or off > 0 or quiet_mode:
+             if (page_count % 1000) == 0:
+                print "traversed %d pages ...\n" % (page_count)
+        else:
+                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)
+                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,
+                                                                   vmp.vmp_private, vmp.vmp_reference)
+
+                if hasattr(vmp,'slid'):
+                    vmp_slid = vmp.slid
+                else:
+                    vmp_slid = 0
+                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,
+                                                                    vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent,
+                                                                    vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting,
+                                                                    vmp.vmp_restart, vmp.vmp_unusual, 0, 0,
+                                                                    vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid,
+                                                                    vmp.vmp_written_by_kernel)
+
+        if (vmp in pages_seen):
+            print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n"
+            return
+
+        if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)):
+            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)))
+            return
+
+        if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0):
+            print out_string + " page in wired state with wire_count of 0\n"
+            print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n"
+            print "stopping...\n"
+            return
+
+        if (hasattr(vmp, 'vmp_unused_page_bits') and (vmp.vmp_unused_page_bits != 0)):
+            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)
+            print "stopping...\n"
+            return
+
+        if (hasattr(vmp, 'vmp_unused_object_bits') and (vmp.vmp_unused_object_bits != 0)):
+            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)
+            print "stopping...\n"
+            return
+
+        pages_seen.add(vmp)
+
+        if False:
+            hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset)
+            hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
+            hash_page = _vm_page_unpack_ptr(hash_page_list)
+            hash_page_t = 0
+
+            while (hash_page != 0):
+                hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
+                if hash_page_t == vmp:
+                    break
+                hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m)
+
+            if (unsigned(vmp) != unsigned(hash_page_t)):
+                print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n"
+                print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset)))
+                return
+
+        if (page_count >= limit and not(ignore_limit)):
+            print out_string + "Limit reached (%d pages), stopping..." % (limit)
+            break
+
+        print out_string
+
+        if page_found and stop:
+            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)))
+            return
+
+    if (page != 0):
+        print("page found? : %s\n" % page_found)
+
+    if (off > 0):
+        print("page found? : %s\n" % page_found)
+
+    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)))
+
+    if show_compressed != 0 and obj.pager != 0 and unsigned(obj.pager.mo_pager_ops) == unsigned(addressof(kern.globals.compressor_pager_ops)):
+        pager = Cast(obj.pager, 'compressor_pager *')
+        chunks = pager.cpgr_num_slots / 128
+        pagesize = kern.globals.page_size
+
+        page_idx = 0
+        while page_idx < pager.cpgr_num_slots:
+            if chunks != 0:
+                chunk = pager.cpgr_slots.cpgr_islots[page_idx / 128]
+                slot = chunk[page_idx % 128]
+            elif pager.cpgr_num_slots > 2:
+                slot = pager.cpgr_slots.cpgr_dslots[page_idx]
+            else:
+                slot = pager.cpgr_slots.cpgr_eslots[page_idx]
+
+            if slot != 0:
+               print("compressed page for offset: %x slot %x\n" % ((page_idx * pagesize) - obj.paging_offset, slot))
+            page_idx = page_idx + 1
+
+
+@lldb_command("show_all_apple_protect_pagers")
+def ShowAllAppleProtectPagers(cmd_args=None):
+    """Routine to print all apple_protect pagers
+        usage: show_all_apple_protect_pagers
+    """
+    print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "mo_control", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
+    qhead = kern.globals.apple_protect_pager_queue
+    qtype = GetType('apple_protect_pager *')
+    qcnt = kern.globals.apple_protect_pager_count
+    idx = 0
+    for pager in IterateQueue(qhead, qtype, "pager_queue"):
+        idx = idx + 1
+        show_apple_protect_pager(pager, qcnt, idx)
+
+@lldb_command("show_apple_protect_pager")
+def ShowAppleProtectPager(cmd_args=None):
+    """Routine to print out info about an apple_protect pager
+        usage: show_apple_protect_pager <pager>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Invalid argument.", ShowAppleProtectPager.__doc__
+        return
+    pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t')
+    show_apple_protect_pager(pager, 1, 1)
+
+def show_apple_protect_pager(pager, qcnt, idx):
+    object = pager.backing_object
+    shadow = object.shadow
+    while shadow != 0:
+        object = shadow
+        shadow = object.shadow
+    vnode_pager = Cast(object.pager,'vnode_pager *')
+    filename = GetVnodePath(vnode_pager.vnode_handle)
+    print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {: <#018x} {: <#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, pager.ref_count, pager.is_ready, pager.is_mapped, pager.pager_control, 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)
+
+@lldb_command("show_console_ring")
+def ShowConsoleRingData(cmd_args=None):
+    """ Print console ring buffer stats and data
+    """
+    cr = kern.globals.console_ring
+    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)
+    pending_data = []
+    for i in range(unsigned(cr.used)):
+        idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len)
+        pending_data.append("{:c}".format(cr.buffer[idx]))
+
+    if pending_data:
+        print "Data:"
+        print "".join(pending_data)
+
+# Macro: showjetsamsnapshot
+
+@lldb_command("showjetsamsnapshot", "DA")
+def ShowJetsamSnapshot(cmd_args=None, cmd_options={}):
+    """ Dump entries in the jetsam snapshot table
+        usage: showjetsamsnapshot [-D] [-A]
+        Use -D flag to print extra physfootprint details
+        Use -A flag to print all entries (regardless of valid count)
+    """
+
+    # Not shown are uuid, user_data, cpu_time
+
+    global kern
+
+    show_footprint_details = False
+    show_all_entries = False
+
+    if "-D" in cmd_options:
+        show_footprint_details = True
+
+    if "-A" in cmd_options:
+        show_all_entries = True
+
+    valid_count = kern.globals.memorystatus_jetsam_snapshot_count
+    max_count = kern.globals.memorystatus_jetsam_snapshot_max
+
+    if (show_all_entries == True):
+        count = max_count
+    else:
+        count = valid_count
+
+    print "{:s}".format(valid_count)
+    print "{:s}".format(max_count)
+
+    if int(count) == 0:
+        print "The jetsam snapshot is empty."
+        print "Use -A to force dump all entries (regardless of valid count)"
+        return
+
+    # Dumps the snapshot header info
+    print lldb_run_command('p *memorystatus_jetsam_snapshot')
+
+    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}"
+    if (show_footprint_details == True):
+        hdr_format += "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}"
+
+
+    if (show_footprint_details == False):
+        print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax')
+        print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)')
+    else:
+        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')
+        print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
+
+
+    entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
+                   "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
+                   "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
+                   "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\
+                   "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
+
+    if (show_footprint_details == True):
+        entry_format += "{e.jse_internal_pages: >15d} "\
+                        "{e.jse_internal_compressed_pages: >15d} "\
+                        "{e.jse_iokit_mapped_pages: >12d} "\
+                        "{e.jse_purgeable_nonvolatile_pages: >12d} "\
+                        "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
+                        "{e.jse_alternate_accounting_pages: >10d} "\
+                        "{e.jse_alternate_accounting_compressed_pages: >13d} "\
+                        "{e.jse_page_table_pages: >10d}"
+
+    snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries
+    idx = 0
+    while idx < count:
+        current_entry = dereference(Cast(addressof(snapshot_list[idx]), 'jetsam_snapshot_entry *'))
+        print entry_format.format(index=idx, e=current_entry)
+        idx +=1
+    return
+
+# EndMacro: showjetsamsnapshot
+
+# Macro: showvnodecleanblk/showvnodedirtyblk
+
+def _GetBufSummary(buf):
+    """ Get a summary of important information out of a buf_t.
+    """
+    initial = "(struct buf) {0: <#0x} ="
+
+    # List all of the fields in this buf summary.
+    entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq,
+        buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid,
+        buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp,
+        buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata,
+        buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff,
+        buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr]
+
+    # Join an (already decent) string representation of each field
+    # with newlines and indent the region.
+    joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n    ")
+
+    # Add the total string representation to our title and return it.
+    out_str = initial.format(int(buf)) + " {\n    " + joined_strs + "\n}\n\n"
+    return out_str
+
+def _ShowVnodeBlocks(dirty=True, cmd_args=None):
+    """ Display info about all [dirty|clean] blocks in a vnode.
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Please provide a valid vnode argument."
+        return
+
+    vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
+    list_head = vnodeval.v_cleanblkhd;
+    if dirty:
+        list_head = vnodeval.v_dirtyblkhd
+
+    print "Blocklist for vnode {}:".format(cmd_args[0])
+
+    i = 0
+    for buf in IterateListEntry(list_head, 'struct buf *', 'b_hash'):
+        # For each block (buf_t) in the appropriate list,
+        # ask for a summary and print it.
+        print "---->\nblock {}: ".format(i) + _GetBufSummary(buf)
+        i += 1
+    return
+
+@lldb_command('showvnodecleanblk')
+def ShowVnodeCleanBlocks(cmd_args=None):
+    """ Display info about all clean blocks in a vnode.
+        usage: showvnodecleanblk <address of vnode>
+    """
+    _ShowVnodeBlocks(False, cmd_args)
+
+@lldb_command('showvnodedirtyblk')
+def ShowVnodeDirtyBlocks(cmd_args=None):
+    """ Display info about all dirty blocks in a vnode.
+        usage: showvnodedirtyblk <address of vnode>
+    """
+    _ShowVnodeBlocks(True, cmd_args)
+
+# EndMacro: showvnodecleanblk/showvnodedirtyblk
+
+
+@lldb_command("vm_page_lookup_in_map")
+def VmPageLookupInMap(cmd_args=None):
+    """Lookup up a page at a virtual address in a VM map
+        usage: vm_page_lookup_in_map <map> <vaddr>
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        print "Invalid argument.", VmPageLookupInMap.__doc__
+        return
+    map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
+    vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t')
+    print "vaddr {:#018x} in map {: <#018x}".format(vaddr, map)
+    vm_page_lookup_in_map(map, vaddr)
+
+def vm_page_lookup_in_map(map, vaddr):
+    vaddr = unsigned(vaddr)
+    vme_list_head = map.hdr.links
+    vme_ptr_type = GetType('vm_map_entry *')
+    for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
+        if unsigned(vme.links.start) > vaddr:
+            break
+        if unsigned(vme.links.end) <= vaddr:
+            continue
+        offset_in_vme = vaddr - unsigned(vme.links.start)
+        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), vme.vme_object.vmo_object, unsigned(vme.vme_offset) & ~0xFFF)
+        offset_in_object = offset_in_vme + (unsigned(vme.vme_offset) & ~0xFFF)
+        if vme.is_sub_map:
+            print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object, vme.vme_object.vmo_submap)
+            vm_page_lookup_in_map(vme.vme_object.vmo_submap, offset_in_object)
+        else:
+            vm_page_lookup_in_object(vme.vme_object.vmo_object, offset_in_object)
+
+@lldb_command("vm_page_lookup_in_object")
+def VmPageLookupInObject(cmd_args=None):
+    """Lookup up a page at a given offset in a VM object
+        usage: vm_page_lookup_in_object <object> <offset>
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        print "Invalid argument.", VmPageLookupInObject.__doc__
+        return
+    object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
+    offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t')
+    print "offset {:#018x} in object {: <#018x}".format(offset, object)
+    vm_page_lookup_in_object(object, offset)
+
+def vm_page_lookup_in_object(object, offset):
+    offset = unsigned(offset)
+    page_size = kern.globals.page_size
+    trunc_offset = offset & ~(page_size - 1)
+    print "    offset {:#018x} in VM object {: <#018x}".format(offset, object)
+    hash_id = _calc_vm_page_hash(object, trunc_offset)
+    page_list = kern.globals.vm_page_buckets[hash_id].page_list
+    page = _vm_page_unpack_ptr(page_list)
+    while page != 0:
+        m = kern.GetValueFromAddress(page, 'vm_page_t')
+        m_object_val = _vm_page_unpack_ptr(m.vmp_object)
+        m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t')
+        if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset):
+            page = _vm_page_unpack_ptr(m.vmp_next_m)
+            continue
+        print "    resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m))
+        return
+    if object.pager and object.pager_ready:
+        offset_in_pager = trunc_offset + unsigned(object.paging_offset)
+        if not object.internal:
+            print "    offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager)
+            return
+        pager = Cast(object.pager, 'compressor_pager *')
+        ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager)
+        if ret:
+            return
+    if object.shadow and not object.phys_contiguous:
+        offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset)
+        vm_page_lookup_in_object(object.shadow, offset_in_shadow)
+        return
+    print "    page is absent and will be zero-filled on demand"
+    return
+
+@lldb_command("vm_page_lookup_in_compressor_pager")
+def VmPageLookupInCompressorPager(cmd_args=None):
+    """Lookup up a page at a given offset in a compressor pager
+        usage: vm_page_lookup_in_compressor_pager <pager> <offset>
+    """
+    if cmd_args == None or len(cmd_args) < 2:
+        print "Invalid argument.", VmPageLookupInCompressorPager.__doc__
+        return
+    pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t')
+    offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t')
+    print "offset {:#018x} in compressor pager {: <#018x}".format(offset, pager)
+    vm_page_lookup_in_compressor_pager(pager, offset)
+
+def vm_page_lookup_in_compressor_pager(pager, offset):
+    offset = unsigned(offset)
+    page_size = unsigned(kern.globals.page_size)
+    page_num = unsigned(offset / page_size)
+    if page_num > pager.cpgr_num_slots:
+        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)
+        return 0
+    slots_per_chunk = 512 / sizeof ('compressor_slot_t')
+    num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) / slots_per_chunk)
+    if num_chunks > 1:
+        chunk_idx = unsigned(page_num / slots_per_chunk)
+        chunk = pager.cpgr_slots.cpgr_islots[chunk_idx]
+        slot_idx = unsigned(page_num % slots_per_chunk)
+        slot = GetObjectAtIndexFromArray(chunk, slot_idx)
+        slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx)
+    elif pager.cpgr_num_slots > 2:
+        slot_idx = page_num
+        slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx)
+        slot_str = "dslots[{:d}]".format(slot_idx)
+    else:
+        slot_idx = page_num
+        slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx)
+        slot_str = "eslots[{:d}]".format(slot_idx)
+    print "      offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot)
+    if slot == 0:
+        return 0
+    slot_value = dereference(slot)
+    print " value {:#010x}".format(slot_value)
+    vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t'))
+    return 1
+
+@lldb_command("vm_page_lookup_in_compressor")
+def VmPageLookupInCompressor(cmd_args=None):
+    """Lookup up a page in a given compressor slot
+        usage: vm_page_lookup_in_compressor <slot>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Invalid argument.", VmPageLookupInCompressor.__doc__
+        return
+    slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *')
+    print "compressor slot {: <#018x}".format(slot)
+    vm_page_lookup_in_compressor(slot)
+
+C_SV_CSEG_ID = ((1 << 22) - 1)
+
+def vm_page_lookup_in_compressor(slot_ptr):
+    slot_ptr = Cast(slot_ptr, 'compressor_slot_t *')
+    slot_value = dereference(slot_ptr)
+    slot = Cast(slot_value, 'c_slot_mapping')
+    print slot
+    print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx)
+    if slot_ptr == 0:
+        return
+    if slot.s_cseg == C_SV_CSEG_ID:
+        sv = kern.globals.c_segment_sv_hash_table
+        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)
+        return
+    if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available):
+        print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available))
+        return
+    c_segments = kern.globals.c_segments
+    c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1)
+    c_seg = c_segments_elt.c_seg
+    c_no_data = 0
+    if hasattr(c_seg, 'c_state'):
+        c_state = c_seg.c_state
+        if c_state == 0:
+            c_state_str = "C_IS_EMPTY"
+            c_no_data = 1
+        elif c_state == 1:
+            c_state_str = "C_IS_FREE"
+            c_no_data = 1
+        elif c_state == 2:
+            c_state_str = "C_IS_FILLING"
+        elif c_state == 3:
+            c_state_str = "C_ON_AGE_Q"
+        elif c_state == 4:
+            c_state_str = "C_ON_SWAPOUT_Q"
+        elif c_state == 5:
+            c_state_str = "C_ON_SWAPPEDOUT_Q"
+            c_no_data = 1
+        elif c_state == 6:
+            c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q"
+            c_no_data = 1
+        elif c_state == 7:
+            c_state_str = "C_ON_SWAPPEDIN_Q"
+        elif c_state == 8:
+            c_state_str = "C_ON_MAJORCOMPACT_Q"
+        elif c_state == 9:
+            c_state_str = "C_ON_BAD_Q"
+            c_no_data = 1
+        else:
+            c_state_str = "<unknown>"
+    else:
+        c_state = -1
+        c_state_str = "<no c_state field>"
+    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)
+    c_indx = unsigned(slot.s_cindx)
+    if hasattr(c_seg, 'c_slot_var_array'):
+        c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len
+        if c_indx < c_seg_fixed_array_len:
+            cs = c_seg.c_slot_fixed_array[c_indx]
+        else:
+            cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len)
+    else:
+        C_SEG_SLOT_ARRAY_SIZE = 64
+        C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1
+        cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx / C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK)
+    print cs
+    c_slot_unpacked_ptr = vm_unpack_ptr(cs.c_packed_ptr, kern.globals.c_slot_packing_params)
+    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))
+    if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr):
+        print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr)
+    if c_no_data == 0:
+        c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset)
+        c_size = cs.c_size
+        cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size)
+        print cmd
+        print lldb_run_command(cmd)
+    else:
+        print "<no compressed data>"
+
+def print_hex_data(data, begin_offset=0, desc=""):
+    """ print on stdout "hexdump -C < data" like output
+        params:
+            data - bytearray or array of int where each int < 255
+            begin_offset - int offset that should be printed in left column
+            desc - str optional description to print on the first line to describe data
+    """
+    if desc:
+        print "{}:".format(desc)
+    index = 0
+    total_len = len(data)
+    hex_buf = ""
+    char_buf = ""
+    while index < total_len:
+        hex_buf += " {:02x}".format(data[index])
+        if data[index] < 0x20 or data[index] > 0x7e:
+            char_buf += "."
+        else:
+            char_buf += "{:c}".format(data[index])
+        index += 1
+        if index and index % 8 == 0:
+            hex_buf += " "
+        if index > 1 and (index % 16) == 0:
+            print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
+            hex_buf = ""
+            char_buf = ""
+    if index % 16 != 0:
+        print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
+    return
+
+@lldb_command('vm_scan_all_pages')
+def VMScanAllPages(cmd_args=None):
+    """Scans the vm_pages[] array
+    """
+    vm_pages_count = kern.globals.vm_pages_count
+    vm_pages = kern.globals.vm_pages
+
+    free_count = 0
+    local_free_count = 0
+    active_count = 0
+    local_active_count = 0
+    inactive_count = 0
+    speculative_count = 0
+    throttled_count = 0
+    wired_count = 0
+    compressor_count = 0
+    pageable_internal_count = 0
+    pageable_external_count = 0
+    secluded_count = 0
+    secluded_free_count = 0
+    secluded_inuse_count = 0
+
+    i = 0
+    while i < vm_pages_count:
+
+        if i % 10000 == 0:
+            print "{:d}/{:d}...\n".format(i,vm_pages_count)
+
+        m = vm_pages[i]
+
+        internal = 0
+        external = 0
+        m_object_val = _vm_page_unpack_ptr(m.vmp_object)
+
+        if m_object:
+            if m_object.internal:
+                internal = 1
+            else:
+                external = 1
+
+        if m.vmp_wire_count != 0 and m.vmp_local == 0:
+            wired_count = wired_count + 1
+            pageable = 0
+        elif m.vmp_throttled:
+            throttled_count = throttled_count + 1
+            pageable = 0
+        elif m.vmp_active:
+            active_count = active_count + 1
+            pageable = 1
+        elif m.vmp_local:
+            local_active_count = local_active_count + 1
+            pageable = 0
+        elif m.vmp_inactive:
+            inactive_count = inactive_count + 1
+            pageable = 1
+        elif m.vmp_speculative:
+            speculative_count = speculative_count + 1
+            pageable = 0
+        elif m.vmp_free:
+            free_count = free_count + 1
+            pageable = 0
+        elif m.vmp_secluded:
+            secluded_count = secluded_count + 1
+            if m_object == 0:
+                secluded_free_count = secluded_free_count + 1
+            else:
+                secluded_inuse_count = secluded_inuse_count + 1
+            pageable = 0
+        elif m_object == 0 and m.vmp_busy:
+            local_free_count = local_free_count + 1
+            pageable = 0
+        elif m.vmp_compressor:
+            compressor_count = compressor_count + 1
+            pageable = 0
+        else:
+            print "weird page vm_pages[{:d}]?\n".format(i)
+            pageable = 0
+
+        if pageable:
+            if internal:
+                pageable_internal_count = pageable_internal_count + 1
+            else:
+                pageable_external_count = pageable_external_count + 1
+        i = i + 1
+
+    print "vm_pages_count = {:d}\n".format(vm_pages_count)
+
+    print "wired_count = {:d}\n".format(wired_count)
+    print "throttled_count = {:d}\n".format(throttled_count)
+    print "active_count = {:d}\n".format(active_count)
+    print "local_active_count = {:d}\n".format(local_active_count)
+    print "inactive_count = {:d}\n".format(inactive_count)
+    print "speculative_count = {:d}\n".format(speculative_count)
+    print "free_count = {:d}\n".format(free_count)
+    print "local_free_count = {:d}\n".format(local_free_count)
+    print "compressor_count = {:d}\n".format(compressor_count)
+
+    print "pageable_internal_count = {:d}\n".format(pageable_internal_count)
+    print "pageable_external_count = {:d}\n".format(pageable_external_count)
+    print "secluded_count = {:d}\n".format(secluded_count)
+    print "secluded_free_count = {:d}\n".format(secluded_free_count)
+    print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count)
+
+
+@lldb_command('show_all_vm_named_entries')
+def ShowAllVMNamedEntries(cmd_args=None):
+    """ Routine to print a summary listing of all the VM named entries
+    """
+    queue_len = kern.globals.vm_named_entry_count
+    queue_head = kern.globals.vm_named_entry_list
+
+    print 'vm_named_entry_list:{: <#018x}  vm_named_entry_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len)
+
+#    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")
+    idx = 0
+    for entry in IterateQueue(queue_head, 'struct vm_named_entry *', 'named_entry_list'):
+        idx += 1
+        showmemoryentry(entry, idx, queue_len)
+
+@lldb_command('show_vm_named_entry')
+def ShowVMNamedEntry(cmd_args=None):
+    """ Routine to print a VM named entry
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Invalid argument.", ShowMapVMNamedEntry.__doc__
+        return
+    named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t')
+    showmemoryentry(named_entry, 0, 0)
+
+def showmemoryentry(entry, idx=0, queue_len=0):
+    """  Routine to print out a summary a VM memory entry
+        params: 
+            entry - core.value : a object of type 'struct vm_named_entry *'
+        returns:
+            None
+    """
+    show_pager_info = True
+    show_all_shadows = True
+
+    backing = ""
+    if entry.is_sub_map == 1:
+        backing += "SUBMAP"
+    if entry.is_copy == 1:
+        backing += "COPY"
+    if entry.is_object == 1:
+        backing += "OBJECT"
+    if entry.is_sub_map == 0 and entry.is_copy == 0 and entry.is_object == 0:
+        backing += "***?***"
+    prot=""
+    if entry.protection & 0x1:
+        prot += "r"
+    else:
+        prot += "-"
+    if entry.protection & 0x2:
+        prot += "w"
+    else:
+        prot += "-"
+    if entry.protection & 0x4:
+        prot += "x"
+    else:
+        prot += "-"
+    extra_str = ""
+    if hasattr(entry, 'named_entry_alias'):
+        extra_str += " alias={:d}".format(entry.named_entry_alias)
+    if hasattr(entry, 'named_entry_port'):
+        extra_str += " port={:#016x}".format(entry.named_entry_port)
+    print "{:d}/{:d} {: <#018x} ref={:d} prot={:d}/{:s} type={:s} backing={: <#018x} offset={:#016x} dataoffset={:#016x} size={:#016x}{:s}\n".format(idx,queue_len,entry,entry.ref_count,entry.protection,prot,backing,entry.backing.copy,entry.offset,entry.data_offset,entry.size,extra_str)
+    if entry.is_sub_map == 1:
+        showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows)
+    elif entry.is_copy == 1:
+        showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
+    elif entry.is_object == 1:
+        showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
+    else:
+        print "***** UNKNOWN TYPE *****"
+    print " \n"
+
+
+def IterateRBTreeEntry2(element, element_type, field_name1, field_name2):
+    """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
+            element      - value : Value object for rbh_root
+            element_type - str   : Type of the link element
+            field_name   - str   : Name of the field in link element's structure
+        returns:
+            A generator does not return. It is used for iterating
+            value  : an object thats of type (element_type) head->sle_next. Always a pointer object
+    """
+    elt = element.__getattr__('rbh_root')
+    if type(element_type) == str:
+        element_type = gettype(element_type)
+    charp_type = gettype('char *');
+
+    # Walk to find min
+    parent = elt
+    while unsigned(elt) != 0:
+        parent = elt
+        elt = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
+    elt = parent
+
+    # Now elt is min
+    while unsigned(elt) != 0:
+        yield elt
+        # implementation cribbed from RB_NEXT in libkern/tree.h
+        right = cast(elt.__getattr__(field_name1).__getattr__(fieldname2).__getattr__('rbe_right'), element_type)
+        if unsigned(right) != 0:
+            elt = right
+            left = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
+            while unsigned(left) != 0:
+                elt = left
+                left = cast(elt.__getattr__(field_name1).__getattr(__field_name2).__getattr__('rbe_left'), element_type)
+        else:
+
+            # avoid using GetValueFromAddress
+            addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
+            parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
+            parent = cast(parent, element_type)
+
+            if unsigned(parent) != 0:
+                left = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
+            if (unsigned(parent) != 0) and (unsigned(elt) == unsigned(left)):
+                elt = parent
+            else:
+                if unsigned(parent) != 0:
+                    right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
+                while unsigned(parent) != 0 and (unsigned(elt) == unsigned(right)):
+                    elt = parent
+
+                    # avoid using GetValueFromAddress
+                    addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
+                    parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
+                    parent = cast(parent, element_type)
+
+                    right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
+
+                # avoid using GetValueFromAddress
+                addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
+                elt = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
+                elt = cast(elt, element_type)
+
+
+@lldb_command("showmaprb")
+def ShowMapRB(cmd_args=None):
+    """Routine to print out a VM map's RB tree
+        usage: showmaprb <vm_map>
+    """
+    if cmd_args == None or len(cmd_args) < 1:
+        print "Invalid argument.", ShowMapRB.__doc__
+        return
+    map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
+    print GetVMMapSummary.header
+    print GetVMMapSummary(map_val)
+    vme_rb_root = map_val.hdr.rb_head_store
+    vme_ptr_type = GetType('struct vm_map_entry *')
+    print GetVMEntrySummary.header
+    for vme in IterateRBTreeEntry2(vme_rb_root, 'struct vm_map_entry *', 'store', 'entry'):
+        print GetVMEntrySummary(vme)
+    return None
+
+@lldb_command('show_all_owned_objects', 'T')
+def ShowAllOwnedObjects(cmd_args=None, cmd_options={}):
+    """ Routine to print the list of VM objects owned by each task
+        -T: show only ledger-tagged objects
+    """
+    showonlytagged = False
+    if "-T" in cmd_options:
+        showonlytagged = True
+    for task in kern.tasks:
+        ShowTaskOwnedVmObjects(task, showonlytagged)
+
+@lldb_command('show_task_owned_objects', 'T')
+def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}):
+    """ Routine to print the list of VM objects owned by the specified task
+        -T: show only ledger-tagged objects
+    """
+    showonlytagged = False
+    if "-T" in cmd_options:
+        showonlytagged = True
+    task = kern.GetValueFromAddress(cmd_args[0], 'task *')
+    ShowTaskOwnedVmObjects(task, showonlytagged)
+
+@lldb_command('showdeviceinfo', 'J')
+def ShowDeviceInfo(cmd_args=None, cmd_options={}):
+    """ Routine to show basic device information (model, build, ncpus, etc...)
+        Usage: memstats  [-J]
+            -J      : Output json
+    """
+    print_json = False
+    if "-J" in cmd_options:
+        print_json = True
+    device_info = {}
+    device_info["build"] =  str(kern.globals.osversion)
+    device_info["memoryConfig"] = int(kern.globals.max_mem_actual)
+    device_info["ncpu"] = int(kern.globals.ncpu)
+    device_info["pagesize"] = int(kern.globals.page_size)
+    device_info["mlockLimit"] = long(kern.globals.vm_global_user_wire_limit)
+
+
+    if print_json:
+        print json.dumps(device_info)
+    else:
+        PrettyPrintDictionary(device_info)
+
+def ShowTaskOwnedVmObjects(task, showonlytagged=False):
+    """  Routine to print out a summary listing of all the entries in a vm_map
+        params:
+            task - core.value : a object of type 'task *'
+        returns:
+            None
+    """
+    taskobjq_total = lambda:None
+    taskobjq_total.objects = 0
+    taskobjq_total.vsize = 0
+    taskobjq_total.rsize = 0
+    taskobjq_total.wsize = 0
+    taskobjq_total.csize = 0
+    vmo_list_head = task.task_objq
+    vmo_ptr_type = GetType('vm_object *')
+    idx = 0
+    for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"):
+        idx += 1
+        if not showonlytagged or vmo.vo_ledger_tag != 0:
+            if taskobjq_total.objects == 0:
+                print ' \n'
+                print GetTaskSummary.header + ' ' + GetProcSummary.header
+                print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
+                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")
+            ShowOwnedVmObject(vmo, idx, 0, taskobjq_total)
+    if taskobjq_total.objects != 0:
+        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)
+    return None
+
+def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total):
+    """  Routine to print out a VM object owned by a task
+        params:
+            object - core.value : a object of type 'struct vm_object *'
+        returns:
+            None
+    """
+    page_size = kern.globals.page_size
+    if object.purgable == 0:
+        purgable = "N"
+    elif object.purgable == 1:
+        purgable = "V"
+    elif object.purgable == 2:
+        purgable = "E"
+    elif object.purgable == 3:
+        purgable = "D"
+    else:
+        purgable = "?"
+    if object.pager == 0:
+        compressed_count = 0
+    else:
+        compressor_pager = Cast(object.pager, 'compressor_pager *')
+        compressed_count = compressor_pager.cpgr_num_slots_occupied
+
+    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))
+
+    taskobjq_total.objects += 1
+    taskobjq_total.vsize += object.vo_un1.vou_size/page_size
+    taskobjq_total.rsize += object.resident_page_count
+    taskobjq_total.wsize += object.wired_page_count
+    taskobjq_total.csize += compressed_count
+
+def GetProcPIDForObjectOwner(owner):
+    """ same as GetProcPIDForTask() but deals with -1 for a disowned object
+    """
+    if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
+        return -1
+    return GetProcPIDForTask(owner)
+
+def GetProcNameForObjectOwner(owner):
+    """ same as GetProcNameForTask() but deals with -1 for a disowned object
+    """
+    if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
+        return "<disowned>"
+    return GetProcNameForTask(owner)
+
+def GetDescForNamedEntry(mem_entry):
+    out_str = "\n"
+    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)
+    if mem_entry.is_sub_map:
+        out_str += " is_sub_map"
+    elif mem_entry.is_copy:
+        out_str += " is_copy"
+    elif mem_entry.is_object:
+        out_str += " is_object"
+    else:
+        out_str += " ???"
+    return out_str