|
10 | 10 | import subprocess |
11 | 11 | import sys |
12 | 12 | import time |
| 13 | +import webbrowser |
13 | 14 | from contextlib import nullcontext |
14 | 15 |
|
15 | 16 | from .errors import SamplingUnknownProcessError, SamplingModuleNotFoundError, SamplingScriptNotFoundError |
@@ -487,6 +488,12 @@ def _add_format_options(parser, include_compression=True, include_binary=True): |
487 | 488 | help="Output path (default: stdout for pstats, auto-generated for others). " |
488 | 489 | "For heatmap: directory name (default: heatmap_PID)", |
489 | 490 | ) |
| 491 | + output_group.add_argument( |
| 492 | + "--browser", |
| 493 | + action="store_true", |
| 494 | + help="Automatically open HTML output (flamegraph, heatmap) in browser. " |
| 495 | + "When using --subprocesses, only the main process opens the browser", |
| 496 | + ) |
490 | 497 |
|
491 | 498 |
|
492 | 499 | def _add_pstats_options(parser): |
@@ -586,6 +593,32 @@ def _generate_output_filename(format_type, pid): |
586 | 593 | return f"{format_type}_{pid}.{extension}" |
587 | 594 |
|
588 | 595 |
|
| 596 | +def _open_in_browser(path): |
| 597 | + """Open a file or directory in the default web browser. |
| 598 | +
|
| 599 | + Args: |
| 600 | + path: File path or directory path to open |
| 601 | +
|
| 602 | + For directories (heatmap), opens the index.html file inside. |
| 603 | + """ |
| 604 | + abs_path = os.path.abspath(path) |
| 605 | + |
| 606 | + # For heatmap directories, open the index.html file |
| 607 | + if os.path.isdir(abs_path): |
| 608 | + index_path = os.path.join(abs_path, 'index.html') |
| 609 | + if os.path.exists(index_path): |
| 610 | + abs_path = index_path |
| 611 | + else: |
| 612 | + print(f"Warning: Could not find index.html in {path}", file=sys.stderr) |
| 613 | + return |
| 614 | + |
| 615 | + file_url = f"file://{abs_path}" |
| 616 | + try: |
| 617 | + webbrowser.open(file_url) |
| 618 | + except Exception as e: |
| 619 | + print(f"Warning: Could not open browser: {e}", file=sys.stderr) |
| 620 | + |
| 621 | + |
589 | 622 | def _handle_output(collector, args, pid, mode): |
590 | 623 | """Handle output for the collector based on format and arguments. |
591 | 624 |
|
@@ -625,6 +658,10 @@ def _handle_output(collector, args, pid, mode): |
625 | 658 | filename = args.outfile or _generate_output_filename(args.format, pid) |
626 | 659 | collector.export(filename) |
627 | 660 |
|
| 661 | + # Auto-open browser for HTML output if --browser flag is set |
| 662 | + if args.format in ('flamegraph', 'heatmap') and getattr(args, 'browser', False): |
| 663 | + _open_in_browser(filename) |
| 664 | + |
628 | 665 |
|
629 | 666 | def _validate_args(args, parser): |
630 | 667 | """Validate format-specific options and live mode requirements. |
@@ -1161,6 +1198,10 @@ def progress_callback(current, total): |
1161 | 1198 | filename = args.outfile or _generate_output_filename(args.format, os.getpid()) |
1162 | 1199 | collector.export(filename) |
1163 | 1200 |
|
| 1201 | + # Auto-open browser for HTML output if --browser flag is set |
| 1202 | + if args.format in ('flamegraph', 'heatmap') and getattr(args, 'browser', False): |
| 1203 | + _open_in_browser(filename) |
| 1204 | + |
1164 | 1205 | print(f"Replayed {count} samples") |
1165 | 1206 |
|
1166 | 1207 |
|
|
0 commit comments