]> 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
index 963db7bcc604df9de3fe26f521461871c4e29910..be557e7b8059875907f71487a64b70cd3838d963 100755 (executable)
@@ -9,28 +9,124 @@ 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)
-        print "memorystatus_available_pages: {: >10d}".format(kern.globals.memorystatus_available_pages)
-        print "inuse_ptepages_count:    {: >10d}".format(kern.globals.inuse_ptepages_count)
+        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 "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 ):
@@ -64,7 +160,7 @@ def CalculateLedgerPeak(phys_footprint_entry):
         ledger_peak = long(phys_footprint_entry._le._le_max.le_interval_max)
     return ledger_peak
 
-@header("{: >8s} {: >12s} {: >12s} {: >10s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s}  {: <20s}\n".format(
+@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):
@@ -99,7 +195,7 @@ def GetMemoryStatusNode(proc_val):
         out_str += "{: >12s}".format('-')
 
     out_str += "{: >10d}  ".format(phys_footprint_lifetime_max)
-    out_str += "{: >10d}  {: <20s}\n".format(phys_footprint_limit, proc_val.p_comm)
+    out_str += "{: >10d}  {: <32s}\n".format(phys_footprint_limit, GetProcName(proc_val))
     return out_str
 
 @lldb_command('showmemorystatus')
@@ -125,95 +221,195 @@ def ShowMemoryStatus(cmd_args=None):
 
 # EndMacro: showmemorystatus
 
-def GetRealMetadata(meta):
-    """ Get real metadata for a given metadata pointer
+class ZoneMeta(object):
     """
-    try:
-        if unsigned(meta.zindex) != 0x03FF:
-            return meta
-        else:
-            return kern.GetValueFromAddress(unsigned(meta) - unsigned(meta.real_metadata_offset), "struct zone_page_metadata *")
-    except:
-        return 0
-
-def GetFreeList(meta):
-    """ Get the free list pointer for a given metadata pointer
+    Helper class that helpers walking metadata
     """
-    global kern
-    zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
-    zone_map_max_address = kern.GetGlobalVariable('zone_map_max_address')
-    try:
-        if unsigned(meta.freelist_offset) == unsigned(0xffffffff):
-            return 0
+
+    @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:
-            if (unsigned(meta) >= unsigned(zone_map_min_address)) and (unsigned(meta) < unsigned(zone_map_max_address)):
-                page_index = ((unsigned(meta) - unsigned(kern.GetGlobalVariable('zone_metadata_region_min'))) / sizeof('struct zone_page_metadata'))
-                return (unsigned(zone_map_min_address) + (kern.globals.page_size * (page_index))) + meta.freelist_offset
-            else:
-                return (unsigned(meta) + meta.freelist_offset)
-    except:
+            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', 'FREE_CNT', 'ZONE', 'NAME'))
+@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
     """
-    out_str = ""
-    global kern
-    zinfo = 0
-    try:
-        out_str += 'Metadata Description:\n' + GetZoneMetadataSummary.header + '\n'
-        meta = kern.GetValueFromAddress(meta, "struct zone_page_metadata *")
-        if unsigned(meta.zindex) == 255:
-            out_str += "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}\n".format(meta, 0, 0, 0, 0, '(fake multipage meta)')
-            meta = GetRealMetadata(meta)
-            if meta == 0:
-                return ""
-        zinfo = kern.globals.zone_array[unsigned(meta.zindex)]
-        out_str += "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}".format(meta, GetFreeList(meta), meta.page_count, meta.free_count, addressof(zinfo), zinfo.zone_name)
-        return out_str
-    except:
-        out_str = ""
-        return out_str
 
-@header("{:<18s} {:>18s} {:>18s} {:<18s}".format('ADDRESS', 'TYPE', 'OFFSET_IN_PG', '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
     """
-    out_str = ""
     global kern
-    pagesize = kern.globals.page_size
-    zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
-    zone_map_max_address = kern.GetGlobalVariable('zone_map_max_address')
-    if (unsigned(addr) >= unsigned(zone_map_min_address)) and (unsigned(addr) < unsigned(zone_map_max_address)):
-        zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
-        zone_metadata_region_max = kern.GetGlobalVariable('zone_metadata_region_max')
-        if (unsigned(addr) >= unsigned(zone_metadata_region_min)) and (unsigned(addr) < unsigned(zone_metadata_region_max)):
-            metadata_offset = (unsigned(addr) - unsigned(zone_metadata_region_min)) % sizeof('struct zone_page_metadata')
-            page_offset_str = "{:d}/{:d}".format((unsigned(addr) - (unsigned(addr) & ~(pagesize - 1))), pagesize)
-            out_str += WhatIs.header + '\n'
-            out_str += "{:#018x} {:>18s} {:>18s} {:#018x}\n\n".format(unsigned(addr), "Metadata", page_offset_str, unsigned(addr) - metadata_offset)
-            out_str += GetZoneMetadataSummary((unsigned(addr) - metadata_offset)) + '\n\n'
-        else:
-            page_index = ((unsigned(addr) & ~(pagesize - 1)) - unsigned(zone_map_min_address)) / pagesize
-            meta = unsigned(zone_metadata_region_min) + (page_index * sizeof('struct zone_page_metadata'))
-            meta = kern.GetValueFromAddress(meta, "struct zone_page_metadata *")
-            page_meta = GetRealMetadata(meta)
-            if page_meta != 0:
-                zinfo = kern.globals.zone_array[unsigned(page_meta.zindex)]
-                page_offset_str = "{:d}/{:d}".format((unsigned(addr) - (unsigned(addr) & ~(pagesize - 1))), pagesize)
-                out_str += WhatIs.header + '\n'
-                out_str += "{:#018x} {:>18s} {:>18s} {:#018x}\n\n".format(unsigned(addr), "Element", page_offset_str, page_meta)
-                out_str += GetZoneMetadataSummary(unsigned(page_meta)) + '\n\n'
-            else:
-                out_str += "Unmapped address within the zone_map ({:#018x}-{:#018x})".format(zone_map_min_address, zone_map_max_address)
+
+    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:
-        out_str += "Address {:#018x} is outside the zone_map ({:#018x}-{:#018x})\n".format(addr, zone_map_min_address, zone_map_max_address)
+        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
-    return
+
+    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):
@@ -222,194 +418,228 @@ def WhatIsHelper(cmd_args=None):
     """
     if not cmd_args:
         raise ArgumentError("No arguments passed")
-    addr = kern.GetValueFromAddress(cmd_args[0], 'void *')
-    WhatIs(addr)
-    print "Hexdump:\n"
-    try:
-        data_array = kern.GetValueFromAddress(unsigned(addr) - 16, "uint8_t *")
-        print_hex_data(data_array[0:48], unsigned(addr) - 16, "")
-    except:
-        pass
-    return
+    WhatIs(kern.GetValueFromAddress(cmd_args[0], 'void *'))
 
 # Macro: showzcache
 
 @lldb_type_summary(['zone','zone_t'])
-@header("{:^18s} {:<40s} {:>10s} {:>10s} {:>10s} {:>10s}".format(
-'ZONE', 'NAME', 'CACHE_ELTS', 'DEP_VALID', 'DEP_EMPTY','DEP_FULL'))
+@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):
+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
     """
