]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/kcdata.py
xnu-4903.221.2.tar.gz
[apple/xnu.git] / tools / lldbmacros / kcdata.py
index 4ef6a75974fd550b1fb4e602bf375405339838f2..f6db996ccb42d078b06a6646206d59f23992ab98 100755 (executable)
@@ -15,6 +15,12 @@ import logging
 import contextlib
 import base64
 
+class Globals(object):
+    pass
+G = Globals()
+G.accept_incomplete_data = False
+G.data_was_incomplete = False
+
 kcdata_type_def = {
     'KCDATA_TYPE_INVALID':              0x0,
     'KCDATA_TYPE_STRING_DESC':          0x1,
@@ -24,9 +30,9 @@ kcdata_type_def = {
     'KCDATA_TYPE_INT64_DESC':           0x5,
     'KCDATA_TYPE_BINDATA_DESC':         0x6,
     'KCDATA_TYPE_ARRAY':                0x11,
-    'KCDATA_TYPE_TYPEDEFINTION':        0x12,
+    'KCDATA_TYPE_TYPEDEFINITION':       0x12,
     'KCDATA_TYPE_CONTAINER_BEGIN':      0x13,
-    'KCDATA_TYPE_CONTIANER_END':        0x14,
+    'KCDATA_TYPE_CONTAINER_END':        0x14,
 
     'KCDATA_TYPE_ARRAY_PAD0':           0x20,
     'KCDATA_TYPE_ARRAY_PAD1':           0x21,
@@ -68,9 +74,6 @@ kcdata_type_def = {
     'STACKSHOT_KCTYPE_KERN_PAGE_SIZE':  0x910,
     'STACKSHOT_KCTYPE_JETSAM_LEVEL':    0x911,
     'STACKSHOT_KCTYPE_DELTA_SINCE_TIMESTAMP': 0x912,
-    'STACKSHOT_KCTYPE_TASK_DELTA_SNAPSHOT': 0x940,
-    'STACKSHOT_KCTYPE_THREAD_DELTA_SNAPSHOT': 0x941,
-
     'STACKSHOT_KCTYPE_KERN_STACKLR':  0x913,
     'STACKSHOT_KCTYPE_KERN_STACKLR64':  0x914,
     'STACKSHOT_KCTYPE_USER_STACKLR':  0x915,
@@ -82,6 +85,21 @@ kcdata_type_def = {
     'STACKSHOT_KCTYPE_STACKSHOT_FAULT_STATS': 0x91b,
     'STACKSHOT_KCTYPE_KERNELCACHE_LOADINFO': 0x91c,
     'STACKSHOT_KCTYPE_THREAD_WAITINFO' : 0x91d,
+    'STACKSHOT_KCTYPE_THREAD_GROUP_SNAPSHOT' : 0x91e,
+    'STACKSHOT_KCTYPE_THREAD_GROUP' : 0x91f,
+    'STACKSHOT_KCTYPE_JETSAM_COALITION_SNAPSHOT' : 0x920,
+    'STACKSHOT_KCTYPE_JETSAM_COALITION' : 0x921,
+    'STACKSHOT_KCTYPE_THREAD_POLICY_VERSION': 0x922,
+    'STACKSHOT_KCTYPE_INSTRS_CYCLES' : 0x923,
+    'STACKSHOT_KCTYPE_USER_STACKTOP' : 0x924,
+    'STACKSHOT_KCTYPE_ASID' : 0x925,
+    'STACKSHOT_KCTYPE_PAGE_TABLES' : 0x926,
+    'STACKSHOT_KCTYPE_SYS_SHAREDCACHE_LAYOUT' : 0x927,
+
+    'STACKSHOT_KCTYPE_TASK_DELTA_SNAPSHOT': 0x940,
+    'STACKSHOT_KCTYPE_THREAD_DELTA_SNAPSHOT': 0x941,
+
+
 
     'KCDATA_TYPE_BUFFER_END':      0xF19158ED,
 
@@ -120,6 +138,8 @@ kcdata_type_def = {
     'EXIT_REASON_USER_DESC':               0x1002,
     'EXIT_REASON_USER_PAYLOAD':            0x1003,
     'EXIT_REASON_CODESIGNING_INFO':        0x1004,
+    'EXIT_REASON_WORKLOOP_ID':             0x1005,
+    'EXIT_REASON_DISPATCH_QUEUE_NO':       0x1006,
     'KCDATA_BUFFER_BEGIN_CRASHINFO':       0xDEADF157,
     'KCDATA_BUFFER_BEGIN_DELTA_STACKSHOT': 0xDE17A59A,
     'KCDATA_BUFFER_BEGIN_STACKSHOT':       0x59a25807,
@@ -184,12 +204,14 @@ class KCSubTypeElement(object):
         return KCSubTypeElement(st_name, st_type, st_size, st_offset, st_flag)
 
     @staticmethod
-    def FromBasicCtype(st_name, st_type, st_offset=0):
+    def FromBasicCtype(st_name, st_type, st_offset=0, legacy_size=None):
         if st_type <= 0 or st_type > KCSUBTYPE_TYPE.KC_ST_UINT64:
             raise ValueError("Invalid type passed %d" % st_type)
         st_size = struct.calcsize(KCSubTypeElement._unpack_formats[st_type])
         st_flag = 0
         retval = KCSubTypeElement(st_name, st_type, st_size, st_offset, st_flag, KCSubTypeElement._get_naked_element_value)
+        if legacy_size:
+            retval.legacy_size = legacy_size
         return retval
 
     @staticmethod
@@ -239,8 +261,7 @@ class KCSubTypeElement(object):
                 if ord(_v) == 0:
                     break
                 str_arr.append(self.GetValueAsString(base_data, i))
-
-            return '"' + ''.join(str_arr) + '"'
+            return json.dumps(''.join(str_arr))
 
         count = self.count
         if count > len(base_data)/self.size:
@@ -274,14 +295,16 @@ class KCSubTypeElement(object):
 
 
 class KCTypeDescription(object):
-    def __init__(self, t_type_id, t_elements=[], t_name='anon', custom_repr=None, legacy_size=None, merge=False):
+    def __init__(self, t_type_id, t_elements=[], t_name='anon', custom_repr=None, legacy_size=None, merge=False, naked=False):
         self.type_id = t_type_id
         self.elements = t_elements
         self.name = t_name
         self.totalsize = 0
         self.custom_JsonRepr = custom_repr
-        self.legacy_size = legacy_size
+        if legacy_size:
+            self.legacy_size = legacy_size
         self.merge = merge
+        self.naked = naked
         for e in self.elements:
             self.totalsize += e.GetTotalSize()
 
@@ -302,7 +325,8 @@ class KCTypeDescription(object):
 
     @staticmethod
     def FromKCTypeDescription(other, t_type_id, t_name):
-        retval = KCTypeDescription(t_type_id, other.elements, t_name, other.custom_JsonRepr)
+        retval = KCTypeDescription(t_type_id, other.elements, t_name, other.custom_JsonRepr,
+                                   legacy_size=getattr(other, 'legacy_size', None))
         return retval
 
     def ShouldMerge(self):
@@ -313,11 +337,14 @@ class KCTypeDescription(object):
             padding = (flags & KCDATA_FLAGS_STRUCT_PADDING_MASK)
             if padding:
                 base_data = base_data[:-padding]
-        elif self.legacy_size and len(base_data) == self.legacy_size + ((-self.legacy_size) & 0xf):
+        elif hasattr(self, 'legacy_size') and len(base_data) == self.legacy_size + ((-self.legacy_size) & 0xf):
             base_data = base_data[:self.legacy_size]
         if self.custom_JsonRepr:
             return self.custom_JsonRepr([e.GetValue(base_data) for e in self.elements])
-        o = ", ".join(['"%s": %s' % (e.GetName(), e.GetJsonRepr(base_data)) for e in self.elements if not e.ShouldSkip(base_data)])
+        if self.naked:
+            o = ", ".join([e.GetJsonRepr(base_data) for e in self.elements if not e.ShouldSkip(base_data)])
+        else:
+            o = ", ".join(['"%s": %s' % (e.GetName(), e.GetJsonRepr(base_data)) for e in self.elements if not e.ShouldSkip(base_data)])
         if not self.merge:
             o = '{' + o + '}'
         return o
@@ -397,6 +424,9 @@ class KCObject(object):
 
         self.InitAfterParse()
 
+    def __str__(self):
+        return "<KCObject at 0x%x>" % self.offset
+
     def InitAfterParse(self):
         pass
 
@@ -405,7 +435,7 @@ class KCObject(object):
         return KCObject(kcitem.i_type, kcitem.i_data, kcitem.i_offset, kcitem.i_flags)
 
     def IsContainerEnd(self):
-        return self.i_type == GetTypeForName('KCDATA_TYPE_CONTIANER_END')
+        return self.i_type == GetTypeForName('KCDATA_TYPE_CONTAINER_END')
 
     def IsBufferEnd(self):
         return self.i_type == GetTypeForName('KCDATA_TYPE_BUFFER_END')
@@ -447,7 +477,7 @@ class KCObject(object):
             self.obj['typeID'] = self.i_type
             logging.info("0x%08x: %s%s" % (self.offset, INDENT(), self.i_name))
 
-        elif self.i_type == GetTypeForName('KCDATA_TYPE_CONTIANER_END'):
+        elif self.i_type == GetTypeForName('KCDATA_TYPE_CONTAINER_END'):
             self.obj['uniqID'] = self.i_flags
             logging.info("0x%08x: %sEND" % (self.offset, INDENT(end=True)))
 
@@ -469,7 +499,7 @@ class KCObject(object):
             self.obj = u_d[1]
             logging.info("0x%08x: %s%s" % (self.offset, INDENT(), self.i_name))
 
-        elif self.i_type == GetTypeForName('KCDATA_TYPE_TYPEDEFINTION'):
+        elif self.i_type == GetTypeForName('KCDATA_TYPE_TYPEDEFINITION'):
             self.is_naked_type = True
             u_d = struct.unpack_from('II32s', self.i_data)
             self.obj['name'] = u_d[2].split(chr(0))[0]
@@ -479,10 +509,8 @@ class KCObject(object):
             element_arr = []
             for i in range(u_d[1]):
                 e = KCSubTypeElement.FromBinaryTypeData(self.i_data[40+(i*40):])
-                #print str(e)
                 element_arr.append(e)
             type_desc = KCTypeDescription(u_d[0], element_arr, self.obj['name'])
-            #print str(type_desc)
             self.obj['fields'] = [str(e) for e in element_arr]
             KNOWN_TYPES_COLLECTION[type_desc.GetTypeID()] = type_desc
             logging.info("0x%08x: %s%s" % (self.offset, INDENT(), self.i_name))
@@ -493,9 +521,9 @@ class KCObject(object):
             if e_t not in LEGAL_OLD_STYLE_ARRAY_TYPES:
                 raise Exception, "illegal old-style array type: %s (0x%x)" % (GetTypeNameForKey(e_t), e_t)
             e_c = self.i_flags & 0xffffffff
-            e_s = KNOWN_TYPES_COLLECTION[e_t].sizeof()
+            e_s = KNOWN_TYPES_COLLECTION[e_t].legacy_size
             if e_s * e_c > self.i_size:
-                raise Excpetion, "array too small for its count"
+                raise Exception("array too small for its count")
             self.obj['typeID'] = e_t
             self.i_name = GetTypeNameForKey(e_t)
             self.i_type = e_t
@@ -605,11 +633,19 @@ class KCContainerObject(KCObject):
                 if self.IsEndMarker(o):
                     found_end = True
                     break
+                if o.IsBufferEnd():
+                    break
                 if isinstance(o, KCContainerObject):
                     o.ReadItems(iterator)
                 self.AddObject(o)
         if not found_end:
-            raise Exception, self.no_end_message
+            if G.accept_incomplete_data:
+                if not G.data_was_incomplete:
+                    print >>sys.stderr, "kcdata.py WARNING: data is incomplete!"
+                    G.data_was_incomplete = True
+            else:
+                raise Exception, self.no_end_message
+
 
 
 class KCBufferObject(KCContainerObject):
@@ -778,6 +814,8 @@ KNOWN_TYPES_COLLECTION[0x906] = KCTypeDescription(0x906, (
     KCSubTypeElement.FromBasicCtype('ths_rqos_override', KCSUBTYPE_TYPE.KC_ST_UINT8, 102),
     KCSubTypeElement.FromBasicCtype('ths_io_tier', KCSUBTYPE_TYPE.KC_ST_UINT8, 103),
     KCSubTypeElement.FromBasicCtype('ths_thread_t', KCSUBTYPE_TYPE.KC_ST_UINT64, 104),
+    KCSubTypeElement.FromBasicCtype('ths_requested_policy', KCSUBTYPE_TYPE.KC_ST_UINT64, 112),
+    KCSubTypeElement.FromBasicCtype('ths_effective_policy', KCSUBTYPE_TYPE.KC_ST_UINT64, 120),
 ),
     'thread_snapshot',
     legacy_size = 0x68
@@ -796,8 +834,11 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_THREAD_DELTA_SNAPSHOT')]
     KCSubTypeElement.FromBasicCtype('tds_rqos', KCSUBTYPE_TYPE.KC_ST_UINT8, 45),
     KCSubTypeElement.FromBasicCtype('tds_rqos_override', KCSUBTYPE_TYPE.KC_ST_UINT8, 46),
     KCSubTypeElement.FromBasicCtype('tds_io_tier', KCSUBTYPE_TYPE.KC_ST_UINT8, 47),
+    KCSubTypeElement.FromBasicCtype('tds_requested_policy', KCSUBTYPE_TYPE.KC_ST_UINT64, 48),
+    KCSubTypeElement.FromBasicCtype('tds_effective_policy', KCSUBTYPE_TYPE.KC_ST_UINT64, 56),
 ),
-    'thread_delta_snapshot'
+    'thread_delta_snapshot',
+    legacy_size = 48
 )
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_TASK_DELTA_SNAPSHOT')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_TASK_DELTA_SNAPSHOT'), (
@@ -821,20 +862,27 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_TASK_DELTA_SNAPSHOT')] =
 
 KNOWN_TYPES_COLLECTION[0x909] = KCSubTypeElement('pth_name', KCSUBTYPE_TYPE.KC_ST_CHAR, KCSubTypeElement.GetSizeForArray(64, 1), 0, 1)
 
-
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_SYS_SHAREDCACHE_LAYOUT')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_SYS_SHAREDCACHE_LAYOUT'), (
+    KCSubTypeElement('imageLoadAddress', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0),
+    KCSubTypeElement('imageUUID', KCSUBTYPE_TYPE.KC_ST_UINT8, KCSubTypeElement.GetSizeForArray(16, 1), 8, 1)
+),
+    'system_shared_cache_layout'
+)
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('KCDATA_TYPE_LIBRARY_LOADINFO64')] = KCTypeDescription(GetTypeForName('KCDATA_TYPE_LIBRARY_LOADINFO64'), (
     KCSubTypeElement('imageLoadAddress', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0),
     KCSubTypeElement('imageUUID', KCSUBTYPE_TYPE.KC_ST_UINT8, KCSubTypeElement.GetSizeForArray(16, 1), 8, 1)
 ),
-    'dyld_load_info'
+    'dyld_load_info',
+    legacy_size = 24
 )
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('KCDATA_TYPE_LIBRARY_LOADINFO')] = KCTypeDescription(GetTypeForName('KCDATA_TYPE_LIBRARY_LOADINFO'), (
     KCSubTypeElement('imageLoadAddress', KCSUBTYPE_TYPE.KC_ST_UINT32, 4, 0, 0),
     KCSubTypeElement('imageUUID', KCSUBTYPE_TYPE.KC_ST_UINT8, KCSubTypeElement.GetSizeForArray(16, 1), 4, 1)
 ),
-    'dyld_load_info'
+    'dyld_load_info',
+    legacy_size = 20
 )
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_SHAREDCACHE_LOADINFO')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_SHAREDCACHE_LOADINFO'), (
@@ -854,7 +902,7 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_KERNELCACHE_LOADINFO')]
 )
 
 KNOWN_TYPES_COLLECTION[0x33] = KCSubTypeElement('mach_absolute_time', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value)
