Skip to content

Commit f803dbc

Browse files
committed
coredump: integrate parser into gdb script
JIRA: RTOS-1049
1 parent 45d46b5 commit f803dbc

File tree

4 files changed

+309
-127
lines changed

4 files changed

+309
-127
lines changed

coredump_parser/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ NAME := coredump_parser
88
LOCAL_DIR := $(call my-dir)
99
SRCS := $(wildcard $(LOCAL_DIR)*.cpp)
1010
LOCAL_LDLIBS := -lstdc++
11+
SCRIPTS := $(wildcard $(LOCAL_DIR)*.py)
1112

1213

1314
include $(binary.mk)
15+
16+
17+
$(NAME)-scripts:
18+
mkdir -p $(PREFIX_BUILD)/scripts
19+
cp $(SCRIPTS) $(PREFIX_BUILD)/scripts
20+
21+
ALL_COMPONENTS += $(NAME)-scripts
Lines changed: 203 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,211 @@
1-
import gdb
21
from elftools.elf.elffile import ELFFile
32
from elftools.elf.constants import SH_FLAGS
43
from itertools import pairwise
4+
from pathlib import Path
5+
import argparse
56
import struct
7+
import sys
8+
import os
9+
import subprocess
10+
import tempfile
611

712
NT_LMA = 0x00414D4C
813

