-
Notifications
You must be signed in to change notification settings - Fork 258
Add flat/cumulative function summary to handle recursion-heavy profiles #430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -33,6 +33,7 @@ def __init__( | |||||
| unicode: bool = False, | ||||||
| color: bool = False, | ||||||
| flat: bool = False, | ||||||
| show_top: bool = False, | ||||||
| time: LiteralStr["seconds", "percent_of_total"] = "seconds", | ||||||
| flat_time: FlatTimeMode = "self", | ||||||
| short_mode: bool = False, | ||||||
|
|
@@ -41,6 +42,7 @@ def __init__( | |||||
| :param unicode: Use unicode, like box-drawing characters in the output. | ||||||
| :param color: Enable color support, using ANSI color sequences. | ||||||
| :param flat: Display a flat profile instead of a call graph. | ||||||
| :param show_top: Display an aggregated top-functions table. | ||||||
| :param time: How to display the duration of each frame - ``'seconds'`` or ``'percent_of_total'`` | ||||||
| :param flat_time: Show ``'self'`` time or ``'total'`` time (including children) in flat profile. | ||||||
| :param short_mode: Display a short version of the output. | ||||||
|
|
@@ -52,13 +54,17 @@ def __init__( | |||||
| self.unicode = unicode | ||||||
| self.color = color | ||||||
| self.flat = flat | ||||||
| self.show_top = show_top | ||||||
| self.time = time | ||||||
| self.flat_time = flat_time | ||||||
| self.short_mode = short_mode | ||||||
|
|
||||||
| if self.flat and self.timeline: | ||||||
| raise Renderer.MisconfigurationError("Cannot use timeline and flat options together.") | ||||||
|
|
||||||
| if self.flat and self.show_top: | ||||||
| raise Renderer.MisconfigurationError("Cannot use flat and show_top options together.") | ||||||
|
|
||||||
| self.colors = self.colors_enabled if color else self.colors_disabled | ||||||
|
|
||||||
| def render(self, session: Session) -> str: | ||||||
|
|
@@ -73,7 +79,9 @@ def render(self, session: Session) -> str: | |||||
| else: | ||||||
| self.root_frame = frame | ||||||
|
|
||||||
| if self.flat: | ||||||
| if self.show_top: | ||||||
| result += self.render_top_summary(self.root_frame, precision=precision, indent=indent) | ||||||
| elif self.flat: | ||||||
| result += self.render_frame_flat(self.root_frame, precision=precision) | ||||||
| else: | ||||||
|
Comment on lines
+82
to
86
|
||||||
| result += self.render_frame( | ||||||
|
|
@@ -161,6 +169,49 @@ def libraries_for_frames(self, frames: list[Frame]) -> list[str]: | |||||
| libraries.append(library) | ||||||
| return libraries | ||||||
|
|
||||||
| def render_top_summary(self, frame: Frame, precision: int, indent: str = "") -> str: | ||||||
| aggregated: Dict[Tuple[str, str], Dict[str, float]] = {} | ||||||
| frames = [frame] | ||||||
|
|
||||||
| while frames: | ||||||
| current = frames.pop() | ||||||
| function = current.function or "<unknown>" | ||||||
| location = current.code_position_short or "" | ||||||
|
||||||
| location = current.code_position_short or "" | |
| location = current.code_position_short() or "" |
Copilot
AI
Mar 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
total_time is being aggregated by summing current.time for every frame occurrence. For recursive call stacks, inclusive times overlap across nested invocations, so this will significantly over-count (and can exceed the root total), making the new “top functions” view misleading for the recursion-heavy use case this PR targets. Consider defining “total” in a way that avoids double-counting recursion (e.g., subtract time from recursive children with the same key, or compute cumulative time on a call graph with cycle handling, similar to pprof).
Copilot
AI
Mar 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
render_top_summary() prints every aggregated row unconditionally. For large profiles this can produce extremely large outputs, and it also ignores the existing show_all behavior used by render_frame_flat() (0.1% threshold). Consider adding a default filter/limit (e.g., drop entries below a % threshold or show top N) and let show_all=True disable that filter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A new
show_topoption was added toConsoleRenderer, but it isn’t currently exposed throughProfiler.print()/Profiler.output_text()(and the CLI option plumbing appears absent). This will also breaktest_profiler_convenience_methods_have_all_options_available(it asserts Profiler convenience methods mirror renderer signatures). Addshow_top(with matching default/annotation) to the Profiler convenience methods and any option parsing that constructs aConsoleRenderer.