From ac79c88c6fb27bac467c1862ba3c07dc3e7b8051 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sat, 7 Dec 2024 00:55:00 -0600 Subject: [PATCH] scripts: Improved cycle detection notes in scripts - Prevented childrenof memoization from hiding the source of a detected cycle. - Deduplicated multiple cycle detected notes. - Fixed note rendering when last column does not have a notes list. Currently this only happens when entry is None (no results). --- scripts/code.py | 9 +++++++-- scripts/cov.py | 9 +++++++-- scripts/csv.py | 9 +++++++-- scripts/ctx.py | 37 +++++++++++++++++++++++-------------- scripts/data.py | 9 +++++++-- scripts/perf.py | 9 +++++++-- scripts/perfbd.py | 9 +++++++-- scripts/stack.py | 28 +++++++++++++++++++--------- scripts/structs.py | 9 +++++++-- 9 files changed, 91 insertions(+), 37 deletions(-) diff --git a/scripts/code.py b/scripts/code.py index 9c54e2b2..be982d74 100755 --- a/scripts/code.py +++ b/scripts/code.py @@ -889,8 +889,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts diff --git a/scripts/cov.py b/scripts/cov.py index f02cd0e3..452d386d 100755 --- a/scripts/cov.py +++ b/scripts/cov.py @@ -623,8 +623,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts diff --git a/scripts/csv.py b/scripts/csv.py index 1c25399a..9e6e43ce 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -1638,8 +1638,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts diff --git a/scripts/ctx.py b/scripts/ctx.py index 83c5df40..858ebf52 100755 --- a/scripts/ctx.py +++ b/scripts/ctx.py @@ -157,7 +157,8 @@ class CtxResult(co.namedtuple('CtxResult', [ else other.i if self.i is None else min(self.i, other.i), self.children + other.children, - self.notes + other.notes) + list(co.OrderedDict.fromkeys(it.chain( + self.notes, other.notes)).keys())) def openio(path, mode='r', buffering=-1): @@ -681,7 +682,7 @@ def collect(obj_paths, *, def childrenof(entry, seen=set()): # found a cycle? stop here if entry.off in seen: - return [], ['cycle detected'] + return [], ['cycle detected'], True # cached? if not hasattr(childrenof, 'cache'): childrenof.cache = {} @@ -690,7 +691,7 @@ def collect(obj_paths, *, # pointer? deref and include size if entry.tag == 'DW_TAG_pointer_type': - children, notes = [], [] + children, notes, dirty = [], [], False if 'DW_AT_type' in entry: type = info[int(entry['DW_AT_type'].strip('<>'), 0)] # skip modifiers to try to find name @@ -702,8 +703,9 @@ def collect(obj_paths, *, and type.tag != 'DW_TAG_subroutine_type'): name_ = type.name size_ = sizeof(type, seen | {entry.off}) - children_, notes_ = childrenof( + children_, notes_, dirty_ = childrenof( type, seen | {entry.off}) + dirty = dirty or dirty_ children.append(CtxResult(file, name_, size_, children=children_, notes=notes_)) @@ -711,14 +713,15 @@ def collect(obj_paths, *, elif entry.tag in { 'DW_TAG_structure_type', 'DW_TAG_union_type'}: - children, notes = [], [] + children, notes, dirty = [], [], False for child in entry.children: if child.tag != 'DW_TAG_member': continue name_ = child.name size_ = sizeof(child, seen | {entry.off}) - children_, notes_ = childrenof( + children_, notes_, dirty_ = childrenof( child, seen | {entry.off}) + dirty = dirty or dirty_ children.append(CtxResult(file, name_, size_, i=child.off, children=children_, @@ -727,7 +730,7 @@ def collect(obj_paths, *, elif entry.tag in { 'DW_TAG_base_type', 'DW_TAG_subroutine_type'}: - children, notes = [], [] + children, notes, dirty = [], [], False # a modifier? elif (entry.tag in { 'DW_TAG_typedef', @@ -740,17 +743,18 @@ def collect(obj_paths, *, 'DW_TAG_restrict_type'} and 'DW_AT_type' in entry): type = int(entry['DW_AT_type'].strip('<>'), 0) - children, notes = childrenof( + children, notes, dirty = childrenof( info[type], seen | {entry.off}) # void? elif ('DW_AT_type' not in entry and 'DW_AT_byte_size' not in entry): - children, notes = [], [] + children, notes = [], [], False else: assert False, "Unknown dwarf entry? %r" % entry.tag - childrenof.cache[entry.off] = children, notes - return children, notes + if not dirty: + childrenof.cache[entry.off] = children, notes, dirty + return children, notes, dirty # find each function's context for sym in syms: @@ -796,7 +800,7 @@ def collect(obj_paths, *, size_ = sizeof(param) # find children, recursing if necessary - children_, notes_ = childrenof(param) + children_, notes_, _ = childrenof(param) params.append(CtxResult(file, name_, size_, i=param.off, @@ -1078,8 +1082,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts diff --git a/scripts/data.py b/scripts/data.py index 068aefb5..4e823f2c 100755 --- a/scripts/data.py +++ b/scripts/data.py @@ -889,8 +889,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts diff --git a/scripts/perf.py b/scripts/perf.py index 7091fc6b..d029316f 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -1069,8 +1069,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts diff --git a/scripts/perfbd.py b/scripts/perfbd.py index a8718548..cd02425b 100755 --- a/scripts/perfbd.py +++ b/scripts/perfbd.py @@ -1034,8 +1034,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts diff --git a/scripts/stack.py b/scripts/stack.py index 7bcf71bb..69e06047 100755 --- a/scripts/stack.py +++ b/scripts/stack.py @@ -153,7 +153,8 @@ class StackResult(co.namedtuple('StackResult', [ self.frame + other.frame, max(self.limit, other.limit), self.children + other.children, - self.notes + other.notes) + list(co.OrderedDict.fromkeys(it.chain( + self.notes, other.notes)).keys())) def openio(path, mode='r', buffering=-1): @@ -873,7 +874,7 @@ def collect(obj_paths, *, def limitof(func, seen=set()): # found a cycle? stop here if id(func) in seen: - return 0, 0 + return 0, mt.inf # cached? if not hasattr(limitof, 'cache'): limitof.cache = {} @@ -903,7 +904,7 @@ def collect(obj_paths, *, def childrenof(func, seen=set()): # found a cycle? stop here if id(func) in seen: - return [], ['cycle detected'] + return [], ['cycle detected'], True # cached? if not hasattr(childrenof, 'cache'): childrenof.cache = {} @@ -912,17 +913,20 @@ def collect(obj_paths, *, # find children recursively children = [] + dirty = False for addr, callee in func['calls']: file_ = callee['file'] name_ = callee['sym'].name frame_, limit_ = limitof(callee, seen | {id(func)}) - children_, notes_ = childrenof(callee, seen | {id(func)}) + children_, notes_, dirty_ = childrenof(callee, seen | {id(func)}) + dirty = dirty or dirty_ children.append(StackResult(file_, name_, frame_, limit_, children=children_, notes=notes_)) - childrenof.cache[id(func)] = children, [] - return children, [] + if not dirty: + childrenof.cache[id(func)] = children, [], dirty + return children, [], dirty # build results results = [] @@ -930,7 +934,7 @@ def collect(obj_paths, *, file = func['file'] name = func['sym'].name frame, limit = limitof(func) - children, notes = childrenof(func) + children, notes, _ = childrenof(func) results.append(StackResult(file, name, frame, limit, children=children, @@ -1205,8 +1209,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts @@ -1419,6 +1428,7 @@ def main(obj_paths, by=by if by is not None else ['function'], fields=fields, sort=sort, + detect_cycles=False, **args) # error on recursion diff --git a/scripts/structs.py b/scripts/structs.py index 09f5fd68..11bf39aa 100755 --- a/scripts/structs.py +++ b/scripts/structs.py @@ -906,8 +906,13 @@ def table(Result, results, diff_results=None, *, getattr(r, k, None), getattr(diff_r, k, None))))) # append any notes - if hasattr(Result, '_notes'): - entry[-1][1].extend(getattr(r, Result._notes)) + if hasattr(Result, '_notes') and r is not None: + notes = getattr(r, Result._notes) + if isinstance(entry[-1], tuple): + entry[-1] = (entry[-1][0], entry[-1][1] + notes) + else: + entry[-1] = (entry[-1], notes) + return entry # recursive entry helper, only used by some scripts