-KNOWN_TYPES_COLLECTION[0x907] = KCSubTypeElement.FromBasicCtype('donating_pids', KCSUBTYPE_TYPE.KC_ST_INT32)
+KNOWN_TYPES_COLLECTION[0x907] = KCSubTypeElement.FromBasicCtype('donating_pids', KCSUBTYPE_TYPE.KC_ST_INT32, legacy_size=4)
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('KCDATA_TYPE_USECS_SINCE_EPOCH')] = KCSubTypeElement('usecs_since_epoch', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value)
 
@@ -862,7 +910,8 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_KERN_STACKFRAME')] = KCT
     KCSubTypeElement.FromBasicCtype('lr', KCSUBTYPE_TYPE.KC_ST_UINT32),
     KCSubTypeElement.FromBasicCtype('sp', KCSUBTYPE_TYPE.KC_ST_UINT32, 4)
 ),
-    'kernel_stack_frames'
+    'kernel_stack_frames',
+    legacy_size = 8
 )
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_KERN_STACKLR')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_KERN_STACKLR'), (
@@ -888,7 +937,8 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_KERN_STACKFRAME64')] = K
     KCSubTypeElement.FromBasicCtype('lr', KCSUBTYPE_TYPE.KC_ST_UINT64),
     KCSubTypeElement.FromBasicCtype('sp', KCSUBTYPE_TYPE.KC_ST_UINT64, 8)
 ),
