Skip to content

[lldb] Various crashlog improvements and bug fixes #8755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 67 additions & 36 deletions lldb/examples/python/crashlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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",
Expand Down
48 changes: 18 additions & 30 deletions lldb/examples/python/crashlog_scripted_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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:
Expand All @@ -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