in tools/lldbmacros/memory.py [0:0]
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