-    'kernel_stack_frames'
+    'kernel_stack_frames',
+    legacy_size = 16
 )
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_USER_STACKFRAME64')] = KCTypeDescription.FromKCTypeDescription(
@@ -923,6 +973,8 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_BOOTARGS')] = KCSubTypeE
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_KERN_PAGE_SIZE')] = KCSubTypeElement('kernel_page_size', KCSUBTYPE_TYPE.KC_ST_UINT32, 4, 0, 0, KCSubTypeElement._get_naked_element_value)
 
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_THREAD_POLICY_VERSION')] = KCSubTypeElement('thread_policy_version', KCSUBTYPE_TYPE.KC_ST_UINT32, 4, 0, 0, KCSubTypeElement._get_naked_element_value)
+
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_JETSAM_LEVEL')] = KCSubTypeElement('jetsam_level', KCSUBTYPE_TYPE.KC_ST_UINT32, 4, 0, 0, KCSubTypeElement._get_naked_element_value)
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_DELTA_SINCE_TIMESTAMP')] = KCSubTypeElement("stackshot_delta_since_timestamp", KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value)
@@ -945,6 +997,48 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_THREAD_WAITINFO')] = KCT
             ),
             'thread_waitinfo')
 
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_THREAD_GROUP_SNAPSHOT')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_THREAD_GROUP'),
+            (
+                        KCSubTypeElement.FromBasicCtype('tgs_id', KCSUBTYPE_TYPE.KC_ST_UINT64, 0),
+                        KCSubTypeElement('tgs_name', KCSUBTYPE_TYPE.KC_ST_CHAR, KCSubTypeElement.GetSizeForArray(16, 1),
+                            8, 1),
+                        KCSubTypeElement.FromBasicCtype('tgs_flags', KCSUBTYPE_TYPE.KC_ST_UINT64, 24),
+            ),
+            'thread_group_snapshot')
+
+
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_THREAD_GROUP')] = KCSubTypeElement('thread_group', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value)
+
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_JETSAM_COALITION_SNAPSHOT')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_JETSAM_COALITION_SNAPSHOT'),
+            (
+                        KCSubTypeElement.FromBasicCtype('jcs_id', KCSUBTYPE_TYPE.KC_ST_UINT64, 0),
+                        KCSubTypeElement.FromBasicCtype('jcs_flags', KCSUBTYPE_TYPE.KC_ST_UINT64, 8),
+                        KCSubTypeElement.FromBasicCtype('jcs_thread_group', KCSUBTYPE_TYPE.KC_ST_UINT64, 16),
+                        KCSubTypeElement.FromBasicCtype('jcs_leader_task_uniqueid', KCSUBTYPE_TYPE.KC_ST_UINT64, 24)
+            ),
+            'jetsam_coalition_snapshot')
+
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_JETSAM_COALITION')] = KCSubTypeElement('jetsam_coalition', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value)
+
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_INSTRS_CYCLES')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_INSTRS_CYCLES'),
+            (
+                        KCSubTypeElement.FromBasicCtype('ics_instructions', KCSUBTYPE_TYPE.KC_ST_UINT64, 0),
+                        KCSubTypeElement.FromBasicCtype('ics_cycles', KCSUBTYPE_TYPE.KC_ST_UINT64, 8)
+            ),
+            'instrs_cycles_snapshot')
+
+def set_type(name, *args):
+    typ = GetTypeForName(name)
+    KNOWN_TYPES_COLLECTION[typ] = KCTypeDescription(GetTypeForName(typ), *args)
+
+
+set_type('STACKSHOT_KCTYPE_USER_STACKTOP',
+         (
+             KCSubTypeElement.FromBasicCtype('sp', KCSUBTYPE_TYPE.KC_ST_UINT64, 0),
+             KCSubTypeElement('stack_contents', KCSUBTYPE_TYPE.KC_ST_UINT8, KCSubTypeElement.GetSizeForArray(8, 1), 8, 1),
+         ),
+         'user_stacktop')
+
 #KNOWN_TYPES_COLLECTION[0x907] = KCSubTypeElement('donating_pids', KCSUBTYPE_TYPE.KC_ST_UINT32, 4, 0, 0, KCSubTypeElement._get_naked_element_value)
 KNOWN_TYPES_COLLECTION[GetTypeForName('TASK_CRASHINFO_PID')] = KCSubTypeElement('pid', KCSUBTYPE_TYPE.KC_ST_INT32, 4, 0, 0)
 KNOWN_TYPES_COLLECTION[GetTypeForName('TASK_CRASHINFO_PPID')] = KCSubTypeElement('ppid', KCSUBTYPE_TYPE.KC_ST_INT32, 4, 0, 0)
