+def AddressIsFromZoneMap(addr):
+ zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
+ zone_map_max_address = kern.GetGlobalVariable('zone_map_max_address')
+ if (unsigned(addr) >= unsigned(zone_map_min_address)) and (unsigned(addr) < unsigned(zone_map_max_address)):
+ return 1
+ else:
+ return 0
+
+def ElementOffsetInForeignPage():
+ zone_element_alignment = 32 # defined in zalloc.c
+ zone_page_metadata_size = sizeof('struct zone_page_metadata')
+ if zone_page_metadata_size % zone_element_alignment == 0:
+ offset = zone_page_metadata_size
+ else:
+ offset = zone_page_metadata_size + (zone_element_alignment - (zone_page_metadata_size % zone_element_alignment))
+ return unsigned(offset)
+
+def ElementStartAddrFromZonePageMetadata(page_metadata):
+ zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
+ zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
+ page_size = kern.GetGlobalVariable('page_size')
+ if AddressIsFromZoneMap(page_metadata):
+ page_index = (unsigned(page_metadata) - unsigned(zone_metadata_region_min)) / sizeof('struct zone_page_metadata')
+ element_start_addr = unsigned(zone_map_min_address) + unsigned(page_index * page_size)
+ else:
+ element_start_addr = unsigned(page_metadata) + unsigned(ElementOffsetInForeignPage())
+
+ return element_start_addr
+
+def ZonePageStartAddrFromZonePageMetadata(page_metadata):
+ zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
+ zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
+ page_size = kern.GetGlobalVariable('page_size')
+
+ if AddressIsFromZoneMap(page_metadata):
+ page_index = (unsigned(page_metadata) - unsigned(zone_metadata_region_min)) / sizeof('struct zone_page_metadata')
+ zone_page_addr = unsigned(zone_map_min_address) + unsigned(page_index * page_size)
+ else:
+ zone_page_addr = unsigned(page_metadata)
+
+ return unsigned(zone_page_addr)
+
+def CreateFreeElementsList(zone, first_free):
+ free_elements = []
+ if unsigned(first_free) == 0:
+ return free_elements
+ current = first_free
+ while True:
+ free_elements.append(unsigned(current))
+ next = dereference(Cast(current, 'vm_offset_t *'))
+ next = (unsigned(next) ^ unsigned(kern.globals.zp_nopoison_cookie))
+ next = kern.GetValueFromAddress(next, 'vm_offset_t *')
+ if unsigned(next) == 0:
+ break;
+ current = Cast(next, 'void *')
+
+ return free_elements
+
+#Macro: showallocatedzoneelement
+@lldb_command('showallocatedzoneelement')
+def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}):
+ """ 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):
+ page_size = kern.GetGlobalVariable('page_size')
+ elements = []
+ page_queues = ["any_free_foreign", "intermediate", "all_used"]
+ found_total = 0
+
+ for queue in page_queues:
+ found_in_queue = 0
+ if queue == "any_free_foreign" and unsigned(zone.allows_foreign) != 1:
+ continue
+
+ for zone_page_metadata in IterateQueue(zone.pages.__getattr__(queue), 'struct zone_page_metadata *', 'pages'):
+ free_elements = []
+ first_free_element = kern.GetValueFromAddress(GetFreeList(zone_page_metadata))
+ free_elements = CreateFreeElementsList(zone, first_free_element)
+
+ chunk_page_count = zone_page_metadata.page_count
+ element_addr_start = ElementStartAddrFromZonePageMetadata(zone_page_metadata)
+ zone_page_start = ZonePageStartAddrFromZonePageMetadata(zone_page_metadata)
+ next_page = zone_page_start + page_size
+ element_addr_end = zone_page_start + (chunk_page_count * page_size)
+ elem = unsigned(element_addr_start)
+ while elem < element_addr_end:
+ if elem not in free_elements:
+ elements.append(elem)
+ found_in_queue += 1
+ elem += zone.elem_size
+
+ if queue == "any_free_foreign":
+ if (elem + zone.elem_size) >= next_page:
+ zone_page_start = unsigned((elem + page_size) & ~(page_size - 1))
+ next_page = zone_page_start + page_size
+ elem = zone_page_start + unsigned(ElementOffsetInForeignPage())
+
+ found_total += found_in_queue
+# print "Found {0: <d} allocated elements in the {1: <s} page queue".format(found_in_queue, queue)
+
+# print "Total number of allocated elements: {0: <d} in zone {1: <s}".format(found_total, zone.zone_name)
+ return elements
+
+def match_vm_page_attributes(page, matching_attributes):
+ 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.zone_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
+