Python trace: Track Execution

Published on: August 16, 2026
Reading time: 5 minutes
Lines of source code representing execution tracking with Python trace

Python trace follows statement execution, counts how many times source lines run, lists functions reached, and records caller-callee relationships. It works from the command line or through a programmatic API, making it useful for quick coverage reports, educational tools, and hard-to-reproduce control-flow diagnostics.

Configuration is simple, but runtime overhead is not. Line tracing can slow a program dramatically and produce enormous output. Use it in development and controlled tests rather than as permanent production monitoring.

Count executed lines

--count runs a script and writes annotated .cover files.

python -m trace --count -C coverage application.py

Executable lines receive counters. Lines without a number may be comments, docstrings, declarations, or statements that were not considered executable.

Mark missing lines

--missing marks executable lines with no hits using >>>>>>.

python -m trace \
  --count \
  --missing \
  --summary \
  -C coverage \
  application.py

A per-file summary identifies weakly exercised modules, but line coverage does not prove that conditions, combinations, and results are correct.

--trace displays source lines as they run.

python -m trace --trace task.py

The output becomes noisy when imports and dependencies are included. Apply module and directory filters so the report focuses on application code.

Add relative timing

--timing prefixes each traced line with time elapsed since the program started.

python -m trace --trace --timing task.py

Pauses can become visible, but tracing overhead affects the timing. For function-level profiling, use Python pstats with cProfile.

List functions reached

--listfuncs reports functions executed during the run.

python -m trace --listfuncs application.py

This option is mutually exclusive with line tracing and line counting. It is useful for confirming which handlers, callbacks, and feature paths actually ran.

Track call relationships

--trackcalls records caller-callee pairs.

python -m trace --trackcalls application.py

The report is simpler than a complete profiler, but it can reveal unexpected coupling between modules and functions.

Ignore modules

--ignore-module accepts comma-separated module names and can be repeated.

python -m trace \
  --count \
  --ignore-module=urllib,json \
  -C coverage \
  application.py

Filters reduce noise. Do not exclude application code merely to improve a percentage; document the coverage policy.

Ignore directories

--ignore-dir excludes modules located under selected directories.

python -m trace \
  --count \
  --ignore-dir=.venv \
  -C coverage \
  application.py

Use the operating system path separator for several directories. Resolve relative paths so a broad match does not hide more files than intended.

Accumulate several runs

--file stores counters that can be reused across scenarios.

python -m trace --count --no-report \
  --file counts.dat test_a.py
python -m trace --count --no-report \
  --file counts.dat test_b.py
python -m trace --report \
  --file counts.dat \
  -C coverage

--no-report avoids intermediate listings. The final command produces one combined report.

Avoid concurrent writes

The count file is not a transactional database. Several processes writing the same file can lose or corrupt data. Give every worker a separate output and merge results in a controlled step.

Run a module

--module executes a module instead of a script path.

python -m trace --count --module my_package.cli

This better preserves package-relative import behavior.

Use the Trace class

import sys
import trace

tracer = trace.Trace(
    count=True,
    trace=False,
    ignoredirs=[sys.prefix, sys.exec_prefix],
)

tracer.runfunc(run_scenario)
results = tracer.results()
results.write_results(
    show_missing=True,
    summary=True,
    coverdir="coverage",
)

runfunc() is preferable to building a command string when the target is already a Python callable.

run, runctx, and runfunc

run() accepts source text or a code object suitable for exec(). runctx() also receives global and local mappings. runfunc() invokes a callable with arguments.

tracer.runctx(
    "result = calculate(value)",
    {"calculate": calculate},
    {"value": 10},
)

Never interpolate external input into the command. These methods execute real Python code.

Select collection modes

The constructor controls several independent features:

  • count records line counts.
  • trace prints lines.
  • countfuncs lists functions.
  • countcallers records call relationships.
  • timing shows relative time.

Enable only what the investigation needs. Combining modes increases overhead and output volume.

Read accumulated results

results() returns CoverageResults without resetting the tracer.

first = tracer.results()
tracer.runfunc(another_scenario)
second = tracer.results()

The second object reflects accumulated data. Create another Trace instance for isolated runs.

Merge results

CoverageResults.update() combines another result set.

total.update(worker_result)

Only combine data produced from the same source revision. Counts from different commits are not meaningful together.

Handle missing source files

Current versions support ignore_missing_files=True when writing reports.

results.write_results(
    coverdir="coverage",
    ignore_missing_files=True,
)

This is convenient when generated files are cleaned. In audits, a missing source may be important evidence and should not be hidden.

Coverage is not correctness

An executed line can still return the wrong value. The standard module also lacks advanced branch coverage, contexts, HTML output, and plugin integrations available in Coverage.py.

Use trace for quick diagnostics and simple reports. Large projects should normally adopt a dedicated coverage tool.

Trace and traceback are different

The Python traceback guide formats call stacks after exceptions. trace follows normal execution line by line. One explains how an error was reached; the other can show the broader path that ran.

Trace and tracemalloc are different

Python tracemalloc records memory allocations. The similar name does not imply line coverage or execution tracking.

Trace and dis complement each other

Python dis examines compiled instructions, often without executing code. trace observes a particular runtime execution.

Threads and child processes

Tracing relies on interpreter hooks and may not automatically cover every thread created by libraries. Child processes have separate runtimes. Start collection in each worker and merge results afterward.

Async functions and generators

Coroutine and generator lines are counted only when execution enters them. Creating an awaitable or generator without awaiting or iterating it does not contribute coverage. Test scenarios must drive the actual asynchronous flow.

Security

  • The tracer executes the target program.
  • Do not trace untrusted code in the main process.
  • Reports may expose paths and source.
  • Restrict the output directory.
  • Do not accept external expressions for run().
  • Apply time and resource limits.
  • Protect count files and reports.
  • Ignore the standard library and virtual environment.
  • Use --module for packages.
  • Keep counters separate by commit.
  • Do not share one count file between workers.
  • Review missing lines, not just percentages.
  • Combine coverage with behavioral tests.
  • Measure tracing overhead.
  • Use Coverage.py for branch coverage.

Conclusion

Python trace provides line tracing, execution counts, function listing, and caller relationships without external dependencies. It is valuable for quick diagnostics, teaching, and simple coverage.

Consult the official trace documentation and the official Coverage.py documentation.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Laptop with performance charts representing profile analysis with Python pstats
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pstats: Analyze Profiles

    Learn Python pstats to sort, filter, merge, and interpret cProfile data, including callers, callees, internal time, and cumulative time.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Laptop with code representing executable examples tested with Python doctest
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python doctest: Test Examples

    Learn Python doctest to execute examples in docstrings and text files, normalize output, and integrate executable documentation with CI.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Source code on screen representing class and function browsing with Python pyclbr
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pyclbr: Inspect Modules Safely

    Learn Python pyclbr to list classes, functions, methods, and nested definitions without importing or executing the target module.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Monitor with binary code representing Python bytecode opcode instructions
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python opcode: Explore Bytecode

    Learn Python opcode to map bytecode instructions, arguments, jumps, caches, and stack effects through the documented dis APIs.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Developer investigating memory usage with Python tracemalloc
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python tracemalloc: Find Memory Leaks

    Use Python tracemalloc to compare snapshots, locate memory growth, and investigate leaks in long-running applications.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Code with type annotations representing introspection with Python annotationlib
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python annotationlib: Read Annotations

    Learn Python annotationlib in 3.14 to retrieve annotations as values, ForwardRef proxies, or strings while controlling execution risks.

    Ler mais

    Tempo de leitura: 5 minutos
    14/08/2026