@@ -1026,6 +1120,7 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_CPU_TIMES')] = KCTypeDes
     (
         KCSubTypeElement.FromBasicCtype('user_usec', KCSUBTYPE_TYPE.KC_ST_UINT64, 0),
         KCSubTypeElement.FromBasicCtype('system_usec', KCSUBTYPE_TYPE.KC_ST_UINT64, 8),
+        KCSubTypeElement.FromBasicCtype('runnable_usec', KCSUBTYPE_TYPE.KC_ST_UINT64, 16),
     ), 'cpu_times')
 
 KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_STACKSHOT_DURATION')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_STACKSHOT_DURATION'),
@@ -1075,6 +1170,21 @@ KNOWN_TYPES_COLLECTION[GetTypeForName('EXIT_REASON_CODESIGNING_INFO')] = KCTypeD
         KCSubTypeElement.FromBasicCtype('ceri_page_shadow_depth', KCSUBTYPE_TYPE.KC_ST_UINT32, 2104),
     ), 'exit_reason_codesigning_info')
 
+KNOWN_TYPES_COLLECTION[GetTypeForName('EXIT_REASON_WORKLOOP_ID')] = (
+        KCSubTypeElement('exit_reason_workloop_id', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value))
+
+KNOWN_TYPES_COLLECTION[GetTypeForName('EXIT_REASON_DISPATCH_QUEUE_NO')] = (
+        KCSubTypeElement('exit_reason_dispatch_queue_no', KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value))
+
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_ASID')] = (
+    KCSubTypeElement('ts_asid', KCSUBTYPE_TYPE.KC_ST_UINT32, 4, 0, 0))
+
+KNOWN_TYPES_COLLECTION[GetTypeForName('STACKSHOT_KCTYPE_PAGE_TABLES')] = KCTypeDescription(GetTypeForName('STACKSHOT_KCTYPE_PAGE_TABLES'), (
+    KCSubTypeElement(None, KCSUBTYPE_TYPE.KC_ST_UINT64, 8, 0, 0, KCSubTypeElement._get_naked_element_value), ),
+    'ts_pagetable',
+    merge=True,
+    naked=True
+)
 
 def GetSecondsFromMATime(mat, tb):
     return (float(mat) * tb['numer']) / tb['denom']
