@@ -325,21 +325,23 @@ The default configuration works well for most use cases:
325325 :widths: 25 75
326326
327327 * - Option
328- - Default behavior
329- * - ``--interval `` / ``-i ``
328+ - Default
329+ * - Default for ``--interval `` / ``-i ``
330330 - 100 µs between samples (~10,000 samples/sec)
331- * - ``--duration `` / ``-d ``
332- - Profile for 10 seconds
333- * - ``--all-threads `` / ``-a ``
334- - Sample main thread only
335- * - ``--native ``
331+ * - Default for ``--duration `` / ``-d ``
332+ - 10 seconds
333+ * - Default for ``--all-threads `` / ``-a ``
334+ - Main thread only
335+ * - Default for ``--native ``
336336 - No ``<native> `` frames (C code time attributed to caller)
337- * - ``--no-gc ``
338- - Include ``<GC> `` frames when garbage collection is active
339- * - ``--mode ``
337+ * - Default for ``--no-gc ``
338+ - ``<GC> `` frames included when garbage collection is active
339+ * - Default for ``--mode ``
340340 - Wall-clock mode (all samples recorded)
341- * - ``--realtime-stats ``
342- - No live statistics display during profiling
341+ * - Default for ``--realtime-stats ``
342+ - Disabled
343+ * - Default for ``--subprocesses ``
344+ - Disabled
343345
344346
345347Sampling interval and duration
@@ -472,6 +474,78 @@ working correctly and that sufficient samples are being collected. See
472474:ref: `sampling-efficiency ` for details on interpreting these metrics.
473475
474476
477+ Subprocess profiling
478+ --------------------
479+
480+ The :option: `--subprocesses ` option enables automatic profiling of subprocesses
481+ spawned by the target::
482+
483+ python -m profiling.sampling run --subprocesses script.py
484+ python -m profiling.sampling attach --subprocesses 12345
485+
486+ When enabled, the profiler monitors the target process for child process
487+ creation. When a new Python child process is detected, a separate profiler
488+ instance is automatically spawned to profile it. This is useful for
489+ applications that use :mod: `multiprocessing `, :mod: `subprocess `,
490+ :mod: `concurrent.futures ` with :class: `~concurrent.futures.ProcessPoolExecutor `,
491+ or other process spawning mechanisms.
492+
493+ .. code-block :: python
494+ :caption: worker_pool.py
495+
496+ from concurrent.futures import ProcessPoolExecutor
497+ import math
498+
499+ def compute_factorial (n ):
500+ total = 0
501+ for i in range (50 ):
502+ total += math.factorial(n)
503+ return total
504+
505+ if __name__ == " __main__" :
506+ numbers = [5000 + i * 100 for i in range (50 )]
507+ with ProcessPoolExecutor(max_workers = 4 ) as executor:
508+ results = list (executor.map(compute_factorial, numbers))
509+ print (f " Computed { len (results)} factorials " )
510+
511+ ::
512+
513+ python -m profiling.sampling run --subprocesses --flamegraph worker_pool.py
514+
515+ This produces separate flame graphs for the main process and each worker
516+ process: ``flamegraph_<main_pid>.html ``, ``flamegraph_<worker1_pid>.html ``,
517+ and so on.
518+
519+ Each subprocess receives its own output file. The filename is derived from
520+ the specified output path (or the default) with the subprocess's process ID
521+ appended:
522+
523+ - If you specify ``-o profile.html ``, subprocesses produce ``profile_12345.html ``,
524+ ``profile_12346.html ``, and so on
525+ - With default output, subprocesses produce files like ``flamegraph_12345.html ``
526+ or directories like ``heatmap_12345 ``
527+ - For pstats format (which defaults to stdout), subprocesses produce files like
528+ ``profile_12345.pstats ``
529+
530+ The subprocess profilers inherit most sampling options from the parent (interval,
531+ duration, thread selection, native frames, GC frames, async-aware mode, and
532+ output format). All Python descendant processes are profiled recursively,
533+ including grandchildren and further descendants.
534+
535+ Subprocess detection works by periodically scanning for new descendants of
536+ the target process and checking whether each new process is a Python process
537+ by probing the process memory for Python runtime structures. Non-Python
538+ subprocesses (such as shell commands or external tools) are ignored.
539+
540+ There is a limit of 100 concurrent subprocess profilers to prevent resource
541+ exhaustion in programs that spawn many processes. If this limit is reached,
542+ additional subprocesses are not profiled and a warning is printed.
543+
544+ The :option: `--subprocesses ` option is incompatible with :option: `--live ` mode
545+ because live mode uses an interactive terminal interface that cannot
546+ accommodate multiple concurrent profiler displays.
547+
548+
475549.. _sampling-efficiency :
476550
477551Sampling efficiency
@@ -1302,6 +1376,11 @@ Sampling options
13021376 Compatible with ``--live ``, ``--flamegraph ``, ``--heatmap ``, and ``--gecko ``
13031377 formats only.
13041378
1379+ .. option :: --subprocesses
1380+
1381+ Also profile subprocesses. Each subprocess gets its own profiler
1382+ instance and output file. Incompatible with ``--live ``.
1383+
13051384
13061385Mode options
13071386------------
0 commit comments