blazesym/
lib.rs

1//! **blazesym** is a library that can be used to symbolize addresses. Address
2//! symbolization is a common problem in tracing contexts, for example, where
3//! users want to reason about functions by name, but low level components
4//! report only the "raw" addresses (e.g., in the form of stacktraces).
5//!
6//! In addition to symbolization, **blazesym** also provides APIs for the
7//! reverse operation: looking up addresses from symbol names. That can be
8//! useful, for example, for configuring breakpoints or tracepoints.
9//!
10//! # Overview
11//! The crate is organized via public modules that expose functionality
12//! pertaining a certain topic. Specifically, these areas are currently covered:
13//!
14//! - [`symbolize`] covers address symbolization functionality
15//! - [`inspect`] contains APIs for inspecting files such as ELF and Gsym to
16//!   lookup addresses to symbol names, for example
17//! - [`normalize`] exposes address normalization functionality
18//!
19//! C API bindings are defined in a cross-cutting manner as part of the
20//! **blazesym-c** crate (note that Rust code should not have to consume
21//! these functions and at the ABI level this module organization has no
22//! relevance for C).
23//!
24//! # Observability
25//! **blazesym** optionally integrates with the `tracing` crate and
26//! infrastructure and emits spans/events as part of common operations (if the
27//! `tracing` feature is enabled). Please refer to the [`tracing`
28//! documentation][tracing.rs] for guidance on how to configure event
29//! subscription.
30//!
31//! [tracing.rs]: https://tracing.rs
32
33#![cfg_attr(docsrs, feature(doc_cfg))]
34#![cfg_attr(feature = "nightly", feature(test))]
35#![cfg_attr(
36    not(all(
37        feature = "apk",
38        feature = "bpf",
39        feature = "breakpad",
40        feature = "dwarf",
41        feature = "gsym"
42    )),
43    allow(dead_code, unused_imports)
44)]
45#![cfg_attr(not(linux), allow(dead_code, unused_imports))]
46
47
48#[cfg(feature = "nightly")]
49#[allow(unused_extern_crates)]
50extern crate test;
51
52#[macro_use]
53mod cfg;
54#[cfg(feature = "apk")]
55mod apk;
56#[cfg(feature = "breakpad")]
57mod breakpad;
58#[cfg(feature = "dwarf")]
59mod dwarf;
60mod elf;
61mod error;
62mod file_cache;
63#[cfg(feature = "gsym")]
64mod gsym;
65mod insert_map;
66pub mod inspect;
67mod kernel;
68mod maps;
69mod mmap;
70pub mod normalize;
71mod pathlike;
72mod perf_map;
73mod pid;
74pub mod symbolize;
75#[cfg(any(test, feature = "test"))]
76mod test_helper;
77mod util;
78mod vdso;
79#[cfg(feature = "apk")]
80mod zip;
81
82use std::result;
83
84
85pub use crate::error::Error;
86pub use crate::error::ErrorExt;
87pub use crate::error::ErrorKind;
88pub use crate::error::IntoError;
89pub use crate::mmap::Mmap;
90pub use crate::normalize::buildid::BuildId;
91pub use crate::pid::Pid;
92
93/// A result type using our [`Error`] by default.
94pub type Result<T, E = Error> = result::Result<T, E>;
95
96
97/// A type representing addresses.
98pub type Addr = u64;
99
100
101/// The type of a symbol.
102#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
103#[non_exhaustive]
104pub enum SymType {
105    /// The symbol type is unspecified or unknown.
106    ///
107    /// In input contexts this variant can be used to encompass all
108    /// other variants (functions and variables), whereas in output
109    /// contexts it means that the type is not known.
110    #[default]
111    Undefined,
112    /// The symbol is a function.
113    Function,
114    /// The symbol is a variable.
115    Variable,
116}
117
118
119/// A type representing an optional value or a default.
120#[derive(Clone, Debug, PartialEq)]
121pub enum MaybeDefault<T> {
122    /// Nothing.
123    None,
124    /// Use the context-dependent default value.
125    Default,
126    /// A provided value.
127    Some(T),
128}
129
130impl<T> From<T> for MaybeDefault<T> {
131    #[inline]
132    fn from(value: T) -> Self {
133        Self::Some(value)
134    }
135}
136
137
138/// Utility functionality not specific to any overarching theme.
139pub mod helper {
140    use super::Addr;
141    use super::Result;
142
143    pub use crate::normalize::buildid::read_elf_build_id;
144    pub use crate::normalize::buildid::read_elf_build_id_from_mmap;
145    pub use crate::normalize::ioctl::is_procmap_query_supported;
146
147    cfg_breakpad! {
148        pub use crate::breakpad::BreakpadResolver;
149    }
150    pub use crate::elf::ElfResolver;
151    cfg_gsym! {
152        use std::path::Path;
153        use crate::symbolize::Symbolize;
154        use crate::symbolize::ResolvedSym;
155        use crate::symbolize::Reason;
156        use crate::symbolize::FindSymOpts;
157        use crate::gsym;
158
159        /// A symbol resolver for the GSYM format.
160        // We provide a wrapper type here to eliminate the need for a lifetime.
161        #[derive(Debug)]
162        pub struct GsymResolver(gsym::GsymResolver<'static>);
163
164        impl GsymResolver {
165            /// Create a `GsymResolver` that loads data from the provided file.
166            #[inline]
167            pub fn open<P>(path: P) -> Result<Self>
168            where
169                P: AsRef<Path>,
170            {
171                Ok(Self(gsym::GsymResolver::open(path)?))
172            }
173        }
174
175        impl Symbolize for GsymResolver {
176            #[inline]
177            fn find_sym(&self, addr: Addr, opts: &FindSymOpts) -> Result<Result<ResolvedSym<'_>, Reason>> {
178                self.0.find_sym(addr, opts)
179            }
180        }
181    }
182}
183
184/// Implementation details shared with other closely related crates.
185///
186/// NOT PART OF PUBLIC API SURFACE!
187#[doc(hidden)]
188pub mod __private {
189    pub use crate::util::bytes_to_path;
190    pub use crate::util::stat;
191    pub use crate::util::ReadRaw;
192
193    #[cfg(feature = "apk")]
194    pub mod zip {
195        pub use crate::zip::Archive;
196    }
197
198    #[cfg(linux)]
199    #[cfg(feature = "test")]
200    pub use crate::test_helper::find_gettimeofday_in_process;
201    #[cfg(feature = "test")]
202    pub use crate::test_helper::find_the_answer_fn;
203    #[cfg(feature = "test")]
204    pub use crate::test_helper::find_the_answer_fn_in_zip;
205    #[cfg(linux)]
206    #[cfg(feature = "test")]
207    pub use crate::test_helper::find_vdso_range;
208}
209
210
211#[cfg(feature = "tracing")]
212#[macro_use]
213#[allow(unused_imports)]
214mod log {
215    pub(crate) use tracing::debug;
216    pub(crate) use tracing::error;
217    pub(crate) use tracing::info;
218    pub(crate) use tracing::instrument;
219    pub(crate) use tracing::trace;
220    pub(crate) use tracing::warn;
221}
222
223#[cfg(not(feature = "tracing"))]
224#[macro_use]
225#[allow(unused_imports)]
226mod log {
227    macro_rules! debug {
228        ($($args:tt)*) => {{
229          if false {
230            // Make sure to use `args` to prevent any warnings about
231            // unused variables.
232            let _args = format_args!($($args)*);
233          }
234        }};
235    }
236    pub(crate) use debug;
237    pub(crate) use debug as error;
238    pub(crate) use debug as info;
239    pub(crate) use debug as trace;
240    pub(crate) use debug as warn;
241}
242
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    use std::path::Path;
249
250    use crate::symbolize::FindSymOpts;
251    use crate::symbolize::Symbolize as _;
252
253
254    /// "Test" our public `GsymResolver`.
255    #[test]
256    fn gsym_resolver() {
257        let test_gsym = Path::new(&env!("CARGO_MANIFEST_DIR"))
258            .join("data")
259            .join("test-stable-addrs.gsym");
260
261        let resolver = helper::GsymResolver::open(test_gsym).unwrap();
262        let sym = resolver
263            .find_sym(0x2000200, &FindSymOpts::Basic)
264            .unwrap()
265            .unwrap();
266        assert_eq!(sym.name, "factorial");
267    }
268}