Quick Start
Installation
pymontrace may be installed from PyPI
either using pip
or pipx
as the situation calls.
pip install pymontrace
pipx install pymontrace
It's also possible to run without an explicit install using pipx run
or
uvx
.
Simple Examples
The following are pretty naïve but illustrative. We make the assumption you are able to find the process ID (PID) of a process you wish to trace and we use the dummy value 1234 here.
Listing probes
List every top level python function and class method of every loaded module.
The points at which the func
probe may attach.
pymontrace -p 1234 -l 'func:*:start'
Note: on macOS you'll need to use
sudo
to trace a running process.
List all probe sites of a module:
pymontrace -p 1234 -l 'func:mymodule.*:'
List every line of every loaded module:
pymontrace -p 1234 -l 'line:'
Note: You'll likely see the warning
WARN: dropped buffer(s)
whizz past and not see many files with names starting near the start of the alphabet. pymontrace drops trace data if too much is produced too quickly. It's best to try to employ some filters.
List the first line of every loaded module
pymontrace -p 1234 -l 'line::1'
List every line of the contextlib
module:
pymontrace -p 1234 -l 'line:*/contextlib.py:'
Tracing
Observe the entrance to every python function call. Use CTRL-C to end.
pymontrace -p 1234 -e 'func:*:start {{ print(qualname()) }}'
Observe the entry and exit to every python function from a module, including the arguments it was called with.
pymontrace -p 1234 -e '
func:mymodule.*:start {{ print("->", funcname(), args()) }}
func:mymodule.*:return {{ print("<-", funcname()) }}
'
Count the number of times each function is called during the duration of the trace.
pymontrace -p 1234 -e 'func:*:start {{ maps.calls[qualname()] = agg.count() }}'
Print the minimum and maximum value that a given function was called with
pymontrace -p 1234 -e '
func:mymodule.myfunc:start {{
arg0 = next(iter(args().values()))
vars.maxval = agg.max(arg0)
vars.minval = agg.min(arg0)
}}
pymontrace::END {{
print("max =", vars.maxval)
print("min =", vars.minval)
}}
'
Plot a histogram of the ms durations of a top-level function called g
:
pymontrace -p 1234 -e '
pymontrace::BEGIN {{
import time
import threading
vars.monotime = time.monotonic_ns
vars.threads = {}
vars.get_ident = threading.get_ident
}}
func:__main__.g:start {{
vars.threads[vars.get_ident()] = vars.monotime()
}}
func:__main__.g:return {{
end = vars.monotime()
i = vars.get_ident()
if (start := vars.threads.pop(i, 0)) != 0:
maps.calls[i] = agg.quantize((end - start) / 1000 / 1000)
}}
'
Which may output something like the following:
Waiting for process to reach safepoint...
Probes installed. Hit CTRL-C to end...
^CRemoving probes...
Waiting for process to reach safepoint...
calls
8562249600:
value ------------- Distribution ------------- count
128 | 0
256 |@@@@@@@@@@@@@@@@@@@@@@@ 4
512 |@@@@@@@@@@@ 2
1024 | 0
2048 | 0
4096 | 0
8192 | 0
16384 | 0
32768 | 0
65536 | 0
131072 | 0
262144 | 0
524288 | 0
1048576 | 0
2097152 | 0
4194304 | 0
8388608 | 0
16777216 | 0
33554432 | 0
67108864 | 0
134217728 | 0
268435456 | 0
536870912 | 0
1073741824 | 0
2147483648 |@@@@@@ 1
4294967296 | 0
Tracing Scripts
Visualize the execution flow of a script
pymontrace -c 'myscript.py 1 7' -e '
pymontrace::BEGIN {{
vars.prefix = ""
}}
func:__main__.*:start {{
print(vars.prefix, "->", funcname())
vars.prefix += " "
}}
func:__main__.*:return {{
vars.prefix = vars.prefix[:-2]
print(vars.prefix, "<-", funcname())
}}
func:__main__.*:unwind {{
vars.prefix = vars.prefix[:-2]
print(vars.prefix, "<*", funcname())
}}
'
Which could output something similar to:
Probes installed. Hit CTRL-C to end...
-> <module>
-> main
-> f
-> g
<- g
-> g
<- g
-> g
<- g
-> g
<- g
-> g
^C <* g
<* f
<* main
<* <module>