scripts: Added Line class for collect_dwarf_lines

It seems like a good rule of thumb is for every XInfo class to be paired
with at least a small X class wrapper:

- Easier to extend without breaking tuple unpacking everywhere
- Better code readability
- Better memory reuse in _by_addr caches (less tuple repacking)
This commit is contained in:
Christopher Haster
2024-12-05 11:37:30 -06:00
parent b4038e3c27
commit e4ff9a1701
2 changed files with 57 additions and 33 deletions

View File

@@ -397,6 +397,18 @@ def collect_syms(obj_path, global_=False, sections=None, *,
return SymInfo(syms) return SymInfo(syms)
class Line(co.namedtuple('Line', ['file', 'line', 'addr'])):
__slots__ = ()
def __new__(cls, file, line, addr):
return super().__new__(cls, file, line, addr)
def __repr__(self):
return '%s(%r, %r, 0x%x)' % (
self.__class__.__name__,
self.file,
self.line,
self.addr)
class LineInfo: class LineInfo:
def __init__(self, lines): def __init__(self, lines):
self.lines = lines self.lines = lines
@@ -410,20 +422,20 @@ class LineInfo:
if not hasattr(self, '_by_addr'): if not hasattr(self, '_by_addr'):
# sort and keep first when duplicates # sort and keep first when duplicates
lines = self.lines.copy() lines = self.lines.copy()
lines.sort(key=lambda x: (x[2], x[0], x[1])) lines.sort(key=lambda x: (x.addr, x.file, x.line))
by_addr = [] by_addr = []
for file, line, addr in lines: for line in lines:
if (len(by_addr) == 0 if (len(by_addr) == 0
or by_addr[-1][2] != addr): or by_addr[-1].addr != line.addr):
by_addr.append((file, line, addr)) by_addr.append(line)
self._by_addr = by_addr self._by_addr = by_addr
# find file+line by addr # find file+line by addr
i = bisect.bisect(self._by_addr, k, i = bisect.bisect(self._by_addr, k,
key=lambda x: x[2]) key=lambda x: x.addr)
if i > 0: if i > 0:
return self._by_addr[i-1][0], self._by_addr[i-1][1] return self._by_addr[i-1]
else: else:
return d return d
@@ -437,19 +449,19 @@ class LineInfo:
lines.sort() lines.sort()
by_line = [] by_line = []
for file, line, addr in lines: for line in lines:
if (len(by_line) == 0 if (len(by_line) == 0
or by_line[-1][0] != file or by_line[-1].file != line.file
or by_line[-1][1] != line): or by_line[-1].line != line.line):
by_line.append((file, line, addr)) by_line.append(line)
self._by_line = by_line self._by_line = by_line
# find addr by file+line tuple # find addr by file+line tuple
i = bisect.bisect(self._by_line, k, i = bisect.bisect(self._by_line, k,
key=lambda x: (x[0], x[1])) key=lambda x: (x.file, x.line))
# make sure file at least matches! # make sure file at least matches!
if i > 0 and self._by_line[i-1][0] == k[0]: if i > 0 and self._by_line[i-1].file == k[0]:
return self._by_line[i-1][2] return self._by_line[i-1]
else: else:
return d return d
@@ -534,7 +546,7 @@ def collect_dwarf_lines(obj_path, *,
or m.group('op_copy') or m.group('op_copy')
or m.group('op_end')): or m.group('op_end')):
file = os.path.abspath(files.get(op_file, '?')) file = os.path.abspath(files.get(op_file, '?'))
lines.append((file, op_line, op_addr)) lines.append(Line(file, op_line, op_addr))
if m.group('op_end'): if m.group('op_end'):
op_file = 1 op_file = 1
@@ -686,7 +698,7 @@ def collect_decompressed(path, *,
# find file+line # find file+line
line_ = lines.get(addr) line_ = lines.get(addr)
if line_ is not None: if line_ is not None:
file, line = line_ file, line = line_.file, line_.line
else: else:
file, line = re.sub('(\.o)?$', '.c', dso, 1), 0 file, line = re.sub('(\.o)?$', '.c', dso, 1), 0

View File

@@ -303,6 +303,18 @@ def collect_syms(obj_path, global_=False, sections=None, *,
return SymInfo(syms) return SymInfo(syms)
class Line(co.namedtuple('Line', ['file', 'line', 'addr'])):
__slots__ = ()
def __new__(cls, file, line, addr):
return super().__new__(cls, file, line, addr)
def __repr__(self):
return '%s(%r, %r, 0x%x)' % (
self.__class__.__name__,
self.file,
self.line,
self.addr)
class LineInfo: class LineInfo:
def __init__(self, lines): def __init__(self, lines):
self.lines = lines self.lines = lines
@@ -316,20 +328,20 @@ class LineInfo:
if not hasattr(self, '_by_addr'): if not hasattr(self, '_by_addr'):
# sort and keep first when duplicates # sort and keep first when duplicates
lines = self.lines.copy() lines = self.lines.copy()
lines.sort(key=lambda x: (x[2], x[0], x[1])) lines.sort(key=lambda x: (x.addr, x.file, x.line))
by_addr = [] by_addr = []
for file, line, addr in lines: for line in lines:
if (len(by_addr) == 0 if (len(by_addr) == 0
or by_addr[-1][2] != addr): or by_addr[-1].addr != line.addr):
by_addr.append((file, line, addr)) by_addr.append(line)
self._by_addr = by_addr self._by_addr = by_addr
# find file+line by addr # find file+line by addr
i = bisect.bisect(self._by_addr, k, i = bisect.bisect(self._by_addr, k,
key=lambda x: x[2]) key=lambda x: x.addr)
if i > 0: if i > 0:
return self._by_addr[i-1][0], self._by_addr[i-1][1] return self._by_addr[i-1]
else: else:
return d return d
@@ -343,19 +355,19 @@ class LineInfo:
lines.sort() lines.sort()
by_line = [] by_line = []
for file, line, addr in lines: for line in lines:
if (len(by_line) == 0 if (len(by_line) == 0
or by_line[-1][0] != file or by_line[-1].file != line.file
or by_line[-1][1] != line): or by_line[-1].line != line.line):
by_line.append((file, line, addr)) by_line.append(line)
self._by_line = by_line self._by_line = by_line
# find addr by file+line tuple # find addr by file+line tuple
i = bisect.bisect(self._by_line, k, i = bisect.bisect(self._by_line, k,
key=lambda x: (x[0], x[1])) key=lambda x: (x.file, x.line))
# make sure file at least matches! # make sure file at least matches!
if i > 0 and self._by_line[i-1][0] == k[0]: if i > 0 and self._by_line[i-1].file == k[0]:
return self._by_line[i-1][2] return self._by_line[i-1]
else: else:
return d return d
@@ -439,7 +451,7 @@ def collect_dwarf_lines(obj_path, *,
or m.group('op_copy') or m.group('op_copy')
or m.group('op_end')): or m.group('op_end')):
file = os.path.abspath(files.get(op_file, '?')) file = os.path.abspath(files.get(op_file, '?'))
lines.append((file, op_line, op_addr)) lines.append(Line(file, op_line, op_addr))
if m.group('op_end'): if m.group('op_end'):
op_file = 1 op_file = 1
@@ -606,9 +618,9 @@ def collect_job(path, start, stop, syms, lines, *,
# the first stack frame, so we can use that as a point # the first stack frame, so we can use that as a point
# of reference # of reference
if last_delta is None: if last_delta is None:
addr__ = lines.get((last_file, last_line)) line_ = lines.get((last_file, last_line))
if addr__ is not None: if line_ is not None:
last_delta = addr__ - addr_ last_delta = line_.addr - addr_
else: else:
# can't reverse ASLR, give up on backtrace # can't reverse ASLR, give up on backtrace
commit() commit()
@@ -644,7 +656,7 @@ def collect_job(path, start, stop, syms, lines, *,
# find file+line # find file+line
line_ = lines.get(addr) line_ = lines.get(addr)
if line_ is not None: if line_ is not None:
file, line = line_ file, line = line_.file, line_.line
elif len(last_stack) == 0: elif len(last_stack) == 0:
file, line = last_file, last_line file, line = last_file, last_line
else: else: