Skip to content

Commit 27f27d1

Browse files
committed
[lldb] Use ObjectFileJSON to create modules for interactive crashlogs
Create an artificial module using a JSON object file when we can't locate the module and dSYM through dsymForUUID (or however locate_module_and_debug_symbols is implemented). By parsing the symbols from the crashlog and making them part of the JSON object file, LLDB can symbolicate frames it otherwise wouldn't be able to, as there is no module for it. For non-interactive crashlogs, that never was a problem because we could simply show the "pre-symbolicated" frame from the input. For interactive crashlogs, we need a way to pass the symbol information to LLDB so that it can symbolicate the frames, which is what motivated the JSON object file format. Differential revision: https://reviews.llvm.org/D148172
1 parent fcc5f9e commit 27f27d1

File tree

5 files changed

+90
-54
lines changed

5 files changed

+90
-54
lines changed

lldb/examples/python/crashlog.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class Thread:
8080
def __init__(self, index, app_specific_backtrace):
8181
self.index = index
8282
self.id = index
83+
self.images = list()
8384
self.frames = list()
8485
self.idents = list()
8586
self.registers = dict()
@@ -456,6 +457,11 @@ def parse_json(buffer):
456457
except:
457458
return None
458459

460+
def __init__(self, debugger, path, verbose):
461+
super().__init__(debugger, path, verbose)
462+
# List of DarwinImages sorted by their index.
463+
self.images = list()
464+
459465
def parse(self):
460466
try:
461467
self.parse_process_info(self.data)
@@ -506,7 +512,6 @@ def parse_crash_reason(self, json_exception):
506512
exception_extra)
507513

508514
def parse_images(self, json_images):
509-
idx = 0
510515
for json_image in json_images:
511516
img_uuid = uuid.UUID(json_image['uuid'])
512517
low = int(json_image['base'])
@@ -518,8 +523,8 @@ def parse_images(self, json_images):
518523
darwin_image = self.crashlog.DarwinImage(low, high, name, version,
519524
img_uuid, path,
520525
self.verbose)
526+
self.images.append(darwin_image)
521527
self.crashlog.images.append(darwin_image)
522-
idx += 1
523528

524529
def parse_main_image(self, json_data):
525530
if 'procName' in json_data:
@@ -539,6 +544,17 @@ def parse_frames(self, thread, json_frames):
539544
frame_offset = int(json_frame['imageOffset'])
540545
image_addr = self.get_used_image(image_id)['base']
541546
pc = image_addr + frame_offset
547+
548+
if 'symbol' in json_frame:
549+
symbol = json_frame['symbol']
550+
location = int(json_frame['symbolLocation'])
551+
image = self.images[image_id]
552+
image.symbols[symbol] = {
553+
"name": symbol,
554+
"type": "code",
555+
"address": frame_offset - location
556+
}
557+
542558
thread.frames.append(self.crashlog.Frame(idx, pc, frame_offset))
543559

544560
# on arm64 systems, if it jump through a null function pointer,
@@ -1015,40 +1031,25 @@ def SymbolicateCrashLog(crash_log, options):
10151031
target = crash_log.create_target()
10161032
if not target:
10171033
return
1018-
exe_module = target.GetModuleAtIndex(0)
1019-
images_to_load = list()
1020-
loaded_images = list()
1034+
1035+
10211036
if options.load_all_images:
1022-
# --load-all option was specified, load everything up
10231037
for image in crash_log.images:
1024-
images_to_load.append(image)
1025-
else:
1026-
# Only load the images found in stack frames for the crashed threads
1027-
if options.crashed_only:
1028-
for thread in crash_log.threads:
1029-
if thread.did_crash():
1030-
for ident in thread.idents:
1031-
images = crash_log.find_images_with_identifier(ident)
1032-
if images:
1033-
for image in images:
1034-
images_to_load.append(image)
1035-
else:
1036-
print('error: can\'t find image for identifier "%s"' % ident)
1037-
else:
1038-
for ident in crash_log.idents:
1039-
images = crash_log.find_images_with_identifier(ident)
1040-
if images:
1041-
for image in images:
1042-
images_to_load.append(image)
1043-
else:
1044-
print('error: can\'t find image for identifier "%s"' % ident)
1038+
image.resolve = True
1039+
elif options.crashed_only:
1040+
for thread in crash_log.threads:
1041+
if thread.did_crash():
1042+
for ident in thread.idents:
1043+
for image in self.crashlog.find_images_with_identifier(ident):
1044+
image.resolve = True
10451045

10461046
futures = []
1047+
loaded_images = []
10471048
with concurrent.futures.ThreadPoolExecutor() as executor:
10481049
def add_module(image, target):
10491050
return image, image.add_module(target)
10501051

1051-
for image in images_to_load:
1052+
for image in crash_log.images:
10521053
futures.append(executor.submit(add_module, image=image, target=target))
10531054

10541055
for future in concurrent.futures.as_completed(futures):

