X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..5ba3f43ea354af8ad55bea84372a2bc834d8757c:/tools/lldbmacros/userspace.py diff --git a/tools/lldbmacros/userspace.py b/tools/lldbmacros/userspace.py old mode 100644 new mode 100755 index 21eeb328a..88a8858fc --- a/tools/lldbmacros/userspace.py +++ b/tools/lldbmacros/userspace.py @@ -140,11 +140,11 @@ def ShowThreadUserStack(cmd_args=None): ShowX86UserStack(thread) elif kern.arch == "arm": ShowARMUserStack(thread) - elif kern.arch == "arm64": + elif kern.arch.startswith("arm64"): ShowARM64UserStack(thread) return True -@lldb_command('printuserdata','X') +@lldb_command('printuserdata','XO:') def PrintUserspaceData(cmd_args=None, cmd_options={}): """ Read userspace data for given task and print based on format provided. Syntax: (lldb) printuserdata @@ -152,9 +152,12 @@ def PrintUserspaceData(cmd_args=None, cmd_options={}): : pointer to task : address to user space memory : String representation for processing the data and printing it. - e.g Q -> unsigned long long, q-> long long, I-> unsigned int, i->int + e.g Q -> unsigned long long, q -> long long, I -> unsigned int, i -> int + 10i -> 10 ints, 20s -> 20 character string, s -> null terminated string + See: https://docs.python.org/2/library/struct.html#format-characters options: -X : print all values in hex. + -O : Save data to file """ if not cmd_args or len(cmd_args) < 3: @@ -163,6 +166,10 @@ def PrintUserspaceData(cmd_args=None, cmd_options={}): uspace_addr = ArgumentStringToInt(cmd_args[1]) format_specifier_str = cmd_args[2] user_data_len = 0 + if format_specifier_str == "s": + print "0x%x: " % uspace_addr + GetUserspaceString(task, uspace_addr) + return True + try: user_data_len = struct.calcsize(format_specifier_str) except Exception, e: @@ -172,39 +179,85 @@ def PrintUserspaceData(cmd_args=None, cmd_options={}): if not user_data_string: print "Could not read any data from userspace address." return False + if "-O" in cmd_options: + fh = open(cmd_options["-O"],"w") + fh.write(user_data_string) + fh.close() + print "Written %d bytes to %s." % (user_data_len, cmd_options['-O']) + return True upacked_data = struct.unpack(format_specifier_str, user_data_string) + element_size = user_data_len / len(upacked_data) for i in range(len(upacked_data)): if "-X" in cmd_options: - print "%d: " % i + hex(upacked_data[i]) + print "0x%x: " % (uspace_addr + i*element_size) + hex(upacked_data[i]) else: - print "%d: " % i + str(upacked_data[i]) + print "0x%x: " % (uspace_addr + i*element_size) + str(upacked_data[i]) return True -@lldb_command('showtaskuserstacks') -def ShowTaskUserStacks(cmd_args=None): - """ Print out the user stack for each thread in a task, followed by the user libraries. - Syntax: (lldb) showtaskuserstacks - The format is compatible with CrashTracer. You can also use the speedtracer plugin as follows - (lldb) showtaskuserstacks -p speedtracer - Note: the address ranges are approximations. Also the list may not be completely accurate. This command expects memory read failures - and hence will skip a library if unable to read information. Please use your good judgement and not take the output as accurate +@lldb_command('showtaskuserargs') +def ShowTaskUserArgs(cmd_args=None, cmd_options={}): + """ Read the process argv, env, and apple strings from the user stack + Syntax: (lldb) showtaskuserargs + params: + : pointer to task """ - if not cmd_args: + if not cmd_args or len(cmd_args) != 1: raise ArgumentError("Insufficient arguments") task = kern.GetValueFromAddress(cmd_args[0], 'task *') + proc = Cast(task.bsd_info, 'proc *') + + format_string = "Q" if kern.ptrsize == 8 else "I" + + string_area_size = proc.p_argslen + string_area_addr = proc.user_stack - string_area_size + + string_area = GetUserDataAsString(task, string_area_addr, string_area_size) + if not string_area: + print "Could not read any data from userspace address." + return False + + i = 0 + pos = string_area_addr - kern.ptrsize + + for name in ["apple", "env", "argv"] : + while True: + if name == "argv" : + if i == proc.p_argc: + break + i += 1 + + pos -= kern.ptrsize + + user_data_string = GetUserDataAsString(task, pos, kern.ptrsize) + ptr = struct.unpack(format_string, user_data_string)[0] + + if ptr == 0: + break + + if string_area_addr <= ptr and ptr < string_area_addr+string_area_size : + string_offset = ptr - string_area_addr + string = string_area[string_offset:]; + else: + string = GetUserspaceString(task, ptr) + + print name + "[]: " + string + + return True + +def ShowTaskUserStacks(task): #print GetTaskSummary.header + " " + GetProcSummary.header pval = Cast(task.bsd_info, 'proc *') #print GetTaskSummary(task) + " " + GetProcSummary(pval) + "\n \n" crash_report_format_string = """\ -Process: {pid: <10d} +Process: {pname:s} [{pid:d}] Path: {path: <50s} Identifier: {pname: <30s} Version: ??? (???) Code Type: {parch: <20s} -Parent Process: {ppname: >20s}[{ppid:d}] +Parent Process: {ppname:s} [{ppid:d}] Date/Time: {timest:s}.000 -0800 OS Version: {osversion: <20s} @@ -220,11 +273,14 @@ Synthetic crash log generated from Kernel userstacks """ user_lib_rex = re.compile("([0-9a-fx]+)\s-\s([0-9a-fx]+)\s+(.*?)\s", re.IGNORECASE|re.MULTILINE) from datetime import datetime - ts = datetime.fromtimestamp(int(pval.p_start.tv_sec)) - date_string = ts.strftime('%Y-%m-%d %H:%M:%S') - is_64 = False - if pval.p_flag & 0x4 : - is_64 = True + if pval: + ts = datetime.fromtimestamp(int(pval.p_start.tv_sec)) + date_string = ts.strftime('%Y-%m-%d %H:%M:%S') + else: + date_string = "none" + is_64 = True + if pval and (pval.p_flag & 0x4) == 0 : + is_64 = False parch_s = "" if kern.arch == "x86_64" or kern.arch == "i386": @@ -236,15 +292,25 @@ Synthetic crash log generated from Kernel userstacks parch_s = kern.arch osversion = "iOS" osversion += " ({:s})".format(kern.globals.osversion) - print crash_report_format_string.format(pid = pval.p_pid, - pname = pval.p_comm, - path = pval.p_comm, - ppid = pval.p_ppid, - ppname = GetProcNameForPid(pval.p_ppid), + if pval: + pid = pval.p_pid + pname = pval.p_comm + path = pval.p_comm + ppid = pval.p_ppid + else: + pid = 0 + pname = "unknown" + path = "unknown" + ppid = 0 + + print crash_report_format_string.format(pid = pid, + pname = pname, + path = path, + ppid = ppid, + ppname = GetProcNameForPid(ppid), timest = date_string, parch = parch_s, osversion = osversion - ) print "Binary Images:" ShowTaskUserLibraries([hex(task)]) @@ -260,7 +326,7 @@ Synthetic crash log generated from Kernel userstacks printthread_user_stack_ptr = ShowX86UserStack if kern.arch == "arm": printthread_user_stack_ptr = ShowARMUserStack - elif kern.arch =="arm64": + elif kern.arch.startswith("arm64"): printthread_user_stack_ptr = ShowARM64UserStack counter = 0 @@ -277,6 +343,36 @@ Synthetic crash log generated from Kernel userstacks print "Enable debugging ('(lldb) xnudebug debug') to see detailed trace." return +@lldb_command('showtaskuserstacks', "P:F:") +def ShowTaskUserStacksCmdHelper(cmd_args=None, cmd_options={}): + """ Print out the user stack for each thread in a task, followed by the user libraries. + Syntax: (lldb) showtaskuserstacks + or: (lldb) showtaskuserstacks -P + or: (lldb) showtaskuserstacks -F + The format is compatible with CrashTracer. You can also use the speedtracer plugin as follows + (lldb) showtaskuserstacks -p speedtracer + + Note: the address ranges are approximations. Also the list may not be completely accurate. This command expects memory read failures + and hence will skip a library if unable to read information. Please use your good judgement and not take the output as accurate + """ + task_list = [] + if "-F" in cmd_options: + task_list = FindTasksByName(cmd_options["-F"]) + elif "-P" in cmd_options: + pidval = ArgumentStringToInt(cmd_options["-P"]) + for t in kern.tasks: + pval = Cast(t.bsd_info, 'proc *') + if pval and pval.p_pid == pidval: + task_list.append(t) + break + elif cmd_args: + t = kern.GetValueFromAddress(cmd_args[0], 'task *') + task_list.append(t) + else: + raise ArgumentError("Insufficient arguments") + + for task in task_list: + ShowTaskUserStacks(task) def GetUserDataAsString(task, addr, size): """ Get data from task's address space as a string of bytes @@ -300,7 +396,7 @@ def GetUserDataAsString(task, addr, size): if not WriteInt64ToMemoryAddress(0, kdp_pmap_addr): debuglog("Failed to reset in kdp_pmap from GetUserDataAsString.") return "" - elif kern.arch in ['arm', 'arm64', 'x86_64'] and long(size) < (2 * kern.globals.page_size): + elif (kern.arch == 'x86_64' or kern.arch.startswith('arm')) and (long(size) < (2 * kern.globals.page_size)): # Without the benefit of a KDP stub on the target, try to # find the user task's physical mapping and memcpy the data. # If it straddles a page boundary, copy in two passes @@ -367,35 +463,35 @@ def _ExtractDataFromString(strdata, offset, data_type, length=0): return 0 return struct.unpack(unpack_str, strdata[offset:(offset + length)])[0] -def GetPathForImage(task, path_address): +def GetUserspaceString(task, string_address): """ Maps 32 bytes at a time and packs as string params: task: obj - referencing task to read data from - path_address: int - address where the image path is stored + string_address: int - address where the image path is stored returns: str - string path of the file. "" if failed to read. """ done = False retval = "" - if path_address == 0: + if string_address == 0: done = True while not done: - path_str_data = GetUserDataAsString(task, path_address, 32) - if len(path_str_data) == 0: + str_data = GetUserDataAsString(task, string_address, 32) + if len(str_data) == 0: break i = 0 while i < 32: - if ord(path_str_data[i]): - retval += path_str_data[i] + if ord(str_data[i]): + retval += str_data[i] else: break i += 1 if i < 32: done = True else: - path_address += 32 + string_address += 32 return retval def GetImageInfo(task, mh_image_address, mh_path_address, approx_end_address=None): @@ -455,7 +551,7 @@ def GetImageInfo(task, mh_image_address, mh_path_address, approx_end_address=Non found_uuid_data = True 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_data) #also print image path - path_out_string = GetPathForImage(task, mh_path_address) + path_out_string = GetUserspaceString(task, mh_path_address) path_base_name = path_out_string.split("/")[-1] retval = print_format.format(mh_image_address, image_end_load_address, path_base_name, uuid_out_string, path_out_string) elif lc_cmd == 0xe: @@ -465,7 +561,7 @@ def GetImageInfo(task, mh_image_address, mh_path_address, approx_end_address=Non lc_idx += 1 if not found_uuid_data: - path_out_string = GetPathForImage(task, mh_path_address) + path_out_string = GetUserspaceString(task, mh_path_address) path_base_name = path_out_string.split("/")[-1] uuid_out_string = "" @@ -642,7 +738,7 @@ def ShowTaskUserDyldInfo(cmd_args=None): dyld_all_imfo_infos_slide = (dyld_all_image_infos_address - dyld_all_image_infos_dyldAllImageInfosAddress) dyld_all_image_infos_dyldVersion_postslide = (dyld_all_image_infos_dyldVersion + dyld_all_imfo_infos_slide) - path_out = GetPathForImage(task, dyld_all_image_infos_dyldVersion_postslide) + path_out = GetUserspaceString(task, dyld_all_image_infos_dyldVersion_postslide) out_str += "[dyld-{:s}]\n".format(path_out) out_str += "version \t\t\t\t: {:d}\n".format(dyld_all_image_infos_version) out_str += "infoArrayCount \t\t\t\t: {:d}\n".format(dyld_all_image_infos_infoArrayCount) @@ -671,7 +767,7 @@ def ShowTaskUserDyldInfo(cmd_args=None): out_str += "errorMessage \t\t\t\t: {:#x}\n".format(dyld_all_image_infos_errorMessage) if dyld_all_image_infos_errorMessage != 0: - out_str += GetPathForImage(task, dyld_all_image_infos_errorMessage) + out_str += GetUserspaceString(task, dyld_all_image_infos_errorMessage) out_str += "terminationFlags \t\t\t: {:#x}\n".format(dyld_all_image_infos_terminationFlags) out_str += "coreSymbolicationShmPage \t\t: {:#x}\n".format(dyld_all_image_infos_coreSymbolicationShmPage) @@ -713,17 +809,17 @@ def ShowTaskUserDyldInfo(cmd_args=None): out_str += "errorClientOfDylibPath \t\t\t: {:#x}\n".format(dyld_all_image_infos_errorClientOfDylibPath) if dyld_all_image_infos_errorClientOfDylibPath != 0: out_str += "\t\t\t\t" - out_str += GetPathForImage(task, dyld_all_image_infos_errorClientOfDylibPath) + out_str += GetUserspaceString(task, dyld_all_image_infos_errorClientOfDylibPath) out_str += "\n" out_str += "errorTargetDylibPath \t\t\t: {:#x}\n".format(dyld_all_image_infos_errorTargetDylibPath) if dyld_all_image_infos_errorTargetDylibPath != 0: out_str += "\t\t\t\t" - out_str += GetPathForImage(task, dyld_all_image_infos_errorTargetDylibPath) + out_str += GetUserspaceString(task, dyld_all_image_infos_errorTargetDylibPath) out_str += "\n" out_str += "errorSymbol \t\t\t\t: {:#x}\n".format(dyld_all_image_infos_errorSymbol) if dyld_all_image_infos_errorSymbol != 0: out_str += "\t\t\t\t" - out_str += GetPathForImage(task, dyld_all_image_infos_errorSymbol) + out_str += GetUserspaceString(task, dyld_all_image_infos_errorSymbol) out_str += "\n" if dyld_all_image_infos_version >= 12: @@ -769,5 +865,54 @@ def ShowOSMalloc(cmd_args=None): # EndMacro: showosmalloc +@lldb_command('savekcdata', 'T:O:') +def SaveKCDataToFile(cmd_args=None, cmd_options={}): + """ Save the data referred by the kcdata_descriptor structure. + options: + -T: pointer to task if memory referenced is in userstask. + -O: path to file to save data. default: /tmp/kcdata..bin + Usage: (lldb) savekcdata -T -O /path/to/outputfile.bin + """ + if not cmd_args: + raise ArgumentError('Please provide the kcdata descriptor.') + + kcdata = kern.GetValueFromAddress(cmd_args[0], 'kcdata_descriptor_t') + + outputfile = '/tmp/kcdata.{:s}.bin'.format(str(time.time())) + task = None + if '-O' in cmd_options: + outputfile = cmd_options['-O'] + if '-T' in cmd_options: + task = kern.GetValueFromAddress(cmd_options['-T'], 'task_t') + + memory_begin_address = unsigned(kcdata.kcd_addr_begin) + memory_size = 16 + unsigned(kcdata.kcd_addr_end) - memory_begin_address + flags_copyout = unsigned(kcdata.kcd_flags) + if flags_copyout: + if not task: + raise ArgumentError('Invalid task pointer provided.') + memory_data = GetUserDataAsString(task, memory_begin_address, memory_size) + else: + data_ptr = kern.GetValueFromAddress(memory_begin_address, 'uint8_t *') + if data_ptr == 0: + print "Kcdata descriptor is NULL" + return False + memory_data = [] + for i in range(memory_size): + memory_data.append(chr(data_ptr[i])) + if i % 50000 == 0: + print "%d of %d \r" % (i, memory_size), + memory_data = ''.join(memory_data) + + if len(memory_data) != memory_size: + print "Failed to read {:d} bytes from address {: <#020x}".format(memory_size, memory_begin_address) + return False + + fh = open(outputfile, 'w') + fh.write(memory_data) + fh.close() + print "Saved {:d} bytes to file {:s}".format(memory_size, outputfile) + return True +