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

    Developer configuring a Python HTTPS server and TLS certificate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: Build a Local HTTPS Server in Python

    Learn Python HTTPSServer for local HTTPS services, TLS certificates, threaded handling, testing, and practical security limits.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Protected files representing secure TAR extraction with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: Extract TAR Safely

    Learn Python tarfile extraction_filter for safer TAR extraction, path validation, links, permissions, and resource limits.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Python code showing warnings controlled with catch_warnings
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: Capture Python Warnings in Tests

    Learn Python catch_warnings to capture, test, and control warnings with focused filters and safe temporary scopes.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Python code representing persistent pickle references
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: Serialize External References

    Learn Python pickle persistent_id for stable external references, validation, security, performance, and long-term compatibility.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Binary code representing Python buffers and memory views
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: Count Values Without Buffer Copies

    Learn Python memoryview.count to count bytes and values in buffers without copies, with formats, limits, and practical safety.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026
    Python code and type annotations
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: Avoid Circular Imports in Annotations

    Learn Python annotationlib to inspect deferred annotations, avoid circular imports, and build safer runtime tooling.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026