[Lldb-commits] [lldb] 7c54ffd - [lldb/crashlog] Add CrashLogScriptedProcess & remove interactive mode

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Wed Feb 16 11:45:16 PST 2022


Author: Med Ismail Bennani
Date: 2022-02-16T11:44:07-08:00
New Revision: 7c54ffdc6c2e55565344a8d6200f63ee016891a9

URL: https://github.com/llvm/llvm-project/commit/7c54ffdc6c2e55565344a8d6200f63ee016891a9
DIFF: https://github.com/llvm/llvm-project/commit/7c54ffdc6c2e55565344a8d6200f63ee016891a9.diff

LOG: [lldb/crashlog] Add CrashLogScriptedProcess & remove interactive mode

This patch introduces a new type of ScriptedProcess: CrashLogScriptedProcess.
It takes advantage of lldb's crashlog parsers and Scripted Processes to
reconstruct a static debugging session with symbolicated stackframes, instead
of just dumping out everything in the user's terminal.

The crashlog command also has an interactive mode that only provide a
very limited experience. This is why this patch removes all the logic
for this interactive mode and creates CrashLogScriptedProcess instead.

This will fetch and load all the libraries that were used by the crashed
thread and re-create all the frames artificially.

rdar://88721117

Differential Revision: https://reviews.llvm.org/D119501

Signed-off-by: Med Ismail Bennani <medismail.bennani at gmail.com>

Added: 
    lldb/examples/python/scripted_process/crashlog_scripted_process.py
    lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/scripted_crashlog.ips
    lldb/test/Shell/ScriptInterpreter/Python/Crashlog/scripted_crashlog_json.test

Modified: 
    lldb/bindings/python/CMakeLists.txt
    lldb/examples/python/crashlog.py
    lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt
index 82a52da89a7e3..afad367baa9fa 100644
--- a/lldb/bindings/python/CMakeLists.txt
+++ b/lldb/bindings/python/CMakeLists.txt
@@ -114,6 +114,7 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
       ${swig_target}
       ${lldb_python_target_dir} "macosx"
       FILES "${LLDB_SOURCE_DIR}/examples/python/crashlog.py"
