Skip to content

Commit bf341e7

Browse files
committed
gh-150327: Add Perfetto trace output to profiling.sampling
1 parent 7745710 commit bf341e7

4 files changed

Lines changed: 477 additions & 4 deletions

File tree

Lib/profiling/sampling/cli.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from .stack_collector import CollapsedStackCollector, FlamegraphCollector, DiffFlamegraphCollector
2121
from .heatmap_collector import HeatmapCollector
2222
from .gecko_collector import GeckoCollector
23+
from .perfetto_collector import PerfettoCollector
2324
from .jsonl_collector import JsonlCollector
2425
from .binary_collector import BinaryCollector
2526
from .binary_reader import BinaryReader
@@ -101,6 +102,7 @@ def __call__(self, parser, namespace, values, option_string=None):
101102
"flamegraph": "html",
102103
"diff_flamegraph": "html",
103104
"gecko": "json",
105+
"perfetto": "pftrace",
104106
"heatmap": "html",
105107
"jsonl": "jsonl",
106108
"binary": "bin",
@@ -112,6 +114,7 @@ def __call__(self, parser, namespace, values, option_string=None):
112114
"flamegraph": FlamegraphCollector,
113115
"diff_flamegraph": DiffFlamegraphCollector,
114116
"gecko": GeckoCollector,
117+
"perfetto": PerfettoCollector,
115118
"heatmap": HeatmapCollector,
116119
"jsonl": JsonlCollector,
117120
"binary": BinaryCollector,
@@ -483,6 +486,13 @@ def _add_format_options(parser, include_compression=True, include_binary=True):
483486
dest="format",
484487
help="Generate Gecko format for Firefox Profiler",
485488
)
489+
format_group.add_argument(
490+
"--perfetto",
491+
action="store_const",
492+
const="perfetto",
493+
dest="format",
494+
help="Generate a Perfetto trace (StackSample protos) for the Perfetto UI",
495+
)
486496
format_group.add_argument(
487497
"--heatmap",
488498
action="store_const",
@@ -631,7 +641,7 @@ def _sort_to_mode(sort_choice):
631641

632642
def _create_collector(format_type, sample_interval_usec, skip_idle, opcodes=False,
633643
mode=None, output_file=None, compression='auto',
634-
diff_baseline=None):
644+
diff_baseline=None, target_pid=None):
635645
"""Create the appropriate collector based on format type.
636646
637647
Args:
@@ -645,6 +655,7 @@ def _create_collector(format_type, sample_interval_usec, skip_idle, opcodes=Fals
645655
output_file: Output file path (required for binary format)
646656
compression: Compression type for binary format ('auto', 'zstd', 'none')
647657
diff_baseline: Path to baseline binary file for differential flamegraph
658+
target_pid: PID of the process being profiled
648659
649660
Returns:
650661
A collector instance of the appropriate type
@@ -677,6 +688,13 @@ def _create_collector(format_type, sample_interval_usec, skip_idle, opcodes=Fals
677688
skip_idle = False
678689
return collector_class(sample_interval_usec, skip_idle=skip_idle, opcodes=opcodes)
679690

691+
if format_type == "perfetto":
692+
# Like gecko, keep all threads so idle/GIL-wait state intervals are seen.
693+
return collector_class(
694+
sample_interval_usec, skip_idle=False, opcodes=opcodes,
695+
pid=target_pid,
696+
)
697+
680698
if format_type == "jsonl":
681699
return collector_class(
682700
sample_interval_usec, skip_idle=skip_idle, mode=mode
@@ -957,7 +975,7 @@ def _validate_args(args, parser):
957975
)
958976

959977
# Validate --opcodes is only used with compatible formats
960-
opcodes_compatible_formats = ("live", "gecko", "flamegraph", "diff_flamegraph", "heatmap", "binary")
978+
opcodes_compatible_formats = ("live", "gecko", "perfetto", "flamegraph", "diff_flamegraph", "heatmap", "binary")
961979
if getattr(args, 'opcodes', False) and args.format not in opcodes_compatible_formats:
962980
parser.error(
963981
f"--opcodes is only compatible with {', '.join('--' + f for f in opcodes_compatible_formats)}."
@@ -1177,7 +1195,8 @@ def _handle_attach(args):
11771195
args.format, args.sample_interval_usec, skip_idle, args.opcodes, mode,
11781196
output_file=output_file,
11791197
compression=getattr(args, 'compression', 'auto'),
1180-
diff_baseline=args.diff_baseline
1198+
diff_baseline=args.diff_baseline,
1199+
target_pid=args.pid,
11811200
)
11821201

11831202
with _get_child_monitor_context(args, args.pid):
@@ -1284,7 +1303,8 @@ def _handle_run(args):
12841303
args.format, args.sample_interval_usec, skip_idle, args.opcodes, mode,
12851304
output_file=output_file,
12861305
compression=getattr(args, 'compression', 'auto'),
1287-
diff_baseline=args.diff_baseline
1306+
diff_baseline=args.diff_baseline,
1307+
target_pid=process.pid,
12881308
)
12891309

12901310
with _get_child_monitor_context(args, process.pid):

0 commit comments

Comments
 (0)