py = '3.11' tmp_path = PosixPath('/tmp/pytest-of-root/pytest-0/test_attach_container_like_3_11') prefix = ['unshare', '-p', '-f', '-r'] @pytest.mark.parametrize("prefix", [[], ["unshare", "-p", "-f", "-r"]]) @pytest.mark.skipif(platform.system() != "Linux", reason="Linux only") @requires_sudo @allpythons() def test_attach_container_like(py, tmp_path, prefix): """Test in container-like conditions. We test that we can still attach Austin to a running process even if we don't have access to the binary files to determine the location of the symbols. We also test against an interpreter started in a different PID namespace to emulate a container as closely as possible. """ venv_path = tmp_path / ".venv" p = run_python(py, "-m", "venv", "--copies", str(venv_path)) p.wait(30) assert 0 == p.returncode, "Virtual environment was created successfully" env = os.environ.copy() env["LD_LIBRARY_PATH"] = str(venv_path / "lib") env["PATH"] = str(venv_path / "bin") + os.pathsep + env["PATH"] # Give the target enough lifetime (sleepy.py sleeps `interval` seconds # twice) that a successful attach -- which then blocks sampling until # the target exits, since "-Cp" has no exposure limit -- has real time # left to collect a useful sample, rather than attaching moments before # the target's natural death and coming back empty. with run_python( py, target("sleepy.py"), "10", env=env, prefix=prefix, sleep_after=0.5 ) as p: rmtree(venv_path) # With a prefix like "unshare -p -f -r", the PID we attach to is the # launcher, not the exec'd Python interpreter -- austin's own "-C" # fallback needs that child to have actually forked+exec'd by the # time it scans for it. A failed attach returns fast (no child # found yet), so retry_until gets several real attempts cheaply; # poll instead of trusting a single fixed-delay snapshot. result = retry_until( lambda: austin("-Cp", str(p.pid)), lambda r: has_frame(r.samples, filename="sleepy.py", function=""), ) assert result.returncode == 0, result.stderr or result.stdout ts = threads(result.samples) > assert len(ts) == 1, result.stderr or result.stdout E AssertionError: [1m _ _ [0m E [1m __ _ _ _ __| |_(_)_ _ [0m E [1m/ _` | || (_-< _| | ' \ [0m E [1m\__,_|\_,_/__/\__|_|_||_| [0m [1;36m4.1.0 [0m [gcc 13.3.0] E 👽 Parent is not a Python process. E E 😣 No samples collected. E E assert 0 == 1 E + where 0 = len(set()) test/functional/test_attach.py:259: AssertionError