diff --git a/scripts/code.py b/scripts/code.py index 23fe2945..43ae73de 100755 --- a/scripts/code.py +++ b/scripts/code.py @@ -506,34 +506,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -690,7 +725,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/cov.py b/scripts/cov.py index 11c4e59d..fb62ba97 100755 --- a/scripts/cov.py +++ b/scripts/cov.py @@ -410,34 +410,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -594,7 +629,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/csv.py b/scripts/csv.py index 07374c56..c45d0042 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -1422,34 +1422,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -1606,7 +1641,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/ctx.py b/scripts/ctx.py index e77c8c62..80fb0866 100755 --- a/scripts/ctx.py +++ b/scripts/ctx.py @@ -129,7 +129,7 @@ class RInt(co.namedtuple('RInt', 'x')): class CtxResult(co.namedtuple('CtxResult', [ 'file', 'function', 'size', - 'children', 'notes'])): + 'i', 'children', 'notes'])): _by = ['file', 'function'] _fields = ['size'] _sort = ['size'] @@ -137,15 +137,19 @@ class CtxResult(co.namedtuple('CtxResult', [ __slots__ = () def __new__(cls, file='', function='', size=0, - children=None, notes=None): + i=None, children=None, notes=None): return super().__new__(cls, file, function, RInt(size), + i, children if children is not None else [], notes if notes is not None else []) def __add__(self, other): return CtxResult(self.file, self.function, max(self.size, other.size), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), self.children + other.children, self.notes + other.notes) @@ -575,8 +579,9 @@ def collect(obj_paths, *, size_ = sizeof(type, seen | {entry.off}) children_, notes_ = childrenof( type, seen | {entry.off}) - children.append(CtxResult( - file, name_, size_, children_, notes_)) + children.append(CtxResult(file, name_, size_, + children=children_, + notes=notes_)) # struct? union? elif entry.tag in { 'DW_TAG_structure_type', @@ -589,8 +594,10 @@ def collect(obj_paths, *, size_ = sizeof(child, seen | {entry.off}) children_, notes_ = childrenof( child, seen | {entry.off}) - children.append(CtxResult( - file, name_, size_, children_, notes_)) + children.append(CtxResult(file, name_, size_, + i=child.off, + children=children_, + notes=notes_)) # base type? function pointer? elif entry.tag in { 'DW_TAG_base_type', @@ -656,13 +663,16 @@ def collect(obj_paths, *, # find children, recursing if necessary children_, notes_ = childrenof(param) - params.append(CtxResult( - file, name_, size_, children_, notes_)) + params.append(CtxResult(file, name_, size_, + i=param.off, + children=children_, + notes=notes_)) # context = sum of params name = entry.name size = sum((param.size for param in params), start=RInt(0)) - results.append(CtxResult(file, name, size, params)) + results.append(CtxResult(file, name, size, + children=params)) return results @@ -726,34 +736,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -910,7 +955,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/data.py b/scripts/data.py index 69bf9a07..7b73be09 100755 --- a/scripts/data.py +++ b/scripts/data.py @@ -506,34 +506,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -690,7 +725,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/perf.py b/scripts/perf.py index 9e3f19a8..8b20a1f0 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -812,34 +812,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -996,7 +1031,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/perfbd.py b/scripts/perfbd.py index 5c0d0a6b..18d7817d 100755 --- a/scripts/perfbd.py +++ b/scripts/perfbd.py @@ -775,34 +775,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -959,7 +994,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/stack.py b/scripts/stack.py index 3bd11c74..d336bd18 100755 --- a/scripts/stack.py +++ b/scripts/stack.py @@ -286,7 +286,7 @@ def collect(ci_paths, *, results = {} for source, (s_file, s_function, frame, _) in callgraph.items(): limit = find_limit(source) - results[source] = StackResult(s_file, s_function, frame, limit, []) + results[source] = StackResult(s_file, s_function, frame, limit) # connect parents to their children, this may create a fully cyclic graph # in the case of recursion @@ -358,34 +358,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -542,7 +577,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort( diff --git a/scripts/structs.py b/scripts/structs.py index f1b6266e..ffc8b17e 100755 --- a/scripts/structs.py +++ b/scripts/structs.py @@ -129,7 +129,7 @@ class RInt(co.namedtuple('RInt', 'x')): class StructResult(co.namedtuple('StructResult', [ 'file', 'struct', 'size', 'align', - 'children'])): + 'i', 'children'])): _by = ['file', 'struct'] _fields = ['size', 'align'] _sort = ['size', 'align'] @@ -137,15 +137,19 @@ class StructResult(co.namedtuple('StructResult', [ __slots__ = () def __new__(cls, file='', struct='', size=0, align=0, - children=None): + i=None, children=None): return super().__new__(cls, file, struct, RInt(size), RInt(align), + i, children if children is not None else []) def __add__(self, other): return StructResult(self.file, self.struct, self.size + other.size, max(self.align, other.align), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), self.children + other.children) @@ -456,7 +460,9 @@ def collect(obj_paths, *, align_ = alignof(child) children_ = childrenof(child) children.append(StructResult( - file, name_, size_, align_, children_)) + file, name_, size_, align_, + i=child.off, + children=children_)) # indirect type? elif 'DW_AT_type' in entry: type = int(entry['DW_AT_type'].strip('<>'), 0) @@ -519,11 +525,13 @@ def collect(obj_paths, *, # these separately if entry.tag == 'DW_TAG_typedef': typedefs[entry.off] = StructResult( - file, name, size, align, children) + file, name, size, align, + children=children) typedefed.add(int(entry['DW_AT_type'].strip('<>'), 0)) else: types[entry.off] = StructResult( - file, name, size, align, children) + file, name, size, align, + children=children) # let typedefs take priority results.extend(typedefs.values()) @@ -593,34 +601,69 @@ def table(Result, results, diff_results=None, *, if diff_results is not None: diff_results = fold(Result, diff_results, by=by) - # reduce children to hot paths? + # reduce children to hot paths? only used by some scripts if hot: - def rec_hot(results_, seen=set()): - if not results_: - return [] + # subclass to reintroduce __dict__ + class HotResult(Result): + i = None + children = None + notes = None + def __new__(cls, r, i=None, children=None, notes=None): + self = HotResult._make(r) + self.i = i + self.children = children if children is not None else [] + self.notes = notes if notes is not None else [] + if hasattr(r, 'notes'): + self.notes.extend(r.notes) + return self - r = max(results_, - key=lambda r: tuple( - tuple((getattr(r, k),) - if getattr(r, k, None) is not None - else () - for k in ( - [k] if k else [ - k for k in Result._sort - if k in fields]) - if k in fields) - for k in it.chain(hot, [None]))) + def __add__(self, other): + return HotResult( + Result.__add__(self, other), + self.i if other.i is None + else other.i if self.i is None + else min(self.i, other.i), + self.children + other.children, + self.notes + other.notes) - # found a cycle? - if (detect_cycles - and tuple(getattr(r, k) for k in Result._by) in seen): - return [] + def hot_(results_, depth_): + hot_ = [] + def recurse(results_, depth_, seen=set()): + nonlocal hot_ + if not results_: + return - return [r._replace(children=[])] + rec_hot( - r.children, - seen | {tuple(getattr(r, k) for k in Result._by)}) + # find the hottest result + r = max(results_, + key=lambda r: tuple( + tuple((getattr(r, k),) + if getattr(r, k, None) is not None + else () + for k in ( + [k] if k else [ + k for k in Result._sort + if k in fields]) + if k in fields) + for k in it.chain(hot, [None]))) + hot_.append(HotResult(r, i=len(hot_))) - results = [r._replace(children=rec_hot(r.children)) for r in results] + # found a cycle? + if (detect_cycles + and tuple(getattr(r, k) for k in Result._by) in seen): + hot_[-1].notes.append('cycle detected') + return + + # recurse? + if depth_ > 1: + recurse(r.children, + depth_-1, + seen | {tuple(getattr(r, k) for k in Result._by)}) + + recurse(results_, depth_) + return hot_ + + results = [r._replace(children=hot_(r.children, depth-1)) + for r in results] # organize by name table = { @@ -777,7 +820,7 @@ def table(Result, results, diff_results=None, *, names_ = list(table_.keys()) # sort the children layer - names_.sort() + names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n)) if sort: for k, reverse in reversed(sort): names_.sort(