diff --git a/pyinstrument/renderers/base.py b/pyinstrument/renderers/base.py index 9c7cab0c..5ec5a2d6 100644 --- a/pyinstrument/renderers/base.py +++ b/pyinstrument/renderers/base.py @@ -112,7 +112,6 @@ def preprocess(self, root_frames: Dict[str, Frame | None] | None) -> Dict[str, F frames = root_frames if frames is not None: for thread_id, frame in frames.items(): - print(thread_id, frame) for processor in self.processors: frame = processor(frame, options=self.processor_options) frames[thread_id] = frame diff --git a/pyinstrument/renderers/console.py b/pyinstrument/renderers/console.py index 11ecc40c..0583bb89 100644 --- a/pyinstrument/renderers/console.py +++ b/pyinstrument/renderers/console.py @@ -63,18 +63,19 @@ def __init__( def render(self, session: Session) -> str: result = self.render_preamble(session) - frame = self.preprocess(session.root_frame()) + frames = self.preprocess(session.root_frames()) indent = ". " if self.short_mode else "" - if frame is None: + if frames is None: result += f"{indent}No samples were recorded.\n" else: - self.root_frame = frame + self.root_frames = frames - if self.flat: - result += self.render_frame_flat(self.root_frame, indent=indent) - else: - result += self.render_frame(self.root_frame, indent=indent, child_indent=indent) + for thread_id, root_frame in frames.items(): + if self.flat: + result += self.render_frame_flat(root_frame, indent=indent) + else: + result += self.render_frame(root_frame, indent=indent, child_indent=indent) result += f"{indent}\n" @@ -126,7 +127,7 @@ def should_render_frame_in_group(self, frame: Frame) -> bool: assert frame.group return ( frame.group.root == frame - or frame.total_self_time > 0.2 * self.root_frame.time + or frame.total_self_time > 0.2 * self.root_frames[frame.thread_id].time or frame in frame.group.exit_frames ) @@ -222,7 +223,7 @@ def walk(frame: Frame): if not self.show_all: # remove nodes that represent less than 0.1% of the total time id_time_pairs = [ - pair for pair in id_time_pairs if pair[1] / self.root_frame.time > 0.001 + pair for pair in id_time_pairs if pair[1] / self.root_frames[frame.thread_id].time > 0.001 ] result = "" @@ -235,7 +236,7 @@ def walk(frame: Frame): def frame_description(self, frame: Frame, *, override_time: float | None = None) -> str: time = override_time if override_time is not None else frame.time - time_color = self._ansi_color_for_time(time) + time_color = self._ansi_color_for_time(frame.thread_id, time) if self.time == "percent_of_total": time_str = f"{self.frame_proportion_of_total_time(time) * 100:.1f}%" @@ -260,13 +261,13 @@ def frame_description(self, frame: Frame, *, override_time: float | None = None) return f"{value_str} {function_str} {code_position_str}" - def frame_proportion_of_total_time(self, time: float) -> float: - if self.root_frame.time == 0: + def frame_proportion_of_total_time(self, thread_id: str, time: float) -> float: + if self.root_frames[thread_id].time == 0: return 1 - return time / self.root_frame.time + return time / self.root_frames[thread_id].time - def _ansi_color_for_time(self, time: float) -> str: - proportion_of_total = self.frame_proportion_of_total_time(time) + def _ansi_color_for_time(self, thread_id: str, time: float) -> str: + proportion_of_total = self.frame_proportion_of_total_time(thread_id, time) if proportion_of_total > 0.6: return self.colors.red diff --git a/pyinstrument/renderers/jsonrenderer.py b/pyinstrument/renderers/jsonrenderer.py index 5f33e490..ad65a9fe 100644 --- a/pyinstrument/renderers/jsonrenderer.py +++ b/pyinstrument/renderers/jsonrenderer.py @@ -63,7 +63,7 @@ def render_frame(self, frame: Frame | None): return "{%s}" % ",".join(property_decls) def render(self, session: Session): - frame = self.preprocess(session.root_frame()) + frames = self.preprocess(session.root_frames()) property_decls: list[str] = [] property_decls.append('"start_time": %f' % session.start_time) @@ -71,7 +71,8 @@ def render(self, session: Session): property_decls.append('"sample_count": %d' % session.sample_count) property_decls.append('"target_description": %s' % encode_str(session.target_description)) property_decls.append('"cpu_time": %f' % session.cpu_time) - property_decls.append('"root_frame": %s' % self.render_frame(frame)) + property_decls.append('"root_frames": {%s}' % ','.join( + self.render_frame(frame) for frame in frames)) return "{%s}\n" % ",".join(property_decls) diff --git a/pyinstrument/renderers/speedscope.py b/pyinstrument/renderers/speedscope.py index 6bb20581..2963c1cb 100644 --- a/pyinstrument/renderers/speedscope.py +++ b/pyinstrument/renderers/speedscope.py @@ -207,6 +207,7 @@ def render_frame(self, frame: Frame | None) -> list[SpeedscopeEvent]: return events_array def render(self, session: Session): + # FIXME: this is known broken with threads frame = self.preprocess(session.root_frame()) id_: str = time.strftime("%Y-%m-%dT%H-%M-%S", time.localtime(session.start_time)) diff --git a/pyinstrument/session.py b/pyinstrument/session.py index 2d1918ef..a0702eb7 100644 --- a/pyinstrument/session.py +++ b/pyinstrument/session.py @@ -96,7 +96,6 @@ def to_json(self, include_frame_records: bool = True): return result - #FIXME: start time is per thread @staticmethod def from_json(json_dict: dict[str, Any]): return Session( @@ -148,7 +147,7 @@ def combine(session1: Session, session2: Session) -> Session: def current_sys_prefixes() -> list[str]: return [sys.prefix, sys.base_prefix, sys.exec_prefix, sys.base_exec_prefix] - # FIXME: remove + # FIXME: remove after converting all to this def root_frame(self, trim_stem: bool = True) -> Frame | None: return None