Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,10 +581,11 @@ $ plgrp -a 0-2 101398
disk, since pstack(1) reads symbols from the on-disk ELF image. \
This commonly occurs when a binary or library is reinstalled while \
a process still uses the older version.",
synopsis: "[-r] [pid[/tid] | core]...",
synopsis: "[-q] [pid[/tid] | core]...",
options: &[(
"-r, --raw",
"Show raw function symbol names. Do not attempt to demangle C++ names.",
"-q, --quiet",
"Suppress demangling, module path, source location, and inline frame \
information. Only addresses and raw symbol names with offsets are shown.",
)],
operands: &[
(
Expand Down
87 changes: 58 additions & 29 deletions src/bin/pstack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,39 @@
use std::path::Path;
use std::process::exit;

use ptools::stack::SourceLocation;
use ptools::ProcHandle;

fn format_source(src: &SourceLocation) -> String {
let basename = Path::new(src.file())
.file_name()
.map(|n| n.to_string_lossy())
.unwrap_or_else(|| src.file().into());
if src.line() > 0 {
format!("{basename}:{}", src.line())
} else {
basename.into_owned()
}
}

fn print_stack(
handle: Option<&ProcHandle>,
tid_filter: Option<u64>,
raw: bool,
core_path: Option<&Path>,
quiet: bool,
) -> Result<(), ptools::stack::Error> {
let mut opts = ptools::stack::TraceOptions::new();
let opts = opts
.thread_names(true)
.symbols(true)
.demangle(!quiet)
.module(!quiet)
.source(!quiet)
.inlines(!quiet);
let process = if let Some(path) = core_path {
ptools::stack::TraceOptions::new()
.thread_names(true)
.symbols(true)
.demangle(!raw)
.trace_core(path, handle)?
opts.trace_core(path, handle)?
} else {
ptools::stack::TraceOptions::new()
.thread_names(true)
.symbols(true)
.demangle(!raw)
.trace(handle.unwrap().pid() as u32)?
opts.trace(handle.unwrap().pid() as u32)?
};

if let Some(h) = handle {
Expand All @@ -53,17 +66,33 @@ fn print_stack(
}
println!("{}: {}", thread.id(), thread.name().unwrap_or("<unknown>"));
for frame in thread.frames() {
match frame.symbol() {
Some(symbol) => {
println!(
"{:#016x} {}+{:#x}",
frame.ip(),
symbol.name(),
symbol.offset()
);
if frame.is_inline() {
match frame.symbol() {
Some(symbol) => {
print!("{:#016x} {} [inlined]", frame.ip(), symbol.name());
}
None => print!("{:#016x} - ??? [inlined]", frame.ip()),
}
} else {
match frame.symbol() {
Some(symbol) => {
print!(
"{:#016x} {}+{:#x}",
frame.ip(),
symbol.name(),
symbol.offset()
);
}
None => print!("{:#016x} - ???", frame.ip()),
}
None => println!("{:#016x} - ???", frame.ip()),
}
if let Some(module) = frame.module() {
print!(" in {module}");
}
if let Some(src) = frame.source() {
print!(" at {}", format_source(src));
}
println!();
}
println!();
}
Expand All @@ -72,16 +101,16 @@ fn print_stack(
}

struct Args {
raw: bool,
quiet: bool,
operands: Vec<String>,
}

fn print_usage() {
eprintln!("Usage: pstack [-r] [pid[/thread] | core]...");
eprintln!("Usage: pstack [-q] [pid[/thread] | core]...");
eprintln!("Print stack traces of running processes or core dumps.");
eprintln!();
eprintln!("Options:");
eprintln!(" -r, --raw Show raw function symbol names; do not demangle");
eprintln!(" -q, --quiet Suppress demangling, module, and source info");
eprintln!(" -h, --help Print help");
eprintln!(" -V, --version Print version");
}
Expand All @@ -90,7 +119,7 @@ fn parse_args() -> Args {
use lexopt::prelude::*;

let mut args = Args {
raw: false,
quiet: false,
operands: Vec::new(),
};
let mut parser = lexopt::Parser::from_env();
Expand All @@ -100,6 +129,9 @@ fn parse_args() -> Args {
exit(2);
}) {
match arg {
Short('q') | Long("quiet") => {
args.quiet = true;
}
Short('h') | Long("help") => {
print_usage();
exit(0);
Expand All @@ -108,9 +140,6 @@ fn parse_args() -> Args {
println!("pstack {}", env!("CARGO_PKG_VERSION"));
exit(0);
}
Short('r') | Long("raw") => {
args.raw = true;
}
Value(val) => {
args.operands.push(val.to_string_lossy().into_owned());
}
Expand Down Expand Up @@ -150,7 +179,7 @@ fn main() {
} else {
None
};
if let Err(e) = print_stack(Some(&handle), tid, args.raw, core_path) {
if let Err(e) = print_stack(Some(&handle), tid, core_path, args.quiet) {
eprintln!("pstack: {}: {e}", handle.pid());
error = true;
}
Expand All @@ -163,7 +192,7 @@ fn main() {
// (no systemd-coredump metadata available).
let path = Path::new(operand.as_str());
if !operand.bytes().all(|b| b.is_ascii_digit()) && path.exists() {
if let Err(e) = print_stack(None, None, args.raw, Some(path)) {
if let Err(e) = print_stack(None, None, Some(path), args.quiet) {
eprintln!("pstack: {}: {e}", path.display());
error = true;
}
Expand Down
105 changes: 105 additions & 0 deletions src/dw/dwfl/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use foreign_types::{ForeignTypeRef, Opaque};
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_int;
use std::ptr;

use super::super::elf::Symbol;
Expand All @@ -31,6 +32,54 @@ unsafe impl ForeignTypeRef for ModuleRef {
}

impl ModuleRef {
/// Returns the module name (e.g. the shared library path).
pub fn name(&self) -> Option<&CStr> {
unsafe {
let name = crate::dw_sys::dwfl_module_info(
self.as_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
);
if name.is_null() {
None
} else {
Some(CStr::from_ptr(name))
}
}
}

/// Returns source file and line number for an address, if available.
pub fn getsrc(&self, addr: u64) -> Option<SourceLine<'_>> {
unsafe {
let line = crate::dw_sys::dwfl_module_getsrc(self.as_ptr(), addr);
if line.is_null() {
return None;
}
let mut lineno: libc::c_int = 0;
let file = crate::dw_sys::dwfl_lineinfo(
line,
ptr::null_mut(),
&mut lineno,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
);
if file.is_null() {
return None;
}
Some(SourceLine {
file: CStr::from_ptr(file),
line: lineno,
_module: PhantomData,
})
}
}

/// Returns the name of the containing the address.
pub fn addr_name(&self, addr: u64) -> Result<&CStr, Error> {
unsafe {
Expand All @@ -43,6 +92,43 @@ impl ModuleRef {
}
}

/// Returns the CU DIE and bias for an address in this module.
///
/// The returned `Dwarf_Die` is the compilation unit DIE that covers `addr`.
/// The bias is the relocation offset applied to the module.
pub fn addrdie(&self, addr: u64) -> Option<(crate::dw_sys::Dwarf_Die, u64)> {
unsafe {
let mut bias: crate::dw_sys::Dwarf_Addr = 0;
let result = crate::dw_sys::dwfl_module_addrdie(self.as_ptr(), addr, &mut bias);
if result.is_null() {
None
} else {
Some((*result, bias))
}
}
}

/// Returns the DWARF scopes (DIEs) containing the given PC within this
/// module's compilation unit DIE.
///
/// Returns the scope array and its length on success. The caller must
/// free the returned pointer with `libc::free`.
pub fn getscopes(
&self,
cudie: &mut crate::dw_sys::Dwarf_Die,
pc: u64,
) -> Option<(*mut crate::dw_sys::Dwarf_Die, c_int)> {
unsafe {
let mut scopes: *mut crate::dw_sys::Dwarf_Die = ptr::null_mut();
let n = crate::dw_sys::dwarf_getscopes(cudie, pc, &mut scopes);
if n <= 0 {
None
} else {
Some((scopes, n))
}
}
}

/// Returns information about the symbol containing the address.
pub fn addr_info(&self, addr: u64) -> Result<AddrInfo<'_>, Error> {
unsafe {
Expand Down Expand Up @@ -84,6 +170,25 @@ pub struct AddrInfo<'a> {
_module: PhantomData<&'a ModuleRef>,
}

/// Source file and line number for an address.
pub struct SourceLine<'a> {
file: &'a CStr,
line: libc::c_int,
_module: PhantomData<&'a ModuleRef>,
}

impl<'a> SourceLine<'a> {
/// Returns the source file path.
pub fn file(&self) -> &'a CStr {
self.file
}

/// Returns the line number, or 0 if unknown.
pub fn line(&self) -> i32 {
self.line
}
}

impl<'a> AddrInfo<'a> {
/// Returns the name of the symbol.
pub fn name(&self) -> &'a CStr {
Expand Down
1 change: 1 addition & 0 deletions src/dw_sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#![allow(bad_style)]

pub use self::dwarf::*;
pub use self::elfutils::*;
pub use self::libelf::*;

Expand Down
Loading
Loading