@@ -1120,6 +1230,7 @@ def GetStateDescription(s):
     TH_UNINT = 0x08
     TH_TERMINATE = 0x10
     TH_TERMINATE2 = 0x20
+    TH_WAIT_REPORT = 0x40
     TH_IDLE = 0x80
     if (s & TH_WAIT):
         retval.append("TH_WAIT")
@@ -1133,6 +1244,8 @@ def GetStateDescription(s):
         retval.append("TH_TERMINATE")
     if (s & TH_TERMINATE2):
         retval.append("TH_TERMINATE2")
+    if (s & TH_WAIT_REPORT):
+        retval.append("TH_WAIT_REPORT")
     if (s & TH_IDLE):
         retval.append("TH_IDLE")
     return retval
@@ -1141,15 +1254,145 @@ def GetStateDescription(s):
 def format_uuid(elementValues):
     return ''.join("%02x" % i for i in elementValues)
 
-def SaveStackshotReport(j, outfile_name, dsc_uuid, dsc_libs_arr):
+kThreadWaitNone                        = 0x00
+kThreadWaitKernelMutex          = 0x01
+kThreadWaitPortReceive          = 0x02
+kThreadWaitPortSetReceive       = 0x03
+kThreadWaitPortSend             = 0x04
+kThreadWaitPortSendInTransit    = 0x05
+kThreadWaitSemaphore            = 0x06
+kThreadWaitKernelRWLockRead     = 0x07
+kThreadWaitKernelRWLockWrite    = 0x08
+kThreadWaitKernelRWLockUpgrade  = 0x09
+kThreadWaitUserLock             = 0x0a
+kThreadWaitPThreadMutex         = 0x0b
+kThreadWaitPThreadRWLockRead    = 0x0c
+kThreadWaitPThreadRWLockWrite   = 0x0d
+kThreadWaitPThreadCondVar       = 0x0e
+kThreadWaitParkedWorkQueue      = 0x0f
+kThreadWaitWorkloopSyncWait     = 0x10
+kThreadWaitOnProcess            = 0x11
+
+
+UINT64_MAX = 0xffffffffffffffff
+STACKSHOT_WAITOWNER_KERNEL      = (UINT64_MAX - 1)
+STACKSHOT_WAITOWNER_PORT_LOCKED = (UINT64_MAX - 2)
+STACKSHOT_WAITOWNER_PSET_LOCKED = (UINT64_MAX - 3)
+STACKSHOT_WAITOWNER_INTRANSIT   = (UINT64_MAX - 4)
+STACKSHOT_WAITOWNER_MTXSPIN     = (UINT64_MAX - 5)
+STACKSHOT_WAITOWNER_THREQUESTED = (UINT64_MAX - 6)
+STACKSHOT_WAITOWNER_SUSPENDED   = (UINT64_MAX - 7)
+
+def formatWaitInfo(info):
+    s = 'thread %d: ' % info['waiter'];
+    type = info['wait_type']
+    context = info['context']
+    owner = info['owner']
+    if type == kThreadWaitKernelMutex:
+        s += 'kernel mutex %x' % context
+        if owner == STACKSHOT_WAITOWNER_MTXSPIN:
+            s += " in spin mode"
+        elif owner:
+            s += " owned by thread %u" % owner
+        else:
+            s += "with unknown owner"
+    elif type == kThreadWaitPortReceive:
+        s += "mach_msg receive on "
+        if owner == STACKSHOT_WAITOWNER_PORT_LOCKED:
+            s += "locked port %x" % context
+        elif owner == STACKSHOT_WAITOWNER_INTRANSIT:
+            s += "intransit port %x" % context
+        elif owner:
+            s += "port %x name %x" % (context, owner)
+        else:
+            s += "port %x" % context
+    elif type == kThreadWaitPortSetReceive:
+        if owner == STACKSHOT_WAITOWNER_PSET_LOCKED:
+            s += "mach_msg receive on locked port set %x" % context
+        else:
+            s += "mach_msg receive on port set %x" % context
+    elif type == kThreadWaitPortSend:
+        s += "mach_msg send on "
+        if owner == STACKSHOT_WAITOWNER_PORT_LOCKED:
+            s += "locked port %x" % context
+        elif owner == STACKSHOT_WAITOWNER_INTRANSIT:
+            s += "intransit port %x" % context
+        elif owner == STACKSHOT_WAITOWNER_KERNEL:
+            s += "port %x owned by kernel" % context
+        elif owner:
+            s += "port %x owned by pid %d" % (context, owner)
+        else:
+            s += "port %x with unknown owner" % context
+    elif type == kThreadWaitPortSendInTransit:
+        s += "mach_msg send on port %x in transit to " % context
+        if owner:
+            s += "port %x" % owner
+        else:
+            s += "unknown port"
+    elif type == kThreadWaitSemaphore:
+        s += "semaphore port %x " % context
+        if owner:
+            s += "owned by pid %d" % owner
+        else:
+            s += "with unknown owner"
+    elif type == kThreadWaitKernelRWLockRead:
+        s += "krwlock %x for reading" % context
+    elif type == kThreadWaitKernelRWLockWrite:
+        s += "krwlock %x for writing" % context
+    elif type == kThreadWaitKernelRWLockUpgrade:
+        s += "krwlock %x for upgrading" % context
+    elif type == kThreadWaitUserLock:
+        if owner:
+            s += "unfair lock %x owned by thread %d" % (context, owner)
+        else:
+            s += "spin lock %x" % context
+    elif type == kThreadWaitPThreadMutex:
+        s += "pthread mutex %x" % context
+        if owner:
+            s += " owned by thread %d" % owner
+        else:
+            s += " with unknown owner"
+    elif type == kThreadWaitPThreadRWLockRead:
+        s += "pthread rwlock %x for reading" % context
+    elif type == kThreadWaitPThreadRWLockWrite:
+        s += "pthread rwlock %x for writing" % context
+    elif type == kThreadWaitPThreadCondVar:
+        s += "pthread condvar %x" % context
+    elif type == kThreadWaitWorkloopSyncWait:
+        s += "workloop sync wait"
+        if owner == STACKSHOT_WAITOWNER_SUSPENDED:
+            s += ", suspended"
+        elif owner == STACKSHOT_WAITOWNER_THREQUESTED:
+            s += ", thread requested"
+        elif owner != 0:
+            s += ", owned by thread %u" % owner
+        else:
+            s += ", unknown owner"
+        s += ", workloop id %x" % context
+    elif type == kThreadWaitOnProcess:
+        s += "waitpid, for pid %d" % owner
+
+    else:
+        s += "unknown type %d (owner %d, context %x)" % (type, owner, context)
+
+    return s
+        
+
+def SaveStackshotReport(j, outfile_name, dsc_uuid, dsc_libs_arr, incomplete):
     import time
     from operator import itemgetter, attrgetter
     ss = j.get('kcdata_stackshot')
     if not ss:
         print "No KCDATA_BUFFER_BEGIN_STACKSHOT object found. Skipping writing report."
         return