9-
class NoMMULoad(gdb.Command):
10-
"""Auto-load core file and offset symbols on nommu target."""
11-
12-
def __init__(self):
13-
super(NoMMULoad, self).__init__("nommu-load", gdb.COMMAND_USER)
14-
self.target_arch = None
15-
16-
17-
def parse_core_file(self, filename):
18-
with open(filename, 'rb') as f:
19-
elffile = ELFFile(f)
20-
self.target_arch = elffile.get_machine_arch()
21-
for segment in elffile.iter_segments():
22-
if segment['p_type'] != 'PT_NOTE':
23-
continue
24-
for note in segment.iter_notes():
25-
if note['n_type'] != NT_LMA:
26-
continue
27-
values = struct.unpack(f'<{len(note["n_desc"]) // 4}I', note['n_desc'])
28-
res = {}
29-
for lma, vma in pairwise(values):
30-
res[vma] = lma
31-
return res
32-
return {}
33-
34-
def parse_symbol_file(self, filename):
35-
section_to_segment_mapping = {}
36-
37-
with open(filename, 'rb') as f:
38-
elffile = ELFFile(f)
39-
for section in elffile.iter_sections():
40-
if not (section['sh_flags'] & SH_FLAGS.SHF_ALLOC) or section['sh_size'] == 0:
41-
continue
42-
43-
sec_start_addr = section['sh_addr']
44-
sec_end_addr = sec_start_addr + section['sh_size']
45-
46-
for segment in elffile.iter_segments():
47-
if segment['p_type'] != 'PT_LOAD':
48-
continue
49-
seg_start_addr = segment['p_vaddr']
50-
seg_end_addr = seg_start_addr + segment['p_memsz']
51-
52-
if sec_start_addr >= seg_start_addr and sec_end_addr <= seg_end_addr:
53-
section_to_segment_mapping[section.name] = {
54-
'seg_addr': seg_start_addr,
55-
'offset': section['sh_addr'] - seg_start_addr,
56-
}
57-
break
58-
59-
return section_to_segment_mapping
60-
61-
def create_mapping_args(self, core_file, symbol_file):
62-
core_segment_mapping = self.parse_core_file(core_file)
63-
elf_section_mapping = self.parse_symbol_file(symbol_file)
64-
args = []
65-
for section_name, section_info in elf_section_mapping.items():
66-
if section_info['seg_addr'] not in core_segment_mapping:
67-
print(f"Warning: Segment containing '{section_name}' not found in core NT_FILE mapping.")
68-
if section_name == '.text':
69-
print("Warning: No valid segment mapping found for '.text' section!")
70-
return []
14+
COREDUMP_PARSER_PATH = (Path(__file__).parent.parent / "prog" / "coredump_parser").absolute()
15+
COREDUMP_PARSER_OUTPUT = Path(tempfile.gettempdir()) / "phoenix-coredumps"
16+
17+
os.makedirs(COREDUMP_PARSER_OUTPUT, exist_ok=True)
18+
19+
20+
def has_lma_note(elffile):
21+
for segment in elffile.iter_segments():
22+
if segment['p_type'] != 'PT_NOTE':
23+
continue
24+
for note in segment.iter_notes():
25+
if note['n_type'] == NT_LMA:
26+
return True
27+
return False
28+
29+
def parse_core_file(elffile):
30+
for segment in elffile.iter_segments():
31+
if segment['p_type'] != 'PT_NOTE':
32+
continue
33+
for note in segment.iter_notes():
34+
if note['n_type'] != NT_LMA:
35+
continue
36+
values = struct.unpack(f'<{len(note["n_desc"]) // 4}I', note['n_desc'])
37+
res = {}
38+
for lma, vma in pairwise(values):
39+
res[vma] = lma
40+
return res
41+
return {}
42+
43+
def parse_symbol_file(elffile):
44+
section_to_segment_mapping = {}
45+
46+
for section in elffile.iter_sections():
47+
if not (section['sh_flags'] & SH_FLAGS.SHF_ALLOC) or section['sh_size'] == 0:
48+
continue
49+
50+
sec_start_addr = section['sh_addr']
51+
sec_end_addr = sec_start_addr + section['sh_size']
52+
53+
for segment in elffile.iter_segments():
54+
if segment['p_type'] != 'PT_LOAD':
7155
continue
72-
new_addr = core_segment_mapping[section_info['seg_addr']] + section_info['offset']
73-
args.append(f"-s {section_name} {new_addr:#x}")
74-
if len(args) == 0:
75-
print("Error: No valid segment mappings found.")
76-
return args
77-
78-
def invoke(self, arg, from_tty):
79-
args = gdb.string_to_argv(arg)
80-
81-
if len(args) < 2:
82-
print("Usage: nommu-load <symbol-file> <core-file>")
83-
return
84-
85-
symbol_file = args[0]
86-
core_file = args[1]
87-
88-
mapping_args = self.create_mapping_args(core_file, symbol_file)
89-
try:
90-
gdb.execute(f"add-symbol-file {symbol_file} {' '.join(mapping_args)}")
91-
gdb.execute(f"exec-file {symbol_file} {' '.join(mapping_args)}")
92-
except gdb.error as e:
93-
print(f"Failed to load symbols: {e}")
94-
95-
try:
96-
gdb.execute(f"target core {core_file}")
97-
except gdb.error as e:
98-
print(f"Failed to load core file: {e}")
99-
return
100-
101-
NoMMULoad()
56+
seg_start_addr = segment['p_vaddr']
57+
seg_end_addr = seg_start_addr + segment['p_memsz']
58+
59+
if sec_start_addr >= seg_start_addr and sec_end_addr <= seg_end_addr:
60+
section_to_segment_mapping[section.name] = {
61+
'seg_addr': seg_start_addr,
62+
'offset': section['sh_addr'] - seg_start_addr,
63+
}
64+
break
65+
66+
return section_to_segment_mapping
67+
68+
def create_mapping_args(core_file, symbol_file):
69+
core_segment_mapping = parse_core_file(core_file)
70+
elf_section_mapping = parse_symbol_file(symbol_file)
71+
args = []
72+
for section_name, section_info in elf_section_mapping.items():
73+
if section_info['seg_addr'] not in core_segment_mapping:
74+
print(f"Warning: Segment containing '{section_name}' not found in core NT_FILE mapping.", file=sys.stderr)
75+
if section_name == '.text':
76+
print("Warning: No valid segment mapping found for '.text' section!", file=sys.stderr)
77+
return []
78+
continue
79+
new_addr = core_segment_mapping[section_info['seg_addr']] + section_info['offset']
80+
args.append(f"-s {section_name} {new_addr:#x}")
81+
if len(args) == 0:
82+
print("Error: No valid segment mappings found.", file=sys.stderr)
83+
return args
84+
85+
def run_parser(coredump=None, symbolfile=None):
86+
arglist = [COREDUMP_PARSER_OUTPUT]
87+
if symbolfile: arglist.append(Path(symbolfile).name)
88+
try:
89+
if coredump:
90+
with open(coredump, "rb") as f:
91+
result = subprocess.run([COREDUMP_PARSER_PATH, *arglist], check=True, stdin=f, stdout=subprocess.PIPE)
92+
return result.stdout.decode('utf-8').strip()
93+
else:
94+
result = subprocess.run([COREDUMP_PARSER_PATH, *arglist], check=True, stdout=subprocess.PIPE)
95+
return result.stdout.decode('utf-8').strip()
96+
except subprocess.CalledProcessError as e:
97+
print(f"Running coredump_parser failed with exit status: {e.returncode}", file=sys.stderr)
98+
return []
99+
100+
def generate_gdb_commands(args):
101+
"""
102+
If symbols is None, only corefile will be loaded
103+
If core is None, corefile will be loaded from stdin
104+
"""
105+
coreelf = None
106+
symbolelf = None
107+
corefile = None
108+
symbolfile = None
109+
110+
try:
111+
corefile = open(args.core, 'rb')
112+
coreelf = ELFFile(corefile)
113+
except Exception as e:
114+
args.core = run_parser(args.core, args.symbol)
115+
if args.core:
116+
print(f"Using core file '{args.core}' with symbol file '{args.symbol}'.", file=sys.stderr)
117+
corefile = open(args.core, 'rb')
118+
coreelf = ELFFile(corefile)
119+
120+
if args.symbol:
121+
symbolfile = open(args.symbol, 'rb')
122+
symbolelf = ELFFile(symbolfile)
123+
124+
if not coreelf:
125+
print("No core file found.", file=sys.stderr)
126+
if corefile: corefile.close()
127+
if symbolfile: symbolfile.close()
128+
return []
129+
130+
commands = []
131+
if has_lma_note(coreelf):
132+
if not symbolelf:
133+
print("Warning: NOMMU coreelf detected, but no symbol file found. Symbols won't be resolved properly when loading symbols separately!", file=sys.stderr)
134+
else:
135+
mapping_args = create_mapping_args(coreelf, symbolelf)
136+
commands.append(f"symbol-file") # clear previous
137+
commands.append(f"exec-file") # clear previous
138+
commands.append(f"add-symbol-file {args.symbol} {' '.join(mapping_args)}")
139+
commands.append(f"exec-file {args.symbol} {' '.join(mapping_args)}")
140+
elif symbolelf:
141+
commands.append(f"symbol-file") # clear previous
142+
commands.append(f"exec-file") # clear previous
143+
commands.append(f"add-symbol-file {args.symbol}")
144+
commands.append(f"exec-file {args.symbol}")
145+
146+
commands.append(f"core-file {args.core}")
147+
if corefile: corefile.close()
148+
if symbolfile: symbolfile.close()
149+
return commands
150+
151+
def parse_args(args):
152+
parser = argparse.ArgumentParser(add_help=True)
153+
parser.add_argument("-c", "--core", help="Path to the core file or to text containg dump (by default stdin will be used)")
154+
parser.add_argument("-s", "--symbol", "--sym", help="Path to the symbol file (by default in gdb currently loaded symbol file will be used)")
155+
try:
156+
args = parser.parse_args(args)
157+
except SystemExit:
158+
return {"help": True}
159+
return args
160+
161+
is_in_gdb = True
162+
try:
163+
import gdb
164+
except ImportError:
165+
is_in_gdb = False
166+
167+
if is_in_gdb:
168+
class PhoenixCoreCommand(gdb.Command):
169+
"""
170+
Auto-load Phoenix core file and offset symbols on NOMMU targets.
171+
Use `phoenix-load --help` for more information.
172+
"""
173+
174+
def __init__(self):
175+
super(PhoenixCoreCommand, self).__init__("phoenix-core", gdb.COMMAND_USER)
176+
self.target_arch = None
177+
178+
def invoke(self, arg, from_tty):
179+
args = parse_args(gdb.string_to_argv(arg))
180+
if "help" in args:
181+
return
182+
if not args.symbol:
183+
try:
184+
if gdb.objfiles():
185+
args.symbol = gdb.objfiles()[0].filename
186+
except gdb.error as e:
187+
print(f"Phoenix-core: Note: Could not determine current GDB symbol file:", e, file=sys.stderr)
188+
commands = generate_gdb_commands(args)
189+
for command in commands:
190+
gdb.execute(command)
191+
def complete(self, text, word):
192+
options = ["-c", "--core", "-s", "--symbol", "--sym", "-h", "--help"]
193+
if word and word.startswith("-"):
194+
return [opt for opt in options if opt.startswith(word)]
195+
return gdb.COMPLETE_FILENAME
196+
197+
198+
PhoenixCoreCommand()
199+
200+
elif __name__ == "__main__":
201+
args = parse_args(sys.argv[1:])
202+
if "help" in args:
203+
exit(0)
204+
commands = generate_gdb_commands(args)
205+
if commands:
206+
print("\n\n# Generated GDB commands:\n")
207+
for cmd in commands:
208+
print(cmd)
209+
print()
210+
else:
211+
print("No commands generated.", file=sys.stderr)

0 commit comments

Comments
 (0)