-    out_string = ""
-    format_string = '{:#018x} {:<40s} {:>10d} {:>10s} {:>10d} {:>10d}'
-    cache_elem_count = 0
+    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 i in range(0, kern.globals.machine_info.physical_cpu):
-            cache = zone.zcache[0].zcc_per_cpu_caches[i]
+        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
-        
-        if zone.zcache[0].zcc_depot_index != -1:
-            cache_elem_count += zone.zcache[0].zcc_depot_index * mag_capacity
-            out_string += format_string.format(zone, zone.zone_name, cache_elem_count, "Y", depot_capacity - zone.zcache[0].zcc_depot_index, zone.zcache[0].zcc_depot_index)
-        else:
-            out_string += format_string.format(zone, zone.zone_name, cache_elem_count, "N", 0, 0)
-
-    return out_string
-
-@lldb_command('showzcache')
-def ZcachePrint(cmd_args=None):
+            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
-    print GetZoneCacheSummary.header
-    for zval in kern.zones:
-        if zval.__getattr__('cpu_cache_enabled') :
-            print GetZoneCacheSummary(zval)
+    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} {:40s} {:>10s} {:>10s}".format(
+@header("{:18s}  {:32s}  {:<10s}  {:<10s}".format(
 'ZONE', 'NAME', 'CACHE_ELTS', 'CPU_INFO'))
 
