+@lldb_command('show_all_purgeable_objects')
+def ShowAllPurgeableVmObjects(cmd_args=None):
+ """ Routine to print a summary listing of all the purgeable vm objects
+ """
+ print "\n-------------------- VOLATILE OBJECTS --------------------\n"
+ ShowAllPurgeableVolatileVmObjects()
+ print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n"
+ ShowAllPurgeableNonVolatileVmObjects()
+
+@lldb_command('show_all_purgeable_nonvolatile_objects')
+def ShowAllPurgeableNonVolatileVmObjects(cmd_args=None):
+ """ Routine to print a summary listing of all the vm objects in
+ the purgeable_nonvolatile_queue
+ """
+
+ nonvolatile_total = lambda:None
+ nonvolatile_total.objects = 0
+ nonvolatile_total.vsize = 0
+ nonvolatile_total.rsize = 0
+ nonvolatile_total.wsize = 0
+ nonvolatile_total.csize = 0
+ nonvolatile_total.disowned_objects = 0
+ nonvolatile_total.disowned_vsize = 0
+ nonvolatile_total.disowned_rsize = 0
+ nonvolatile_total.disowned_wsize = 0
+ nonvolatile_total.disowned_csize = 0
+
+ queue_len = kern.globals.purgeable_nonvolatile_count
+ queue_head = kern.globals.purgeable_nonvolatile_queue
+
+ print 'purgeable_nonvolatile_queue:{: <#018x} purgeable_volatile_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len)
+ print 'N:non-volatile V:volatile E:empty D:deny\n'
+
+ print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
+ idx = 0
+ for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
+ idx += 1
+ ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total)
+ print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.disowned_objects, nonvolatile_total.disowned_vsize, nonvolatile_total.disowned_rsize, nonvolatile_total.disowned_wsize, nonvolatile_total.disowned_csize)
+ print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.objects, nonvolatile_total.vsize, nonvolatile_total.rsize, nonvolatile_total.wsize, nonvolatile_total.csize)
+
+
+def ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total):
+ """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue
+ params:
+ object - core.value : a object of type 'struct vm_object *'
+ returns:
+ None
+ """
+ page_size = kern.globals.page_size
+ if object.purgable == 0:
+ purgable = "N"
+ elif object.purgable == 1:
+ purgable = "V"
+ elif object.purgable == 2:
+ purgable = "E"
+ elif object.purgable == 3:
+ purgable = "D"
+ else:
+ purgable = "?"
+ if object.pager == 0:
+ compressed_count = 0
+ else:
+ compressor_pager = Cast(object.pager, 'compressor_pager *')
+ compressed_count = compressor_pager.cpgr_num_slots_occupied
+
+ print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
+
+ nonvolatile_total.objects += 1
+ nonvolatile_total.vsize += object.vo_un1.vou_size/page_size
+ nonvolatile_total.rsize += object.resident_page_count
+ nonvolatile_total.wsize += object.wired_page_count
+ nonvolatile_total.csize += compressed_count
+ if object.vo_un2.vou_owner == 0:
+ nonvolatile_total.disowned_objects += 1
+ nonvolatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
+ nonvolatile_total.disowned_rsize += object.resident_page_count
+ nonvolatile_total.disowned_wsize += object.wired_page_count
+ nonvolatile_total.disowned_csize += compressed_count
+
+
+@lldb_command('show_all_purgeable_volatile_objects')
+def ShowAllPurgeableVolatileVmObjects(cmd_args=None):
+ """ Routine to print a summary listing of all the vm objects in
+ the purgeable queues
+ """
+ volatile_total = lambda:None
+ volatile_total.objects = 0
+ volatile_total.vsize = 0
+ volatile_total.rsize = 0
+ volatile_total.wsize = 0
+ volatile_total.csize = 0
+ volatile_total.disowned_objects = 0
+ volatile_total.disowned_vsize = 0
+ volatile_total.disowned_rsize = 0
+ volatile_total.disowned_wsize = 0
+ volatile_total.disowned_csize = 0
+
+ purgeable_queues = kern.globals.purgeable_queues
+ print "---------- OBSOLETE\n"
+ ShowPurgeableQueue(purgeable_queues[0], volatile_total)
+ print "\n\n---------- FIFO\n"
+ ShowPurgeableQueue(purgeable_queues[1], volatile_total)
+ print "\n\n---------- LIFO\n"
+ ShowPurgeableQueue(purgeable_queues[2], volatile_total)
+
+ print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.disowned_objects, volatile_total.disowned_vsize, volatile_total.disowned_rsize, volatile_total.disowned_wsize, volatile_total.disowned_csize)
+ print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.objects, volatile_total.vsize, volatile_total.rsize, volatile_total.wsize, volatile_total.csize)
+ purgeable_count = kern.globals.vm_page_purgeable_count
+ purgeable_wired_count = kern.globals.vm_page_purgeable_wired_count
+ if purgeable_count != volatile_total.rsize or purgeable_wired_count != volatile_total.wsize:
+ mismatch = "<--------- MISMATCH\n"
+ else:
+ mismatch = ""
+ print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count, purgeable_wired_count, mismatch)
+
+
+def ShowPurgeableQueue(qhead, volatile_total):
+ print "----- GROUP 0\n"
+ ShowPurgeableGroup(qhead.objq[0], volatile_total)
+ print "----- GROUP 1\n"
+ ShowPurgeableGroup(qhead.objq[1], volatile_total)
+ print "----- GROUP 2\n"
+ ShowPurgeableGroup(qhead.objq[2], volatile_total)
+ print "----- GROUP 3\n"
+ ShowPurgeableGroup(qhead.objq[3], volatile_total)
+ print "----- GROUP 4\n"
+ ShowPurgeableGroup(qhead.objq[4], volatile_total)
+ print "----- GROUP 5\n"
+ ShowPurgeableGroup(qhead.objq[5], volatile_total)
+ print "----- GROUP 6\n"
+ ShowPurgeableGroup(qhead.objq[6], volatile_total)
+ print "----- GROUP 7\n"
+ ShowPurgeableGroup(qhead.objq[7], volatile_total)
+
+def ShowPurgeableGroup(qhead, volatile_total):
+ idx = 0
+ for object in IterateQueue(qhead, 'struct vm_object *', 'objq'):
+ if idx == 0:
+# print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s} {:18s} {:>6s} {:<20s} {:s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process","volatilizer","pid","process","")
+ print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
+ idx += 1
+ ShowPurgeableVolatileVmObject(object, idx, volatile_total)
+
+def ShowPurgeableVolatileVmObject(object, idx, volatile_total):
+ """ Routine to print out a summary a VM object in a purgeable queue
+ params:
+ object - core.value : a object of type 'struct vm_object *'
+ returns:
+ None
+ """
+## if int(object.vo_un2.vou_owner) != int(object.vo_purgeable_volatilizer):
+# diff=" !="
+## else:
+# diff=" "
+ page_size = kern.globals.page_size
+ if object.purgable == 0:
+ purgable = "N"
+ elif object.purgable == 1:
+ purgable = "V"
+ elif object.purgable == 2:
+ purgable = "E"
+ elif object.purgable == 3:
+ purgable = "D"
+ else:
+ purgable = "?"
+ if object.pager == 0:
+ compressed_count = 0
+ else:
+ compressor_pager = Cast(object.pager, 'compressor_pager *')
+ compressed_count = compressor_pager.cpgr_num_slots_occupied
+# print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {: <#018x} {:>6d} {:<20s} {: <#018x} {:>6d} {:<20s} {:s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count,object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner),object.vo_purgeable_volatilizer,GetProcPIDForObjectOwner(object.vo_purgeable_volatilizer),GetProcNameForObjectOwner(object.vo_purgeable_volatilizer),diff)
+ print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
+ volatile_total.objects += 1
+ volatile_total.vsize += object.vo_un1.vou_size/page_size
+ volatile_total.rsize += object.resident_page_count
+ volatile_total.wsize += object.wired_page_count
+ volatile_total.csize += compressed_count
+ if object.vo_un2.vou_owner == 0:
+ volatile_total.disowned_objects += 1
+ volatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
+ volatile_total.disowned_rsize += object.resident_page_count
+ volatile_total.disowned_wsize += object.wired_page_count
+ volatile_total.disowned_csize += compressed_count
+
+
+def GetCompressedPagesForObject(obj):
+ """Stuff
+ """
+ pager = Cast(obj.pager, 'compressor_pager_t')
+ return pager.cpgr_num_slots_occupied
+ """ # commented code below
+ if pager.cpgr_num_slots > 128:
+ slots_arr = pager.cpgr_slots.cpgr_islots
+ num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
+ index = 0
+ compressor_slot = 0
+ compressed_pages = 0
+ while index < num_indirect_slot_ptr:
+ compressor_slot = 0
+ if slots_arr[index]:
+ while compressor_slot < 128:
+ if slots_arr[index][compressor_slot]:
+ compressed_pages += 1
+ compressor_slot += 1
+ index += 1
+ else:
+ slots_arr = pager.cpgr_slots.cpgr_dslots
+ compressor_slot = 0
+ compressed_pages = 0
+ while compressor_slot < pager.cpgr_num_slots:
+ if slots_arr[compressor_slot]:
+ compressed_pages += 1
+ compressor_slot += 1
+ return compressed_pages
+ """
+
+def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
+ """ Routine to print out a summary listing of all the entries in a vm_map
+ params:
+ task - core.value : a object of type 'task *'
+ returns:
+ None
+ """
+ print "vm_map entries for task " + hex(task)
+ print GetTaskSummary.header
+ print GetTaskSummary(task)
+ if not task.map:
+ print "Task {0: <#020x} has map = 0x0"
+ return None
+ showmapvme(task.map, 0, 0, show_pager_info, show_all_shadows, False)
+
+@lldb_command("showmapvme", "A:B:F:PRST")
+def ShowMapVME(cmd_args=None, cmd_options={}):
+ """Routine to print out info about the specified vm_map and its vm entries
+ usage: showmapvme <vm_map> [-A start] [-B end] [-S] [-P]
+ Use -A <start> flag to start at virtual address <start>
+ Use -B <end> flag to end at virtual address <end>
+ Use -F <virtaddr> flag to find just the VME containing the given VA
+ Use -S flag to show VM object shadow chains
+ Use -P flag to show pager info (mapped file, compressed pages, ...)
+ Use -R flag to reverse order
+ Use -T to show red-black tree pointers
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", ShowMapVME.__doc__
+ return
+ show_pager_info = False
+ show_all_shadows = False
+ show_rb_tree = False
+ start_vaddr = 0
+ end_vaddr = 0
+ reverse_order = False
+ if "-A" in cmd_options:
+ start_vaddr = unsigned(int(cmd_options['-A'], 16))
+ if "-B" in cmd_options:
+ end_vaddr = unsigned(int(cmd_options['-B'], 16))
+ if "-F" in cmd_options:
+ start_vaddr = unsigned(int(cmd_options['-F'], 16))
+ end_vaddr = start_vaddr
+ if "-P" in cmd_options:
+ show_pager_info = True
+ if "-S" in cmd_options:
+ show_all_shadows = True
+ if "-R" in cmd_options:
+ reverse_order = True
+ if "-T" in cmd_options:
+ show_rb_tree = True
+ map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
+ showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+@lldb_command("showmapcopyvme", "A:B:F:PRST")
+def ShowMapCopyVME(cmd_args=None, cmd_options={}):
+ """Routine to print out info about the specified vm_map_copy and its vm entries
+ usage: showmapcopyvme <vm_map_copy> [-A start] [-B end] [-S] [-P]
+ Use -A <start> flag to start at virtual address <start>
+ Use -B <end> flag to end at virtual address <end>
+ Use -F <virtaddr> flag to find just the VME containing the given VA
+ Use -S flag to show VM object shadow chains
+ Use -P flag to show pager info (mapped file, compressed pages, ...)
+ Use -R flag to reverse order
+ Use -T to show red-black tree pointers
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", ShowMapVME.__doc__
+ return
+ show_pager_info = False
+ show_all_shadows = False
+ show_rb_tree = False
+ start_vaddr = 0
+ end_vaddr = 0
+ reverse_order = False
+ if "-A" in cmd_options:
+ start_vaddr = unsigned(int(cmd_options['-A'], 16))
+ if "-B" in cmd_options:
+ end_vaddr = unsigned(int(cmd_options['-B'], 16))
+ if "-F" in cmd_options:
+ start_vaddr = unsigned(int(cmd_options['-F'], 16))
+ end_vaddr = start_vaddr
+ if "-P" in cmd_options:
+ show_pager_info = True
+ if "-S" in cmd_options:
+ show_all_shadows = True
+ if "-R" in cmd_options:
+ reverse_order = True
+ if "-T" in cmd_options:
+ show_rb_tree = True
+ map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_copy_t')
+ showmapcopyvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+@lldb_command("showvmobject", "A:B:PRST")
+def ShowVMObject(cmd_args=None, cmd_options={}):
+ """Routine to print out a VM object and its shadow chain
+ usage: showvmobject <vm_object> [-S] [-P]
+ -S: show VM object shadow chain
+ -P: show pager info (mapped file, compressed pages, ...)
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", ShowMapVME.__doc__
+ return
+ show_pager_info = False
+ show_all_shadows = False
+ if "-P" in cmd_options:
+ show_pager_info = True
+ if "-S" in cmd_options:
+ show_all_shadows = True
+ object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
+ showvmobject(object, 0, 0, show_pager_info, show_all_shadows)
+
+def showvmobject(object, offset=0, size=0, show_pager_info=False, show_all_shadows=False):
+ page_size = kern.globals.page_size
+ vnode_pager_ops = kern.globals.vnode_pager_ops
+ vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
+ depth = 0
+ if size == 0 and object != 0 and object.internal:
+ size = object.vo_un1.vou_size
+ while object != 0:
+ depth += 1
+ if show_all_shadows == False and depth != 1 and object.shadow != 0:
+ offset += unsigned(object.vo_un2.vou_shadow_offset)
+ object = object.shadow
+ continue
+ if object.copy_strategy == 0:
+ copy_strategy="N"
+ elif object.copy_strategy == 2:
+ copy_strategy="D"
+ elif object.copy_strategy == 4:
+ copy_strategy="S"
+
+ else:
+ copy_strategy=str(object.copy_strategy)
+ if object.internal:
+ internal = "internal"
+ else:
+ internal = "external"
+ purgeable = "NVED"[int(object.purgable)]
+ pager_string = ""
+ if object.phys_contiguous:
+ pager_string = pager_string + "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2.vou_shadow_offset), unsigned(object.vo_un1.vou_size))
+ pager = object.pager
+ if show_pager_info and pager != 0:
+ if object.internal:
+ pager_string = pager_string + "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
+ elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr:
+ vnode_pager = Cast(pager,'vnode_pager *')
+ pager_string = pager_string + "-> " + GetVnodePath(vnode_pager.vnode_handle)
+ else:
+ pager_string = pager_string + "-> {:s}:{: <#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager)
+ print "{:>18d} {:#018x}:{:#018x} {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} purg:{:1s} {:s} wtag:{:d} ({:d} {:d} {:d}) {:s}".format(depth,offset,offset+size,object,object.ref_count,object.true_share,copy_strategy,purgeable,internal,object.wire_tag,unsigned(object.vo_un1.vou_size)/page_size,object.resident_page_count,object.wired_page_count,pager_string)
+# print " #{:<5d} obj {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} {:s} size:{:<10d} wired:{:<10d} resident:{:<10d} reusable:{:<10d}".format(depth,object,object.ref_count,object.true_share,copy_strategy,internal,object.vo_un1.vou_size/page_size,object.wired_page_count,object.resident_page_count,object.reusable_page_count)
+ offset += unsigned(object.vo_un2.vou_shadow_offset)
+ object = object.shadow
+
+def showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order=False, show_rb_tree=False):
+ rsize = 0
+ if map.pmap != 0:
+ rsize = int(map.pmap.stats.resident_count)
+ print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map","pmap","size","#ents","rsize","start","end","pgshift")
+ print "{: <#018x} {: <#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(map,map.pmap,unsigned(map.size),map.hdr.nentries,rsize,map.hdr.links.start,map.hdr.links.end,map.hdr.page_shift)
+ showmaphdrvme(map.hdr, map.pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+def showmapcopyvme(mapcopy, start_vaddr=0, end_vaddr=0, show_pager_info=True, show_all_shadows=True, reverse_order=False, show_rb_tree=False):
+ print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map_copy","offset","size","#ents","rsize","start","end","pgshift")
+ print "{: <#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(mapcopy,mapcopy.offset,mapcopy.size,mapcopy.c_u.hdr.nentries,0,mapcopy.c_u.hdr.links.start,mapcopy.c_u.hdr.links.end,mapcopy.c_u.hdr.page_shift)
+ showmaphdrvme(mapcopy.c_u.hdr, 0, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
+
+def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree):
+ page_size = kern.globals.page_size
+ vnode_pager_ops = kern.globals.vnode_pager_ops
+ vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
+ if hasattr(kern.globals, 'compressor_object'):
+ compressor_object = kern.globals.compressor_object
+ else:
+ compressor_object = -1;
+ vme_list_head = maphdr.links
+ vme_ptr_type = GetType('vm_map_entry *')
+ print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
+ last_end = unsigned(maphdr.links.start)
+ skipped_entries = 0
+ for vme in IterateQueue(vme_list_head, vme_ptr_type, "links", reverse_order):
+ if start_vaddr != 0 and end_vaddr != 0:
+ if unsigned(vme.links.start) > end_vaddr:
+ break
+ if unsigned(vme.links.end) <= start_vaddr:
+ last_end = unsigned(vme.links.end)
+ skipped_entries = skipped_entries + 1
+ continue
+ if skipped_entries != 0:
+ print "... skipped {:d} entries ...".format(skipped_entries)
+ skipped_entries = 0
+ if unsigned(vme.links.start) != last_end:
+ print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme.links.start,(unsigned(vme.links.start) - last_end)/page_size)
+ last_end = unsigned(vme.links.end)
+ size = unsigned(vme.links.end) - unsigned(vme.links.start)
+ object = vme.vme_object.vmo_object
+ if object == 0:
+ object_str = "{: <#018x}".format(object)
+ elif vme.is_sub_map:
+ if object == kern.globals.bufferhdr_map:
+ object_str = "BUFFERHDR_MAP"
+ elif object == kern.globals.mb_map:
+ object_str = "MB_MAP"
+ elif object == kern.globals.bsd_pageable_map:
+ object_str = "BSD_PAGEABLE_MAP"
+ elif object == kern.globals.ipc_kernel_map:
+ object_str = "IPC_KERNEL_MAP"
+ elif object == kern.globals.ipc_kernel_copy_map:
+ object_str = "IPC_KERNEL_COPY_MAP"
+ elif object == kern.globals.kalloc_map:
+ object_str = "KALLOC_MAP"
+ elif hasattr(kern.globals, 'compressor_map') and object == kern.globals.compressor_map:
+ object_str = "COMPRESSOR_MAP"
+ elif hasattr(kern.globals, 'gzalloc_map') and object == kern.globals.gzalloc_map:
+ object_str = "GZALLOC_MAP"
+ elif hasattr(kern.globals, 'g_kext_map') and object == kern.globals.g_kext_map:
+ object_str = "G_KEXT_MAP"
+ elif hasattr(kern.globals, 'vector_upl_submap') and object == kern.globals.vector_upl_submap:
+ object_str = "VECTOR_UPL_SUBMAP"
+ else:
+ object_str = "submap:{: <#018x}".format(object)
+ else:
+ if object == kern.globals.kernel_object:
+ object_str = "KERNEL_OBJECT"
+ elif object == kern.globals.vm_submap_object:
+ object_str = "VM_SUBMAP_OBJECT"
+ elif object == compressor_object:
+ object_str = "COMPRESSOR_OBJECT"
+ else:
+ object_str = "{: <#018x}".format(object)
+ offset = unsigned(vme.vme_offset) & ~0xFFF
+ tag = unsigned(vme.vme_offset & 0xFFF)
+ protection = ""
+ if vme.protection & 0x1:
+ protection +="r"
+ else:
+ protection += "-"
+ if vme.protection & 0x2:
+ protection += "w"
+ else:
+ protection += "-"
+ if vme.protection & 0x4:
+ protection += "x"
+ else:
+ protection += "-"
+ max_protection = ""
+ if vme.max_protection & 0x1:
+ max_protection +="r"
+ else:
+ max_protection += "-"
+ if vme.max_protection & 0x2:
+ max_protection += "w"
+ else:
+ max_protection += "-"
+ if vme.max_protection & 0x4:
+ max_protection += "x"
+ else:
+ max_protection += "-"
+ vme_flags = ""
+ if vme.is_sub_map:
+ vme_flags += "s"
+ if vme.needs_copy:
+ vme_flags += "n"
+ if vme.use_pmap:
+ vme_flags += "p"
+ if vme.wired_count:
+ vme_flags += "w"
+ if vme.used_for_jit:
+ vme_flags += "j"
+ tagstr = ""
+ if pmap == kern.globals.kernel_pmap:
+ xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *')
+ if xsite and xsite.site.flags & 0x0200:
+ tagstr = ".{:<3d}".format(xsite.loadTag)
+ rb_info = ""
+ if show_rb_tree:
+ rb_info = "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme.store.entry.rbe_left, vme.store.entry.rbe_right, vme.store.entry.rbe_parent)
+ print "{: <#018x} {:#018x}:{:#018x} {:>10d} {:>3d}{:<4s} {:3s}/{:3s}/{:<8s} {:<18s} {:<#18x} {:s}".format(vme,vme.links.start,vme.links.end,(unsigned(vme.links.end)-unsigned(vme.links.start))/page_size,tag,tagstr,protection,max_protection,vme_flags,object_str,offset, rb_info)
+ if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and vme.vme_object.vmo_object != 0:
+ object = vme.vme_object.vmo_object
+ else:
+ object = 0
+ showvmobject(object, offset, size, show_pager_info, show_all_shadows)
+ if start_vaddr != 0 or end_vaddr != 0:
+ print "..."
+ elif unsigned(maphdr.links.end) > last_end:
+ print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,maphdr.links.end,(unsigned(maphdr.links.end) - last_end)/page_size)
+ return None
+
+def CountMapTags(map, tagcounts, slow):
+ page_size = unsigned(kern.globals.page_size)
+ vme_list_head = map.hdr.links
+ vme_ptr_type = GetType('vm_map_entry *')
+ for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
+ object = vme.vme_object.vmo_object
+ tag = vme.vme_offset & 0xFFF
+ if object == kern.globals.kernel_object:
+ count = 0
+ if not slow:
+ count = unsigned(vme.links.end - vme.links.start) / page_size
+ else:
+ addr = unsigned(vme.links.start)
+ while addr < unsigned(vme.links.end):
+ hash_id = _calc_vm_page_hash(object, addr)
+ page_list = kern.globals.vm_page_buckets[hash_id].page_list
+ page = _vm_page_unpack_ptr(page_list)
+ while (page != 0):
+ vmpage = kern.GetValueFromAddress(page, 'vm_page_t')
+ if (addr == unsigned(vmpage.vmp_offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vmp_object))):
+ if (not vmpage.vmp_local) and (vmpage.vmp_wire_count > 0):
+ count += 1
+ break
+ page = _vm_page_unpack_ptr(vmpage.vmp_next_m)
+ addr += page_size
+ tagcounts[tag] += count
+ elif vme.is_sub_map:
+ CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow)
+ return None
+
+def CountWiredObject(object, tagcounts):
+ tagcounts[unsigned(object.wire_tag)] += object.wired_page_count
+ return None
+
+def GetKmodIDName(kmod_id):
+ kmod_val = kern.globals.kmod
+ for kmod in IterateLinkedList(kmod_val, 'next'):
+ if (kmod.id == kmod_id):
+ return "{:<50s}".format(kmod.name)
+ return "??"
+
+FixedTags = {
+ 0: "VM_KERN_MEMORY_NONE",
+ 1: "VM_KERN_MEMORY_OSFMK",
+ 2: "VM_KERN_MEMORY_BSD",
+ 3: "VM_KERN_MEMORY_IOKIT",
+ 4: "VM_KERN_MEMORY_LIBKERN",
+ 5: "VM_KERN_MEMORY_OSKEXT",
+ 6: "VM_KERN_MEMORY_KEXT",
+ 7: "VM_KERN_MEMORY_IPC",
+ 8: "VM_KERN_MEMORY_STACK",
+ 9: "VM_KERN_MEMORY_CPU",
+ 10: "VM_KERN_MEMORY_PMAP",
+ 11: "VM_KERN_MEMORY_PTE",
+ 12: "VM_KERN_MEMORY_ZONE",
+ 13: "VM_KERN_MEMORY_KALLOC",
+ 14: "VM_KERN_MEMORY_COMPRESSOR",
+ 15: "VM_KERN_MEMORY_COMPRESSED_DATA",
+ 16: "VM_KERN_MEMORY_PHANTOM_CACHE",
+ 17: "VM_KERN_MEMORY_WAITQ",
+ 18: "VM_KERN_MEMORY_DIAG",
+ 19: "VM_KERN_MEMORY_LOG",
+ 20: "VM_KERN_MEMORY_FILE",
+ 21: "VM_KERN_MEMORY_MBUF",
+ 22: "VM_KERN_MEMORY_UBC",
+ 23: "VM_KERN_MEMORY_SECURITY",
+ 24: "VM_KERN_MEMORY_MLOCK",
+ 25: "VM_KERN_MEMORY_REASON",
+ 26: "VM_KERN_MEMORY_SKYWALK",
+ 27: "VM_KERN_MEMORY_LTABLE",
+ 28: "VM_KERN_MEMORY_HV",
+ 255:"VM_KERN_MEMORY_ANY",
+}
+
+def GetVMKernName(tag):
+ """ returns the formatted name for a vmtag and
+ the sub-tag for kmod tags.
+ """
+ if ((tag <= 27) or (tag == 255)):
+ return (FixedTags[tag], "")
+ site = kern.globals.vm_allocation_sites[tag]
+ if site:
+ if site.flags & 0x007F:
+ cstr = addressof(site.subtotals[site.subtotalscount])
+ return ("{:<50s}".format(str(Cast(cstr, 'char *'))), "")
+ else:
+ if site.flags & 0x0200:
+ xsite = Cast(site,'OSKextAccount *')
+ tagstr = ".{:<3d}".format(xsite.loadTag)
+ return (GetKmodIDName(xsite.loadTag), tagstr);
+ else:
+ return (kern.Symbolicate(site), "")
+ return ("", "")
+
+@lldb_command("showvmtags", "ASJ")
+def showvmtags(cmd_args=None, cmd_options={}):
+ """Routine to print out info about kernel wired page allocations
+ usage: showvmtags
+ iterates kernel map and vm objects totaling allocations by tag.
+ usage: showvmtags -S
+ also iterates kernel object pages individually - slow.
+ usage: showvmtags -A
+ show all tags, even tags that have no wired count
+ usage: showvmtags -J
+ Output json
+ """
+ slow = False
+ print_json = False
+ if "-S" in cmd_options:
+ slow = True
+ all_tags = False
+ if "-A" in cmd_options:
+ all_tags = True
+ if "-J" in cmd_options:
+ print_json = True
+
+ page_size = unsigned(kern.globals.page_size)
+ nsites = unsigned(kern.globals.vm_allocation_tag_highest) + 1
+ tagcounts = [0] * nsites
+ tagpeaks = [0] * nsites
+ tagmapped = [0] * nsites
+
+ if kern.globals.vm_tag_active_update:
+ for tag in range(nsites):
+ site = kern.globals.vm_allocation_sites[tag]
+ if site:
+ tagcounts[tag] = unsigned(site.total)
+ tagmapped[tag] = unsigned(site.mapped)
+ tagpeaks[tag] = unsigned(site.peak)
+ else:
+ queue_head = kern.globals.vm_objects_wired
+ for object in IterateQueue(queue_head, 'struct vm_object *', 'wired_objq'):
+ if object != kern.globals.kernel_object:
+ CountWiredObject(object, tagcounts)
+
+ CountMapTags(kern.globals.kernel_map, tagcounts, slow)
+
+ total = 0
+ totalmapped = 0
+ tags = []
+ for tag in range(nsites):
+ if all_tags or tagcounts[tag] or tagmapped[tag]:
+ current = {}
+ total += tagcounts[tag]
+ totalmapped += tagmapped[tag]
+ (sitestr, tagstr) = GetVMKernName(tag)
+ current["name"] = sitestr
+ current["size"] = tagcounts[tag]
+ current["mapped"] = tagmapped[tag]
+ current["peak"] = tagpeaks[tag]
+ current["tag"] = tag
+ current["tagstr"] = tagstr
+ current["subtotals"] = []
+
+ site = kern.globals.vm_allocation_sites[tag]
+ for sub in range(site.subtotalscount):
+ alloctag = unsigned(site.subtotals[sub].tag)
+ amount = unsigned(site.subtotals[sub].total)
+ subsite = kern.globals.vm_allocation_sites[alloctag]
+ if alloctag and subsite:
+ (sitestr, tagstr) = GetVMKernName(alloctag)
+ current["subtotals"].append({
+ "amount": amount,
+ "flags": int(subsite.flags),
+ "tag": alloctag,
+ "tagstr": tagstr,
+ "sitestr": sitestr,
+ })
+ tags.append(current)
+
+ if print_json:
+ print json.dumps(tags)
+ else:
+ print " vm_allocation_tag_highest: {:<7d} ".format(nsites - 1)
+ print " {:<7s} {:>7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod", "peak", "size", "mapped", "name")
+ for tag in tags:
+ if not tagstr:
+ tagstr = ""
+ print " {:>3d}{:<4s} {:>7d}K {:>7d}K {:>7d}K {:<50s}".format(tag["tag"], tag["tagstr"], tag["peak"] / 1024, tag["size"] / 1024, tag["mapped"] / 1024, tag["name"])
+ for sub in tag["subtotals"]:
+ if ((sub["flags"] & 0x007f) == 0):
+ kind_str = "named"
+ else:
+ kind_str = "from"
+
+ print " {:>7s} {:>7s} {:>7s} {:>7d}K {:s} {:>3d}{:<4s} {:<50s}".format(" ", " ", " ", sub["amount"] / 1024, kind_str, sub["tag"], sub["tagstr"], sub["sitestr"])
+
+ print "Total: {:>7d}K {:>7d}K".format(total / 1024, totalmapped / 1024)
+ return None
+
+
+def FindVMEntriesForVnode(task, vn):
+ """ returns an array of vme that have the vnode set to defined vnode
+ each entry in array is of format (vme, start_addr, end_address, protection)
+ """
+ retval = []
+ vmmap = task.map
+ pmap = vmmap.pmap
+ pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops))
+ debuglog("pager_ops_addr %s" % hex(pager_ops_addr))
+
+ if unsigned(pmap) == 0:
+ return retval
+ vme_list_head = vmmap.hdr.links
+ vme_ptr_type = gettype('vm_map_entry *')
+ for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'):
+ #print vme
+ if unsigned(vme.is_sub_map) == 0 and unsigned(vme.vme_object.vmo_object) != 0:
+ obj = vme.vme_object.vmo_object
+ else:
+ continue
+
+ while obj != 0:
+ if obj.pager != 0:
+ if obj.internal:
+ pass
+ else:
+ vn_pager = Cast(obj.pager, 'vnode_pager *')
+ if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn):
+ retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection)))
+ obj = obj.shadow
+ return retval
+
+@lldb_command('showtaskloadinfo')
+def ShowTaskLoadInfo(cmd_args=None, cmd_options={}):
+ """ Print the load address and uuid for the process
+ Usage: (lldb)showtaskloadinfo <task_t>
+ """
+ if not cmd_args:
+ raise ArgumentError("Insufficient arguments")
+ t = kern.GetValueFromAddress(cmd_args[0], 'struct task *')
+ print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
+ p = Cast(t.bsd_info, 'struct proc *')
+ uuid = p.p_uuid
+ uuid_out_string = "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a=uuid)
+ filepath = GetVnodePath(p.p_textvp)
+ libname = filepath.split('/')[-1]
+ #print "uuid: %s file: %s" % (uuid_out_string, filepath)
+ mappings = FindVMEntriesForVnode(t, p.p_textvp)
+ load_addr = 0
+ end_addr = 0
+ for m in mappings:
+ if m[3] == 5:
+ load_addr = m[1]
+ end_addr = m[2]
+ #print "Load address: %s" % hex(m[1])
+ print print_format.format(load_addr, end_addr, libname, uuid_out_string, filepath)
+ return None
+
+@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
+@lldb_command('vmpagelookup')
+def VMPageLookup(cmd_args=None):
+ """ Print the pages in the page bucket corresponding to the provided object and offset.
+ Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
+ """
+ if cmd_args == None or len(cmd_args) < 2:
+ raise ArgumentError("Please specify an object and offset.")
+ format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n"
+
+ obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
+ off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
+
+ hash_id = _calc_vm_page_hash(obj, off)
+
+ page_list = kern.globals.vm_page_buckets[hash_id].page_list
+ print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list)))
+
+ print VMPageLookup.header
+ page = _vm_page_unpack_ptr(page_list)
+ while (page != 0) :
+ pg_t = kern.GetValueFromAddress(page, 'vm_page_t')
+ print format_string.format(page, pg_t.vmp_offset, _vm_page_unpack_ptr(pg_t.vmp_object))
+ page = _vm_page_unpack_ptr(pg_t.vmp_next_m)
+
+
+
+@lldb_command('vmpage_get_phys_page')
+def VmPageGetPhysPage(cmd_args=None):
+ """ return the physical page for a vm_page_t
+ usage: vm_page_get_phys_page <vm_page_t>
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
+ return
+
+ page = kern.GetValueFromAddress(cmd_args[0], 'vm_page_t')
+ phys_page = _vm_page_get_phys_page(page)
+ print("phys_page = 0x%x\n" % phys_page)
+
+
+def _vm_page_get_phys_page(page):
+ if kern.arch == 'x86_64':
+ return page.vmp_phys_page
+
+ if page == 0 :
+ return 0
+
+ m = unsigned(page)
+ if m >= unsigned(kern.globals.vm_page_array_beginning_addr) and m < unsigned(kern.globals.vm_page_array_ending_addr) :
+ return (m - unsigned(kern.globals.vm_page_array_beginning_addr)) / sizeof('struct vm_page') + unsigned(kern.globals.vm_first_phys_ppnum)
+
+ page_with_ppnum = Cast(page, 'uint32_t *')
+ ppnum_offset = sizeof('struct vm_page') / sizeof('uint32_t')
+ return page_with_ppnum[ppnum_offset]
+
+
+@lldb_command('vmpage_unpack_ptr')
+def VmPageUnpackPtr(cmd_args=None):
+ """ unpack a pointer
+ usage: vm_page_unpack_ptr <packed_ptr>
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
+ return
+
+ packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long')
+ unpacked = _vm_page_unpack_ptr(packed)
+ print("unpacked pointer = 0x%x\n" % unpacked)
+
+
+def _vm_page_unpack_ptr(page):
+ if kern.ptrsize == 4 :
+ return page
+
+ if page == 0 :
+ return page
+
+ params = kern.globals.vm_page_packing_params
+ ptr_shift = params.vmpp_shift
+ ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask
+
+ # when no mask and shift on 64bit systems, we're working with real/non-packed pointers
+ if ptr_shift == 0 and ptr_mask == 0:
+ return page
+
+ if unsigned(page) & unsigned(ptr_mask):
+ masked_page = (unsigned(page) & ~ptr_mask)
+ # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge
+ vm_pages_addr = unsigned(addressof(kern.globals.vm_pages[0]))
+ element_size = unsigned(addressof(kern.globals.vm_pages[1])) - vm_pages_addr
+ return (vm_pages_addr + masked_page * element_size)
+ return unsigned(vm_unpack_pointer(page, params))
+
+@lldb_command('calcvmpagehash')
+def CalcVMPageHash(cmd_args=None):
+ """ Get the page bucket corresponding to the provided object and offset.
+ Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
+ """
+ if cmd_args == None or len(cmd_args) < 2:
+ raise ArgumentError("Please specify an object and offset.")
+
+ obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
+ off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
+
+ hash_id = _calc_vm_page_hash(obj, off)
+
+ print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
+ return None
+
+def _calc_vm_page_hash(obj, off):
+ bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
+ hash_mask = (int) (kern.globals.vm_page_hash_mask)
+
+ one = (obj * bucket_hash) & 0xFFFFFFFF
+ two = off >> unsigned(kern.globals.page_shift)
+ three = two ^ bucket_hash
+ four = one + three
+ hash_id = four & hash_mask
+
+ return hash_id
+
+#Macro: showallocatedzoneelement
+@lldb_command('showallocatedzoneelement')
+def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}):
+ """ Show all the allocated elements in a zone
+ usage: showzoneallocelements <address of zone>
+ """
+ if len(cmd_args) < 1:
+ raise ArgumentError("Please specify a zone")
+
+ zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
+ elements = FindAllocatedElementsInZone(zone)
+ i = 1
+ for elem in elements:
+ print "{0: >10d}/{1:<10d} element: {2: <#20x}".format(i, len(elements), elem)
+ i += 1
+
+#EndMacro: showallocatedzoneelement
+
+def FindAllocatedElementsInZone(zone):
+ elements = []
+
+ if not zone.z_self or zone.permanent:
+ return elements
+
+ for head in [zone.pages_any_free_foreign, zone.pages_all_used_foreign,
+ zone.pages_intermediate, zone.pages_all_used]:
+
+ for meta in ZoneIteratePageQueue(head):
+ free_elements = set(meta.iterateFreeList())
+
+ for elem in meta.iterateElements():
+ if elem in free_elements:
+ continue
+
+ if elem not in free_elements:
+ elements.append(elem)
+ elem += zone.z_elem_size
+
+ return elements
+
+def match_vm_page_attributes(page, matching_attributes):
+ page_ptr = addressof(page)
+ unpacked_vm_object = _vm_page_unpack_ptr(page.vmp_object)
+ matched_attributes = 0
+ if "vmp_q_state" in matching_attributes and (page.vmp_q_state == matching_attributes["vmp_q_state"]):
+ matched_attributes += 1
+ if "vm_object" in matching_attributes and (unsigned(unpacked_vm_object) == unsigned(matching_attributes["vm_object"])):
+ matched_attributes += 1
+ if "vmp_offset" in matching_attributes and (unsigned(page.vmp_offset) == unsigned(matching_attributes["vmp_offset"])):
+ matched_attributes += 1
+ if "phys_page" in matching_attributes and (unsigned(_vm_page_get_phys_page(page_ptr)) == unsigned(matching_attributes["phys_page"])):
+ matched_attributes += 1
+ if "bitfield" in matching_attributes and unsigned(page.__getattr__(matching_attributes["bitfield"])) == 1:
+ matched_attributes += 1
+
+ return matched_attributes
+
+#Macro scan_vm_pages
+@header("{0: >26s}{1: >20s}{2: >10s}{3: >20s}{4: >20s}{5: >16s}".format("vm_pages_index/zone", "vm_page", "q_state", "vm_object", "offset", "ppn", "bitfield", "from_zone_map"))
+@lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA')
+def ScanVMPages(cmd_args=None, cmd_options={}):
+ """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes.
+ usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone]
+
+ scan_vm_pages -A: scan vm pages in the global vm_pages array
+ scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone
+ scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue
+ scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object
+ scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value
+ scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number
+ scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set
+ scan_vm_pages <-A> -I <start_index>: Start the scan from start_index
+ scan_vm_pages <-A> -N <npages>: Scan at most npages
+ """
+ if (len(cmd_options) < 1):
+ raise ArgumentError("Please specify at least one matching attribute")
+
+ vm_pages = kern.globals.vm_pages
+ vm_pages_count = kern.globals.vm_pages_count
+
+ start_index = 0
+ npages = vm_pages_count
+ scan_vmpages_array = False
+ scan_vmpages_zone = False
+ attribute_count = 0
+
+ if "-A" in cmd_options:
+ scan_vmpages_array = True
+
+ if "-Z" in cmd_options:
+ scan_vmpages_zone = True
+
+ if scan_vmpages_array == False and scan_vmpages_zone == False:
+ raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)")
+
+ attribute_values = {}
+ if "-S" in cmd_options:
+ attribute_values["vmp_q_state"] = kern.GetValueFromAddress(cmd_options["-S"], 'int')
+ attribute_count += 1
+
+ if "-O" in cmd_options:
+ attribute_values["vm_object"] = kern.GetValueFromAddress(cmd_options["-O"], 'vm_object_t')
+ attribute_count += 1
+
+ if "-F" in cmd_options:
+ attribute_values["vmp_offset"] = kern.GetValueFromAddress(cmd_options["-F"], 'unsigned long long')
+ attribute_count += 1
+
+ if "-P" in cmd_options:
+ attribute_values["phys_page"] = kern.GetValueFromAddress(cmd_options["-P"], 'unsigned int')
+ attribute_count += 1
+
+ if "-B" in cmd_options:
+ valid_vmp_bitfields = [
+ "vmp_in_background",
+ "vmp_on_backgroundq",
+ "vmp_gobbled",
+ "vmp_laundry",
+ "vmp_no_cache",
+ "vmp_private",
+ "vmp_reference",
+ "vmp_busy",
+ "vmp_wanted",
+ "vmp_tabled",
+ "vmp_hashed",
+ "vmp_fictitious",
+ "vmp_clustered",
+ "vmp_pmapped",
+ "vmp_xpmapped",
+ "vmp_free_when_done",
+ "vmp_absent",
+ "vmp_error",
+ "vmp_dirty",
+ "vmp_cleaning",
+ "vmp_precious",
+ "vmp_overwriting",
+ "vmp_restart",
+ "vmp_unusual",
+ "vmp_cs_validated",
+ "vmp_cs_tainted",
+ "vmp_cs_nx",
+ "vmp_reusable",
+ "vmp_lopage",
+ "vmp_written_by_kernel",
+ "vmp_unused_object_bits"
+ ]
+ attribute_values["bitfield"] = cmd_options["-B"]
+ if attribute_values["bitfield"] in valid_vmp_bitfields:
+ attribute_count += 1
+ else:
+ raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield))
+
+ if "-I" in cmd_options:
+ start_index = kern.GetValueFromAddress(cmd_options["-I"], 'int')
+ npages = vm_pages_count - start_index
+
+ if "-N" in cmd_options:
+ npages = kern.GetValueFromAddress(cmd_options["-N"], 'int')
+ if npages == 0:
+ raise ArgumentError("You specified -N 0, nothing to be scanned")
+
+ end_index = start_index + npages - 1
+ if end_index >= vm_pages_count:
+ raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count))
+
+ header_after_n_lines = 40
+ format_string = "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}"
+
+ found_in_array = 0
+ if scan_vmpages_array:
+ print "Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index, end_index, attribute_count)
+ i = start_index
+ while i <= end_index:
+ page = vm_pages[i]
+ if match_vm_page_attributes(page, attribute_values) == attribute_count:
+ if found_in_array % header_after_n_lines == 0:
+ print ScanVMPages.header
+
+ print format_string.format(str(i), addressof(page), page.vmp_q_state, _vm_page_unpack_ptr(page.vmp_object), page.vmp_offset, _vm_page_get_phys_page(addressof(page)))
+ found_in_array += 1
+
+ i += 1
+
+ found_in_zone = 0
+ if scan_vmpages_zone:
+ page_size = kern.GetGlobalVariable('page_size')
+ num_zones = kern.GetGlobalVariable('num_zones')
+ zone_array = kern.GetGlobalVariable('zone_array')
+ print "Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count)
+ i = 0
+ while i < num_zones:
+ zone = zone_array[i]
+ if str(zone.z_name) == "vm pages":
+ break;
+ i += 1
+
+ if i == num_zones:
+ print "Cannot find vm_pages zone, skip the scan"
+ else:
+ print "Scanning page queues in the vm_pages zone..."
+ elements = FindAllocatedElementsInZone(zone)
+ for elem in elements:
+ page = kern.GetValueFromAddress(elem, 'vm_page_t')
+
+ if match_vm_page_attributes(page, attribute_values) == attribute_count:
+ if found_in_zone % header_after_n_lines == 0:
+ print ScanVMPages.header
+
+ vm_object = _vm_page_unpack_ptr(page.vmp_object)
+ phys_page = _vm_page_get_phys_page(page)
+ print format_string.format("vm_pages zone", elem, page.vmp_q_state, vm_object, page.vmp_offset, phys_page)
+ found_in_zone += 1
+
+ total = found_in_array + found_in_zone
+ print "Found {0:d} vm pages ({1:d} in array, {2:d} in zone) matching the requested {3:d} attribute(s)".format(total, found_in_array, found_in_zone, attribute_count)
+
+#EndMacro scan_vm_pages
+
+VM_PAGE_IS_WIRED = 1
+
+@header("{0: <10s} of {1: <10s} {2: <20s} {3: <20s} {4: <20s} {5: <10s} {6: <5s}\t {7: <28s}\t{8: <50s}".format("index", "total", "vm_page_t", "offset", "next", "phys_page", "wire#", "first bitfield", "second bitfield"))
+@lldb_command('vmobjectwalkpages', 'CSBNQP:O:')
+def VMObjectWalkPages(cmd_args=None, cmd_options={}):
+ """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
+ specifically look for this page, highlighting it in the output or noting if it was not found. For
+ each page, we confirm that it points to the object. We also keep track of the number of pages we
+ see and compare this to the object's resident page count field.
+ Usage:
+ vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
+ vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages
+ vmobjectwalkpages <vm_object_t> -B : Walk and print all the pages for a given object (up to 4K pages by default), traversing the memq backwards
+ vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
+ vmobjectwalkpages <vm_object_t> -Q : Walk all pages for a given object, looking for known signs of corruption (i.e. q_state == VM_PAGE_IS_WIRED && wire_count == 0)
+ vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
+ vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
+ vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset
+
+ """
+
+ if (cmd_args == None or len(cmd_args) < 1):
+ raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
+
+ out_string = ""
+
+ obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
+
+ page = 0
+ if "-P" in cmd_options:
+ page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
+
+ off = -1
+ if "-O" in cmd_options:
+ off = kern.GetValueFromAddress(cmd_options['-O'], 'vm_offset_t')
+
+ stop = 0
+ if "-S" in cmd_options:
+ if page == 0 and off < 0:
+ raise ArgumentError("-S can only be passed when a page is specified with -P or -O")
+ stop = 1
+
+ walk_backwards = False
+ if "-B" in cmd_options:
+ walk_backwards = True
+
+ quiet_mode = False
+ if "-Q" in cmd_options:
+ quiet_mode = True
+
+ if not quiet_mode:
+ print VMObjectWalkPages.header
+ format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
+ first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
+ second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
+ second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
+ second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
+ second_bitfield_format_string += "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
+
+ limit = 4096 #arbitrary limit of number of pages to walk
+ ignore_limit = 0
+ if "-N" in cmd_options:
+ ignore_limit = 1
+
+ show_compressed = 0
+ if "-C" in cmd_options:
+ show_compressed = 1
+
+ page_count = 0
+ res_page_count = unsigned(obj.resident_page_count)
+ page_found = False
+ pages_seen = set()
+
+ for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr):
+ page_count += 1
+ out_string = ""
+ if (page != 0 and not(page_found) and vmp == page):
+ out_string += "******"
+ page_found = True
+
+ if (off > 0 and not(page_found) and vmp.vmp_offset == off):
+ out_string += "******"
+ page_found = True
+
+ if page != 0 or off > 0 or quiet_mode:
+ if (page_count % 1000) == 0:
+ print "traversed %d pages ...\n" % (page_count)
+ else:
+ out_string += format_string.format(page_count, res_page_count, vmp, vmp.vmp_offset, _vm_page_unpack_ptr(vmp.vmp_listq.next), _vm_page_get_phys_page(vmp), vmp.vmp_wire_count)
+ out_string += first_bitfield_format_string.format(vmp.vmp_q_state, vmp.vmp_in_background, vmp.vmp_on_backgroundq, vmp.vmp_gobbled, vmp.vmp_laundry, vmp.vmp_no_cache,
+ vmp.vmp_private, vmp.vmp_reference)
+
+ if hasattr(vmp,'slid'):
+ vmp_slid = vmp.slid
+ else:
+ vmp_slid = 0
+ out_string += second_bitfield_format_string.format(vmp.vmp_busy, vmp.vmp_wanted, vmp.vmp_tabled, vmp.vmp_hashed, vmp.vmp_fictitious, vmp.vmp_clustered,
+ vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent,
+ vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting,
+ vmp.vmp_restart, vmp.vmp_unusual, 0, 0,
+ vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid,
+ vmp.vmp_written_by_kernel)
+
+ if (vmp in pages_seen):
+ print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n"
+ return
+
+ if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)):
+ print out_string + " vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " points to different vm_object_t: " + "{0: <#020x}".format(unsigned(_vm_page_unpack_ptr(vmp.vmp_object)))
+ return
+
+ if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0):
+ print out_string + " page in wired state with wire_count of 0\n"
+ print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n"
+ print "stopping...\n"
+ return
+
+ if (hasattr(vmp, 'vmp_unused_page_bits') and (vmp.vmp_unused_page_bits != 0)):
+ print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused__pageq_bits: %d\n" % (vmp.vmp_unused_page_bits)
+ print "stopping...\n"
+ return
+
+ if (hasattr(vmp, 'vmp_unused_object_bits') and (vmp.vmp_unused_object_bits != 0)):
+ print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused_object_bits : %d\n" % (vmp.vmp_unused_object_bits)
+ print "stopping...\n"
+ return
+
+ pages_seen.add(vmp)
+
+ if False:
+ hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset)
+ hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
+ hash_page = _vm_page_unpack_ptr(hash_page_list)
+ hash_page_t = 0
+
+ while (hash_page != 0):
+ hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
+ if hash_page_t == vmp:
+ break
+ hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m)
+
+ if (unsigned(vmp) != unsigned(hash_page_t)):
+ print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n"
+ print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset)))
+ return
+
+ if (page_count >= limit and not(ignore_limit)):
+ print out_string + "Limit reached (%d pages), stopping..." % (limit)
+ break
+
+ print out_string
+
+ if page_found and stop:
+ print("Object reports resident page count of: %d we stopped after traversing %d and finding the requested page.\n" % (unsigned(obj.res_page_count), unsigned(page_count)))
+ return
+
+ if (page != 0):
+ print("page found? : %s\n" % page_found)
+
+ if (off > 0):
+ print("page found? : %s\n" % page_found)
+
+ print("Object reports resident page count of %d, we saw %d pages when we walked the resident list.\n" % (unsigned(obj.resident_page_count), unsigned(page_count)))
+
+ if show_compressed != 0 and obj.pager != 0 and unsigned(obj.pager.mo_pager_ops) == unsigned(addressof(kern.globals.compressor_pager_ops)):
+ pager = Cast(obj.pager, 'compressor_pager *')
+ chunks = pager.cpgr_num_slots / 128
+ pagesize = kern.globals.page_size
+
+ page_idx = 0
+ while page_idx < pager.cpgr_num_slots:
+ if chunks != 0:
+ chunk = pager.cpgr_slots.cpgr_islots[page_idx / 128]
+ slot = chunk[page_idx % 128]
+ elif pager.cpgr_num_slots > 2:
+ slot = pager.cpgr_slots.cpgr_dslots[page_idx]
+ else:
+ slot = pager.cpgr_slots.cpgr_eslots[page_idx]
+
+ if slot != 0:
+ print("compressed page for offset: %x slot %x\n" % ((page_idx * pagesize) - obj.paging_offset, slot))
+ page_idx = page_idx + 1
+
+
+@lldb_command("show_all_apple_protect_pagers")
+def ShowAllAppleProtectPagers(cmd_args=None):
+ """Routine to print all apple_protect pagers
+ usage: show_all_apple_protect_pagers
+ """
+ print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "mo_control", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
+ qhead = kern.globals.apple_protect_pager_queue
+ qtype = GetType('apple_protect_pager *')
+ qcnt = kern.globals.apple_protect_pager_count
+ idx = 0
+ for pager in IterateQueue(qhead, qtype, "pager_queue"):
+ idx = idx + 1
+ show_apple_protect_pager(pager, qcnt, idx)
+
+@lldb_command("show_apple_protect_pager")
+def ShowAppleProtectPager(cmd_args=None):
+ """Routine to print out info about an apple_protect pager
+ usage: show_apple_protect_pager <pager>
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", ShowAppleProtectPager.__doc__
+ return
+ pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t')
+ show_apple_protect_pager(pager, 1, 1)
+
+def show_apple_protect_pager(pager, qcnt, idx):
+ object = pager.backing_object
+ shadow = object.shadow
+ while shadow != 0:
+ object = shadow
+ shadow = object.shadow
+ vnode_pager = Cast(object.pager,'vnode_pager *')
+ filename = GetVnodePath(vnode_pager.vnode_handle)
+ print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {: <#018x} {: <#018x} {:#018x} {:#018x} {:#018x} {:#018x}\n\tcrypt_info:{: <#018x} <decrypt:{: <#018x} end:{:#018x} ops:{: <#018x} refs:{:<d}>\n\tvnode:{: <#018x} {:s}\n".format(idx, qcnt, pager, pager.ref_count, pager.is_ready, pager.is_mapped, pager.pager_control, pager.backing_object, pager.backing_offset, pager.crypto_backing_offset, pager.crypto_start, pager.crypto_end, pager.crypt_info, pager.crypt_info.page_decrypt, pager.crypt_info.crypt_end, pager.crypt_info.crypt_ops, pager.crypt_info.crypt_refcnt, vnode_pager.vnode_handle, filename)
+
+@lldb_command("show_console_ring")
+def ShowConsoleRingData(cmd_args=None):
+ """ Print console ring buffer stats and data
+ """
+ cr = kern.globals.console_ring
+ print "console_ring = {:#018x} buffer = {:#018x} length = {:<5d} used = {:<5d} read_ptr = {:#018x} write_ptr = {:#018x}".format(addressof(cr), cr.buffer, cr.len, cr.used, cr.read_ptr, cr.write_ptr)
+ pending_data = []
+ for i in range(unsigned(cr.used)):
+ idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len)
+ pending_data.append("{:c}".format(cr.buffer[idx]))
+
+ if pending_data:
+ print "Data:"
+ print "".join(pending_data)
+
+# Macro: showjetsamsnapshot
+
+@lldb_command("showjetsamsnapshot", "DA")
+def ShowJetsamSnapshot(cmd_args=None, cmd_options={}):
+ """ Dump entries in the jetsam snapshot table
+ usage: showjetsamsnapshot [-D] [-A]
+ Use -D flag to print extra physfootprint details
+ Use -A flag to print all entries (regardless of valid count)
+ """
+
+ # Not shown are uuid, user_data, cpu_time
+
+ global kern
+
+ show_footprint_details = False
+ show_all_entries = False
+
+ if "-D" in cmd_options:
+ show_footprint_details = True
+
+ if "-A" in cmd_options:
+ show_all_entries = True
+
+ valid_count = kern.globals.memorystatus_jetsam_snapshot_count
+ max_count = kern.globals.memorystatus_jetsam_snapshot_max
+
+ if (show_all_entries == True):
+ count = max_count
+ else:
+ count = valid_count
+
+ print "{:s}".format(valid_count)
+ print "{:s}".format(max_count)
+
+ if int(count) == 0:
+ print "The jetsam snapshot is empty."
+ print "Use -A to force dump all entries (regardless of valid count)"
+ return
+
+ # Dumps the snapshot header info
+ print lldb_run_command('p *memorystatus_jetsam_snapshot')
+
+ hdr_format = "{0: >32s} {1: >5s} {2: >4s} {3: >6s} {4: >6s} {5: >20s} {6: >20s} {7: >20s} {8: >5s} {9: >10s} {10: >6s} {11: >6s} {12: >10s} {13: >15s} {14: >15s} {15: >15s}"
+ if (show_footprint_details == True):
+ hdr_format += "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}"
+
+
+ if (show_footprint_details == False):
+ print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax')
+ print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)')
+ else:
+ print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax', '|| internal', 'internal_comp', 'iokit_mapped', 'purge_nonvol', 'purge_nonvol_comp', 'alt_acct', 'alt_acct_comp', 'page_table')
+ print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
+
+
+ entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
+ "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
+ "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
+ "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\
+ "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
+
+ if (show_footprint_details == True):
+ entry_format += "{e.jse_internal_pages: >15d} "\
+ "{e.jse_internal_compressed_pages: >15d} "\
+ "{e.jse_iokit_mapped_pages: >12d} "\
+ "{e.jse_purgeable_nonvolatile_pages: >12d} "\
+ "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
+ "{e.jse_alternate_accounting_pages: >10d} "\
+ "{e.jse_alternate_accounting_compressed_pages: >13d} "\
+ "{e.jse_page_table_pages: >10d}"
+
+ snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries
+ idx = 0
+ while idx < count:
+ current_entry = dereference(Cast(addressof(snapshot_list[idx]), 'jetsam_snapshot_entry *'))
+ print entry_format.format(index=idx, e=current_entry)
+ idx +=1
+ return
+
+# EndMacro: showjetsamsnapshot
+
+# Macro: showvnodecleanblk/showvnodedirtyblk
+
+def _GetBufSummary(buf):
+ """ Get a summary of important information out of a buf_t.
+ """
+ initial = "(struct buf) {0: <#0x} ="
+
+ # List all of the fields in this buf summary.
+ entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq,
+ buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid,
+ buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp,
+ buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata,
+ buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff,
+ buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr]
+
+ # Join an (already decent) string representation of each field
+ # with newlines and indent the region.
+ joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n ")
+
+ # Add the total string representation to our title and return it.
+ out_str = initial.format(int(buf)) + " {\n " + joined_strs + "\n}\n\n"
+ return out_str
+
+def _ShowVnodeBlocks(dirty=True, cmd_args=None):
+ """ Display info about all [dirty|clean] blocks in a vnode.
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Please provide a valid vnode argument."
+ return
+
+ vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
+ list_head = vnodeval.v_cleanblkhd;
+ if dirty:
+ list_head = vnodeval.v_dirtyblkhd
+
+ print "Blocklist for vnode {}:".format(cmd_args[0])
+
+ i = 0
+ for buf in IterateListEntry(list_head, 'struct buf *', 'b_hash'):
+ # For each block (buf_t) in the appropriate list,
+ # ask for a summary and print it.
+ print "---->\nblock {}: ".format(i) + _GetBufSummary(buf)
+ i += 1
+ return
+
+@lldb_command('showvnodecleanblk')
+def ShowVnodeCleanBlocks(cmd_args=None):
+ """ Display info about all clean blocks in a vnode.
+ usage: showvnodecleanblk <address of vnode>
+ """
+ _ShowVnodeBlocks(False, cmd_args)
+
+@lldb_command('showvnodedirtyblk')
+def ShowVnodeDirtyBlocks(cmd_args=None):
+ """ Display info about all dirty blocks in a vnode.
+ usage: showvnodedirtyblk <address of vnode>
+ """
+ _ShowVnodeBlocks(True, cmd_args)
+
+# EndMacro: showvnodecleanblk/showvnodedirtyblk
+
+
+@lldb_command("vm_page_lookup_in_map")
+def VmPageLookupInMap(cmd_args=None):
+ """Lookup up a page at a virtual address in a VM map
+ usage: vm_page_lookup_in_map <map> <vaddr>
+ """
+ if cmd_args == None or len(cmd_args) < 2:
+ print "Invalid argument.", VmPageLookupInMap.__doc__
+ return
+ map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
+ vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t')
+ print "vaddr {:#018x} in map {: <#018x}".format(vaddr, map)
+ vm_page_lookup_in_map(map, vaddr)
+
+def vm_page_lookup_in_map(map, vaddr):
+ vaddr = unsigned(vaddr)
+ vme_list_head = map.hdr.links
+ vme_ptr_type = GetType('vm_map_entry *')
+ for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
+ if unsigned(vme.links.start) > vaddr:
+ break
+ if unsigned(vme.links.end) <= vaddr:
+ continue
+ offset_in_vme = vaddr - unsigned(vme.links.start)
+ print " offset {:#018x} in map entry {: <#018x} [{:#018x}:{:#018x}] object {: <#018x} offset {:#018x}".format(offset_in_vme, vme, unsigned(vme.links.start), unsigned(vme.links.end), vme.vme_object.vmo_object, unsigned(vme.vme_offset) & ~0xFFF)
+ offset_in_object = offset_in_vme + (unsigned(vme.vme_offset) & ~0xFFF)
+ if vme.is_sub_map:
+ print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object, vme.vme_object.vmo_submap)
+ vm_page_lookup_in_map(vme.vme_object.vmo_submap, offset_in_object)
+ else:
+ vm_page_lookup_in_object(vme.vme_object.vmo_object, offset_in_object)
+
+@lldb_command("vm_page_lookup_in_object")
+def VmPageLookupInObject(cmd_args=None):
+ """Lookup up a page at a given offset in a VM object
+ usage: vm_page_lookup_in_object <object> <offset>
+ """
+ if cmd_args == None or len(cmd_args) < 2:
+ print "Invalid argument.", VmPageLookupInObject.__doc__
+ return
+ object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
+ offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t')
+ print "offset {:#018x} in object {: <#018x}".format(offset, object)
+ vm_page_lookup_in_object(object, offset)
+
+def vm_page_lookup_in_object(object, offset):
+ offset = unsigned(offset)
+ page_size = kern.globals.page_size
+ trunc_offset = offset & ~(page_size - 1)
+ print " offset {:#018x} in VM object {: <#018x}".format(offset, object)
+ hash_id = _calc_vm_page_hash(object, trunc_offset)
+ page_list = kern.globals.vm_page_buckets[hash_id].page_list
+ page = _vm_page_unpack_ptr(page_list)
+ while page != 0:
+ m = kern.GetValueFromAddress(page, 'vm_page_t')
+ m_object_val = _vm_page_unpack_ptr(m.vmp_object)
+ m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t')
+ if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset):
+ page = _vm_page_unpack_ptr(m.vmp_next_m)
+ continue
+ print " resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m))
+ return
+ if object.pager and object.pager_ready:
+ offset_in_pager = trunc_offset + unsigned(object.paging_offset)
+ if not object.internal:
+ print " offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager)
+ return
+ pager = Cast(object.pager, 'compressor_pager *')
+ ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager)
+ if ret:
+ return
+ if object.shadow and not object.phys_contiguous:
+ offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset)
+ vm_page_lookup_in_object(object.shadow, offset_in_shadow)
+ return
+ print " page is absent and will be zero-filled on demand"
+ return
+
+@lldb_command("vm_page_lookup_in_compressor_pager")
+def VmPageLookupInCompressorPager(cmd_args=None):
+ """Lookup up a page at a given offset in a compressor pager
+ usage: vm_page_lookup_in_compressor_pager <pager> <offset>
+ """
+ if cmd_args == None or len(cmd_args) < 2:
+ print "Invalid argument.", VmPageLookupInCompressorPager.__doc__
+ return
+ pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t')
+ offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t')
+ print "offset {:#018x} in compressor pager {: <#018x}".format(offset, pager)
+ vm_page_lookup_in_compressor_pager(pager, offset)
+
+def vm_page_lookup_in_compressor_pager(pager, offset):
+ offset = unsigned(offset)
+ page_size = unsigned(kern.globals.page_size)
+ page_num = unsigned(offset / page_size)
+ if page_num > pager.cpgr_num_slots:
+ print " *** ERROR: vm_page_lookup_in_compressor_pager({: <#018x},{:#018x}): page_num {:#x} > num_slots {:#x}".format(pager, offset, page_num, pager.cpgr_num_slots)
+ return 0
+ slots_per_chunk = 512 / sizeof ('compressor_slot_t')
+ num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) / slots_per_chunk)
+ if num_chunks > 1:
+ chunk_idx = unsigned(page_num / slots_per_chunk)
+ chunk = pager.cpgr_slots.cpgr_islots[chunk_idx]
+ slot_idx = unsigned(page_num % slots_per_chunk)
+ slot = GetObjectAtIndexFromArray(chunk, slot_idx)
+ slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx)
+ elif pager.cpgr_num_slots > 2:
+ slot_idx = page_num
+ slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx)
+ slot_str = "dslots[{:d}]".format(slot_idx)
+ else:
+ slot_idx = page_num
+ slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx)
+ slot_str = "eslots[{:d}]".format(slot_idx)
+ print " offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot)
+ if slot == 0:
+ return 0
+ slot_value = dereference(slot)
+ print " value {:#010x}".format(slot_value)
+ vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t'))
+ return 1
+
+@lldb_command("vm_page_lookup_in_compressor")
+def VmPageLookupInCompressor(cmd_args=None):
+ """Lookup up a page in a given compressor slot
+ usage: vm_page_lookup_in_compressor <slot>
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", VmPageLookupInCompressor.__doc__
+ return
+ slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *')
+ print "compressor slot {: <#018x}".format(slot)
+ vm_page_lookup_in_compressor(slot)
+
+C_SV_CSEG_ID = ((1 << 22) - 1)
+
+def vm_page_lookup_in_compressor(slot_ptr):
+ slot_ptr = Cast(slot_ptr, 'compressor_slot_t *')
+ slot_value = dereference(slot_ptr)
+ slot = Cast(slot_value, 'c_slot_mapping')
+ print slot
+ print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx)
+ if slot_ptr == 0:
+ return
+ if slot.s_cseg == C_SV_CSEG_ID:
+ sv = kern.globals.c_segment_sv_hash_table
+ print "single value[{:#d}]: ref {:d} value {:#010x}".format(slot.s_cindx, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_ref, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_data)
+ return
+ if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available):
+ print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available))
+ return
+ c_segments = kern.globals.c_segments
+ c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1)
+ c_seg = c_segments_elt.c_seg
+ c_no_data = 0
+ if hasattr(c_seg, 'c_state'):
+ c_state = c_seg.c_state
+ if c_state == 0:
+ c_state_str = "C_IS_EMPTY"
+ c_no_data = 1
+ elif c_state == 1:
+ c_state_str = "C_IS_FREE"
+ c_no_data = 1
+ elif c_state == 2:
+ c_state_str = "C_IS_FILLING"
+ elif c_state == 3:
+ c_state_str = "C_ON_AGE_Q"
+ elif c_state == 4:
+ c_state_str = "C_ON_SWAPOUT_Q"
+ elif c_state == 5:
+ c_state_str = "C_ON_SWAPPEDOUT_Q"
+ c_no_data = 1
+ elif c_state == 6:
+ c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q"
+ c_no_data = 1
+ elif c_state == 7:
+ c_state_str = "C_ON_SWAPPEDIN_Q"
+ elif c_state == 8:
+ c_state_str = "C_ON_MAJORCOMPACT_Q"
+ elif c_state == 9:
+ c_state_str = "C_ON_BAD_Q"
+ c_no_data = 1
+ else:
+ c_state_str = "<unknown>"
+ else:
+ c_state = -1
+ c_state_str = "<no c_state field>"
+ print "c_segments[{:d}] {: <#018x} c_seg {: <#018x} c_state {:#x}={:s}".format(slot.s_cseg-1, c_segments_elt, c_seg, c_state, c_state_str)
+ c_indx = unsigned(slot.s_cindx)
+ if hasattr(c_seg, 'c_slot_var_array'):
+ c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len
+ if c_indx < c_seg_fixed_array_len:
+ cs = c_seg.c_slot_fixed_array[c_indx]
+ else:
+ cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len)
+ else:
+ C_SEG_SLOT_ARRAY_SIZE = 64
+ C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1
+ cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx / C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK)
+ print cs
+ c_slot_unpacked_ptr = vm_unpack_ptr(cs.c_packed_ptr, kern.globals.c_slot_packing_params)
+ print "c_slot {: <#018x} c_offset {:#x} c_size {:#x} c_packed_ptr {:#x} (unpacked: {: <#018x})".format(cs, cs.c_offset, cs.c_size, cs.c_packed_ptr, unsigned(c_slot_unpacked_ptr))
+ if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr):
+ print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr)
+ if c_no_data == 0:
+ c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset)
+ c_size = cs.c_size
+ cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size)
+ print cmd
+ print lldb_run_command(cmd)
+ else:
+ print "<no compressed data>"
+
+def print_hex_data(data, begin_offset=0, desc=""):
+ """ print on stdout "hexdump -C < data" like output
+ params:
+ data - bytearray or array of int where each int < 255
+ begin_offset - int offset that should be printed in left column
+ desc - str optional description to print on the first line to describe data
+ """
+ if desc:
+ print "{}:".format(desc)
+ index = 0
+ total_len = len(data)
+ hex_buf = ""
+ char_buf = ""
+ while index < total_len:
+ hex_buf += " {:02x}".format(data[index])
+ if data[index] < 0x20 or data[index] > 0x7e:
+ char_buf += "."
+ else:
+ char_buf += "{:c}".format(data[index])
+ index += 1
+ if index and index % 8 == 0:
+ hex_buf += " "
+ if index > 1 and (index % 16) == 0:
+ print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
+ hex_buf = ""
+ char_buf = ""
+ if index % 16 != 0:
+ print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
+ return
+
+@lldb_command('vm_scan_all_pages')
+def VMScanAllPages(cmd_args=None):
+ """Scans the vm_pages[] array
+ """
+ vm_pages_count = kern.globals.vm_pages_count
+ vm_pages = kern.globals.vm_pages
+
+ free_count = 0
+ local_free_count = 0
+ active_count = 0
+ local_active_count = 0
+ inactive_count = 0
+ speculative_count = 0
+ throttled_count = 0
+ wired_count = 0
+ compressor_count = 0
+ pageable_internal_count = 0
+ pageable_external_count = 0
+ secluded_count = 0
+ secluded_free_count = 0
+ secluded_inuse_count = 0
+
+ i = 0
+ while i < vm_pages_count:
+
+ if i % 10000 == 0:
+ print "{:d}/{:d}...\n".format(i,vm_pages_count)
+
+ m = vm_pages[i]
+
+ internal = 0
+ external = 0
+ m_object_val = _vm_page_unpack_ptr(m.vmp_object)
+
+ if m_object:
+ if m_object.internal:
+ internal = 1
+ else:
+ external = 1
+
+ if m.vmp_wire_count != 0 and m.vmp_local == 0:
+ wired_count = wired_count + 1
+ pageable = 0
+ elif m.vmp_throttled:
+ throttled_count = throttled_count + 1
+ pageable = 0
+ elif m.vmp_active:
+ active_count = active_count + 1
+ pageable = 1
+ elif m.vmp_local:
+ local_active_count = local_active_count + 1
+ pageable = 0
+ elif m.vmp_inactive:
+ inactive_count = inactive_count + 1
+ pageable = 1
+ elif m.vmp_speculative:
+ speculative_count = speculative_count + 1
+ pageable = 0
+ elif m.vmp_free:
+ free_count = free_count + 1
+ pageable = 0
+ elif m.vmp_secluded:
+ secluded_count = secluded_count + 1
+ if m_object == 0:
+ secluded_free_count = secluded_free_count + 1
+ else:
+ secluded_inuse_count = secluded_inuse_count + 1
+ pageable = 0
+ elif m_object == 0 and m.vmp_busy:
+ local_free_count = local_free_count + 1
+ pageable = 0
+ elif m.vmp_compressor:
+ compressor_count = compressor_count + 1
+ pageable = 0
+ else:
+ print "weird page vm_pages[{:d}]?\n".format(i)
+ pageable = 0
+
+ if pageable:
+ if internal:
+ pageable_internal_count = pageable_internal_count + 1
+ else:
+ pageable_external_count = pageable_external_count + 1
+ i = i + 1
+
+ print "vm_pages_count = {:d}\n".format(vm_pages_count)
+
+ print "wired_count = {:d}\n".format(wired_count)
+ print "throttled_count = {:d}\n".format(throttled_count)
+ print "active_count = {:d}\n".format(active_count)
+ print "local_active_count = {:d}\n".format(local_active_count)
+ print "inactive_count = {:d}\n".format(inactive_count)
+ print "speculative_count = {:d}\n".format(speculative_count)
+ print "free_count = {:d}\n".format(free_count)
+ print "local_free_count = {:d}\n".format(local_free_count)
+ print "compressor_count = {:d}\n".format(compressor_count)
+
+ print "pageable_internal_count = {:d}\n".format(pageable_internal_count)
+ print "pageable_external_count = {:d}\n".format(pageable_external_count)
+ print "secluded_count = {:d}\n".format(secluded_count)
+ print "secluded_free_count = {:d}\n".format(secluded_free_count)
+ print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count)
+
+
+@lldb_command('show_all_vm_named_entries')
+def ShowAllVMNamedEntries(cmd_args=None):
+ """ Routine to print a summary listing of all the VM named entries
+ """
+ queue_len = kern.globals.vm_named_entry_count
+ queue_head = kern.globals.vm_named_entry_list
+
+ print 'vm_named_entry_list:{: <#018x} vm_named_entry_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len)
+
+# print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
+ idx = 0
+ for entry in IterateQueue(queue_head, 'struct vm_named_entry *', 'named_entry_list'):
+ idx += 1
+ showmemoryentry(entry, idx, queue_len)
+
+@lldb_command('show_vm_named_entry')
+def ShowVMNamedEntry(cmd_args=None):
+ """ Routine to print a VM named entry
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", ShowMapVMNamedEntry.__doc__
+ return
+ named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t')
+ showmemoryentry(named_entry, 0, 0)
+
+def showmemoryentry(entry, idx=0, queue_len=0):
+ """ Routine to print out a summary a VM memory entry
+ params:
+ entry - core.value : a object of type 'struct vm_named_entry *'
+ returns:
+ None
+ """
+ show_pager_info = True
+ show_all_shadows = True
+
+ backing = ""
+ if entry.is_sub_map == 1:
+ backing += "SUBMAP"
+ if entry.is_copy == 1:
+ backing += "COPY"
+ if entry.is_object == 1:
+ backing += "OBJECT"
+ if entry.is_sub_map == 0 and entry.is_copy == 0 and entry.is_object == 0:
+ backing += "***?***"
+ prot=""
+ if entry.protection & 0x1:
+ prot += "r"
+ else:
+ prot += "-"
+ if entry.protection & 0x2:
+ prot += "w"
+ else:
+ prot += "-"
+ if entry.protection & 0x4:
+ prot += "x"
+ else:
+ prot += "-"
+ extra_str = ""
+ if hasattr(entry, 'named_entry_alias'):
+ extra_str += " alias={:d}".format(entry.named_entry_alias)
+ if hasattr(entry, 'named_entry_port'):
+ extra_str += " port={:#016x}".format(entry.named_entry_port)
+ print "{:d}/{:d} {: <#018x} ref={:d} prot={:d}/{:s} type={:s} backing={: <#018x} offset={:#016x} dataoffset={:#016x} size={:#016x}{:s}\n".format(idx,queue_len,entry,entry.ref_count,entry.protection,prot,backing,entry.backing.copy,entry.offset,entry.data_offset,entry.size,extra_str)
+ if entry.is_sub_map == 1:
+ showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows)
+ elif entry.is_copy == 1:
+ showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
+ elif entry.is_object == 1:
+ showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
+ else:
+ print "***** UNKNOWN TYPE *****"
+ print " \n"
+
+
+def IterateRBTreeEntry2(element, element_type, field_name1, field_name2):
+ """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
+ element - value : Value object for rbh_root
+ element_type - str : Type of the link element
+ field_name - str : Name of the field in link element's structure
+ returns:
+ A generator does not return. It is used for iterating
+ value : an object thats of type (element_type) head->sle_next. Always a pointer object
+ """
+ elt = element.__getattr__('rbh_root')
+ if type(element_type) == str:
+ element_type = gettype(element_type)
+ charp_type = gettype('char *');
+
+ # Walk to find min
+ parent = elt
+ while unsigned(elt) != 0:
+ parent = elt
+ elt = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
+ elt = parent
+
+ # Now elt is min
+ while unsigned(elt) != 0:
+ yield elt
+ # implementation cribbed from RB_NEXT in libkern/tree.h
+ right = cast(elt.__getattr__(field_name1).__getattr__(fieldname2).__getattr__('rbe_right'), element_type)
+ if unsigned(right) != 0:
+ elt = right
+ left = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
+ while unsigned(left) != 0:
+ elt = left
+ left = cast(elt.__getattr__(field_name1).__getattr(__field_name2).__getattr__('rbe_left'), element_type)
+ else:
+
+ # avoid using GetValueFromAddress
+ addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
+ parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
+ parent = cast(parent, element_type)
+
+ if unsigned(parent) != 0:
+ left = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
+ if (unsigned(parent) != 0) and (unsigned(elt) == unsigned(left)):
+ elt = parent
+ else:
+ if unsigned(parent) != 0:
+ right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
+ while unsigned(parent) != 0 and (unsigned(elt) == unsigned(right)):
+ elt = parent
+
+ # avoid using GetValueFromAddress
+ addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
+ parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
+ parent = cast(parent, element_type)
+
+ right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
+
+ # avoid using GetValueFromAddress
+ addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
+ elt = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
+ elt = cast(elt, element_type)
+
+
+@lldb_command("showmaprb")
+def ShowMapRB(cmd_args=None):
+ """Routine to print out a VM map's RB tree
+ usage: showmaprb <vm_map>
+ """
+ if cmd_args == None or len(cmd_args) < 1:
+ print "Invalid argument.", ShowMapRB.__doc__
+ return
+ map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
+ print GetVMMapSummary.header
+ print GetVMMapSummary(map_val)
+ vme_rb_root = map_val.hdr.rb_head_store
+ vme_ptr_type = GetType('struct vm_map_entry *')
+ print GetVMEntrySummary.header
+ for vme in IterateRBTreeEntry2(vme_rb_root, 'struct vm_map_entry *', 'store', 'entry'):
+ print GetVMEntrySummary(vme)
+ return None
+
+@lldb_command('show_all_owned_objects', 'T')
+def ShowAllOwnedObjects(cmd_args=None, cmd_options={}):
+ """ Routine to print the list of VM objects owned by each task
+ -T: show only ledger-tagged objects
+ """
+ showonlytagged = False
+ if "-T" in cmd_options:
+ showonlytagged = True
+ for task in kern.tasks:
+ ShowTaskOwnedVmObjects(task, showonlytagged)
+
+@lldb_command('show_task_owned_objects', 'T')
+def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}):
+ """ Routine to print the list of VM objects owned by the specified task
+ -T: show only ledger-tagged objects
+ """
+ showonlytagged = False
+ if "-T" in cmd_options:
+ showonlytagged = True
+ task = kern.GetValueFromAddress(cmd_args[0], 'task *')
+ ShowTaskOwnedVmObjects(task, showonlytagged)
+
+@lldb_command('showdeviceinfo', 'J')
+def ShowDeviceInfo(cmd_args=None, cmd_options={}):
+ """ Routine to show basic device information (model, build, ncpus, etc...)
+ Usage: memstats [-J]
+ -J : Output json
+ """
+ print_json = False
+ if "-J" in cmd_options:
+ print_json = True
+ device_info = {}
+ device_info["build"] = str(kern.globals.osversion)
+ device_info["memoryConfig"] = int(kern.globals.max_mem_actual)
+ device_info["ncpu"] = int(kern.globals.ncpu)
+ device_info["pagesize"] = int(kern.globals.page_size)
+ device_info["mlockLimit"] = long(kern.globals.vm_global_user_wire_limit)
+
+
+ if print_json:
+ print json.dumps(device_info)
+ else:
+ PrettyPrintDictionary(device_info)
+
+def ShowTaskOwnedVmObjects(task, showonlytagged=False):
+ """ Routine to print out a summary listing of all the entries in a vm_map
+ params:
+ task - core.value : a object of type 'task *'
+ returns:
+ None
+ """
+ taskobjq_total = lambda:None
+ taskobjq_total.objects = 0
+ taskobjq_total.vsize = 0
+ taskobjq_total.rsize = 0
+ taskobjq_total.wsize = 0
+ taskobjq_total.csize = 0
+ vmo_list_head = task.task_objq
+ vmo_ptr_type = GetType('vm_object *')
+ idx = 0
+ for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"):
+ idx += 1
+ if not showonlytagged or vmo.vo_ledger_tag != 0:
+ if taskobjq_total.objects == 0:
+ print ' \n'
+ print GetTaskSummary.header + ' ' + GetProcSummary.header
+ print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
+ print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>2s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tg","owner","pid","process")
+ ShowOwnedVmObject(vmo, idx, 0, taskobjq_total)
+ if taskobjq_total.objects != 0:
+ print " total:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(taskobjq_total.objects, taskobjq_total.vsize, taskobjq_total.rsize, taskobjq_total.wsize, taskobjq_total.csize)
+ return None
+
+def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total):
+ """ Routine to print out a VM object owned by a task
+ params:
+ object - core.value : a object of type 'struct vm_object *'
+ returns:
+ None
+ """
+ page_size = kern.globals.page_size
+ if object.purgable == 0:
+ purgable = "N"
+ elif object.purgable == 1:
+ purgable = "V"
+ elif object.purgable == 2:
+ purgable = "E"
+ elif object.purgable == 3:
+ purgable = "D"
+ else:
+ purgable = "?"
+ if object.pager == 0:
+ compressed_count = 0
+ else:
+ compressor_pager = Cast(object.pager, 'compressor_pager *')
+ compressed_count = compressor_pager.cpgr_num_slots_occupied
+
+ print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>2d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
+
+ taskobjq_total.objects += 1
+ taskobjq_total.vsize += object.vo_un1.vou_size/page_size
+ taskobjq_total.rsize += object.resident_page_count
+ taskobjq_total.wsize += object.wired_page_count
+ taskobjq_total.csize += compressed_count
+
+def GetProcPIDForObjectOwner(owner):
+ """ same as GetProcPIDForTask() but deals with -1 for a disowned object
+ """
+ if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
+ return -1
+ return GetProcPIDForTask(owner)
+
+def GetProcNameForObjectOwner(owner):
+ """ same as GetProcNameForTask() but deals with -1 for a disowned object
+ """
+ if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
+ return "<disowned>"
+ return GetProcNameForTask(owner)
+
+def GetDescForNamedEntry(mem_entry):
+ out_str = "\n"
+ out_str += "\t\tmem_entry {:#08x} ref:{:d} offset:{:#08x} size:{:#08x} prot{:d} backing {:#08x}".format(mem_entry, mem_entry.ref_count, mem_entry.offset, mem_entry.size, mem_entry.protection, mem_entry.backing.copy)
+ if mem_entry.is_sub_map:
+ out_str += " is_sub_map"
+ elif mem_entry.is_copy:
+ out_str += " is_copy"
+ elif mem_entry.is_object:
+ out_str += " is_object"
+ else:
+ out_str += " ???"
+ return out_str