+            "${LLDB_SOURCE_DIR}/examples/python/scripted_process/crashlog_scripted_process.py"
             "${LLDB_SOURCE_DIR}/examples/darwin/heap_find/heap.py")
 
     create_python_package(

diff  --git a/lldb/examples/python/crashlog.py b/lldb/examples/python/crashlog.py
index bc57a275ce744..2f5cafc49503c 100755
--- a/lldb/examples/python/crashlog.py
+++ b/lldb/examples/python/crashlog.py
@@ -63,7 +63,6 @@
 
 from lldb.utils import symbolication
 
-
 def read_plist(s):
     if sys.version_info.major == 3:
         return plistlib.loads(s)
@@ -780,138 +779,6 @@ def usage():
     sys.exit(0)
 
 
-class Interactive(cmd.Cmd):
-    '''Interactive prompt for analyzing one or more Darwin crash logs, type "help" to see a list of supported commands.'''
-    image_option_parser = None
-
-    def __init__(self, crash_logs):
-        cmd.Cmd.__init__(self)
-        self.use_rawinput = False
-        self.intro = 'Interactive crashlogs prompt, type "help" to see a list of supported commands.'
-        self.crash_logs = crash_logs
-        self.prompt = '% '
-
-    def default(self, line):
-        '''Catch all for unknown command, which will exit the interpreter.'''
-        print("uknown command: %s" % line)
-        return True
-
-    def do_q(self, line):
-        '''Quit command'''
-        return True
-
-    def do_quit(self, line):
-        '''Quit command'''
-        return True
-
-    def do_symbolicate(self, line):
-        description = '''Symbolicate one or more darwin crash log files by index to provide source file and line information,
-        inlined stack frames back to the concrete functions, and disassemble the location of the crash
-        for the first frame of the crashed thread.'''
-        option_parser = CreateSymbolicateCrashLogOptions(
-            'symbolicate', description, False)
-        command_args = shlex.split(line)
-        try:
-            (options, args) = option_parser.parse_args(command_args)
-        except:
-            return
-
-        if args:
-            # We have arguments, they must valid be crash log file indexes
-            for idx_str in args:
-                idx = int(idx_str)
-                if idx < len(self.crash_logs):
-                    SymbolicateCrashLog(self.crash_logs[idx], options)
-                else:
-                    print('error: crash log index %u is out of range' % (idx))
-        else:
-            # No arguments, symbolicate all crash logs using the options
-            # provided
-            for idx in range(len(self.crash_logs)):
-                SymbolicateCrashLog(self.crash_logs[idx], options)
-
-    def do_list(self, line=None):
-        '''Dump a list of all crash logs that are currently loaded.
-
-        USAGE: list'''
-        print('%u crash logs are loaded:' % len(self.crash_logs))
-        for (crash_log_idx, crash_log) in enumerate(self.crash_logs):
-            print('[%u] = %s' % (crash_log_idx, crash_log.path))
-
-    def do_image(self, line):
-        '''Dump information about one or more binary images in the crash log given an image basename, or all images if no arguments are provided.'''
-        usage = "usage: %prog [options] <PATH> [PATH ...]"
-        description = '''Dump information about one or more images in all crash logs. The <PATH> can be a full path, image basename, or partial path. Searches are done in this order.'''
-        command_args = shlex.split(line)
-        if not self.image_option_parser:
-            self.image_option_parser = optparse.OptionParser(
-                description=description, prog='image', usage=usage)
-            self.image_option_parser.add_option(
-                '-a',
-                '--all',
-                action='store_true',
-                help='show all images',
-                default=False)
-        try:
-            (options, args) = self.image_option_parser.parse_args(command_args)
-        except:
-            return
-
-        if args:
-            for image_path in args:
-                fullpath_search = image_path[0] == '/'
-                for (crash_log_idx, crash_log) in enumerate(self.crash_logs):
-                    matches_found = 0
-                    for (image_idx, image) in enumerate(crash_log.images):
-                        if fullpath_search:
-                            if image.get_resolved_path() == image_path:
-                                matches_found += 1
-                                print('[%u] ' % (crash_log_idx), image)
-                        else:
-                            image_basename = image.get_resolved_path_basename()
-                            if image_basename == image_path:
-                                matches_found += 1
-                                print('[%u] ' % (crash_log_idx), image)
-                    if matches_found == 0:
-                        for (image_idx, image) in enumerate(crash_log.images):
-                            resolved_image_path = image.get_resolved_path()
-                            if resolved_image_path and string.find(
-                                    image.get_resolved_path(), image_path) >= 0:
-                                print('[%u] ' % (crash_log_idx), image)
-        else:
-            for crash_log in self.crash_logs:
-                for (image_idx, image) in enumerate(crash_log.images):
-                    print('[%u] %s' % (image_idx, image))
-        return False
-
-
-def interactive_crashlogs(debugger, options, args):
-    crash_log_files = list()
-    for arg in args:
-        for resolved_path in glob.glob(arg):
-            crash_log_files.append(resolved_path)
-
-    crash_logs = list()
-    for crash_log_file in crash_log_files:
-        try:
-            crash_log = CrashLogParser().parse(debugger, crash_log_file, options.verbose)
-        except Exception as e:
-            print(e)
-            continue
-        if options.debug:
-            crash_log.dump()
-        if not crash_log.images:
-            print('error: no images in crash log "%s"' % (crash_log))
-            continue
-        else:
-            crash_logs.append(crash_log)
-
-    interpreter = Interactive(crash_logs)
-    # List all crash logs that were imported
-    interpreter.do_list()
-    interpreter.cmdloop()
-
-
 def save_crashlog(debugger, command, exe_ctx, result, dict):
     usage = "usage: %prog [options] <output-path>"
     description = '''Export the state of current target into a crashlog file'''
@@ -1106,6 +973,43 @@ def SymbolicateCrashLog(crash_log, options):
         for error in crash_log.errors:
             print(error)
 
+def load_crashlog_in_scripted_process(debugger, crash_log_file):
+    result = lldb.SBCommandReturnObject()
+
+    crashlog_path = os.path.expanduser(crash_log_file)
+    if not os.path.exists(crashlog_path):
+        result.PutCString("error: crashlog file %s does not exist" % crashlog_path)
+
+    try:
+        crashlog = CrashLogParser().parse(debugger, crashlog_path, False)
+    except Exception as e:
+        result.PutCString("error: python exception: %s" % e)
+        return
+
+    target = crashlog.create_target()
+    if not target:
+        result.PutCString("error: couldn't create target")
+        return
+
+    ci = debugger.GetCommandInterpreter()
+    if not ci:
+        result.PutCString("error: couldn't get command interpreter")
+        return
+
+    res = lldb.SBCommandReturnObject()
+    ci.HandleCommand('script from lldb.macosx import crashlog_scripted_process', res)
+    if not res.Succeeded():
+        result.PutCString("error: couldn't import crashlog scripted process module")
+        return
+
+    structured_data = lldb.SBStructuredData()
+    structured_data.SetFromJSON(json.dumps({ "crashlog_path" : crashlog_path }))
+    launch_info = lldb.SBLaunchInfo(None)
+    launch_info.SetProcessPluginName("ScriptedProcess")
+    launch_info.SetScriptedProcessClassName("crashlog_scripted_process.CrashLogScriptedProcess")
+    launch_info.SetScriptedProcessDictionary(structured_data)
+    error = lldb.SBError()
+    process = target.Launch(launch_info, error)
 
 def CreateSymbolicateCrashLogOptions(
         command_name,
@@ -1209,8 +1113,14 @@ def CreateSymbolicateCrashLogOptions(
             '-i',
             '--interactive',
             action='store_true',
-            help='parse all crash logs and enter interactive mode',
+            help='parse a crash log and load it in a ScriptedProcess',
             default=False)
+        option_parser.add_option(
+            '-b',
+            '--batch',
+            action='store_true',
+            help='dump symbolicated stackframes without creating a debug session',
+            default=True)
     return option_parser
 
 
@@ -1242,11 +1152,23 @@ def SymbolicateCrashLogs(debugger, command_args):
         time.sleep(options.debug_delay)
     error = lldb.SBError()
 
-    if args:
+    def should_run_in_interactive_mode(options, ci):
         if options.interactive:
-            interactive_crashlogs(debugger, options, args)
+            return True
+        elif options.batch:
+            return False
+        # elif ci and ci.IsInteractive():
+        #     return True
         else:
-            for crash_log_file in args:
+            return False
+
+    ci = debugger.GetCommandInterpreter()
+
+    if args:
+        for crash_log_file in args:
+            if should_run_in_interactive_mode(options, ci):
+                load_crashlog_in_scripted_process(debugger, crash_log_file)
+            else:
                 crash_log = CrashLogParser().parse(debugger, crash_log_file, options.verbose)
                 SymbolicateCrashLog(crash_log, options)
 

diff  --git a/lldb/examples/python/scripted_process/crashlog_scripted_process.py b/lldb/examples/python/scripted_process/crashlog_scripted_process.py
new file mode 100644
index 0000000000000..fca1daa3adce0
--- /dev/null
+++ b/lldb/examples/python/scripted_process/crashlog_scripted_process.py
@@ -0,0 +1,148 @@
+import os,json,struct,signal
+
+from typing import Any, Dict
+
+import lldb
+from lldb.plugins.scripted_process import ScriptedProcess
+from lldb.plugins.scripted_process import ScriptedThread
+
+from lldb.macosx.crashlog import CrashLog,CrashLogParser
+
+class CrashLogScriptedProcess(ScriptedProcess):
+    def parse_crashlog(self):
+        try:
+            crash_log = CrashLogParser().parse(self.dbg, self.crashlog_path, False)
+        except Exception as e:
+            return
+
+        self.pid = crash_log.process_id
+        self.crashed_thread_idx = crash_log.crashed_thread_idx
+        self.loaded_images = []
+
+        for thread in crash_log.threads:
+            if thread.did_crash():
+                for ident in thread.idents:
+                    images = crash_log.find_images_with_identifier(ident)
+                    if images:
+                        for image in images:
+                            #TODO: Add to self.loaded_images and load images in lldb
+                            err = image.add_module(self.target)
+                            if err:
+                                print(err)
+                            else:
+                                self.loaded_images.append(image)
+            self.threads[thread.index] = CrashLogScriptedThread(self, None, thread)
+
+    def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
+        super().__init__(target, args)
+
+        if not self.target or not self.target.IsValid():
+            return
+
+        self.crashlog_path = None
+
+        crashlog_path = args.GetValueForKey("crashlog_path")
+        if crashlog_path and crashlog_path.IsValid():
+            if crashlog_path.GetType() == lldb.eStructuredDataTypeString:
+                self.crashlog_path = crashlog_path.GetStringValue(4096)
+
+        if not self.crashlog_path:
+            return
+
+        self.pid = super().get_process_id()
+        self.crashed_thread_idx = 0
+        self.parse_crashlog()
+
+    def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
+        return None
+
+    def get_thread_with_id(self, tid: int):
+        return {}
+
+    def get_registers_for_thread(self, tid: int):
+        return {}
+
+    def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData:
+        # NOTE: CrashLogs don't contain any memory.
+        return lldb.SBData()
+
+    def get_loaded_images(self):
+        # TODO: Iterate over corefile_target modules and build a data structure
+        # from it.
+        return self.loaded_images
+
+    def get_process_id(self) -> int:
+        return self.pid
+
+    def should_stop(self) -> bool:
+        return True
+
+    def is_alive(self) -> bool:
+        return True
+
+    def get_scripted_thread_plugin(self):
+        return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__
+
+class CrashLogScriptedThread(ScriptedThread):
+    def create_register_ctx(self):
+        if not self.has_crashed:
+            return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0)
+
+        if not self.backing_thread or not len(self.backing_thread.registers):
+            return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0)
+
+        for reg in self.register_info['registers']:
+            reg_name = reg['name']
+            if reg_name in self.backing_thread.registers:
+                self.register_ctx[reg_name] = self.backing_thread.registers[reg_name]
+            else:
+                self.register_ctx[reg_name] = 0
+
+        return self.register_ctx
+
+    def create_stackframes(self):
+        if not self.has_crashed:
+            return None
+
+        if not self.backing_thread or not len(self.backing_thread.frames):
+            return None
+
+        for frame in self.backing_thread.frames:
+            sym_addr = lldb.SBAddress()
+            sym_addr.SetLoadAddress(frame.pc, self.target)
+            if not sym_addr.IsValid():
+                continue
+            self.frames.append({"idx": frame.index, "pc": frame.pc})
+
+        return self.frames
+
+    def __init__(self, process, args, crashlog_thread):
+        super().__init__(process, args)
+
+        self.backing_thread = crashlog_thread
+        self.idx = self.backing_thread.index
+        self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx)
+        self.create_stackframes()
+
+    def get_thread_id(self) -> int:
+        return self.idx
+
+    def get_name(self) -> str:
+        return CrashLogScriptedThread.__name__ + ".thread-" + str(self.idx)
+
+    def get_state(self):
+        if not self.has_crashed:
+            return lldb.eStateStopped
+        return lldb.eStateCrashed
+
+    def get_stop_reason(self) -> Dict[str, Any]:
+        if not self.has_crashed:
+            return { "type": lldb.eStopReasonNone, "data": {  }}
+        # TODO: Investigate what stop reason should be reported when crashed
+        return { "type": lldb.eStopReasonException, "data": { "desc": "EXC_BAD_ACCESS" }}
+
+    def get_register_context(self) -> str:
+        if not self.register_ctx:
+            self.register_ctx = self.create_register_ctx()
+
+        return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values())

