diff --git a/src/process.rs b/src/process.rs index 6c69231b..2ac5d5c3 100644 --- a/src/process.rs +++ b/src/process.rs @@ -40,7 +40,9 @@ mod os_impl { #[cfg(target_os = "linux")] mod os_impl { use super::*; - use nix::{self, {sys::{ptrace, wait}, {unistd::Pid as Tid}}}; + use std::os::unix::io::AsRawFd; + use std::fs::File; + use nix::{self, {sys::{ptrace, wait}, {unistd::Pid as Tid}}, {sched::{setns, CloneFlags}}}; /// This locks a target process using ptrace, and prevents it from running while this /// struct is alive @@ -79,6 +81,35 @@ mod os_impl { Ok(path.to_string_lossy().to_string()) } + + pub struct Namespace { + ns_file: Option + } + + impl Namespace { + pub fn new(pid: Pid) -> Result { + let self_ns = File::open("/proc/self/ns/mnt")?; + let target_ns = File::open(format!("/proc/{}/ns/mnt", pid))?; + let fd = target_ns.as_raw_fd(); + if fd != self_ns.as_raw_fd() { + setns(fd, CloneFlags::from_bits_truncate(0))?; + info!("Process {} appears to be running in a different namespace - setting namespace to match", pid); + Ok(Namespace{ns_file: Some(self_ns)}) + } else { + Ok(Namespace{ns_file: None}) + } + } + } + + impl Drop for Namespace { + fn drop(&mut self) { + if let Some(ns_file) = self.ns_file.as_ref() { + setns(ns_file.as_raw_fd(), CloneFlags::from_bits_truncate(0)).unwrap(); + info!("Restored process namespace"); + } + } + } + struct ThreadLock { tid: Tid } diff --git a/src/python_spy.rs b/src/python_spy.rs index 7c1dc74b..d50e3a24 100644 --- a/src/python_spy.rs +++ b/src/python_spy.rs @@ -382,6 +382,11 @@ impl PythonProcessInfo { map.filename().as_ref().unwrap_or(&"".to_owned())); } + // on linux, support profiling processes running in docker containers by setting + // the namespace to match that of the target process when reading in binaries + #[cfg(target_os="linux")] + let _namespace = process::Namespace::new(pid)?; + // parse the main python binary let (python_binary, python_filename) = { // Get the memory address for the executable by matching against virtual memory maps