Skip to content

add support for nongnu-libunwind #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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
31 changes: 29 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install LLVM toolchain
run: sudo apt install llvm autotools-dev autoconf libunwind-dev -y

- name: Install Rust toolchain
uses: actions-rs/[email protected]
Expand Down Expand Up @@ -56,8 +59,32 @@ jobs:
uses: actions-rs/[email protected]
with:
command: build
args: --all-features --target ${{ matrix.target }}
args: --features flamegraph,protobuf,cpp --target ${{ matrix.target }}

build_nongnu_libunwind:
name: Build with nongnu-libunwind
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install LLVM toolchain
run: sudo apt install llvm autotools-dev autoconf libunwind-dev -y

- name: Install Rust toolchain
uses: actions-rs/[email protected]
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy

- name: Run cargo build
uses: actions-rs/[email protected]
with:
command: build
args: --all-features

test:
name: Test
strategy:
Expand All @@ -81,4 +108,4 @@ jobs:
uses: actions-rs/[email protected]
with:
command: test
args: --all-features
args: --features flamegraph,protobuf,cpp
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0


## [Unreleased]
### Added
- Add `nongnu-libunwind` feature to dynamically link with the `nongnu-libunwind` [@yangkeao](https://github.com/YangKeao)

## [0.6.2] - 2021-12-24
### Added
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ default = ["cpp"]
flamegraph = ["inferno"]
protobuf = ["prost", "prost-derive", "prost-build"]
cpp = ["symbolic-demangle/cpp"]
nongnu-libunwind = ["libunwind-rs"]

[dependencies]
backtrace = "0.3"
Expand All @@ -32,6 +33,7 @@ inferno = { version = "0.10", default-features = false, features = ["nameattr"],
prost = { version = "0.9", optional = true }
prost-derive = { version = "0.9", optional = true }
criterion = {version = "0.3", optional = true}
libunwind-rs = { version = "0.3", optional = true }

[dependencies.symbolic-demangle]
version = "8.0"
Expand Down
18 changes: 18 additions & 0 deletions src/backtrace/backtrace_rs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
impl super::Frame for backtrace::Frame {
type S = backtrace::Symbol;

fn resolve_symbol<F: FnMut(&Self::S)>(&self, cb: F) {
backtrace::resolve_frame(self, cb);
}

fn symbol_address(&self) -> *mut libc::c_void {
self.symbol_address()
}
}

pub fn trace<F: FnMut(&Frame) -> bool>(cb: F) {
unsafe { backtrace::trace_unsynchronized(cb) }
}

pub use backtrace::Frame;
pub use backtrace::Symbol;
46 changes: 46 additions & 0 deletions src/backtrace/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use libc::c_void;
use std::path::PathBuf;

pub trait Symbol: Sized {
fn name(&self) -> Option<Vec<u8>>;
fn addr(&self) -> Option<*mut c_void>;
fn lineno(&self) -> Option<u32>;
fn filename(&self) -> Option<PathBuf>;
}

impl Symbol for backtrace::Symbol {
fn name(&self) -> Option<Vec<u8>> {
self.name().map(|name| name.as_bytes().to_vec())
}

fn addr(&self) -> Option<*mut libc::c_void> {
self.addr()
}

fn lineno(&self) -> Option<u32> {
self.lineno()
}

fn filename(&self) -> Option<std::path::PathBuf> {
self.filename().map(|filename| filename.to_owned())
}
}

pub trait Frame: Sized + Clone {
type S: Symbol;

fn resolve_symbol<F: FnMut(&Self::S)>(&self, cb: F);
fn symbol_address(&self) -> *mut c_void;
}

#[cfg(not(feature = "nongnu-libunwind"))]
mod backtrace_rs;

#[cfg(not(feature = "nongnu-libunwind"))]
pub use backtrace_rs::{trace, Frame as FrameImpl, Symbol as SymbolImpl};

#[cfg(feature = "nongnu-libunwind")]
mod nongnu_unwind;

#[cfg(feature = "nongnu-libunwind")]
pub use nongnu_unwind::{trace, Frame as FrameImpl, Symbol as SymbolImpl};
49 changes: 49 additions & 0 deletions src/backtrace/nongnu_unwind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use libc::c_void;

use libunwind_rs::Cursor;

// TODO: need a better Debug implementation
#[derive(Clone, Debug)]
pub struct Frame {
pub ip: usize,
pub sp: usize,
pub symbol_address: usize,
}

impl super::Frame for Frame {
type S = backtrace::Symbol;

fn resolve_symbol<F: FnMut(&Self::S)>(&self, cb: F) {
backtrace::resolve(self.ip as *mut c_void, cb)
}

fn symbol_address(&self) -> *mut libc::c_void {
self.symbol_address as *mut c_void
}
}

pub fn trace<F: FnMut(&Frame) -> bool>(mut cb: F) {
// TODO: come up with a better way to handle this error
let _ = Cursor::local(|mut cursor| -> Result<(), libunwind_rs::Error> {
loop {
let mut symbol_address = 0;
if let Ok(proc_info) = cursor.proc_info() {
symbol_address = proc_info.start();
}

if cb(&Frame {
ip: cursor.ip()?,
sp: cursor.sp()?,
symbol_address,
}) && cursor.step()?
{
continue;
}

break;
}
Ok(())
});
}

pub use backtrace::Symbol;
25 changes: 14 additions & 11 deletions src/frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use std::hash::{Hash, Hasher};
use std::os::raw::c_void;
use std::path::PathBuf;

use backtrace::Frame;
use smallvec::SmallVec;
use symbolic_demangle::demangle;

use crate::backtrace::{Frame, FrameImpl};
use crate::{MAX_DEPTH, MAX_THREAD_NAME};

#[derive(Clone)]
pub struct UnresolvedFrames {
pub frames: SmallVec<[Frame; MAX_DEPTH]>,
pub frames: SmallVec<[FrameImpl; MAX_DEPTH]>,
pub thread_name: [u8; MAX_THREAD_NAME],
pub thread_name_length: usize,
pub thread_id: u64,
Expand All @@ -39,7 +39,7 @@ impl Debug for UnresolvedFrames {
}

impl UnresolvedFrames {
pub fn new(frames: SmallVec<[Frame; MAX_DEPTH]>, tn: &[u8], thread_id: u64) -> Self {
pub fn new(frames: SmallVec<[FrameImpl; MAX_DEPTH]>, tn: &[u8], thread_id: u64) -> Self {
let thread_name_length = tn.len();
let mut thread_name = [0; MAX_THREAD_NAME];
thread_name[0..thread_name_length].clone_from_slice(tn);
Expand Down Expand Up @@ -96,7 +96,7 @@ pub struct Symbol {

impl Symbol {
pub fn raw_name(&self) -> &[u8] {
self.name.as_deref().unwrap_or(b"Unknow")
self.name.as_deref().unwrap_or(b"Unknown")
}

pub fn name(&self) -> String {
Expand All @@ -111,7 +111,7 @@ impl Symbol {
self.filename
.as_ref()
.map(|name| name.as_os_str().to_string_lossy())
.unwrap_or_else(|| Cow::Borrowed("Unknow"))
.unwrap_or_else(|| Cow::Borrowed("Unknown"))
}

pub fn lineno(&self) -> u32 {
Expand All @@ -121,13 +121,16 @@ impl Symbol {

unsafe impl Send for Symbol {}

impl From<&backtrace::Symbol> for Symbol {
fn from(symbol: &backtrace::Symbol) -> Self {
impl<T> From<&T> for Symbol
where
T: crate::backtrace::Symbol,
{
fn from(symbol: &T) -> Self {
Symbol {
name: symbol.name().map(|name| name.as_bytes().to_vec()),
name: symbol.name(),
addr: symbol.addr(),
lineno: symbol.lineno(),
filename: symbol.filename().map(|filename| filename.to_owned()),
filename: symbol.filename(),
}
}
}
Expand Down Expand Up @@ -177,9 +180,9 @@ impl From<UnresolvedFrames> for Frames {
let mut frame_iter = frames.frames.iter();

while let Some(frame) = frame_iter.next() {
let mut symbols = Vec::new();
let mut symbols: Vec<Symbol> = Vec::new();

backtrace::resolve_frame(frame, |symbol| {
frame.resolve_symbol(|symbol| {
let symbol = Symbol::from(symbol);
symbols.push(symbol);
});
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub const MAX_DEPTH: usize = 32;
/// Define the MAX supported thread name length. TODO: make this variable mutable.
pub const MAX_THREAD_NAME: usize = 16;

mod backtrace;
mod collector;
mod error;
mod frames;
Expand Down
26 changes: 12 additions & 14 deletions src/profiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
use std::convert::TryInto;
use std::os::raw::c_int;

use backtrace::Frame;
use nix::sys::signal;
use parking_lot::RwLock;
use smallvec::SmallVec;

#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
use findshlibs::{Segment, SharedLibrary, TargetSharedLibrary};

use crate::backtrace::{trace, FrameImpl};
use crate::collector::Collector;
use crate::error::{Error, Result};
use crate::frames::UnresolvedFrames;
Expand Down Expand Up @@ -244,20 +244,18 @@ extern "C" fn perf_signal_handler(
}
}

let mut bt: SmallVec<[Frame; MAX_DEPTH]> = SmallVec::with_capacity(MAX_DEPTH);
let mut bt: SmallVec<[FrameImpl; MAX_DEPTH]> = SmallVec::with_capacity(MAX_DEPTH);
let mut index = 0;

unsafe {
backtrace::trace_unsynchronized(|frame| {
if index < MAX_DEPTH {
bt.push(frame.clone());
index += 1;
true
} else {
false
}
});
}
trace(|frame| {
if index < MAX_DEPTH {
bt.push(frame.clone());
index += 1;
true
} else {
false
}
});

let current_thread = unsafe { libc::pthread_self() };
let mut name = [0; MAX_THREAD_NAME];
Expand Down Expand Up @@ -349,7 +347,7 @@ impl Profiler {
// This function has to be AS-safe
pub fn sample(
&mut self,
backtrace: SmallVec<[Frame; MAX_DEPTH]>,
backtrace: SmallVec<[FrameImpl; MAX_DEPTH]>,
thread_name: &[u8],
thread_id: u64,
) {
Expand Down