Skip to content

Commit

Permalink
Use /proc/pid/exe for the Python binary in Linux (#364)
Browse files Browse the repository at this point in the history
Instead of "/proc/pid/root/$(readlink /proc/pid/exe)", we'll just use /proc/pid/exe
which provides access to the file even if it was deleted on-disk.

This is not uncommon - for example, Python may be upgraded and thus long-running
processes no longer have the correct file at the specified path.
After this change we can profile/dump those processes (only if they are Python-binary
based and not libpython-based; the latter is inaccessible if deleted... perhaps only
partially accessible via /proc/pid/map_files).
  • Loading branch information
Jongy authored Mar 21, 2021
1 parent 3322942 commit e905a07
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 5 deletions.
10 changes: 8 additions & 2 deletions src/binary_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@ impl BinaryInfo {
}

/// Uses goblin to parse a binary file, returns information on symbols/bss/adjusted offset etc
pub fn parse_binary(_pid: remoteprocess::Pid, filename: &str, addr: u64, size: u64) -> Result<BinaryInfo, Error> {
pub fn parse_binary(_pid: remoteprocess::Pid, filename: &str, addr: u64, size: u64, _is_bin: bool) -> Result<BinaryInfo, Error> {
// on linux the process could be running in docker, access the filename through procfs
// if filename is the binary executable (not libpython) - take it from /proc/pid/exe, which works
// across namespaces just like /proc/pid/root, and also if the file was deleted.
#[cfg(target_os="linux")]
let filename = &format!("/proc/{}/root{}", _pid, filename);
let filename = &if _is_bin {
format!("/proc/{}/exe", _pid)
} else {
format!("/proc/{}/root{}", _pid, filename)
};

let offset = addr;

Expand Down
6 changes: 3 additions & 3 deletions src/python_spy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ impl PythonProcessInfo {

// TODO: consistent types? u64 -> usize? for map.start etc
#[allow(unused_mut)]
let python_binary = parse_binary(process.pid, &filename, map.start() as u64, map.size() as u64)
let python_binary = parse_binary(process.pid, &filename, map.start() as u64, map.size() as u64, true)
.and_then(|mut pb| {
// windows symbols are stored in separate files (.pdb), load
#[cfg(windows)]
Expand Down Expand Up @@ -792,7 +792,7 @@ impl PythonProcessInfo {
if let Some(filename) = &libpython.filename() {
info!("Found libpython binary @ {}", filename);
#[allow(unused_mut)]
let mut parsed = parse_binary(process.pid, filename, libpython.start() as u64, libpython.size() as u64)?;
let mut parsed = parse_binary(process.pid, filename, libpython.start() as u64, libpython.size() as u64, false)?;
#[cfg(windows)]
parsed.symbols.extend(get_windows_python_symbols(process.pid, filename, libpython.start() as u64)?);
libpython_binary = Some(parsed);
Expand Down Expand Up @@ -822,7 +822,7 @@ impl PythonProcessInfo {
if let Some(libpython) = python_dyld_data {
info!("Found libpython binary from dyld @ {}", libpython.filename);

let mut binary = parse_binary(process.pid, &libpython.filename, libpython.segment.vmaddr, libpython.segment.vmsize)?;
let mut binary = parse_binary(process.pid, &libpython.filename, libpython.segment.vmaddr, libpython.segment.vmsize, false)?;

// TODO: bss addr offsets returned from parsing binary are wrong
// (assumes data section isn't split from text section like done here).
Expand Down

0 comments on commit e905a07

Please sign in to comment.