+
     timestamp = ss.get('usecs_since_epoch', int(time.time()))
-    timestamp = time.strftime("%Y-%m-%d %H:%M:%S %z",time.gmtime(timestamp))
+    try:
+        timestamp = time.strftime("%Y-%m-%d %H:%M:%S %z",time.gmtime(timestamp))
+    except ValueError, e:
+        print "couldn't convert timestamp:", str(e)
+        timestamp = None
+
     os_version = ss.get('osversion', 'Unknown')
     timebase = ss.get('mach_timebase_info', {"denom": 1, "numer": 1})
     if not dsc_uuid and 'imageSlidBaseAddress' not in ss.get('shared_cache_dyld_load_info'):
@@ -1167,9 +1410,9 @@ def SaveStackshotReport(j, outfile_name, dsc_uuid, dsc_libs_arr):
         return
 
     dsc_common = [format_uuid(ss.get('shared_cache_dyld_load_info')['imageUUID']),
-                  shared_cache_base_addr,
-                  "S"
-                 ]
+                  shared_cache_base_addr, "S" ]
+
+    dsc_layout = ss.get('system_shared_cache_layout')
 
     dsc_libs = []
     print "Shared cache UUID found from the binary data is <%s> " % str(dsc_common[0])
@@ -1181,16 +1424,23 @@ def SaveStackshotReport(j, outfile_name, dsc_uuid, dsc_libs_arr):
         for i in dsc_libs_arr:
             _uuid = i[2].lower().replace('-','').strip()
             _addr = int(i[0], 16) + _load_addr
