]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/userspace.py
xnu-4903.241.1.tar.gz
[apple/xnu.git] / tools / lldbmacros / userspace.py
old mode 100644 (file)
new mode 100755 (executable)
index 21eeb32..3413fff
@@ -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 <task_t> <uspace_address> <format_specifier>
@@ -152,9 +152,12 @@ def PrintUserspaceData(cmd_args=None, cmd_options={}):
             <task_t> : pointer to task
             <uspace_address> : address to user space memory
             <format_specifier> : 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 <file path>: 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 <task_t>
-        The format is compatible with CrashTracer. You can also use the speedtracer plugin as follows
-        (lldb) showtaskuserstacks <task_t> -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 <task_t>
+        params:
+            <task_t> : 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 *')
+    ptrsize = 8 if int(task.t_flags) & 0x1 else 4
+
+    format_string = "Q" if 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 - ptrsize
+
+    for name in ["apple", "env", "argv"] :
+        while True:
+            if name == "argv" :
+                if i == proc.p_argc:
+                    break
+                i += 1
+
+            pos -= ptrsize
+
+            user_data_string = GetUserDataAsString(task, pos, 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 <task_t>
+            or: (lldb) showtaskuserstacks -P <pid>
+            or: (lldb) showtaskuserstacks -F <task_name>
+        The format is compatible with CrashTracer. You can also use the speedtracer plugin as follows
+        (lldb) showtaskuserstacks <task_t> -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: <task_t> pointer to task if memory referenced is in userstask.
+            -O: <output file path> path to file to save data. default: /tmp/kcdata.<timestamp>.bin
+        Usage: (lldb) savekcdata <kcdata_descriptor_t> -T <task_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
+