-def GetZoneCacheCPUSummary(zone):
+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
     """
-    out_string = ""
-    format_string = '{:#018x} {:40s} {:10d} {cpuinfo:s}'
+    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') :
-        for i in range(0, kern.globals.machine_info.physical_cpu):
-            if i != 0:
+        i = 0
+        for cache in IterateZPerCPU(zone.zcache.zcc_pcpu, 'struct zcc_per_cpu_cache *'):
+            if i is not 0:
                 cpu_info += ", "
-            cache = zone.zcache[0].zcc_per_cpu_caches[i]
             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)
-        if zone.zcache[0].zcc_depot_index != -1:
-            cache_elem_count += zone.zcache[0].zcc_depot_index * mag_capacity
-
-    out_string += format_string.format(zone, zone.zone_name, cache_elem_count,cpuinfo = cpu_info)
+            i += 1
+        cache_elem_count += zone.zcache.zcc_depot.zcc_depot_index * mag_capacity
 
-    return out_string
+    print O.format(format_string, zone, ZoneName(zone), cache_elem_count,cpuinfo = cpu_info)
 
-@lldb_command('showzcachecpu')
-def ZcacheCPUPrint(cmd_args=None):
+@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
-    print GetZoneCacheCPUSummary.header
-    for zval in kern.zones:
-        if zval.__getattr__('cpu_cache_enabled') :
-            print GetZoneCacheCPUSummary(zval)
+    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}  {:_^23s}  {:_^24s}  {:_^13s}  {:_^31s}\n"+
-"{:<18s}  {:>11s} {:>11s}  {:>8s} {:>7s} {:>7s}  {:>6s} {:>6s}  {:>7s} {:>5s} {:>3s} {:>5s} {:>7s}   {:<15s} {:<20s}").format(
+@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', 'ALLOC', 'FREE', 'ALLOC', 'FREE', 'CACHE', 'COUNT', 'FREE', 'SIZE', 'ELTS', 'PGS', 'WASTE', 'ELT_SZ', 'FLAGS', 'NAME'))
-def GetZoneSummary(zone):
+'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
+          zone_val: value - obj representing a zone in kernel
         returns:
           str - summary of the zone
     """
-    out_string = ""
-    format_string = '{zone:#018x}  {zone.cur_size:11,d} {free_size:11,d}  {zone.count:8,d} {zone.countfree:7,d} {cache_elem_count:7,d}  {zone.page_count:6,d} {zone.count_all_free_pages:6,d}  {zone.alloc_size:7,d} {alloc_count:5,d} {alloc_pages:3,d} {alloc_waste:5,d} {zone.elem_size:7,d}   {markings:<15s} {zone.zone_name:<20s} '
     pagesize = kern.globals.page_size
+    out_string = ""
+    zone = GetZone(zone_val, marks)
 
-    free_size = zone.countfree * zone.elem_size
-    mag_capacity = kern.GetGlobalVariable('magazine_element_count')
-
-    alloc_pages = zone.alloc_size / pagesize
-    alloc_count = zone.alloc_size / zone.elem_size
-    alloc_waste = 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_without_vm_priv", "A"],
-            ["doing_alloc_with_vm_priv",    "S"],
-            ["waiting",                     "W"],
-            ["cpu_cache_enabled",           "E"]
-            ]
-    if kern.arch == 'x86_64':
-        marks.append(["gzalloc_exempt",     "M"])
-        marks.append(["alignment_required", "N"])
+    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 not zone.__getattr__("zone_valid") :
+    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+=" "
-    cache_elem_count = 0
-    if zone.__getattr__('cpu_cache_enabled') :
-        for i in range(0, kern.globals.machine_info.physical_cpu):
-            cache = zone.zcache[0].zcc_per_cpu_caches[i]
-            cache_elem_count += cache.current.zcc_magazine_index
-            cache_elem_count += cache.previous.zcc_magazine_index
-        if zone.zcache[0].zcc_depot_index != -1:
-            cache_elem_count += zone.zcache[0].zcc_depot_index * mag_capacity
 
