scripts: Option for result scripts to force children ordering
This extends the recursive part of the table renderer to sort children by the optional "i" field, if available. Note this only affects children entries. The top-level entries are strictly ordered by the relevant "by" fields. I just haven't seen a use case for this yet, and not sorting "i" at the top-level reduces that number of things that can go wrong for scripts without children. --- This also rewrites -t/--hot to take advantage of children ordering by injecting a totally-no-hacky HotResult subclass. Now -t/--hot should be strictly ordered by the call depth! Though note entries that share "by" fields are still merged... This also gives us a way to introduce the "cycle detected" note and respect -z/--depth, so overall a big improvement for -t/--hot.
This commit is contained in:
+59
-24
@@ -506,34 +506,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -690,7 +725,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+59
-24
@@ -410,34 +410,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -594,7 +629,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+59
-24
@@ -1422,34 +1422,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -1606,7 +1641,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+78
-33
@@ -129,7 +129,7 @@ class RInt(co.namedtuple('RInt', 'x')):
|
|||||||
class CtxResult(co.namedtuple('CtxResult', [
|
class CtxResult(co.namedtuple('CtxResult', [
|
||||||
'file', 'function',
|
'file', 'function',
|
||||||
'size',
|
'size',
|
||||||
'children', 'notes'])):
|
'i', 'children', 'notes'])):
|
||||||
_by = ['file', 'function']
|
_by = ['file', 'function']
|
||||||
_fields = ['size']
|
_fields = ['size']
|
||||||
_sort = ['size']
|
_sort = ['size']
|
||||||
@@ -137,15 +137,19 @@ class CtxResult(co.namedtuple('CtxResult', [
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
def __new__(cls, file='', function='', size=0,
|
def __new__(cls, file='', function='', size=0,
|
||||||
children=None, notes=None):
|
i=None, children=None, notes=None):
|
||||||
return super().__new__(cls, file, function,
|
return super().__new__(cls, file, function,
|
||||||
RInt(size),
|
RInt(size),
|
||||||
|
i,
|
||||||
children if children is not None else [],
|
children if children is not None else [],
|
||||||
notes if notes is not None else [])
|
notes if notes is not None else [])
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return CtxResult(self.file, self.function,
|
return CtxResult(self.file, self.function,
|
||||||
max(self.size, other.size),
|
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.children + other.children,
|
||||||
self.notes + other.notes)
|
self.notes + other.notes)
|
||||||
|
|
||||||
@@ -575,8 +579,9 @@ def collect(obj_paths, *,
|
|||||||
size_ = sizeof(type, seen | {entry.off})
|
size_ = sizeof(type, seen | {entry.off})
|
||||||
children_, notes_ = childrenof(
|
children_, notes_ = childrenof(
|
||||||
type, seen | {entry.off})
|
type, seen | {entry.off})
|
||||||
children.append(CtxResult(
|
children.append(CtxResult(file, name_, size_,
|
||||||
file, name_, size_, children_, notes_))
|
children=children_,
|
||||||
|
notes=notes_))
|
||||||
# struct? union?
|
# struct? union?
|
||||||
elif entry.tag in {
|
elif entry.tag in {
|
||||||
'DW_TAG_structure_type',
|
'DW_TAG_structure_type',
|
||||||
@@ -589,8 +594,10 @@ def collect(obj_paths, *,
|
|||||||
size_ = sizeof(child, seen | {entry.off})
|
size_ = sizeof(child, seen | {entry.off})
|
||||||
children_, notes_ = childrenof(
|
children_, notes_ = childrenof(
|
||||||
child, seen | {entry.off})
|
child, seen | {entry.off})
|
||||||
children.append(CtxResult(
|
children.append(CtxResult(file, name_, size_,
|
||||||
file, name_, size_, children_, notes_))
|
i=child.off,
|
||||||
|
children=children_,
|
||||||
|
notes=notes_))
|
||||||
# base type? function pointer?
|
# base type? function pointer?
|
||||||
elif entry.tag in {
|
elif entry.tag in {
|
||||||
'DW_TAG_base_type',
|
'DW_TAG_base_type',
|
||||||
@@ -656,13 +663,16 @@ def collect(obj_paths, *,
|
|||||||
# find children, recursing if necessary
|
# find children, recursing if necessary
|
||||||
children_, notes_ = childrenof(param)
|
children_, notes_ = childrenof(param)
|
||||||
|
|
||||||
params.append(CtxResult(
|
params.append(CtxResult(file, name_, size_,
|
||||||
file, name_, size_, children_, notes_))
|
i=param.off,
|
||||||
|
children=children_,
|
||||||
|
notes=notes_))
|
||||||
|
|
||||||
# context = sum of params
|
# context = sum of params
|
||||||
name = entry.name
|
name = entry.name
|
||||||
size = sum((param.size for param in params), start=RInt(0))
|
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
|
return results
|
||||||
|
|
||||||
@@ -726,34 +736,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -910,7 +955,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+59
-24
@@ -506,34 +506,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -690,7 +725,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+59
-24
@@ -812,34 +812,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -996,7 +1031,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+59
-24
@@ -775,34 +775,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -959,7 +994,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+60
-25
@@ -286,7 +286,7 @@ def collect(ci_paths, *,
|
|||||||
results = {}
|
results = {}
|
||||||
for source, (s_file, s_function, frame, _) in callgraph.items():
|
for source, (s_file, s_function, frame, _) in callgraph.items():
|
||||||
limit = find_limit(source)
|
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
|
# connect parents to their children, this may create a fully cyclic graph
|
||||||
# in the case of recursion
|
# in the case of recursion
|
||||||
@@ -358,34 +358,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -542,7 +577,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
+72
-29
@@ -129,7 +129,7 @@ class RInt(co.namedtuple('RInt', 'x')):
|
|||||||
class StructResult(co.namedtuple('StructResult', [
|
class StructResult(co.namedtuple('StructResult', [
|
||||||
'file', 'struct',
|
'file', 'struct',
|
||||||
'size', 'align',
|
'size', 'align',
|
||||||
'children'])):
|
'i', 'children'])):
|
||||||
_by = ['file', 'struct']
|
_by = ['file', 'struct']
|
||||||
_fields = ['size', 'align']
|
_fields = ['size', 'align']
|
||||||
_sort = ['size', 'align']
|
_sort = ['size', 'align']
|
||||||
@@ -137,15 +137,19 @@ class StructResult(co.namedtuple('StructResult', [
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
def __new__(cls, file='', struct='', size=0, align=0,
|
def __new__(cls, file='', struct='', size=0, align=0,
|
||||||
children=None):
|
i=None, children=None):
|
||||||
return super().__new__(cls, file, struct,
|
return super().__new__(cls, file, struct,
|
||||||
RInt(size), RInt(align),
|
RInt(size), RInt(align),
|
||||||
|
i,
|
||||||
children if children is not None else [])
|
children if children is not None else [])
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return StructResult(self.file, self.struct,
|
return StructResult(self.file, self.struct,
|
||||||
self.size + other.size,
|
self.size + other.size,
|
||||||
max(self.align, other.align),
|
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)
|
self.children + other.children)
|
||||||
|
|
||||||
|
|
||||||
@@ -456,7 +460,9 @@ def collect(obj_paths, *,
|
|||||||
align_ = alignof(child)
|
align_ = alignof(child)
|
||||||
children_ = childrenof(child)
|
children_ = childrenof(child)
|
||||||
children.append(StructResult(
|
children.append(StructResult(
|
||||||
file, name_, size_, align_, children_))
|
file, name_, size_, align_,
|
||||||
|
i=child.off,
|
||||||
|
children=children_))
|
||||||
# indirect type?
|
# indirect type?
|
||||||
elif 'DW_AT_type' in entry:
|
elif 'DW_AT_type' in entry:
|
||||||
type = int(entry['DW_AT_type'].strip('<>'), 0)
|
type = int(entry['DW_AT_type'].strip('<>'), 0)
|
||||||
@@ -519,11 +525,13 @@ def collect(obj_paths, *,
|
|||||||
# these separately
|
# these separately
|
||||||
if entry.tag == 'DW_TAG_typedef':
|
if entry.tag == 'DW_TAG_typedef':
|
||||||
typedefs[entry.off] = StructResult(
|
typedefs[entry.off] = StructResult(
|
||||||
file, name, size, align, children)
|
file, name, size, align,
|
||||||
|
children=children)
|
||||||
typedefed.add(int(entry['DW_AT_type'].strip('<>'), 0))
|
typedefed.add(int(entry['DW_AT_type'].strip('<>'), 0))
|
||||||
else:
|
else:
|
||||||
types[entry.off] = StructResult(
|
types[entry.off] = StructResult(
|
||||||
file, name, size, align, children)
|
file, name, size, align,
|
||||||
|
children=children)
|
||||||
|
|
||||||
# let typedefs take priority
|
# let typedefs take priority
|
||||||
results.extend(typedefs.values())
|
results.extend(typedefs.values())
|
||||||
@@ -593,34 +601,69 @@ def table(Result, results, diff_results=None, *,
|
|||||||
if diff_results is not None:
|
if diff_results is not None:
|
||||||
diff_results = fold(Result, diff_results, by=by)
|
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:
|
if hot:
|
||||||
def rec_hot(results_, seen=set()):
|
# subclass to reintroduce __dict__
|
||||||
if not results_:
|
class HotResult(Result):
|
||||||
return []
|
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_,
|
def __add__(self, other):
|
||||||
key=lambda r: tuple(
|
return HotResult(
|
||||||
tuple((getattr(r, k),)
|
Result.__add__(self, other),
|
||||||
if getattr(r, k, None) is not None
|
self.i if other.i is None
|
||||||
else ()
|
else other.i if self.i is None
|
||||||
for k in (
|
else min(self.i, other.i),
|
||||||
[k] if k else [
|
self.children + other.children,
|
||||||
k for k in Result._sort
|
self.notes + other.notes)
|
||||||
if k in fields])
|
|
||||||
if k in fields)
|
|
||||||
for k in it.chain(hot, [None])))
|
|
||||||
|
|
||||||
# found a cycle?
|
def hot_(results_, depth_):
|
||||||
if (detect_cycles
|
hot_ = []
|
||||||
and tuple(getattr(r, k) for k in Result._by) in seen):
|
def recurse(results_, depth_, seen=set()):
|
||||||
return []
|
nonlocal hot_
|
||||||
|
if not results_:
|
||||||
|
return
|
||||||
|
|
||||||
return [r._replace(children=[])] + rec_hot(
|
# find the hottest result
|
||||||
r.children,
|
r = max(results_,
|
||||||
seen | {tuple(getattr(r, k) for k in Result._by)})
|
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
|
# organize by name
|
||||||
table = {
|
table = {
|
||||||
@@ -777,7 +820,7 @@ def table(Result, results, diff_results=None, *,
|
|||||||
names_ = list(table_.keys())
|
names_ = list(table_.keys())
|
||||||
|
|
||||||
# sort the children layer
|
# sort the children layer
|
||||||
names_.sort()
|
names_.sort(key=lambda n: (getattr(table_[n], 'i', None), n))
|
||||||
if sort:
|
if sort:
|
||||||
for k, reverse in reversed(sort):
|
for k, reverse in reversed(sort):
|
||||||
names_.sort(
|
names_.sort(
|
||||||
|
|||||||
Reference in New Issue
Block a user