]> 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 d3cbeb3..be557e7
 
 """ 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 *
-import sys, shlex
+import sys
+import shlex
 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
-@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.
+        usage: memstats -J
+                Output json
     """
+    print_json = False
+    if "-J" in cmd_options:
+        print_json = True
+
+    memstats = {}
     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
-    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
-        returns 
+        returns
          - False on failure
-         - True on success 
+         - True on success
     """
     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()
-    if result.split(":")[1].strip().find('None') == -1 : 
+    if result.split(":")[1].strip().find('None') == -1 :
         return True
-    else: 
+    else:
         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
-        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
-    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
 
-@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 *
@@ -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_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
-    
-    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
+    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,
-        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:
-        out_str += "{: ^12d}".format(phys_footprint_spike)
+        out_str += "{: >12d}".format(phys_footprint_spike)
     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):
@@ -109,8 +206,8 @@ def ShowMemoryStatus(cmd_args=None):
     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
@@ -121,96 +218,495 @@ def ShowMemoryStatus(cmd_args=None):
         bucket_index += 1
     print "\n\n"
     Memstats()
-    
+
 # 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
 
+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'])
-@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
-        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
     """
+    pagesize = kern.globals.page_size
     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=""
+    if zone["destroyed"]:
+        markings+="I"
+        
     for mark in marks:
-        if zone.__getattr__(mark[0]) :
-            markings+=mark[1]
+        if zone[mark[0]]:
+            markings += mark[1]
         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
 
-@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
+        usage: zprint -J
+                Output json
     All columns are printed in decimal
     Legend:
         C - collectable
+        D - destructible
         X - expandable
         $ - not encrypted during hibernation
-        @ - allocs and frees are accounted to caller process for KPRVT
         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)
-        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
+        E - Per-cpu caching is enabled for this zone
         L - zone is being monitored by zleaks
         G - currently running GC
+        I - zone was destroyed and is no longer valid
     """
     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
-        returns 
+        returns
          - False on failure
-         - True on success 
+         - True on success
     """
     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
-    else: 
+    else:
         return False
 
 
@@ -236,10 +732,14 @@ def ShowZfreeListHeader(zone):
         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(
-                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
@@ -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 *'))
-        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))
@@ -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(
-              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 *')
 
+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')
@@ -295,31 +804,14 @@ def ShowZfreeList(cmd_args=None):
         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
-            zfirst = Cast(free_page_meta.elements, 'void *')
-            if unsigned(zfirst) != 0:
+            zfirst = free_page_meta.getFreeList()
+            if zfirst != 0:
                 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:
@@ -327,20 +819,28 @@ def ShowZfreeList(cmd_args=None):
 
 # 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):
-    """ 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__
@@ -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.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
-    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):
-        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
 
-# 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
-    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
-    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
 
-# 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.
-        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:
-        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
-    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):
-    """ 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
@@ -446,66 +974,97 @@ def FindElem(cmd_args=None):
         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
-    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
-  
-    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))
-                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
 
-@lldb_command('btlog_find', "A")
+@lldb_command('btlog_find', "AS")
 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
 
@@ -582,14 +1141,14 @@ def ShowZstackTrace(cmd_args=None):
     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
-            depth: int - The depth of the ztrace stack 
+            depth: int - The depth of the ztrace stack
         returns:
             None
     """
@@ -608,7 +1167,7 @@ def ShowZstackTraceHelper(stack, depth):
 
 @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
@@ -627,7 +1186,7 @@ def ShowZallocs(cmd_args=None):
     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
@@ -651,7 +1210,7 @@ def ShowZallocsForTrace(cmd_args=None):
     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)
@@ -752,19 +1311,68 @@ def ShowZstats(cmd_args=None):
 
 #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:
-            None
+            str - string with backtrace in it.
     """
     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]
@@ -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
-    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
@@ -789,12 +1397,13 @@ def ShowZStackRecord(zstack_record, zstack_index):
     """
     out_str = ('-' * 8)
     if zstack_record.operation == 1:
-        out_str += "ALLOC  "
+        out_str += "ALLOC.  "
     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
-    ShowBtlogBacktrace(kern.globals.zlog_btlog.btrecord_btdepth, zstack_record)
+    print GetBtlogBacktrace(btrecord_btdepth, zstack_record)
+    print " \n"
 
 # 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))
-    
-    
-# 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
-@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 )
+        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 *')
-    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
-        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:
-        ShowTaskVMEntries(task)
+        ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
 
 @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
     """
+    page_size = kern.globals.page_size
     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 = ''
 
-    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 *')
@@ -910,12 +1579,12 @@ def ShowAllVMStats(cmd_args=None):
         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
-        params: 
+        params:
             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"):
-        print GetVMEntrySummary(vme)
+        print GetVMEntrySummary(vme, show_pager_info, show_all_shadows)
     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:
-        print "Invalid argument.", ShowMap.__doc__
+        print "Invalid argument.", ShowMapVME.__doc__
         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'])
-@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 = ""
-    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)
-    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. """
+    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)]
-    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"
-    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
@@ -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')
-    
+
 
 @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):
-    """ returns a string representation of kext information 
+    """ returns a string representation of kext information
     """
     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'])
-@header("")    
+@header("")  
 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
+    kextuuidinfo = GetKextLoadInformation(show_progress=(config['verbosity'] > vHUMAN))
     print "{: <36s} ".format("UUID") + GetKextSummary.header
-    kextuuidinfo = GetKextLoadInformation()
     for kval in IterateLinkedList(kmod_val, 'next'):
         uuid = "........-....-....-....-............"
         kaddr = unsigned(kval.address)