-            dsc_libs.append([_uuid, _addr, "P"])
+            dsc_libs.append([_uuid, _addr, "C"])
             #print "adding ", [_uuid, _addr, "C"]
     elif dsc_uuid:
         print "Provided shared cache UUID does not match. Skipping writing report."
         return
+    elif dsc_layout:
+        print "Found in memory system shared cache layout with {} images".format(len(dsc_layout))
+        slide = ss.get('shared_cache_dyld_load_info')['imageLoadAddress']
+
+        for image in dsc_layout:
+            dsc_libs.append([format_uuid(image['imageUUID']), image['imageLoadAddress'] + slide, "C"])
 
     AllImageCatalog = []
     obj = {}
     obj["kernel"] = os_version
-    obj["date"] = timestamp
+    if timestamp is not None:
+        obj["date"] = timestamp
     obj["reason"] = "kernel panic stackshot"
     obj["incident"] = "ABCDEFGH-1234-56IJ-789K-0LMNOPQRSTUV"
     obj["crashReporterKey"] = "12ab34cd45aabbccdd6712ab34cd45aabbccdd67"
@@ -1198,6 +1448,11 @@ def SaveStackshotReport(j, outfile_name, dsc_uuid, dsc_libs_arr):
     obj["frontmostPids"] = [0]
     obj["exception"] = "0xDEADF157"
     obj["processByPid"] = {}
+
+    if incomplete:
+        obj["reason"] = "!!!INCOMPLETE!!! kernel panic stackshot"
+        obj["notes"] = "This stackshot report generated from incomplete data!   Some information is missing! "
+        
     processByPid = obj["processByPid"]
     ssplist = ss.get('task_snapshots', {})
     kern_load_info = []
@@ -1242,8 +1497,19 @@ def SaveStackshotReport(j, outfile_name, dsc_uuid, dsc_libs_arr):
 
         pr_libs.sort(key=itemgetter(1))
 
+        if 'task_snapshot' not in piddata:
+            continue
         tasksnap = piddata['task_snapshot']
         tsnap["pid"] = tasksnap["ts_pid"]
+        if 'ts_asid' in piddata:
+            tsnap["asid"] = piddata["ts_asid"]
+
+        if 'ts_pagetable' in piddata:
+            pagetables = []
+            for tte in piddata["ts_pagetable"]:
+                pagetables.append(tte)
+            tsnap["pageTables"] = pagetables
+
         tsnap["residentMemoryBytes"] = tasksnap["ts_task_size"]
         tsnap["timesDidThrottle"] = tasksnap["ts_did_throttle"]
         tsnap["systemTimeTask"] = GetSecondsFromMATime(tasksnap["ts_system_time_in_terminated_th"], timebase)
