scripts: Adopted ctx.py-related changes in other result scripts
- Adopted higher-level collect data structures:
- high-level DwarfEntry/DwarfInfo class
- high-level SymInfo class
- high-level LineInfo class
Note these had to be moved out of function scope due to pickling
issues in perf.py/perfbd.py. These were only function-local to
minimize scope leak so this fortunately was an easy change.
- Adopted better list-default patterns in Result types:
def __new__(..., children=None):
return Result(..., children if children is not None else [])
A classic python footgun.
- Adopted notes rendering, though this is only used by ctx.py at the
moment.
- Reverted to sorting children entries, for now.
Unfortunately there's no easy way to sort the result entries in
perf.py/perfbd.py before folding. Folding is going to make a mess
of more complicated children anyways, so another solution is
needed...
And some other shared miscellany.
This commit is contained in:
+148
-163
@@ -160,61 +160,67 @@ def openio(path, mode='r', buffering=-1):
|
||||
else:
|
||||
return open(path, mode, buffering)
|
||||
|
||||
def collect_syms(obj_path, global_only=False, *,
|
||||
objdump_path=None,
|
||||
**args):
|
||||
class SymInfo:
|
||||
def __init__(self, syms):
|
||||
self.syms = syms
|
||||
class SymInfo:
|
||||
def __init__(self, syms):
|
||||
self.syms = syms
|
||||
|
||||
def get(self, k, d=None):
|
||||
# allow lookup by both symbol and address
|
||||
if isinstance(k, str):
|
||||
# organize by symbol, note multiple symbols can share a name
|
||||
if not hasattr(self, '_by_sym'):
|
||||
self._by_sym = {}
|
||||
for sym, addr, size in self.syms:
|
||||
self._by_sym[sym] = (addr, size)
|
||||
return self._by_sym.get(k, d)
|
||||
def get(self, k, d=None):
|
||||
# allow lookup by both symbol and address
|
||||
if isinstance(k, str):
|
||||
# organize by symbol, note multiple symbols can share a name
|
||||
if not hasattr(self, '_by_sym'):
|
||||
by_sym = {}
|
||||
for sym, addr, size in self.syms:
|
||||
if sym not in by_sym:
|
||||
by_sym[sym] = []
|
||||
if (addr, size) not in by_sym[sym]:
|
||||
by_sym[sym].append((addr, size))
|
||||
self._by_sym = by_sym
|
||||
return self._by_sym.get(k, d)
|
||||
|
||||
else:
|
||||
import bisect
|
||||
|
||||
# organize by address
|
||||
if not hasattr(self, '_by_addr'):
|
||||
# sort and keep largest/first when duplicates
|
||||
syms = self.syms.copy()
|
||||
syms.sort(key=lambda x: (x[1], -x[2], x[0]))
|
||||
|
||||
by_addr = []
|
||||
for name, addr, size in syms:
|
||||
if (len(by_addr) == 0
|
||||
or by_addr[-1][0] != addr):
|
||||
by_addr.append((name, addr, size))
|
||||
self._by_addr = by_addr
|
||||
|
||||
# find sym by range
|
||||
i = bisect.bisect(self._by_addr, k,
|
||||
key=lambda x: x[1])
|
||||
# check that we're actually in this sym's size
|
||||
if i > 0 and k < self._by_addr[i-1][1]+self._by_addr[i-1][2]:
|
||||
return self._by_addr[i-1][0]
|
||||
else:
|
||||
import bisect
|
||||
return d
|
||||
|
||||
# organize by address
|
||||
if not hasattr(self, '_by_addr'):
|
||||
# sort and keep largest/first when duplicates
|
||||
syms = self.syms.copy()
|
||||
syms.sort(key=lambda x: (x[1], -x[2], x[0]))
|
||||
def __getitem__(self, k):
|
||||
v = self.get(k)
|
||||
if v is None:
|
||||
raise KeyError(k)
|
||||
return v
|
||||
|
||||
self._by_addr = []
|
||||
for name, addr, size in syms:
|
||||
if (len(self._by_addr) == 0
|
||||
or self._by_addr[-1][0] != addr):
|
||||
self._by_addr.append((name, addr, size))
|
||||
def __contains__(self, k):
|
||||
return self.get(k) is not None
|
||||
|
||||
# find sym by range
|
||||
i = bisect.bisect(self._by_addr, k, key=lambda x: x[1])
|
||||
# check that we're actually in this sym's size
|
||||
if i > 0 and k < self._by_addr[i-1][1]+self._by_addr[i-1][2]:
|
||||
return self._by_addr[i-1]
|
||||
else:
|
||||
return None
|
||||
def __len__(self):
|
||||
return len(self.syms)
|
||||
|
||||
def __getitem__(self, k):
|
||||
v = self.get(k)
|
||||
if v is None:
|
||||
raise KeyError(k)
|
||||
return v
|
||||
|
||||
def __contains__(self, k):
|
||||
return self.get(k) is not None
|
||||
|
||||
def __len__(self):
|
||||
return len(self.syms)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.syms)
|
||||
def __iter__(self):
|
||||
return iter(self.syms)
|
||||
|
||||
def collect_syms(obj_path, global_only=False, *,
|
||||
objdump_path=OBJDUMP_PATH,
|
||||
**args):
|
||||
symbol_pattern = re.compile(
|
||||
'^(?P<addr>[0-9a-fA-F]+)'
|
||||
' (?P<scope>.).*'
|
||||
@@ -260,28 +266,6 @@ def collect_syms(obj_path, global_only=False, *,
|
||||
def collect_dwarf_files(obj_path, *,
|
||||
objdump_path=OBJDUMP_PATH,
|
||||
**args):
|
||||
class FileInfo:
|
||||
def __init__(self, files):
|
||||
self.files = files
|
||||
|
||||
def get(self, k, d=None):
|
||||
return self.files.get(k, d)
|
||||
|
||||
def __getitem__(self, k):
|
||||
v = self.get(k)
|
||||
if v is None:
|
||||
raise KeyError(k)
|
||||
return v
|
||||
|
||||
def __contains__(self, k):
|
||||
return self.get(k) is not None
|
||||
|
||||
def __len__(self):
|
||||
return len(self.files)
|
||||
|
||||
def __iter__(self):
|
||||
return (v for k, v in self.files.items())
|
||||
|
||||
line_pattern = re.compile(
|
||||
'^\s*(?P<no>[0-9]+)'
|
||||
'(?:\s+(?P<dir>[0-9]+))?'
|
||||
@@ -322,7 +306,7 @@ def collect_dwarf_files(obj_path, *,
|
||||
raise sp.CalledProcessError(proc.returncode, proc.args)
|
||||
|
||||
# simplify paths
|
||||
files_ = {}
|
||||
files_ = co.OrderedDict()
|
||||
for no, file in files.items():
|
||||
if os.path.commonpath([
|
||||
os.getcwd(),
|
||||
@@ -332,104 +316,104 @@ def collect_dwarf_files(obj_path, *,
|
||||
files_[no] = os.path.abspath(file)
|
||||
files = files_
|
||||
|
||||
return FileInfo(files)
|
||||
return files
|
||||
|
||||
# each dwarf entry can have attrs and children entries
|
||||
class DwarfEntry:
|
||||
def __init__(self, level, off, tag, ats={}, children=[]):
|
||||
self.level = level
|
||||
self.off = off
|
||||
self.tag = tag
|
||||
self.ats = ats or {}
|
||||
self.children = children or []
|
||||
|
||||
def get(self, k, d=None):
|
||||
return self.ats.get(k, d)
|
||||
|
||||
def __getitem__(self, k):
|
||||
return self.ats[k]
|
||||
|
||||
def __contains__(self, k):
|
||||
return k in self.ats
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%d, 0x%x, %r, %r)' % (
|
||||
self.__class__.__name__,
|
||||
self.level,
|
||||
self.off,
|
||||
self.tag,
|
||||
self.ats)
|
||||
|
||||
@ft.cached_property
|
||||
def name(self):
|
||||
if 'DW_AT_name' in self:
|
||||
name = self['DW_AT_name'].split(':')[-1].strip()
|
||||
# prefix with struct/union/enum
|
||||
if self.tag == 'DW_TAG_structure_type':
|
||||
name = 'struct ' + name
|
||||
elif self.tag == 'DW_TAG_union_type':
|
||||
name = 'union ' + name
|
||||
elif self.tag == 'DW_TAG_enumeration_type':
|
||||
name = 'enum ' + name
|
||||
return name
|
||||
else:
|
||||
return None
|
||||
|
||||
# a collection of dwarf entries
|
||||
class DwarfInfo:
|
||||
def __init__(self, entries):
|
||||
self.entries = entries
|
||||
|
||||
def get(self, k, d=None):
|
||||
# allow lookup by both offset and dwarf name
|
||||
if not isinstance(k, str):
|
||||
return self.entries.get(k, d)
|
||||
|
||||
else:
|
||||
import difflib
|
||||
|
||||
# organize entries by name
|
||||
if not hasattr(self, '_by_name'):
|
||||
self._by_name = {}
|
||||
for entry in self.entries.values():
|
||||
if entry.name is not None:
|
||||
self._by_name[entry.name] = entry
|
||||
|
||||
# exact match? avoid difflib if we can for speed
|
||||
if k in self._by_name:
|
||||
return self._by_name[k]
|
||||
# find the best matching dwarf entry with difflib
|
||||
#
|
||||
# this can be different from the actual symbol because
|
||||
# of optimization passes
|
||||
else:
|
||||
name, entry = max(
|
||||
self._by_name.items(),
|
||||
key=lambda entry: difflib.SequenceMatcher(
|
||||
None, entry[0], k, False).ratio(),
|
||||
default=(None, None))
|
||||
return entry
|
||||
|
||||
def __getitem__(self, k):
|
||||
v = self.get(k)
|
||||
if v is None:
|
||||
raise KeyError(k)
|
||||
return v
|
||||
|
||||
def __contains__(self, k):
|
||||
return self.get(k) is not None
|
||||
|
||||
def __len__(self):
|
||||
return len(self.entries)
|
||||
|
||||
def __iter__(self):
|
||||
return (v for k, v in self.entries.items())
|
||||
|
||||
def collect_dwarf_info(obj_path, filter=None, *,
|
||||
objdump_path=OBJDUMP_PATH,
|
||||
**args):
|
||||
filter_, filter = filter, __builtins__.filter
|
||||
|
||||
# each dwarf entry can have attrs and children entries
|
||||
class DwarfEntry:
|
||||
def __init__(self, level, off, tag, ats={}, children=[]):
|
||||
self.level = level
|
||||
self.off = off
|
||||
self.tag = tag
|
||||
self.ats = ats or {}
|
||||
self.children = children or []
|
||||
|
||||
def get(self, k, d=None):
|
||||
return self.ats.get(k, d)
|
||||
|
||||
def __getitem__(self, k):
|
||||
return self.ats[k]
|
||||
|
||||
def __contains__(self, k):
|
||||
return k in self.ats
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%d, 0x%x, %r, %r)' % (
|
||||
self.__class__.__name__,
|
||||
self.level,
|
||||
self.off,
|
||||
self.tag,
|
||||
self.ats)
|
||||
|
||||
@ft.cached_property
|
||||
def name(self):
|
||||
if 'DW_AT_name' in self:
|
||||
name = self['DW_AT_name'].split(':')[-1].strip()
|
||||
# prefix with struct/union/enum
|
||||
if self.tag == 'DW_TAG_structure_type':
|
||||
name = 'struct ' + name
|
||||
elif self.tag == 'DW_TAG_union_type':
|
||||
name = 'union ' + name
|
||||
elif self.tag == 'DW_TAG_enumeration_type':
|
||||
name = 'enum ' + name
|
||||
return name
|
||||
else:
|
||||
return None
|
||||
|
||||
# a collection of dwarf entries
|
||||
class DwarfInfo:
|
||||
def __init__(self, entries):
|
||||
self.entries = entries
|
||||
|
||||
def get(self, k, d=None):
|
||||
# allow lookup by both offset and dwarf name
|
||||
if not isinstance(k, str):
|
||||
return self.entries.get(k, d)
|
||||
|
||||
else:
|
||||
import difflib
|
||||
|
||||
# organize entries by name
|
||||
if not hasattr(self, '_by_name'):
|
||||
self._by_name = {}
|
||||
for entry in self.entries.values():
|
||||
if entry.name is not None:
|
||||
self._by_name[entry.name] = entry
|
||||
|
||||
# exact match? avoid difflib if we can for speed
|
||||
if k in self._by_name:
|
||||
return self._by_name[k]
|
||||
# find the best matching dwarf entry with difflib
|
||||
#
|
||||
# this can be different from the actual symbol because
|
||||
# of optimization passes
|
||||
else:
|
||||
name, entry = max(
|
||||
self._by_name.items(),
|
||||
key=lambda entry: difflib.SequenceMatcher(
|
||||
None, entry[0], k, False).ratio(),
|
||||
default=(None, None))
|
||||
return entry
|
||||
|
||||
def __getitem__(self, k):
|
||||
v = self.get(k)
|
||||
if v is None:
|
||||
raise KeyError(k)
|
||||
return v
|
||||
|
||||
def __contains__(self, k):
|
||||
return self.get(k) is not None
|
||||
|
||||
def __len__(self):
|
||||
return len(self.entries)
|
||||
|
||||
def __iter__(self):
|
||||
return (v for k, v in self.entries.items())
|
||||
|
||||
info_pattern = re.compile(
|
||||
'^\s*(?:<(?P<level>[^>]*)>'
|
||||
'\s*<(?P<off>[^>]*)>'
|
||||
@@ -925,7 +909,8 @@ def table(Result, results, diff_results=None, *,
|
||||
for r in results_}
|
||||
names_ = list(table_.keys())
|
||||
|
||||
# only sort the children layer if explicitly requested
|
||||
# sort the children layer
|
||||
names_.sort()
|
||||
if sort:
|
||||
for k, reverse in reversed(sort):
|
||||
names_.sort(
|
||||
|
||||
Reference in New Issue
Block a user