-    out_string += format_string.format(zone=zone, free_size=free_size, alloc_count=alloc_count,
-                    alloc_pages=alloc_pages, alloc_waste=alloc_waste, cache_elem_count=cache_elem_count, markings=markings)
+    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.exhaustible :
-            out_string += "(max: {:d})".format(zone.max_size)
+    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', fancy=True)
+@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
@@ -423,9 +653,53 @@ def Zprint(cmd_args=None, cmd_options={}, O=None):
         I - zone was destroyed and is no longer valid
     """
     global kern
-    with O.table(GetZoneSummary.header):
+
+    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:
-            print GetZoneSummary(zval)
+            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 ):
@@ -460,12 +734,12 @@ def ShowZfreeListHeader(zone):
     """
 
     scaled_factor = (unsigned(kern.globals.zp_factor) +
-            (unsigned(zone.elem_size) >> unsigned(kern.globals.zp_scale)))
+            (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, scaled_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
@@ -485,7 +759,7 @@ def ShowZfreeListChain(zone, zfirst, zlimit):
         znext = dereference(Cast(current, '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.elem_size) - sizeof('vm_offset_t')), '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))
@@ -497,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')
@@ -523,25 +804,13 @@ def ShowZfreeList(cmd_args=None):
         zlimit = ArgumentStringToInt(cmd_args[1])
     ShowZfreeListHeader(zone)
 
-    if unsigned(zone.allows_foreign) == 1:
-        for free_page_meta in IterateQueue(zone.pages.any_free_foreign, '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 = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
-            if unsigned(zfirst) != 0:
+            zfirst = free_page_meta.getFreeList()
+            if 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 = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
-        if unsigned(zfirst) != 0:
-            ShowZfreeListChain(zone, zfirst, zlimit)
-    for free_page_meta in IterateQueue(zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
-        if ShowZfreeList.elts_found == zlimit:
-            break
-        zfirst = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
-        if unsigned(zfirst) != 0:
-            ShowZfreeListChain(zone, zfirst, zlimit)
 
     if ShowZfreeList.elts_found == zlimit:
         print "Stopped at {0: <d} elements!".format(zlimit)
@@ -559,7 +828,7 @@ def ZstackShowZonesBeingLogged(cmd_args=None):
     global kern
     for zval in kern.zones:
         if zval.zlog_btlog:
-          print "Zone: %s with its BTLog at: 0x%lx" % (zval.zone_name, zval.zlog_btlog)
+          print "Zone: %s with its BTLog at: 0x%lx" % (ZoneName(zval), zval.zlog_btlog)
 
 # EndMacro: zstack_showzonesbeinglogged
 
@@ -1042,6 +1311,55 @@ def ShowZstats(cmd_args=None):
 
 #EndMacro: showzstats
 
+#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:
@@ -1229,7 +1547,7 @@ def ShowAllVMStats(cmd_args=None):
     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} {p.p_comm: <20s} {s.error}"
+    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 *')
@@ -1261,7 +1579,7 @@ 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) / page_size), 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, show_pager_info, show_all_shadows):
@@ -1317,17 +1635,17 @@ 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)
     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)
+    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'])
@@ -2063,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
@@ -2087,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: <6s} {8: <35s}".format('vnode', 'usecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'cs_version', '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: <6s} {8: <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)
@@ -2118,7 +2437,7 @@ def GetVnodeSummary(vnode):
             mapped = '1'
         else:
             mapped = '0'
-    out_str += format_string.format(vnode, usecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, csblob_version, 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')
@@ -2242,7 +2561,7 @@ def ShowProcVnodes(cmd_args=None):
         fpp = dereference(fpptr)
         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'
@@ -2811,6 +3130,45 @@ def ShowMapVME(cmd_args=None, cmd_options={}):
     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
@@ -2878,13 +3236,13 @@ def showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, r
     rsize = 0
     if map.pmap != 0:
         rsize = int(map.pmap.stats.resident_count)
-    print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s}".format("vm_map","pmap","size","#ents","rsize","start","end")
-    print "{: <#018x} {: <#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x}".format(map,map.pmap,unsigned(map.size),map.hdr.nentries,rsize,map.hdr.links.start,map.hdr.links.end)
+    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}".format("vm_map_copy","pmap","size","#ents","rsize","start","end")
-    print "{: <#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x}".format(mapcopy,0,0,mapcopy.c_u.hdr.nentries,0,mapcopy.c_u.hdr.links.start,mapcopy.c_u.hdr.links.end)
+    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):
@@ -2931,8 +3289,6 @@ def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_al
                 object_str = "IPC_KERNEL_COPY_MAP"
             elif object == kern.globals.kalloc_map:
                 object_str = "KALLOC_MAP"
-            elif object == kern.globals.zone_map:
-                object_str = "ZONE_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:
@@ -3081,6 +3437,7 @@ FixedTags = {
     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",
 }
 
@@ -3104,7 +3461,7 @@ def GetVMKernName(tag):
                 return (kern.Symbolicate(site), "")
     return ("", "")
 
