diff --git a/lldb/examples/python/crashlog.py b/lldb/examples/python/crashlog.py index 923d7ec2e9373..08f39d516d32f 100755 --- a/lldb/examples/python/crashlog.py +++ b/lldb/examples/python/crashlog.py @@ -252,7 +252,7 @@ def add_ident(self, ident): self.idents.append(ident) def did_crash(self): - return self.reason is not None + return self.crashed def __str__(self): if self.app_specific_backtrace: @@ -414,9 +414,20 @@ def locate_module_and_debug_symbols(self): with print_lock: print('falling back to binary inside "%s"' % dsym) self.symfile = dsym - for filename in os.listdir(dwarf_dir): - self.path = os.path.join(dwarf_dir, filename) - if self.find_matching_slice(): + # Look for the executable next to the dSYM bundle. + parent_dir = os.path.dirname(dsym) + executables = [] + for root, _, files in os.walk(parent_dir): + for file in files: + abs_path = os.path.join(root, file) + if os.path.isfile(abs_path) and os.access( + abs_path, os.X_OK + ): + executables.append(abs_path) + for binary in executables: + basename = os.path.basename(binary) + if basename == self.identifier: + self.path = binary found_matching_slice = True break if found_matching_slice: @@ -522,6 +533,49 @@ def create_target(self): def get_target(self): return self.target + def load_images(self, options, loaded_images=None): + if not loaded_images: + loaded_images = [] + images_to_load = self.images + if options.load_all_images: + for image in self.images: + image.resolve = True + elif options.crashed_only: + for thread in self.threads: + if thread.did_crash(): + images_to_load = [] + for ident in thread.idents: + for image in self.find_images_with_identifier(ident): + image.resolve = True + images_to_load.append(image) + + futures = [] + with tempfile.TemporaryDirectory() as obj_dir: + with concurrent.futures.ThreadPoolExecutor() as executor: + + def add_module(image, target, obj_dir): + return image, image.add_module(target, obj_dir) + + for image in images_to_load: + if image not in loaded_images: + if image.uuid == uuid.UUID(int=0): + continue + futures.append( + executor.submit( + add_module, + image=image, + target=self.target, + obj_dir=obj_dir, + ) + ) + + for future in concurrent.futures.as_completed(futures): + image, err = future.result() + if err: + print(err) + else: + loaded_images.append(image) + class CrashLogFormatException(Exception): pass @@ -1404,36 +1458,7 @@ def SymbolicateCrashLog(crash_log, options): if not target: return - if options.load_all_images: - for image in crash_log.images: - image.resolve = True - elif options.crashed_only: - for thread in crash_log.threads: - if thread.did_crash(): - for ident in thread.idents: - for image in crash_log.find_images_with_identifier(ident): - image.resolve = True - - futures = [] - loaded_images = [] - with tempfile.TemporaryDirectory() as obj_dir: - with concurrent.futures.ThreadPoolExecutor() as executor: - - def add_module(image, target, obj_dir): - return image, image.add_module(target, obj_dir) - - for image in crash_log.images: - futures.append( - executor.submit( - add_module, image=image, target=target, obj_dir=obj_dir - ) - ) - for future in concurrent.futures.as_completed(futures): - image, err = future.result() - if err: - print(err) - else: - loaded_images.append(image) + crash_log.load_images(options) if crash_log.backtraces: for thread in crash_log.backtraces: @@ -1465,6 +1490,7 @@ def load_crashlog_in_scripted_process(debugger, crashlog_path, options, result): raise InteractiveCrashLogException( "couldn't create target provided by the user (%s)" % options.target_path ) + crashlog.target = target # 2. If the user didn't provide a target, try to create a target using the symbolicator if not target or not target.IsValid(): @@ -1494,7 +1520,11 @@ def load_crashlog_in_scripted_process(debugger, crashlog_path, options, result): structured_data = lldb.SBStructuredData() structured_data.SetFromJSON( json.dumps( - {"file_path": crashlog_path, "load_all_images": options.load_all_images} + { + "file_path": crashlog_path, + "load_all_images": options.load_all_images, + "crashed_only": options.crashed_only, + } ) ) launch_info = lldb.SBLaunchInfo(None) @@ -1627,7 +1657,8 @@ def CreateSymbolicateCrashLogOptions( "--no-crashed-only", action="store_false", dest="crashed_only", - help="do not symbolicate the crashed thread", + help="in batch mode, symbolicate all threads, not only the crashed one", + default=False, ) arg_parser.add_argument( "--disasm-depth", diff --git a/lldb/examples/python/crashlog_scripted_process.py b/lldb/examples/python/crashlog_scripted_process.py index 43f767de138cd..26c5c37b7371d 100644 --- a/lldb/examples/python/crashlog_scripted_process.py +++ b/lldb/examples/python/crashlog_scripted_process.py @@ -29,27 +29,7 @@ def set_crashlog(self, crashlog): if hasattr(self.crashlog, "asb"): self.extended_thread_info = self.crashlog.asb - if self.load_all_images: - for image in self.crashlog.images: - image.resolve = True - else: - for thread in self.crashlog.threads: - if thread.did_crash(): - for ident in thread.idents: - for image in self.crashlog.find_images_with_identifier(ident): - image.resolve = True - - with tempfile.TemporaryDirectory() as obj_dir: - for image in self.crashlog.images: - if image not in self.loaded_images: - if image.uuid == uuid.UUID(int=0): - continue - err = image.add_module(self.target, obj_dir) - if err: - # Append to SBCommandReturnObject - print(err) - else: - self.loaded_images.append(image) + crashlog.load_images(self.options, self.loaded_images) for thread in self.crashlog.threads: if ( @@ -70,6 +50,10 @@ def set_crashlog(self, crashlog): self.app_specific_thread, self.addr_mask, self.target ) + class CrashLogOptions: + load_all_images = False + crashed_only = True + def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData): super().__init__(exe_ctx, args) @@ -88,13 +72,17 @@ def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData # Return error return + self.options = self.CrashLogOptions() + load_all_images = args.GetValueForKey("load_all_images") if load_all_images and load_all_images.IsValid(): if load_all_images.GetType() == lldb.eStructuredDataTypeBoolean: - self.load_all_images = load_all_images.GetBooleanValue() + self.options.load_all_images = load_all_images.GetBooleanValue() - if not self.load_all_images: - self.load_all_images = False + crashed_only = args.GetValueForKey("crashed_only") + if crashed_only and crashed_only.IsValid(): + if crashed_only.GetType() == lldb.eStructuredDataTypeBoolean: + self.options.crashed_only = crashed_only.GetBooleanValue() self.pid = super().get_process_id() self.crashed_thread_idx = 0 @@ -159,14 +147,14 @@ def resolve_stackframes(thread, addr_mask, target): return frames def create_stackframes(self): - if not (self.scripted_process.load_all_images or self.has_crashed): + if not (self.originating_process.options.load_all_images or self.has_crashed): return None if not self.backing_thread or not len(self.backing_thread.frames): return None self.frames = CrashLogScriptedThread.resolve_stackframes( - self.backing_thread, self.scripted_process.addr_mask, self.target + self.backing_thread, self.originating_process.addr_mask, self.target ) return self.frames @@ -182,7 +170,7 @@ def __init__(self, process, args, crashlog_thread): else: self.name = self.backing_thread.name self.queue = self.backing_thread.queue - self.has_crashed = self.scripted_process.crashed_thread_idx == self.idx + self.has_crashed = self.originating_process.crashed_thread_idx == self.idx self.create_stackframes() def get_state(self): @@ -195,8 +183,8 @@ def get_stop_reason(self) -> Dict[str, Any]: return {"type": lldb.eStopReasonNone} # TODO: Investigate what stop reason should be reported when crashed stop_reason = {"type": lldb.eStopReasonException, "data": {}} - if self.scripted_process.exception: - stop_reason["data"]["mach_exception"] = self.scripted_process.exception + if self.originating_process.exception: + stop_reason["data"]["mach_exception"] = self.originating_process.exception return stop_reason def get_register_context(self) -> str: @@ -209,5 +197,5 @@ def get_register_context(self) -> str: def get_extended_info(self): if self.has_crashed: - self.extended_info = self.scripted_process.extended_thread_info + self.extended_info = self.originating_process.extended_thread_info return self.extended_info