lldb/examples/python/scripted_process/crashlog_scripted_process.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,28 @@ def set_crashlog(self, crashlog):
2222
if hasattr(self.crashlog, 'asb'):
2323
self.extended_thread_info = self.crashlog.asb
2424

25-
def load_images(self, images):
26-
#TODO: Add to self.loaded_images and load images in lldb
27-
if images:
28-
for image in images:
29-
if image not in self.loaded_images:
30-
if image.uuid == uuid.UUID(int=0):
31-
continue
32-
err = image.add_module(self.target)
33-
if err:
34-
# Append to SBCommandReturnObject
35-
print(err)
36-
else:
37-
self.loaded_images.append(image)
25+
if self.load_all_images:
26+
for image in self.crashlog.images:
27+
image.resolve = True
28+
else:
29+
for thread in self.crashlog.threads:
30+
if thread.did_crash():
31+
for ident in thread.idents:
32+
for image in self.crashlog.find_images_with_identifier(ident):
33+
image.resolve = True
34+
35+
for image in self.crashlog.images:
36+
if image not in self.loaded_images:
37+
if image.uuid == uuid.UUID(int=0):
38+
continue
39+
err = image.add_module(self.target)
40+
if err:
41+
# Append to SBCommandReturnObject
42+
print(err)
43+
else:
44+
self.loaded_images.append(image)
3845

3946
for thread in self.crashlog.threads:
40-
if self.load_all_images:
41-
load_images(self, self.crashlog.images)
42-
elif thread.did_crash():
43-
for ident in thread.idents:
44-
load_images(self, self.crashlog.find_images_with_identifier(ident))
45-
4647
if hasattr(thread, 'app_specific_backtrace') and thread.app_specific_backtrace:
4748
# We don't want to include the Application Specific Backtrace
4849
# Thread into the Scripted Process' Thread list.

lldb/examples/python/symbolication.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import sys
3636
import time
3737
import uuid
38+
import json
39+
import tempfile
3840

3941

4042
class Address:
@@ -230,6 +232,7 @@ class Image:
230232
def __init__(self, path, uuid=None):
231233
self.path = path
232234
self.resolved_path = None
235+
self.resolve = False
233236
self.resolved = False
234237
self.unavailable = False
235238
self.uuid = uuid
@@ -240,6 +243,7 @@ def __init__(self, path, uuid=None):
240243
self.module = None
241244
self.symfile = None
242245
self.slide = None
246+
self.symbols = dict()
243247

244248
@classmethod
245249
def InitWithSBTargetAndSBModule(cls, target, module):
@@ -372,14 +376,32 @@ def add_module(self, target):
372376
uuid_str = self.get_normalized_uuid_string()
373377
if uuid_str:
374378
self.module = target.AddModule(None, None, uuid_str)
375-
if not self.module:
379+
if not self.module and self.resolve:
376380
self.locate_module_and_debug_symbols()
377-
if self.unavailable:
378-
return None
379-
resolved_path = self.get_resolved_path()
380-
self.module = target.AddModule(
381-
resolved_path, None, uuid_str, self.symfile)
382-
if not self.module:
381+
if not self.unavailable:
382+
resolved_path = self.get_resolved_path()
383+
self.module = target.AddModule(
384+
resolved_path, None, uuid_str, self.symfile)
385+
if not self.module and self.section_infos:
386+
name = os.path.basename(self.path)
387+
with tempfile.NamedTemporaryFile(suffix='.' + name) as tf:
388+
data = {
389+
'triple': target.triple,
390+
'uuid': uuid_str,
391+
'type': 'sharedlibrary',
392+
'sections': list(),
393+
'symbols': list()
394+
}
395+
for section in self.section_infos:
396+
data['sections'].append({
397+
'name' : section.name,
398+
'size': section.end_addr - section.start_addr
399+
})
400+
data['symbols'] = list(self.symbols.values())
401+
with open(tf.name, 'w') as f:
402+
f.write(json.dumps(data, indent=4))
403+
self.module = target.AddModule(tf.name, None, uuid_str)
404+
if not self.module and not self.unavailable:
383405
return 'error: unable to get module for (%s) "%s"' % (
384406
self.arch, self.get_resolved_path())
385407
if self.has_section_load_info():

lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/interactive_crashlog/multithread-test.ips

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,15 @@
478478
"source" : "A",
479479
"base" : 0,
480480
"uuid" : "00000000-0000-0000-0000-000000000000"
481+
},
482+
{
483+
"arch": "arm64",
484+
"base": 12345,
485+
"name": "bogus.dylib",
486+
"path": "/usr/lib/system/bogus.dylib",
487+
"size": 1000,
488+
"source": "P",
489+
"uuid": "11111111-2222-3333-4444-555555555555"
481490
}
482491
],
483492
"userID": 501,

lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ bt all
4141
# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
4242
# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
4343
# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
44+
45+
image list
46+
# CHECK: 11111111-2222-3333-4444-555555555555 {{.*}}bogus.dylib

0 commit comments

Comments
 (0)