Skip to content

Commit

Permalink
Linux Symbolication Fixes
Browse files Browse the repository at this point in the history
With linux, we were loading up symbolication data even when not doing native
extension profiling - since we unwind the stack to figure out the pthreadid
/native threadid mapping. This caused an issue where loading symbols paniced
(#183). Change to not load symbols unless needed. Also try to handle the
underlying error.
  • Loading branch information
benfred committed Oct 21, 2019
1 parent a0abd4b commit 78d7ef2
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 26 deletions.
11 changes: 10 additions & 1 deletion remoteprocess/src/linux/symbolication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,16 @@ impl Symbolicator {
.find(|ref header| header.p_type == PT_LOAD && header.p_flags & PF_X != 0);

let obj_base = match program_header {
Some(hdr) => { m.start() as u64 - hdr.p_vaddr },
Some(hdr) => {
// Don't panic if v_addr/start is messed up
// (https://github.com/benfred/py-spy/issues/183)
if hdr.p_vaddr > m.start() as u64 {
warn!("Failed to load {} for symbols: v_addr {} is past start {}",
filename, hdr.p_vaddr, m.start());
continue;
}
m.start() as u64 - hdr.p_vaddr
},
None => {
warn!("Failed to find exectuable PT_LOAD header in {}", filename);
continue;
Expand Down
19 changes: 0 additions & 19 deletions src/native_stack_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,25 +249,6 @@ impl NativeStack {
}
Ok(stack)
}

#[cfg(target_os="linux")]
pub fn get_pthread_id(&self, thread: &remoteprocess::Thread, threadids: &HashSet<u64>) -> Result<u64, Error> {
let mut pthread_id = 0;

let mut cursor = self.unwinder.cursor(thread)?;
while let Some(_) = cursor.next() {
// the pthread_id is usually in the top-level frame of the thread, but on some configs
// can be 2nd level. Handle this by taking the top-most rbx value that is one of the
// pthread_ids we're looking for
if let Ok(bx) = cursor.bx() {
if bx != 0 && threadids.contains(&bx) {
pthread_id = bx;
}
}
}

Ok(pthread_id)
}
}

#[derive(Debug)]
Expand Down
29 changes: 23 additions & 6 deletions src/python_spy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,13 @@ impl PythonSpy {

let version_string = format!("python{}.{}", version.major, version.minor);

#[cfg(all(unwind, not(target_os="linux")))]
#[cfg(unwind)]
let native = if config.native {
Some(NativeStack::new(pid, python_info.python_binary, python_info.libpython_binary)?)
} else {
None
};

#[cfg(all(unwind, target_os="linux"))]
let native = Some(NativeStack::new(pid, python_info.python_binary, python_info.libpython_binary)?);

Ok(PythonSpy{pid, process, version, interpreter_address, threadstate_address,
python_filename: python_info.python_filename,
version_string,
Expand Down Expand Up @@ -353,7 +350,7 @@ impl PythonSpy {

let processed_os_threads: HashSet<Tid> = HashSet::from_iter(self.python_thread_ids.values().map(|x| *x));

let native = self.native.as_ref().unwrap();
let unwinder = self.process.unwinder()?;

// Try getting the pthread_id from the native stack registers for threads we haven't looked up yet
for thread in self.process.threads()?.iter() {
Expand All @@ -362,7 +359,7 @@ impl PythonSpy {
continue;
}

match native.get_pthread_id(&thread, &all_python_threads) {
match self._get_pthread_id(&unwinder, &thread, &all_python_threads) {
Ok(pthread_id) => {
if pthread_id != 0 {
self.python_thread_ids.insert(pthread_id, threadid);
Expand Down Expand Up @@ -398,6 +395,26 @@ impl PythonSpy {
Ok(None)
}


#[cfg(all(target_os="linux", unwind))]
pub fn _get_pthread_id(&self, unwinder: &remoteprocess::Unwinder, thread: &remoteprocess::Thread, threadids: &HashSet<u64>) -> Result<u64, Error> {
let mut pthread_id = 0;

let mut cursor = unwinder.cursor(thread)?;
while let Some(_) = cursor.next() {
// the pthread_id is usually in the top-level frame of the thread, but on some configs
// can be 2nd level. Handle this by taking the top-most rbx value that is one of the
// pthread_ids we're looking for
if let Ok(bx) = cursor.bx() {
if bx != 0 && threadids.contains(&bx) {
pthread_id = bx;
}
}
}

Ok(pthread_id)
}

#[cfg(target_os="freebsd")]
fn _get_os_thread_id<I: InterpreterState>(&mut self, _python_thread_id: u64, _interp: &I) -> Result<Option<Tid>, Error> {
Ok(None)
Expand Down

0 comments on commit 78d7ef2

Please sign in to comment.