-@lldb_command("showvmtags", "AS")
+@lldb_command("showvmtags", "ASJ")
 def showvmtags(cmd_args=None, cmd_options={}):
     """Routine to print out info about kernel wired page allocations
         usage: showvmtags
@@ -3113,13 +3470,19 @@ def showvmtags(cmd_args=None, cmd_options={}):
                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
@@ -3143,29 +3506,55 @@ def showvmtags(cmd_args=None, cmd_options={}):
 
     total = 0
     totalmapped = 0
-    print " vm_allocation_tag_highest: {:<7d}  ".format(nsites - 1)
-    print " {:<7s}  {:>7s}   {:>7s}   {:>7s}  {:<50s}".format("tag.kmod", "peak", "size", "mapped", "name")
+    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)
-            site = kern.globals.vm_allocation_sites[tag]
-            print " {:>3d}{:<4s}  {:>7d}K  {:>7d}K  {:>7d}K  {:<50s}".format(tag, tagstr, tagpeaks[tag] / 1024, tagcounts[tag] / 1024, tagmapped[tag] / 1024, sitestr)
+            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:
-                    if ((subsite.flags & 0x007f) == 0):
-                        kind_str = "named"
-                    else:
-                        kind_str = "from"
                     (sitestr, tagstr) = GetVMKernName(alloctag)
-                    print " {:>7s}  {:>7s}   {:>7s}   {:>7d}K      {:s} {:>3d}{:<4s} {:<50s}".format(" ", " ", " ", amount / 1024, kind_str, alloctag, tagstr, sitestr)
+                    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 "Total:              {:>7d}K  {:>7d}K".format(total / 1024, totalmapped / 1024)
+                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
 
 
@@ -3305,19 +3694,21 @@ def _vm_page_unpack_ptr(page):
     if page == 0 :
         return page
 
-    min_addr = kern.globals.vm_min_kernel_and_kext_address
-    ptr_shift = kern.globals.vm_packed_pointer_shift
+    params = kern.globals.vm_page_packing_params
+    ptr_shift = params.vmpp_shift
     ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask
-    #INTEL - min_addr = 0xffffff7f80000000
-    #ARM - min_addr = 0x80000000
-    #ARM64 - min_addr = 0xffffff8000000000
-    if unsigned(page) & unsigned(ptr_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(page) << unsigned(ptr_shift)) + unsigned(min_addr))
+    return unsigned(vm_unpack_pointer(page, params))
 
 @lldb_command('calcvmpagehash')
 def CalcVMPageHash(cmd_args=None):
@@ -3347,64 +3738,6 @@ def _calc_vm_page_hash(obj, off):
 
     return hash_id
 
-def AddressIsFromZoneMap(addr):
-    zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
-    zone_map_max_address = kern.GetGlobalVariable('zone_map_max_address')
-    if (unsigned(addr) >= unsigned(zone_map_min_address)) and (unsigned(addr) < unsigned(zone_map_max_address)):
-        return 1
-    else:
-        return 0
-
-def ElementOffsetInForeignPage():
-    zone_element_alignment = 32 # defined in zalloc.c
-    zone_page_metadata_size = sizeof('struct zone_page_metadata')
-    if zone_page_metadata_size % zone_element_alignment == 0:
-        offset = zone_page_metadata_size
-    else:
-        offset = zone_page_metadata_size + (zone_element_alignment - (zone_page_metadata_size % zone_element_alignment))
-    return unsigned(offset)
-
-def ElementStartAddrFromZonePageMetadata(page_metadata):
-    zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
-    zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
-    page_size = kern.GetGlobalVariable('page_size')
-    if AddressIsFromZoneMap(page_metadata):
-        page_index = (unsigned(page_metadata) - unsigned(zone_metadata_region_min)) / sizeof('struct zone_page_metadata')
-        element_start_addr = unsigned(zone_map_min_address) + unsigned(page_index * page_size)
-    else:
-        element_start_addr = unsigned(page_metadata) + unsigned(ElementOffsetInForeignPage())
-
-    return element_start_addr
-
-def ZonePageStartAddrFromZonePageMetadata(page_metadata):
-    zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
-    zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
-    page_size = kern.GetGlobalVariable('page_size')
-
-    if AddressIsFromZoneMap(page_metadata):
-        page_index = (unsigned(page_metadata) - unsigned(zone_metadata_region_min)) / sizeof('struct zone_page_metadata')
-        zone_page_addr = unsigned(zone_map_min_address) + unsigned(page_index * page_size)
-    else:
-        zone_page_addr = unsigned(page_metadata)
-
-    return unsigned(zone_page_addr)
-
-def CreateFreeElementsList(zone, first_free):
-    free_elements = []
-    if unsigned(first_free) == 0:
-        return free_elements
-    current = first_free
-    while True:
-        free_elements.append(unsigned(current))
-        next = dereference(Cast(current, 'vm_offset_t *'))
-        next = (unsigned(next) ^ unsigned(kern.globals.zp_nopoison_cookie))
-        next = kern.GetValueFromAddress(next, 'vm_offset_t *')
-        if unsigned(next) == 0:
-            break;
-        current = Cast(next, 'void *')
-
-    return free_elements
-
 #Macro: showallocatedzoneelement
 @lldb_command('showallocatedzoneelement')
 def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}):
@@ -3424,43 +3757,25 @@ def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}):
 #EndMacro: showallocatedzoneelement
 
 def FindAllocatedElementsInZone(zone):
-    page_size = kern.GetGlobalVariable('page_size')
     elements = []
-    page_queues = ["any_free_foreign", "intermediate", "all_used"]
-    found_total = 0
 
-    for queue in page_queues:
-        found_in_queue = 0
-        if queue == "any_free_foreign" and unsigned(zone.allows_foreign) != 1:
-            continue
+    if not zone.z_self or zone.permanent:
+        return elements
 
-        for zone_page_metadata in IterateQueue(zone.pages.__getattr__(queue), 'struct zone_page_metadata *', 'pages'):
-            free_elements = []
-            first_free_element = kern.GetValueFromAddress(GetFreeList(zone_page_metadata))
-            free_elements = CreateFreeElementsList(zone, first_free_element)
-
-            chunk_page_count = zone_page_metadata.page_count
-            element_addr_start = ElementStartAddrFromZonePageMetadata(zone_page_metadata)
-            zone_page_start = ZonePageStartAddrFromZonePageMetadata(zone_page_metadata)
-            next_page = zone_page_start + page_size
-            element_addr_end = zone_page_start + (chunk_page_count * page_size)
-            elem = unsigned(element_addr_start)
-            while elem < element_addr_end:
-                if elem not in free_elements:
-                    elements.append(elem)
-                    found_in_queue += 1
-                elem += zone.elem_size
+    for head in [zone.pages_any_free_foreign, zone.pages_all_used_foreign,
+            zone.pages_intermediate, zone.pages_all_used]:
 
-                if queue == "any_free_foreign":
-                    if (elem + zone.elem_size) >= next_page:
-                        zone_page_start = unsigned((elem + page_size) & ~(page_size - 1))
-                        next_page = zone_page_start + page_size
-                        elem = zone_page_start + unsigned(ElementOffsetInForeignPage())
+        for meta in ZoneIteratePageQueue(head):
+            free_elements = set(meta.iterateFreeList())
 
-        found_total += found_in_queue
-#       print "Found {0: <d} allocated elements in the {1: <s} page queue".format(found_in_queue, queue)
+            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
 
-#   print "Total number of allocated elements: {0: <d} in zone {1: <s}".format(found_total, zone.zone_name)
     return elements
 
 def match_vm_page_attributes(page, matching_attributes):
@@ -3615,7 +3930,7 @@ def ScanVMPages(cmd_args=None, cmd_options={}):
         i = 0
         while i < num_zones:
             zone = zone_array[i]
-            if str(zone.zone_name) == "vm pages":
+            if str(zone.z_name) == "vm pages":
                 break;
             i += 1
 
@@ -3758,9 +4073,13 @@ def VMObjectWalkPages(cmd_args=None, cmd_options={}):
             print "stopping...\n"
             return
 
-        if ((vmp.vmp_unused_page_bits != 0) or (vmp.vmp_unused_object_bits != 0)):
-            print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused__pageq_bits: %d unused_object_bits : %d\n" % (vmp.vmp_unused_page_bits,
-                                            vmp.vmp_unused_object_bits)
+        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
 
@@ -4213,7 +4532,7 @@ def vm_page_lookup_in_compressor(slot_ptr):
         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 = (unsigned(cs.c_packed_ptr) << 2) + vm_min_kernel_and_kext_address()
+    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)
@@ -4226,19 +4545,6 @@ def vm_page_lookup_in_compressor(slot_ptr):
     else:
         print "<no compressed data>"
 
-def vm_min_kernel_and_kext_address(cmd_args=None):
-    if hasattr(kern.globals, 'vm_min_kernel_and_kext_address'):
-        return unsigned(kern.globals.vm_min_kernel_and_kext_address)
-    elif kern.arch == 'x86_64':
-        return unsigned(0xffffff7f80000000)
-    elif kern.arch == 'arm64':
-        return unsigned(0xffffff8000000000)
-    elif kern.arch == 'arm':
-        return unsigned(0x80000000)
-    else:
-        print "vm_min_kernel_and_kext_address(): unknown arch '{:s}'".format(kern.arch)
-        return unsigned(0)
-
 def print_hex_data(data, begin_offset=0, desc=""):
     """ print on stdout "hexdump -C < data" like output
         params:
@@ -4265,7 +4571,8 @@ def print_hex_data(data, begin_offset=0, desc=""):
             print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
             hex_buf = ""
             char_buf = ""
-    print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, 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')
@@ -4381,7 +4688,7 @@ def ShowAllVMNamedEntries(cmd_args=None):
 
     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")
+#    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
@@ -4412,8 +4719,10 @@ def showmemoryentry(entry, idx=0, queue_len=0):
         backing += "SUBMAP"
     if entry.is_copy == 1:
         backing += "COPY"
-    if entry.is_sub_map == 0 and entry.is_copy == 0:
+    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"
@@ -4432,13 +4741,16 @@ def showmemoryentry(entry, idx=0, queue_len=0):
         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 "{:>6d}/{:<6d} {: <#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.object,entry.offset,entry.data_offset,entry.size,extra_str)
+    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)
-    if entry.is_copy == 1:
+    elif entry.is_copy == 1:
         showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
-    if entry.is_sub_map == 0 and entry.is_copy == 0:
-        showvmobject(entry.backing.object, entry.offset, entry.size, show_pager_info, show_all_shadows)
+    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):
@@ -4543,6 +4855,28 @@ def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}):
     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:
@@ -4620,11 +4954,13 @@ def GetProcNameForObjectOwner(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.object)
+    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"
-    else:
+    elif mem_entry.is_object:
         out_str += " is_object"
+    else:
+        out_str += " ???"
     return out_str