@@ -1285,12 +1551,31 @@ def SaveStackshotReport(j, outfile_name, dsc_uuid, dsc_libs_arr):
                 for f in thdata["user_stack_frames"]:
                     uframes.append(GetSymbolInfoForFrame(AllImageCatalog, pr_libs, f['lr']))
                 thsnap["userFrames"] = uframes
+
+            if "user_stacktop" in thdata:
+                (address,) = struct.unpack("<Q", struct.pack("B"*8, *thdata["user_stacktop"]["stack_contents"]))
+                thsnap["userStacktop"] = GetSymbolInfoForFrame(AllImageCatalog, pr_libs, address)
+
             if threadsnap['ths_wait_event']:
                 thsnap["waitEvent"] = GetSymbolInfoForFrame(AllImageCatalog, pr_libs, threadsnap['ths_wait_event'])
 
+        if 'thread_waitinfo' in piddata:
+            tsnap['waitInfo'] = map(formatWaitInfo, piddata['thread_waitinfo'])
+
     obj['binaryImages'] = AllImageCatalog
-    fh = open(outfile_name, "w")
-    fh.write('{"bug_type":"288", "timestamp":"'+ timestamp +'", "os_version":"'+ os_version +'"}\n')
+    if outfile_name == '-':
+        fh = sys.stdout
+    else:
+        fh = open(outfile_name, "w")
+
+    header = {}
+    header['bug_type'] = 288
+    if timestamp is not None:
+        header['timestamp'] = timestamp
+    header['os_version'] = os_version
+    fh.write(json.dumps(header))
+    fh.write("\n")
+
     fh.write(json.dumps(obj, sort_keys=False, indent=2, separators=(',', ': ')))
     fh.close()
 
@@ -1364,6 +1649,8 @@ parser.add_argument("-U", "--uuid", required=False, default="", help="UUID of dy
 parser.add_argument("-L", "--layout", required=False, type=argparse.FileType("r"), help="Path to layout file for DyldSharedCache. You can generate one by doing \n\tbash$xcrun -sdk <sdk> dyld_shared_cache_util -text_info </path/to/dyld_shared_cache> ", dest="layout")
 parser.add_argument("-S", "--sdk", required=False, default="", help="sdk property passed to xcrun command to find the required tools. Default is empty string.", dest="sdk")
 parser.add_argument("-D", "--dyld_shared_cache", required=False, default="", help="Path to dyld_shared_cache built by B&I", dest="dsc")
+parser.add_argument("--pretty", default=False, action='store_true', help="make the output a little more human readable")
+parser.add_argument("--incomplete", action='store_true', help="accept incomplete data")
 parser.add_argument("kcdata_file", type=argparse.FileType('r'), help="Path to a kcdata binary file.")
 
 class VerboseAction(argparse.Action):
@@ -1419,6 +1706,34 @@ def iterate_kcdatas(kcdata_file):
             yield kcdata_buffer
 
 
+def prettify(data):
+    if isinstance(data, list):
+        return map(prettify, data);
+
+    elif isinstance(data, dict):
+        newdata = dict()
+        for key, value in data.items():
+            if 'uuid' in key.lower() and isinstance(value, list) and len(value) == 16:
+                value = '%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X' % tuple(value)
+            elif 'address' in key.lower() and isinstance(value, (int, long)):
+                value = '0x%X' % value
+            elif key == 'lr':
+                value = '0x%X' % value
+            elif key == 'thread_waitinfo':
+                value = map(formatWaitInfo, value)
+            elif key == 'stack_contents':
+                print value
+                (address,) = struct.unpack("<Q", struct.pack("B"*8, *value))
+                value = '0x%X' % address
+            else:
+                value = prettify(value);
+            newdata[key] = value
+
+        return newdata
+
+    else:
+        return data
+
 
 if __name__ == '__main__':
     args = parser.parse_args()
@@ -1431,6 +1746,9 @@ if __name__ == '__main__':
             print "%d : %s " % (n, str(t))
         sys.exit(1)
 
+    if args.incomplete or args.stackshot_file:
+        G.accept_incomplete_data = True
+
     for i,kcdata_buffer in enumerate(iterate_kcdatas(args.kcdata_file)):
         if i > 0 and not args.multiple:
             break
@@ -1446,6 +1764,9 @@ if __name__ == '__main__':
             print textwrap.fill(str_data, 100)
             raise
 
+        if args.pretty:
+            json_obj = prettify(json_obj)
+
         dsc_uuid = None
         dsc_libs_arr = []
         libs_re = re.compile("^\s*(0x[a-fA-F0-9]+)\s->\s(0x[a-fA-F0-9]+)\s+<([a-fA-F0-9\-]+)>\s+.*$", re.MULTILINE)
@@ -1460,7 +1781,7 @@ if __name__ == '__main__':
                 dsc_libs_arr = libs_re.findall(_ret[1])
 
         if args.stackshot_file:
-            SaveStackshotReport(json_obj, args.stackshot_file, dsc_uuid, dsc_libs_arr)
+            SaveStackshotReport(json_obj, args.stackshot_file, dsc_uuid, dsc_libs_arr, G.data_was_incomplete)
         elif args.plist:
             import Foundation
             plist = Foundation.NSPropertyListSerialization.dataWithPropertyList_format_options_error_(