Repository navigation
fix(python): read the target's interpreter in austin memory profiles - #137
Merged
Merged
Conversation
austin finds the interpreter (and libpython, for a shared build) by the path in /proc/<pid>/maps and opens that path in its own mount namespace. In the agent's container that path is either missing — "Cannot determine the version of the Python interpreter" for any target whose interpreter the image lacks — or the image's own interpreter. Run austin in a private mount namespace with copies of the target's interpreter files bound at those paths. A bind straight from /proc/<pid>/root is refused across mount namespaces, hence the copy. Also: - annotate a raw memory profile with no samples as "no memory growth" instead of publishing a header-only artefact; other outputs fail with that reason - reduce austin errors to the sentence that explains them, with the PID and binary - drop TestAustin, which profiled a fixed PID and slept 300s - add a CI job that profiles real Python 3.12/3.14 containers with the built image
There was a problem hiding this comment.
Code Review
This pull request updates the Austin Python profiler to run within a private mount namespace using unshare, copying and binding the target's interpreter and libpython files from /proc//root to resolve version determination issues in containerized environments. It also adds robust error parsing, sample counting, and comprehensive tests. The review feedback highlights a critical need to clean up the temporary files in /tmp to prevent disk space exhaustion, and offers valuable suggestions to handle deleted library paths, optimize sample counting by avoiding string allocations, and simplify file appending with deferred closes.
RamanKharchee
previously approved these changes
Oct 3, 2026
…ean up
Address review:
- read the interpreter through /proc/<pid>/exe and libpython through
/proc/<pid>/map_files/<range> instead of /proc/<pid>/root<path>, so a
file replaced on disk since the process started ("(deleted)" in maps)
is read as mapped rather than from its replacement
- remove the per-PID copy directory once austin exits
- count samples on scanner.Bytes() instead of allocating each line
RamanKharchee
approved these changes
Oct 3, 2026
blue4209211
approved these changes
Oct 3, 2026
3 of 4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Python memory profiles (austin) failed for almost every target with:
Cause. austin finds the interpreter, and
libpythonfor a shared build, by the path it reads from/proc/<pid>/maps. It then opens that path in its own mount namespace, not under/proc/<pid>/root. The agent runs in its own container, so that path is one of two things:/usr/local/bin/python3.12) fails with the error above.python:3.14target was read through the image'spython3.14instead of its own.Fix. Run austin in a private mount namespace (
unshare -m --propagation private) in which the target's interpreter andlibpythonsit at the paths austin looks up./proc/<pid>/exefor the interpreter,/proc/<pid>/map_files/<range>for libpython. A file replaced on disk since the process started is therefore still read as mapped.mojo2austin, which runs on that Python, is unaffected.Two follow-on fixes in the same path:
# no memory growth observed during the <N>s window: …and is still published. Any other output type fails with that explanation instead of "no stacks found (maybe due low cpu load)"..txt.GetFileExtensionhad no austin case, so raw output fell through to.svg(agent-raw-<pid>-1.svg.gz). Consumers that pick a renderer by suffix drew the text as a broken image.austin (PID <n>, <binary>): <the sentence that explains it> (exit status N).Also removes
TestAustin. It profiled a hard-coded PID and then slept for 300 seconds, which added five minutes to every CI run.Type of change
Test plan
/proctree; theunsharewrapper's arguments; cleaning austin's real stderr; sample counting; the no-growth handling for raw vs flamegraph output.python-austin-e2e(test/e2e/python-austin.sh). It builds the python image and runs the agent from it against real Python containers with--pid=container:<target> --privileged, the same split view (target's PIDs, own filesystem) a debugger pod has. It asserts samples are collected and that the# python:header matches the target's own version.Engineering detail
Local e2e: built
docker/python/Dockerfilefrom this branch, then rantest/e2e/python-austin.shagainst Python 3.12 (Alpine), 3.12 (Debian, shared libpython), 3.14 (Alpine, the same path as the image's own interpreter) and a steady-state 3.12 process. Results:# python: 3.12.15# python: 3.12.15# python: 3.14.8(the target's version, not the image's 3.14.0)/bin/busybox)austin (PID 1, /bin/busybox): Cannot determine the version of the Python interpreter. … (exit status 10)A process whose interpreter was deleted after it started (
/tmp/py3 (deleted)in maps) profiled correctly: 450 samples,# python: 3.12.15. Noaustin-target-*copy directory was left behind.The mount namespace was checked from outside after each run: no leaked mounts, and the image's
python3.14still reports 3.14.0.go build ./...,go vet ./...andgolangci-lint(changed files) are clean.go test ./...passes on Linux, exceptpkg/util/file TestWritewhen run as root in a container; that test is untouched here.Checklist
go build ./...andgo test ./...pass locally#comment line on a no-growth raw profile.