diff  --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index 1abf0f1409c57..96ae305f875ad 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -303,6 +303,9 @@ bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list,
 
   StructuredData::DictionarySP thread_info_sp = GetInterface().GetThreadsInfo();
 
+  // FIXME: Need to sort the dictionary otherwise the thread ids won't match the
+  // thread indices.
+
   if (!thread_info_sp)
     return ScriptedInterface::ErrorWithMessage<bool>(
         LLVM_PRETTY_FUNCTION,

diff  --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/scripted_crashlog.ips b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/scripted_crashlog.ips
new file mode 100644
index 0000000000000..02dbbb51f4a66
--- /dev/null
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/scripted_crashlog.ips
@@ -0,0 +1,75 @@
+{"app_name":"scripted_crashlog_json.test.tmp.out","timestamp":"2022-02-14 16:30:31.00 -0800","app_version":"","slice_uuid":"b928ee77-9429-334f-ac88-41440bb3d4c7","build_version":"","platform":1,"share_with_app_devs":0,"is_first_party":1,"bug_type":"309","os_version":"macOS 12.3","incident_id":"E57CADE7-DC44-45CE-8D16-18EBC4406B97","name":"scripted_crashlog_json.test.tmp.out"}
+{
+  "uptime" : 260000,
+  "procLaunch" : "2022-02-14 16:30:31.8048 -0800",
+  "procRole" : "Unspecified",
+  "version" : 2,
+  "userID" : 501,
+  "deployVersion" : 210,
+  "modelCode" : "MacBookPro18,2",
+  "procStartAbsTime" : 6478056069413,
+  "coalitionID" : 22196,
+  "osVersion" : {
+    "train" : "macOS 12.3",
+    "build" : "",
+    "releaseType" : ""
+  },
+  "captureTime" : "2022-02-14 16:30:31.8096 -0800",
+  "incident" : "E57CADE7-DC44-45CE-8D16-18EBC4406B97",
+  "bug_type" : "309",
+  "pid" : 92190,
+  "procExitAbsTime" : 6478056175721,
+  "translated" : false,
+  "cpuType" : "ARM-64",
+  "procName" : "scripted_crashlog_json.test.tmp.out",
+  "procPath" : "\/Users\/USER\/*\/scripted_crashlog_json.test.tmp.out",
+  "parentProc" : "zsh",
+  "parentPid" : 82132,
+  "coalitionName" : "com.apple.Terminal",
+  "crashReporterKey" : "CDC11418-EDBF-2A49-0D83-8B441A5004B0",
+  "responsiblePid" : 76395,
+  "responsibleProc" : "Terminal",
+  "wakeTime" : 14889,
+  "sleepWakeUUID" : "BCA947AE-2F0A-44C7-8445-FEDFFA236CD0",
+  "sip" : "enabled",
+  "vmRegionInfo" : "0 is not in any region.  Bytes before following region: 4372692992\n      REGION TYPE                    START - END         [ VSIZE] PRT\/MAX SHRMOD  REGION DETAIL\n      UNUSED SPACE AT START\n--->  \n      __TEXT                      104a20000-104a24000    [   16K] r-x\/r-x SM=COW  ....test.tmp.out",
+  "isCorpse" : 1,
+  "exception" : {"codes":"0x0000000000000001, 0x0000000000000000","rawCodes":[1,0],"type":"EXC_BAD_ACCESS","signal":"SIGSEGV","subtype":"KERN_INVALID_ADDRESS at 0x0000000000000000"},
+  "termination" : {"flags":0,"code":11,"namespace":"SIGNAL","indicator":"Segmentation fault: 11","byProc":"exc handler","byPid":92190},
+  "vmregioninfo" : "0 is not in any region.  Bytes before following region: 4372692992\n      REGION TYPE                    START - END         [ VSIZE] PRT\/MAX SHRMOD  REGION DETAIL\n      UNUSED SPACE AT START\n--->  \n      __TEXT                      104a20000-104a24000    [   16K] r-x\/r-x SM=COW  ....test.tmp.out",
+  "extMods" : {"caller":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"system":{"thread_create":0,"thread_set_state":156,"task_for_pid":28},"targeted":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"warnings":0},
+  "faultingThread" : 0,
+  "threads" : [{"triggered":true,"id":4567339,"threadState":{"x":[{"value":1},{"value":6094187136},{"value":6094187152},{"value":6094187720},{"value":0},{"value":0},{"value":0},{"value":0},{"value":1},{"value":0},{"value":0},{"value":2},{"value":2},{"value":0},{"value":80},{"value":0},{"value":13118353544},{"value":7701436843874442528},{"value":0},{"value":4373676128},{"sourceLine":8,"value":4372709256,"sourceFile":"test.c","symbol":"main","symbolLocation":0},{"value":4373332080,"symbolLocation":0,"symbol":"dyld4::sConfigBuffer"},{"value":0},{"value":0},{"value":0},{"value":0},{"value":0},{"value":0},{"value":0}],"flavor":"ARM_THREAD_STATE64","lr":{"value":4372709248},"cpsr":{"value":1610616832},"fp":{"value":6094186736},"sp":{"value":6094186720},"esr":{"value":2449473606,"description":"(Data Abort) byte write Translation fault"},"pc":{"value":4372709224,"matchesCrashFrame":1},"far":{"value":0}},"queue":"com.apple.main-thread","frames":[{"imageOffset":16232,"sourceLine":3,"sourceFile":"test.c","symbol":"foo","imageIndex":0,"symbolLocation":16},{"imageOffset":16256,"sourceLine":6,"sourceFile":"test.c","symbol":"bar","imageIndex":0,"symbolLocation":12},{"imageOffset":16288,"sourceLine":8,"sourceFile":"test.c","symbol":"main","imageIndex":0,"symbolLocation":24},{"imageOffset":20620,"symbol":"start","symbolLocation":520,"imageIndex":1}]}],
+  "usedImages" : [
+  {
+    "source" : "P",
+    "arch" : "arm64",
+    "base" : 4372692992,
+    "size" : 16384,
+    "uuid" : "b928ee77-9429-334f-ac88-41440bb3d4c7",
+    "path" : "\/Users\/USER\/*\/scripted_crashlog_json.test.tmp.out",
+    "name" : "scripted_crashlog_json.test.tmp.out"
+  },
+  {
+    "source" : "P",
+    "arch" : "arm64e",
+    "base" : 4372938752,
+    "size" : 393216,
+    "uuid" : "41293cda-474b-3700-924e-6ba0f7698eac",
+    "path" : "\/usr\/lib\/dyld",
+    "name" : "dyld"
+  }
+],
+  "sharedCache" : {
+  "base" : 6924156928,
+  "size" : 3151052800,
+  "uuid" : "2ff78c31-e522-3e4a-a414-568e926f7274"
+},
+  "vmSummary" : "ReadOnly portion of Libraries: Total=589.5M resident=0K(0%) swapped_out_or_unallocated=589.5M(100%)\nWritable regions: Total=529.1M written=0K(0%) resident=0K(0%) swapped_out=0K(0%) unallocated=529.1M(100%)\n\n                                VIRTUAL   REGION \nREGION TYPE                        SIZE    COUNT (non-coalesced) \n===========                     =======  ======= \nKernel Alloc Once                   32K        1 \nMALLOC                           137.2M       11 \nMALLOC guard page                   96K        5 \nMALLOC_NANO (reserved)           384.0M        1         reserved VM address space (unallocated)\nSTACK GUARD                       56.0M        1 \nStack                             8176K        1 \n__AUTH                              46K       11 \n__AUTH_CONST                        67K       38 \n__DATA                             173K       36 \n__DATA_CONST                       242K       39 \n__DATA_DIRTY                        73K       21 \n__LINKEDIT                       584.9M        3 \n__OBJC_CONST                        10K        5 \n__OBJC_RO                         82.9M        1 \n__OBJC_RW                         3168K        1 \n__TEXT                            4696K       43 \ndyld private memory               1024K        1 \nshared memory                       48K        2 \n===========                     =======  ======= \nTOTAL                              1.2G      221 \nTOTAL, minus reserved VM space   878.5M      221 \n",
+  "legacyInfo" : {
+  "threadTriggered" : {
+    "queue" : "com.apple.main-thread"
+  }
+},
+  "trialInfo" : {  }
+}

diff  --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/scripted_crashlog_json.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/scripted_crashlog_json.test
new file mode 100644
index 0000000000000..a94a667eae545
--- /dev/null
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/scripted_crashlog_json.test
@@ -0,0 +1,10 @@
+# RUN: %clang_host -g %S/Inputs/test.c -o %t.out
+
+# RUN: cp %S/Inputs/scripted_crashlog.ips %t.crash
+# RUN: %python %S/patch-crashlog.py --binary %t.out --crashlog %t.crash --offsets '{"main":20, "bar":9, "foo":16}' --json
+# RUN: %lldb %t.out -o 'command script import lldb.macosx.crashlog' -o 'crashlog -i %t.crash' -o 'process status' 2>&1 | FileCheck %s
+
+# CHECK: "crashlog" {{.*}} commands have been installed, use the "--help" options on these commands
+# CHECK: Process 92190 stopped
+# CHECK: * thread #1, name = 'CrashLogScriptedThread.thread-0', stop reason = EXC_BAD_ACCESS
+# CHECK: frame #0: 0x0000000104a23f68 scripted_crashlog_json.test.tmp.out`foo at test.c:3:6 [artificial]


        


More information about the lldb-commits mailing list