+        found_kext_summary = None
         for l in kextuuidinfo :
-            if kaddr == int(l[1],16):
+            if kaddr == int(l[3],16):
                 uuid = l[0]
+                found_kext_summary = l
                 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.
-        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 *')
@@ -1060,14 +1807,23 @@ def GetKextLoadInformation(addr=0):
         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 *')
+        # 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 == 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')
@@ -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
-        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
-    
+
     vers_major = version / MAJ_MULT
     version = version - (vers_major * MAJ_MULT)
-    
+
     vers_minor = version / MIN_MULT
     version = version - (vers_minor * MIN_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_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
-    
+
     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}"
-    
+
     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
-    
+
     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=[]):
-    """ 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])
-    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
 
-@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
-        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 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"]
@@ -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))
-        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
+        sections = None
         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]
+                        sections = k[4]
                         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)
-        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()
-    
+    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 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))
-        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
 
@@ -1238,39 +2063,40 @@ def AddKextSyms(cmd_args=[], cmd_options={}):
     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):
-            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
 
-    
+
 
 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):
-    """ 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} " +
-                  "{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')
@@ -1303,7 +2129,7 @@ def ShowSystemLog(cmd_args=None):
         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
@@ -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.output += "/%s" % vnodename 
+        _GetVnodePathName.output += "/%s" % vnodename
 
 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
 
-        # 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):
@@ -1466,6 +2292,8 @@ def GetVnodeLock(lockf):
         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('.')
 
@@ -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))
-        
+
     # 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')
-    
+
     # 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)
-                    lockf_blocker = lockf_blocker.lf_block.tqe_next    
+                    lockf_blocker = lockf_blocker.lf_block.tqe_next
     return out_str
 
 @lldb_command('showvnodelocks')
@@ -1531,7 +2359,7 @@ def ShowVnodeLocks(cmd_args=None):
 # EndMacro: showvnodelocks
 
 # Macro: showproclocks
-            
+
 @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]:
-            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
@@ -1577,13 +2405,14 @@ def ShowProcLocks(cmd_args=None):
 # 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 = ''
-    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)
+    kusecount = int(vnode.v_kusecount)
     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 = '-'
+    csblob_version = '-'
     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'
-    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')
@@ -1631,7 +2462,7 @@ def ShowVnode(cmd_args=None):
     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
@@ -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
-    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
-    fpptr = Cast(fdptr.fd_ofiles, 'fileproc *')
+    fpptr = Cast(fdptr.fd_ofiles, 'uint64_t *')
     while count < fdptr.fd_nfiles:
         fpp = dereference(fpptr)
-        fproc = Cast(fpp, 'fileproc *')
+        fproc = kern.GetValueFromAddress(int(fpp), 'fileproc *')
         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'
-                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
-        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):
@@ -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
-        returns 
+        returns
          - False on failure
-         - True on success 
+         - True on success
     """
     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
-    else: 
+    else:
         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} '
-    
-    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_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
@@ -1835,6 +2666,127 @@ def GetRWLEntry(rwlg):
                                            rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name)
     return out_string
 
+#Macro: showlock
+@lldb_type_summary(['lck_mtx_t *'])
+@header("===== Mutex Lock Summary =====")
+def GetMutexLockSummary(mtx):
+    """ Summarize mutex lock with important information.
+        params:
+        mtx: value - obj representing a mutex lock in kernel
+        returns:
+        out_str - summary of the mutex lock
+    """
+    if not mtx:
+        return "Invalid lock value: 0x0"
+
+    if kern.arch == "x86_64":
+        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"
+    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: "
+    if mtx.lck_mtx_data & 0x1:
+        out_str += "[Interlock Locked] "
+    if mtx.lck_mtx_data & 0x2:
+        out_str += "[Wait Flag]"
+    return out_str
+
+@lldb_type_summary(['lck_spin_t *'])
+@header("===== SpinLock Summary =====")
+def GetSpinLockSummary(spinlock):
+    """ Summarize spinlock with important information.
+        params:
+        spinlock: value - obj representing a spinlock in kernel
+        returns:
+        out_str - summary of the spinlock
+    """
+    if not spinlock:
+        return "Invalid lock value: 0x0"
+
+    out_str = "Lock Type\t\t: SPINLOCK\n"
+    if kern.arch == "x86_64":
+        out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
+        return out_str 
+
+    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')
+def ShowLock(cmd_args=None, cmd_options={}):
+    """ Show info about a lock - its state and owner thread details
+        Usage: showlock <address of a lock>
+        -M : to consider <addr> as lck_mtx_t 
+        -S : to consider <addr> as lck_spin_t 
+    """
+    if not cmd_args:
+        raise ArgumentError("Please specify the address of the lock whose info you want to view.")
+        return
+
+    summary_str = ""
+    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:
+            lock_mtx = kern.GetValueFromAddress(addr, 'lck_mtx_t *')
+            summary_str = GetMutexLockSummary(lock_mtx)
+        elif "-S" in cmd_options:
+            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
+    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
+
 @lldb_command('showallrwlck')
 def ShowAllRWLck(cmd_args=None):
     """ Routine to print a summary listing of all read/writer locks
@@ -1859,12 +2811,10 @@ def ShowBooterMemoryMap(cmd_args=None):
     """ 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
-    
+
     out_string = ""
     
     # Memory type map
@@ -1893,7 +2843,7 @@ def ShowBooterMemoryMap(cmd_args=None):
     
     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])
@@ -1909,3 +2859,2108 @@ def ShowBooterMemoryMap(cmd_args=None):
     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