From 22defbd070fd53669f3e339316599b3f181ee50a Mon Sep 17 00:00:00 2001 From: NoneBack Date: Thu, 28 Nov 2024 17:07:08 +0800 Subject: [PATCH] feat: add symbolizer --- .gitignore | 2 + .rustfmt.toml | 10 + doctor/Cargo.toml | 8 +- doctor/src/lib.rs | 2 +- doctor/src/main.rs | 1 + doctor/src/profiler/dso.rs | 65 ------- doctor/src/profiler/error.rs | 12 ++ doctor/src/profiler/formater.rs | 4 - doctor/src/profiler/mod.rs | 2 +- doctor/src/profiler/perf_record.rs | 6 +- doctor/src/profiler/process.rs | 4 +- doctor/src/profiler/symbolizer/elf.rs | 175 ++++++++++++++++++ doctor/src/profiler/symbolizer/error.rs | 22 +++ doctor/src/{ => profiler}/symbolizer/mod.rs | 1 + doctor/src/profiler/symbolizer/symbol.rs | 23 +++ .../src/profiler/symbolizer/symbol_store.rs | 57 ++++++ doctor/src/profiler/symbolizer/symbolizer.rs | 26 +++ doctor/src/profiler/translator.rs | 101 +++------- doctor/src/symbolizer/elf.rs | 5 - doctor/src/symbolizer/error.rs | 7 - doctor/src/symbolizer/symbol_store.rs | 104 ----------- doctor/src/symbolizer/symbolizer.rs | 11 -- xtask/src/build.rs | 2 +- xtask/src/build_ebpf.rs | 7 +- xtask/src/main.rs | 2 +- xtask/src/run.rs | 13 +- 26 files changed, 381 insertions(+), 291 deletions(-) create mode 100644 .rustfmt.toml delete mode 100644 doctor/src/profiler/dso.rs create mode 100644 doctor/src/profiler/symbolizer/elf.rs create mode 100644 doctor/src/profiler/symbolizer/error.rs rename doctor/src/{ => profiler}/symbolizer/mod.rs (81%) create mode 100644 doctor/src/profiler/symbolizer/symbol.rs create mode 100644 doctor/src/profiler/symbolizer/symbol_store.rs create mode 100644 doctor/src/profiler/symbolizer/symbolizer.rs delete mode 100644 doctor/src/symbolizer/elf.rs delete mode 100644 doctor/src/symbolizer/error.rs delete mode 100644 doctor/src/symbolizer/symbol_store.rs delete mode 100644 doctor/src/symbolizer/symbolizer.rs diff --git a/.gitignore b/.gitignore index 2344b56..4b4905f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ target/ **/.vscode/ .vim/ .vscode/ +*.txt +tests/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..55825ca --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,10 @@ + +edition = "2018" +newline_style = "unix" +# comments +normalize_comments=true +wrap_comments=true +# imports +imports_granularity="Crate" +group_imports="StdExternalCrate" + diff --git a/doctor/Cargo.toml b/doctor/Cargo.toml index 475f369..0ab516f 100644 --- a/doctor/Cargo.toml +++ b/doctor/Cargo.toml @@ -13,12 +13,16 @@ env_logger = "0.10" libc = "0.2" log = "0.4" tokio = { version = "1.25", features = ["macros", "rt", "rt-multi-thread", "net", "signal", "time"] } -blazesym = "0.2.0-rc.0" thiserror = "1.0.63" procfs = "0.16.0" clap = "4.5.9" goblin = {version = "0.8.2", features = ["elf32","elf32"]} -moka = { version = "0.12.8", features = ["future"] } +moka = { version = "0.12.8", features = ["future","sync"] } +sled = { version = "0.34.7" } +gimli = "0.31.0" +memmap2 = "0.9.4" +wholesym = "0.7.0" +symbolic = { version = "12.12.3", features = ["demangle"] } [[bin]] name = "doctor" diff --git a/doctor/src/lib.rs b/doctor/src/lib.rs index 0d9bec0..dced1f0 100644 --- a/doctor/src/lib.rs +++ b/doctor/src/lib.rs @@ -1,2 +1,2 @@ pub mod profiler; -pub mod symbolizer; +// pub mod symbolizer; diff --git a/doctor/src/main.rs b/doctor/src/main.rs index 113550c..95f3ee1 100644 --- a/doctor/src/main.rs +++ b/doctor/src/main.rs @@ -112,6 +112,7 @@ async fn main() -> Result<(), anyhow::Error> { running.store(false, Ordering::SeqCst); }); + let mut translator = Translator::new("/".into()); while running_clone.load(Ordering::SeqCst) { match stacks.pop(0) { diff --git a/doctor/src/profiler/dso.rs b/doctor/src/profiler/dso.rs deleted file mode 100644 index 1624dc6..0000000 --- a/doctor/src/profiler/dso.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::{ - path::{PathBuf}, -}; - -use anyhow::{anyhow, Error, Ok}; -use blazesym::symbolize::{Elf, Input, Source, Symbolizer}; - -pub struct Dso { - path: PathBuf, -} - -impl Dso { - pub fn new(path: PathBuf) -> Self { - Self { path } - } - - pub fn translate_single(&self, symbolizer: &Symbolizer, offset: u64) -> Result { - let src: Source<'_> = Source::Elf(Elf::new(&self.path)); - - let sym = symbolizer - .symbolize_single(&src, Input::FileOffset(offset)) - .map_err(|e| anyhow!("symbolize_single, elf {:?} -> {}", self.path, e))?; - - Ok(match sym { - blazesym::symbolize::Symbolized::Sym(symbol) => symbol.name.to_string(), - blazesym::symbolize::Symbolized::Unknown(r) => { - log::debug!( - "file offset {:x}, elf {:?}, reason {:#?}", - offset, - self.path, - r - ); - "unknown".to_string() - } - }) - } - - pub fn translate( - &self, - symbolizer: &Symbolizer, - offsets: &Vec, - ) -> Result, Error> { - let src = Source::Elf(Elf::new(&self.path)); - - let syms = symbolizer - .symbolize(&src, Input::FileOffset(offsets)) - .map_err(|e| anyhow!("symbolizer -> {}", e))?; - - Ok(syms - .iter() - .map(|sym| match sym { - blazesym::symbolize::Symbolized::Sym(symbol) => symbol.name.to_string(), - blazesym::symbolize::Symbolized::Unknown(r) => { - log::debug!( - "file offset {:#?}, elf {:?}, reason {:#?}", - offsets, - self.path, - r - ); - "unknown".to_string() - } - }) - .collect::>()) - } -} diff --git a/doctor/src/profiler/error.rs b/doctor/src/profiler/error.rs index 496f55e..cd1fe00 100644 --- a/doctor/src/profiler/error.rs +++ b/doctor/src/profiler/error.rs @@ -1,5 +1,7 @@ use thiserror::Error; +use super::symbolizer::error::SymbolizerError; + #[derive(Error, Debug)] pub enum TranslateError { #[error("data not found")] @@ -10,4 +12,14 @@ pub enum TranslateError { Internal, #[error(transparent)] Io(#[from] std::io::Error), // 使用`from`属性自动从`std::io::Error`转换 + #[error("symbolize {0}")] + Symbolize(SymbolizerError), + #[error("anyhow {0}")] + AnyError(#[from] anyhow::Error), +} + +impl From for TranslateError { + fn from(err: SymbolizerError) -> Self { + TranslateError::Symbolize(err) + } } diff --git a/doctor/src/profiler/formater.rs b/doctor/src/profiler/formater.rs index 3f2ff2d..8b13789 100644 --- a/doctor/src/profiler/formater.rs +++ b/doctor/src/profiler/formater.rs @@ -1,5 +1 @@ - - - - diff --git a/doctor/src/profiler/mod.rs b/doctor/src/profiler/mod.rs index 1da4e7d..7e4ffa8 100644 --- a/doctor/src/profiler/mod.rs +++ b/doctor/src/profiler/mod.rs @@ -1,7 +1,7 @@ -pub mod dso; pub mod error; pub mod formater; pub mod perf_record; pub mod process; pub mod profiler; +pub mod symbolizer; pub mod translator; diff --git a/doctor/src/profiler/perf_record.rs b/doctor/src/profiler/perf_record.rs index 71a7e4e..b2a772a 100644 --- a/doctor/src/profiler/perf_record.rs +++ b/doctor/src/profiler/perf_record.rs @@ -10,7 +10,6 @@ pub struct PerfRecord { pub ts: u64, pub cycle: u64, pub frames: Vec, // pub kframes: Option>, - // pub uframes: Option>, } pub struct PerfStackFrame { @@ -33,7 +32,10 @@ impl PerfStackFrame { impl fmt::Display for PerfRecord { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut format_str = format!("{} {} {}\n", self.pid, self.cpu_id, self.cmdline); + let mut format_str = format!( + "{} {} {} {}\n", + self.pid, self.cpu_id, self.tgid, self.cmdline + ); for frame in &self.frames { format_str.push_str( format!( diff --git a/doctor/src/profiler/process.rs b/doctor/src/profiler/process.rs index bb0cad6..9cf89f9 100644 --- a/doctor/src/profiler/process.rs +++ b/doctor/src/profiler/process.rs @@ -46,8 +46,8 @@ impl ProcessMetadata { match &mapper.pathname { procfs::process::MMapPath::Path(p) => { let path = match p.to_str().unwrap().strip_suffix(" (deleted)") { - Some(striped) => self.rootfs.join(striped), - None => self.rootfs.join(p), + Some(striped) => PathBuf::from(striped), + None => p.clone(), }; Ok((path, mapper.offset + (v_addr - mapper.address.0))) diff --git a/doctor/src/profiler/symbolizer/elf.rs b/doctor/src/profiler/symbolizer/elf.rs new file mode 100644 index 0000000..2ccdce4 --- /dev/null +++ b/doctor/src/profiler/symbolizer/elf.rs @@ -0,0 +1,175 @@ +use std::{ + collections::BTreeSet, + fs::{metadata, File}, + os::linux::fs::MetadataExt, + path::PathBuf, +}; +use symbolic::{ + common::Name, + demangle::{Demangle, DemangleOptions}, +}; + +use super::{error::SymbolizerError, symbol::Symbol}; + +use goblin::elf::{program_header::PT_LOAD, Elf, ProgramHeader}; +use log::debug; +use memmap2::MmapOptions; +use wholesym::{SymbolManager, SymbolManagerConfig}; + +#[derive(Debug, Clone)] +pub struct ElfMetadata { + path: PathBuf, + debug_info: BTreeSet, + pt_loads: Vec, + inode: u64, +} + +impl ElfMetadata { + pub fn load_sym_from_elf( + path: &PathBuf, + ) -> Result<(BTreeSet, Vec), SymbolizerError> { + let file = File::open(path.clone()) + .map_err(|e| SymbolizerError::SymbolStoreIOFailed(path.clone(), e))?; + let mmap = unsafe { + MmapOptions::new() + .map(&file) + .map_err(|e| SymbolizerError::MMapIOFailed(path.clone(), e))? + }; + + let elf = Elf::parse(&mmap).expect("Failed to parse ELF file"); + + let pt_loads = elf + .program_headers + .clone() + .into_iter() + .filter(|h| h.p_type == PT_LOAD) + .collect::>(); + + let mut debug_info = BTreeSet::new(); + tokio::task::block_in_place(|| { + let symbol_manager = SymbolManager::with_config(SymbolManagerConfig::default()); + let symbol_map_f = symbol_manager.load_symbol_map_for_binary_at_path(path, None); + let symbol_map = tokio::runtime::Handle::current() + .block_on(symbol_map_f) + .unwrap(); // TODO: deal with + + debug!("eso path: {:?}, build {}", &path, symbol_map.debug_id()); + let opt = DemangleOptions::name_only(); + symbol_map.iter_symbols().for_each(|s| { + let demangled = Name::from(s.1).try_demangle(opt).to_string(); + + debug_info.insert(Symbol { + addr: s.0 as u64, + name: Some(demangled), + }); + }); + + symbol_map.debug_id() + }); + + Ok((debug_info, pt_loads)) + } + pub fn load_sym_from_dwarf(path: &PathBuf) -> Result, SymbolizerError> { + let mut debug_info = BTreeSet::new(); + tokio::task::block_in_place(|| { + let symbol_manager = SymbolManager::with_config(SymbolManagerConfig::default()); + let symbol_map_f = symbol_manager.load_symbol_map_for_binary_at_path(path, None); + let symbol_map = tokio::runtime::Handle::current() + .block_on(symbol_map_f) + .unwrap(); + + debug!("eso path: {:?}, build {}", &path, symbol_map.debug_id()); + symbol_map.iter_symbols().for_each(|s| { + debug_info.insert(Symbol { + addr: s.0 as u64, + name: Some(s.1.to_string()), + }); + }); + }); + + Ok(debug_info) + } + + pub fn get_inode(path: &PathBuf) -> Result { + let f_meta = metadata(path).map_err(SymbolizerError::GetInodeFailed)?; + Ok(f_meta.st_ino()) + } + + pub fn new(path: PathBuf) -> Result { + let (debug_info, pt_loads) = ElfMetadata::load_sym_from_elf(&path)?; + let inode = Self::get_inode(&path)?; + Ok(Self { + path, + debug_info, + pt_loads, + inode, + }) + } + + // translate file offset -> relative offset + fn translate(&self, file_offset: u64) -> Option { + if !self.pt_loads.is_empty() { + Some(file_offset - self.pt_loads[0].p_offset) + } else { + None + } + } + + pub fn find_symbol(&self, offset: u64) -> Result { + match self.translate(offset) { + Some(relative_offset) => { + let target = Symbol { + addr: relative_offset, + name: None, + }; + match self.debug_info.range(..target).next_back() { + Some(sym) => Ok(sym.clone()), + None => Err(SymbolizerError::SymbolNotFound( + self.path.clone(), + relative_offset, + )), + } + } + + None => Err(SymbolizerError::TranslateVirtOffsetFailed( + self.path.clone(), + offset, + )), + } + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// #[tokio::main] +// #[test] +// async fn test_sysm() { +// let elf = ElfMetadata::new( +// "/root/.vscode-server/bin/8b3775030ed1a69b13e4f4c628c612102e30a681/node".into(), +// ) +// .unwrap(); +// println!( +// "debug info {}, syms {}", +// elf.debug_info.len(), +// elf.syms.len() +// ); +// let mut debug_info: Vec<_> = elf.debug_info.iter().map(|s| s).collect(); +// let mut syms: Vec<_> = elf.syms.iter().map(|s| s).collect(); +// debug_info.sort_by_key(|s| s.addr); +// syms.sort_by_key(|s| s.addr); +// // let mut miss = 0; + +// // elf.syms.iter().for_each(|s| { +// // if !debug_info.contains(s) { +// // miss += 1; +// // } +// // println!("sym {:?}", s); +// // }); +// println!( +// "debug info {:#?}, syms {:#?}", +// &debug_info[0..10], +// &syms[0..10] +// ); +// } +// } diff --git a/doctor/src/profiler/symbolizer/error.rs b/doctor/src/profiler/symbolizer/error.rs new file mode 100644 index 0000000..9b54c88 --- /dev/null +++ b/doctor/src/profiler/symbolizer/error.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SymbolizerError { + #[error("fail to open symbol store: {0}")] + OpenSymbolStoreDiskDBFailed(#[from] sled::Error), + #[error("failed to fetch elf: {0}")] + FetchElfFailed(PathBuf), + #[error("load {0}, elf failed: {1}")] + SymbolStoreIOFailed(PathBuf, std::io::Error), + #[error("symbol not found in {0}, off {1}")] + SymbolNotFound(PathBuf, u64), + #[error("translate {0} f_offset {1} to virt_offset failed")] + TranslateVirtOffsetFailed(PathBuf, u64), + #[error("load {0}, elf failed: {1}")] + MMapIOFailed(PathBuf, std::io::Error), + #[error("get inode: {0}")] + GetInodeFailed(std::io::Error), + #[error("gmili load: {0}")] + GmiliFailed(#[from] gimli::Error), +} diff --git a/doctor/src/symbolizer/mod.rs b/doctor/src/profiler/symbolizer/mod.rs similarity index 81% rename from doctor/src/symbolizer/mod.rs rename to doctor/src/profiler/symbolizer/mod.rs index 85d62db..40e4bcc 100644 --- a/doctor/src/symbolizer/mod.rs +++ b/doctor/src/profiler/symbolizer/mod.rs @@ -1,4 +1,5 @@ pub mod elf; pub mod error; +pub mod symbol; pub mod symbol_store; pub mod symbolizer; diff --git a/doctor/src/profiler/symbolizer/symbol.rs b/doctor/src/profiler/symbolizer/symbol.rs new file mode 100644 index 0000000..fa116a0 --- /dev/null +++ b/doctor/src/profiler/symbolizer/symbol.rs @@ -0,0 +1,23 @@ +use std::hash::Hash; +use std::{fmt::Display, hash::Hasher}; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub struct Symbol { + pub(crate) addr: u64, + pub(crate) name: Option, +} + +impl Display for Symbol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.name { + Some(name) => write!(f, "{} (f_0x{:016X})", name, self.addr), + None => write!(f, "Unnamed (f_0x{:016X})", self.addr), + } + } +} + +impl Hash for Symbol { + fn hash(&self, state: &mut H) { + self.addr.hash(state); // only for single dso file + } +} diff --git a/doctor/src/profiler/symbolizer/symbol_store.rs b/doctor/src/profiler/symbolizer/symbol_store.rs new file mode 100644 index 0000000..84a0d2c --- /dev/null +++ b/doctor/src/profiler/symbolizer/symbol_store.rs @@ -0,0 +1,57 @@ +use crate::profiler::symbolizer::elf::ElfMetadata; +use std::path::PathBuf; + +use super::error::SymbolizerError; +use super::symbol::Symbol; + +use moka::sync::Cache; + +pub struct SymbolStore { + cache: Cache, // inode -> elf +} + +impl SymbolStore { + pub fn new(path: PathBuf) -> Result { + Ok(Self { + cache: Cache::new(100), + }) + } + + pub fn get_symbol(&self, dso: &PathBuf, offset: u64) -> Result { + let elf = self.fetch_elf(dso)?; + elf.find_symbol(offset) + } + + fn fetch_elf(&self, dso: &PathBuf) -> Result { + let inode = self.load_elf(dso)?; + match self.cache.get(&inode) { + Some(val) => Ok(val), + None => Err(SymbolizerError::FetchElfFailed(dso.clone())), + } + } + + fn load_elf(&self, dso: &PathBuf) -> Result { + let inode = ElfMetadata::get_inode(dso)?; + if !self.cache.contains_key(&inode) { + log::info!("load elf {dso:?}, {inode}"); + self.cache.insert(inode, ElfMetadata::new(dso.clone())?); + } + + Ok(inode) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fetch_elf() { + // 获取当前可执行文件路径 + println!("start"); + let ss = SymbolStore::new("/home/noneback/workspace/doctor/doctor/tests".into()).unwrap(); + let sym = ss.get_symbol(&"/root/workspace/profiler/bianque/main".into(), 0x3321b); + + println!("sym:\n {}", sym.unwrap()); + } +} diff --git a/doctor/src/profiler/symbolizer/symbolizer.rs b/doctor/src/profiler/symbolizer/symbolizer.rs new file mode 100644 index 0000000..363b0b2 --- /dev/null +++ b/doctor/src/profiler/symbolizer/symbolizer.rs @@ -0,0 +1,26 @@ +use super::{error::SymbolizerError, symbol::Symbol, symbol_store::SymbolStore}; + +use std::path::PathBuf; +pub struct Symbolizer { + dss: SymbolStore, +} + +impl Symbolizer { + pub fn new(path: PathBuf) -> Result { + Ok(Self { + dss: SymbolStore::new(path)?, + }) + } + + pub fn symbolize( + &self, + rootfs: &PathBuf, + dso: &PathBuf, + offset: u64, + ) -> Result { + let dso_path = rootfs.join(dso.strip_prefix("/").unwrap()); + + self.dss.get_symbol(&dso_path, offset) + } + pub fn batch_symbolize(_dso: PathBuf, _offsets: &[u64]) {} +} diff --git a/doctor/src/profiler/translator.rs b/doctor/src/profiler/translator.rs index c59d6a5..45a3888 100644 --- a/doctor/src/profiler/translator.rs +++ b/doctor/src/profiler/translator.rs @@ -1,5 +1,5 @@ use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, fs::File, io::{self, BufRead, BufReader}, path::PathBuf, @@ -8,15 +8,16 @@ use std::{ use anyhow::{anyhow, Error}; use aya::maps::stack_trace::StackTrace; -use blazesym::symbolize::Symbolizer; -use super::{dso::Dso, perf_record::PerfStackFrame, process::ProcessMetadata}; +use super::{ + error::TranslateError, perf_record::PerfStackFrame, process::ProcessMetadata, + symbolizer::symbolizer::Symbolizer, +}; pub struct Translator { rootfs: PathBuf, ksyms: Option>, symbolizer: Arc, - dso_cache: BTreeMap, } impl Translator { @@ -24,8 +25,9 @@ impl Translator { Self { rootfs, ksyms: None, - symbolizer: Arc::new(Symbolizer::new()), - dso_cache: BTreeMap::new(), + symbolizer: Arc::new( + Symbolizer::new("/root/workspace/profiler/bianque/doctor/tests".into()).unwrap(), + ), // TODO: } } @@ -48,8 +50,8 @@ impl Translator { ) -> Result, Error> { // for frame in record.stack_frames {} let ips = utrace.frames().iter().map(|f| f.ip).collect::>(); - self.translate_usyms_v2(pid, ips) - .map_err(|e: Error| anyhow!("translate_utrace pid {} -> {}", pid, e)) + self.translate_usyms(pid, ips) + .map_err(|e| anyhow!("translate_utrace pid {} -> {}", pid, e)) } pub fn translate_ksyms(&mut self, ip: u64) -> Result { @@ -72,88 +74,39 @@ impl Translator { return Ok(ksyms .range(..=ip) .next_back() - .map(|(_, s)| { - PerfStackFrame::new(ip, format!("{}_[k]", s), elf_path, ip) - }) + .map(|(_, s)| PerfStackFrame::new(ip, format!("{}_[k]", s), elf_path, ip)) .unwrap()); } Err(anyhow::anyhow!("translate_ksyms 0x{} NotFound", ip)) } - pub fn translate_usyms_v2( + pub fn translate_usyms( &mut self, pid: u32, ips: Vec, - ) -> Result, Error> { + ) -> Result, TranslateError> { let proc = ProcessMetadata::new(pid)?; let mut frames = Vec::new(); for ip in ips { if let Ok((dso_path, offset)) = proc.abs_addr(ip) { - let dso = self - .dso_cache - .entry(dso_path.clone()) - .or_insert_with(|| Dso::new(dso_path.clone())); - - let sym = dso - .translate_single(&self.symbolizer, offset) - .map_err(|e| { - anyhow!( - "translate_usyms_v2, cmdline {:?}, pid {} -> {}", - proc.cmdline, - pid, - e - ) - })?; - - if !sym.eq("unknown") { - frames.push(PerfStackFrame::new(ip, sym, dso_path.clone(), offset)); - } + let psf = match self.symbolizer.symbolize(&proc.rootfs, &dso_path, offset) { + Ok(sym) => PerfStackFrame::new( + ip, + sym.name.unwrap_or("unknown".into()), + dso_path, + offset, + ), + Err(e) => { + log::debug!("Symbolize {}, file {:#?}: {}", pid, &dso_path, e); + PerfStackFrame::new(ip, "unknown".into(), dso_path.clone(), offset) + } + }; + + frames.push(psf); } } Ok(frames) } - - pub fn translate_usyms( - &mut self, - pid: u32, - ips: Vec, - ) -> Result, Error> { - let proc = ProcessMetadata::new(pid)?; - let mut dso_offsets: HashMap> = HashMap::new(); - - ips.iter().for_each(|&ip| { - if let Ok((dso, offset)) = proc.abs_addr(ip) { - dso_offsets.entry(dso).or_default().push((offset, ip)); - } - }); - - let mut frames = dso_offsets - .iter() - .flat_map(|(path, mapping)| { - // use cache - let dso = self - .dso_cache - .entry(path.clone()) - .or_insert_with(|| Dso::new(path.clone())); - - let offsets = mapping - .iter() - .map(|&(offset, _ip)| offset) - .collect::>(); - let ips = mapping.iter().map(|(_offset, ip)| ip).collect::>(); - - dso.translate(&self.symbolizer, &offsets) - .unwrap() - .into_iter() - .zip(ips) - .map(|(symbol, ip)| (ip, PerfStackFrame::new(*ip, symbol, path.clone(), 0))) - .collect::>() - }) - .collect::>(); - frames.sort_by_key(|item| item.0); - - Ok(frames.into_iter().map(|f| f.1).collect()) - } } diff --git a/doctor/src/symbolizer/elf.rs b/doctor/src/symbolizer/elf.rs deleted file mode 100644 index 24c88b8..0000000 --- a/doctor/src/symbolizer/elf.rs +++ /dev/null @@ -1,5 +0,0 @@ -use std::path::PathBuf; - -struct Elf { - path: PathBuf, -} diff --git a/doctor/src/symbolizer/error.rs b/doctor/src/symbolizer/error.rs deleted file mode 100644 index 56c9953..0000000 --- a/doctor/src/symbolizer/error.rs +++ /dev/null @@ -1,7 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum SymbolizerError { - #[error("fail to open symbol store:{0}")] - OpenSymbolStoreFailed(#[from] std::io::Error), -} diff --git a/doctor/src/symbolizer/symbol_store.rs b/doctor/src/symbolizer/symbol_store.rs deleted file mode 100644 index ee34e7a..0000000 --- a/doctor/src/symbolizer/symbol_store.rs +++ /dev/null @@ -1,104 +0,0 @@ -use anyhow::Error; -use goblin::elf::{Elf, Sym}; -use std::clone; -use std::cmp::Ordering; -use std::collections::{BTreeSet, HashSet}; -use std::fs::File; -use std::hash::{Hash, Hasher}; -use std::io::{BufReader, Read}; -use std::path::PathBuf; - -use moka::future::Cache; - -use super::error::SymbolizerError; - -#[derive(PartialEq, Eq, PartialOrd, Clone)] -struct Symbol { - pub(crate) addr: u64, - - - pub(crate) name: String, -} - - -impl Hash for Symbol { - fn hash(&self, state: &mut H) { - self.addr.hash(state); // only for single dso file - } -} - -impl Ord for Symbol { - fn cmp(&self, other: &Self) -> Ordering { - match self.addr.cmp(&other.addr) { - Ordering::Equal => self.name.cmp(&other.name), - other => other, - } - } -} -// use rusqlite::Connection; -pub struct SymbolStore { - cache: Cache>, - // db: Connection, -} - -impl SymbolStore { - pub fn new(path: PathBuf) -> Result { - // let conn = Connection::open(path)?; - Ok(Self { - cache: Cache::new(100), - // db: conn, - }) - } - - pub fn fetch_process(pid: u64) {} - - pub fn fetch_elf(&self, dso: PathBuf) -> Result<(), Error> { - let file = File::open(dso.clone())?; - let mut reader = BufReader::new(file); - let mut buffer = Vec::new(); - reader.read_to_end(&mut buffer)?; - let elf = Elf::parse(&buffer).expect("Failed to parse ELF file"); - // static syms - let mut syms = elf - .syms - .iter() - .filter(|s| s.is_function()) - .map(|sym| { - let addr = sym.st_value; - let name = elf.strtab.get_at(sym.st_name).unwrap_or("unknow"); - Symbol { addr, String::from("test") } - }) - .collect::>(); - - let dyn_syms = elf - .dynsyms - .iter() - .filter(|s| s.is_function()) - .map(|sym| { - let addr = sym.st_value; - let name = elf.dynstrtab.get_at(sym.st_name).unwrap_or("unknow"); - Symbol { addr, name } - }) - .collect::>(); - - syms.extend(dyn_syms); - self.cache.insert(dso, syms); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::env; - - #[test] - fn test_fetch_elf() { - // 获取当前可执行文件路径 - println!("start"); - let ss = SymbolStore::new("./".into()).unwrap(); - ss.fetch_elf("/proc/135657/root/usr/lib64/mysql/private/libprotobuf-lite.so.3.19.4".into()) - .unwrap(); - println!("end"); - } -} diff --git a/doctor/src/symbolizer/symbolizer.rs b/doctor/src/symbolizer/symbolizer.rs deleted file mode 100644 index 16a5168..0000000 --- a/doctor/src/symbolizer/symbolizer.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::symbol_store::SymbolStore; - -use std::path::PathBuf; -struct Symbolizer { - dss: SymbolStore, -} - -impl Symbolizer { - pub fn symbolize(dso: PathBuf, offset: u64) {} - pub fn batch_symbolize(dso: PathBuf, offsets: &[u64]) {} -} diff --git a/xtask/src/build.rs b/xtask/src/build.rs index ddeee44..c045d14 100644 --- a/xtask/src/build.rs +++ b/xtask/src/build.rs @@ -39,4 +39,4 @@ pub fn build(opts: Options) -> Result<(), anyhow::Error> { .context("Error while building eBPF program")?; build_project(&opts).context("Error while building userspace application")?; Ok(()) -} \ No newline at end of file +} diff --git a/xtask/src/build_ebpf.rs b/xtask/src/build_ebpf.rs index 0d77c10..ed07e84 100644 --- a/xtask/src/build_ebpf.rs +++ b/xtask/src/build_ebpf.rs @@ -42,12 +42,7 @@ pub struct Options { pub fn build_ebpf(opts: Options) -> Result<(), anyhow::Error> { let dir = PathBuf::from("doctor-ebpf"); let target = format!("--target={}", opts.target); - let mut args = vec![ - "build", - target.as_str(), - "-Z", - "build-std=core", - ]; + let mut args = vec!["build", target.as_str(), "-Z", "build-std=core"]; if opts.release { args.push("--release") } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 5079458..3cb0fa9 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,5 +1,5 @@ -mod build_ebpf; mod build; +mod build_ebpf; mod run; use std::process::exit; diff --git a/xtask/src/run.rs b/xtask/src/run.rs index 0773e45..af64840 100644 --- a/xtask/src/run.rs +++ b/xtask/src/run.rs @@ -3,7 +3,10 @@ use std::process::Command; use anyhow::Context as _; use clap::Parser; -use crate::{build::{build, Options as BuildOptions}, build_ebpf::Architecture}; +use crate::{ + build::{build, Options as BuildOptions}, + build_ebpf::Architecture, +}; #[derive(Debug, Parser)] pub struct Options { @@ -21,15 +24,15 @@ pub struct Options { pub run_args: Vec, } - /// Build and run the project pub fn run(opts: Options) -> Result<(), anyhow::Error> { // Build our ebpf program and the project - build(BuildOptions{ + build(BuildOptions { bpf_target: opts.bpf_target, release: opts.release, - }).context("Error while building project")?; - + }) + .context("Error while building project")?; + // profile we are building (release or debug) let profile = if opts.release { "release" } else { "debug" }; let bin_path = format!("target/{profile}/doctor");