diff --git a/scripts/code.py b/scripts/code.py index 8b3f112d..9c54e2b2 100755 --- a/scripts/code.py +++ b/scripts/code.py @@ -393,6 +393,24 @@ class DwarfEntry: else: return None + @ft.cached_property + def addr(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_low_pc' in self): + return int(self['DW_AT_low_pc'], 0) + else: + return None + + @ft.cached_property + def size(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_high_pc' in self): + # this looks wrong, but high_pc does store the size, + # for whatever reason + return int(self['DW_AT_high_pc'], 0) + else: + return None + def info(self, tags=None): # recursively flatten children def flatten(entry): @@ -412,10 +430,42 @@ class DwarfInfo: self.entries = entries def get(self, k, d=None): - # allow lookup by both offset and dwarf name - if not isinstance(k, str): + # allow lookup by offset, symbol, or dwarf name + if not isinstance(k, str) and not hasattr(k, 'addr'): return self.entries.get(k, d) + elif hasattr(k, 'addr'): + import bisect + + # organize by address + if not hasattr(self, '_by_addr'): + # sort and keep largest/first when duplicates + entries = [entry + for entry in self.entries.values() + if entry.addr is not None + and entry.size is not None] + entries.sort(key=lambda x: (x.addr, -x.size)) + + by_addr = [] + for entry in entries: + if (len(by_addr) == 0 + or by_addr[-1].addr != entry.addr): + by_addr.append(entry) + self._by_addr = by_addr + + # find entry by range + i = bisect.bisect(self._by_addr, k.addr, + key=lambda x: x.addr) + # check that we're actually in this entry's size + if (i > 0 + and k.addr + < self._by_addr[i-1].addr + + self._by_addr[i-1].size): + return self._by_addr[i-1] + else: + # fallback to lookup by name + return self.get(k.name, d) + else: # organize entries by name if not hasattr(self, '_by_name'): @@ -548,7 +598,7 @@ def collect(obj_paths, *, # find best matching dwarf entry, this may be slightly different # due to optimizations - entry = info.get(sym.name) + entry = info.get(sym) # if we have no file guess from obj path if entry is not None and 'DW_AT_decl_file' in entry: diff --git a/scripts/ctx.py b/scripts/ctx.py index 138990f9..83c5df40 100755 --- a/scripts/ctx.py +++ b/scripts/ctx.py @@ -402,6 +402,24 @@ class DwarfEntry: else: return None + @ft.cached_property + def addr(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_low_pc' in self): + return int(self['DW_AT_low_pc'], 0) + else: + return None + + @ft.cached_property + def size(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_high_pc' in self): + # this looks wrong, but high_pc does store the size, + # for whatever reason + return int(self['DW_AT_high_pc'], 0) + else: + return None + def info(self, tags=None): # recursively flatten children def flatten(entry): @@ -421,10 +439,42 @@ class DwarfInfo: self.entries = entries def get(self, k, d=None): - # allow lookup by both offset and dwarf name - if not isinstance(k, str): + # allow lookup by offset, symbol, or dwarf name + if not isinstance(k, str) and not hasattr(k, 'addr'): return self.entries.get(k, d) + elif hasattr(k, 'addr'): + import bisect + + # organize by address + if not hasattr(self, '_by_addr'): + # sort and keep largest/first when duplicates + entries = [entry + for entry in self.entries.values() + if entry.addr is not None + and entry.size is not None] + entries.sort(key=lambda x: (x.addr, -x.size)) + + by_addr = [] + for entry in entries: + if (len(by_addr) == 0 + or by_addr[-1].addr != entry.addr): + by_addr.append(entry) + self._by_addr = by_addr + + # find entry by range + i = bisect.bisect(self._by_addr, k.addr, + key=lambda x: x.addr) + # check that we're actually in this entry's size + if (i > 0 + and k.addr + < self._by_addr[i-1].addr + + self._by_addr[i-1].size): + return self._by_addr[i-1] + else: + # fallback to lookup by name + return self.get(k.name, d) + else: # organize entries by name if not hasattr(self, '_by_name'): @@ -709,7 +759,7 @@ def collect(obj_paths, *, continue # find best matching dwarf entry - entry = info.get(sym.name) + entry = info.get(sym) # skip non-functions if entry is None or entry.tag != 'DW_TAG_subprogram': diff --git a/scripts/data.py b/scripts/data.py index 91a101b0..068aefb5 100755 --- a/scripts/data.py +++ b/scripts/data.py @@ -393,6 +393,24 @@ class DwarfEntry: else: return None + @ft.cached_property + def addr(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_low_pc' in self): + return int(self['DW_AT_low_pc'], 0) + else: + return None + + @ft.cached_property + def size(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_high_pc' in self): + # this looks wrong, but high_pc does store the size, + # for whatever reason + return int(self['DW_AT_high_pc'], 0) + else: + return None + def info(self, tags=None): # recursively flatten children def flatten(entry): @@ -412,10 +430,42 @@ class DwarfInfo: self.entries = entries def get(self, k, d=None): - # allow lookup by both offset and dwarf name - if not isinstance(k, str): + # allow lookup by offset, symbol, or dwarf name + if not isinstance(k, str) and not hasattr(k, 'addr'): return self.entries.get(k, d) + elif hasattr(k, 'addr'): + import bisect + + # organize by address + if not hasattr(self, '_by_addr'): + # sort and keep largest/first when duplicates + entries = [entry + for entry in self.entries.values() + if entry.addr is not None + and entry.size is not None] + entries.sort(key=lambda x: (x.addr, -x.size)) + + by_addr = [] + for entry in entries: + if (len(by_addr) == 0 + or by_addr[-1].addr != entry.addr): + by_addr.append(entry) + self._by_addr = by_addr + + # find entry by range + i = bisect.bisect(self._by_addr, k.addr, + key=lambda x: x.addr) + # check that we're actually in this entry's size + if (i > 0 + and k.addr + < self._by_addr[i-1].addr + + self._by_addr[i-1].size): + return self._by_addr[i-1] + else: + # fallback to lookup by name + return self.get(k.name, d) + else: # organize entries by name if not hasattr(self, '_by_name'): @@ -548,7 +598,7 @@ def collect(obj_paths, *, # find best matching dwarf entry, this may be slightly different # due to optimizations - entry = info.get(sym.name) + entry = info.get(sym) # if we have no file guess from obj path if entry is not None and 'DW_AT_decl_file' in entry: diff --git a/scripts/structs.py b/scripts/structs.py index 91b4e18f..09f5fd68 100755 --- a/scripts/structs.py +++ b/scripts/structs.py @@ -264,6 +264,24 @@ class DwarfEntry: else: return None + @ft.cached_property + def addr(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_low_pc' in self): + return int(self['DW_AT_low_pc'], 0) + else: + return None + + @ft.cached_property + def size(self): + if (self.tag == 'DW_TAG_subprogram' + and 'DW_AT_high_pc' in self): + # this looks wrong, but high_pc does store the size, + # for whatever reason + return int(self['DW_AT_high_pc'], 0) + else: + return None + def info(self, tags=None): # recursively flatten children def flatten(entry): @@ -283,10 +301,42 @@ class DwarfInfo: self.entries = entries def get(self, k, d=None): - # allow lookup by both offset and dwarf name - if not isinstance(k, str): + # allow lookup by offset, symbol, or dwarf name + if not isinstance(k, str) and not hasattr(k, 'addr'): return self.entries.get(k, d) + elif hasattr(k, 'addr'): + import bisect + + # organize by address + if not hasattr(self, '_by_addr'): + # sort and keep largest/first when duplicates + entries = [entry + for entry in self.entries.values() + if entry.addr is not None + and entry.size is not None] + entries.sort(key=lambda x: (x.addr, -x.size)) + + by_addr = [] + for entry in entries: + if (len(by_addr) == 0 + or by_addr[-1].addr != entry.addr): + by_addr.append(entry) + self._by_addr = by_addr + + # find entry by range + i = bisect.bisect(self._by_addr, k.addr, + key=lambda x: x.addr) + # check that we're actually in this entry's size + if (i > 0 + and k.addr + < self._by_addr[i-1].addr + + self._by_addr[i-1].size): + return self._by_addr[i-1] + else: + # fallback to lookup by name + return self.get(k.name, d) + else: # organize entries by name if not hasattr(self, '_by_name'):