Skip to content

Commit ad8ce8b

Browse files
committed
Improve crashtracker receiver performance
1 parent 25b69f1 commit ad8ce8b

File tree

5 files changed

+110
-25
lines changed

5 files changed

+110
-25
lines changed

datadog-crashtracker-ffi/src/crash_info/api.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,31 @@ pub unsafe extern "C" fn ddog_crasht_CrashInfo_resolve_names(
6464
})
6565
}
6666

67+
/// # Safety
68+
/// The `crash_info` can be null, but if non-null it must point to a Builder made by this module,
69+
/// which has not previously been dropped.
70+
/// This function will:
71+
// - resolve frame names
72+
// - normalize IPs
73+
// Difference between this function and the two functions above is that this function
74+
// is tailored for performance. It makes sure that required resources between normalize_ips and
75+
// resolve_names are shared and reused, which is not the case when calling the two functions
76+
// separately.
77+
// While this is more efficient, it means that you are force to do both operations, and you are not
78+
// able to do only one of them.
79+
#[no_mangle]
80+
#[must_use]
81+
#[named]
82+
#[cfg(unix)]
83+
pub unsafe extern "C" fn ddog_crasht_CrashInfo_enrich_callstacks(
84+
mut crash_info: *mut Handle<CrashInfo>,
85+
pid: u32,
86+
) -> VoidResult {
87+
wrap_with_void_ffi_result!({
88+
crash_info.to_inner_mut()?.enrich_callstacks(pid)?;
89+
})
90+
}
91+
6792
/// # Safety
6893
/// The `crash_info` can be null, but if non-null it must point to a Builder made by this module,
6994
/// which has not previously been dropped.

datadog-crashtracker/src/crash_info/error_data.rs

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
99
use std::collections::HashMap;
1010
#[cfg(unix)]
1111
use std::path::PathBuf;
12+
#[cfg(unix)]
13+
use std::rc::Rc;
1214

1315
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1416
pub struct ErrorData {
@@ -23,49 +25,69 @@ pub struct ErrorData {
2325
}
2426

2527
#[cfg(unix)]
26-
#[derive(Default)]
27-
pub struct CachedElfResolvers {
28-
elf_resolvers: HashMap<PathBuf, ElfResolver>,
28+
pub struct CachedElfResolvers<'a> {
29+
symbolizer: &'a mut blazesym::symbolize::Symbolizer,
30+
elf_resolvers: HashMap<PathBuf, Rc<ElfResolver>>,
2931
}
3032

3133
#[cfg(unix)]
32-
impl CachedElfResolvers {
33-
pub fn get(&mut self, file_path: &PathBuf) -> anyhow::Result<&ElfResolver> {
34+
impl<'a> CachedElfResolvers<'a> {
35+
pub fn new(symbolizer: &'a mut blazesym::symbolize::Symbolizer) -> Self {
36+
Self {
37+
symbolizer,
38+
elf_resolvers: HashMap::new(),
39+
}
40+
}
41+
42+
pub fn get(&mut self, file_path: &PathBuf) -> anyhow::Result<Rc<ElfResolver>> {
3443
use anyhow::Context;
3544
if !self.elf_resolvers.contains_key(file_path.as_path()) {
36-
let resolver = ElfResolver::open(file_path).with_context(|| {
45+
let resolver = Rc::new(ElfResolver::open(file_path).with_context(|| {
3746
format!(
3847
"ElfResolver::open failed for '{}'",
3948
file_path.to_string_lossy()
4049
)
41-
})?;
50+
})?);
51+
let _ = self
52+
.symbolizer
53+
.register_elf_resolver(file_path.as_path(), Rc::clone(&resolver));
4254
self.elf_resolvers.insert(file_path.clone(), resolver);
4355
}
4456
self.elf_resolvers
4557
.get(file_path.as_path())
4658
.with_context(|| "key '{}' not found in ElfResolver cache")
59+
.cloned()
4760
}
4861
}
4962

5063
#[cfg(unix)]
5164
impl ErrorData {
5265
pub fn normalize_ips(&mut self, pid: u32) -> anyhow::Result<()> {
53-
let mut errors = 0;
54-
let mut elf_resolvers = CachedElfResolvers::default();
66+
let mut symbolizer = blazesym::symbolize::Symbolizer::new();
67+
let mut elf_resolvers = CachedElfResolvers::new(&mut symbolizer);
5568
let normalizer = blazesym::normalize::Normalizer::builder()
5669
.enable_vma_caching(true)
5770
.enable_build_ids(true)
5871
.enable_build_id_caching(true)
5972
.build();
60-
let pid = pid.into();
73+
self.normalize_ips_impl(pid, &normalizer, &mut elf_resolvers)
74+
}
75+
76+
pub(crate) fn normalize_ips_impl(
77+
&mut self,
78+
pid: u32,
79+
normalizer: &blazesym::normalize::Normalizer,
80+
elf_resolvers: &mut CachedElfResolvers,
81+
) -> anyhow::Result<()> {
82+
let mut errors = 0;
6183
self.stack
62-
.normalize_ips(&normalizer, pid, &mut elf_resolvers)
84+
.normalize_ips(normalizer, pid.into(), elf_resolvers)
6385
.unwrap_or_else(|_| errors += 1);
6486

6587
for thread in &mut self.threads {
6688
thread
6789
.stack
68-
.normalize_ips(&normalizer, pid, &mut elf_resolvers)
90+
.normalize_ips(normalizer, pid.into(), elf_resolvers)
6991
.unwrap_or_else(|_| errors += 1);
7092
}
7193
anyhow::ensure!(
@@ -75,21 +97,43 @@ impl ErrorData {
7597
Ok(())
7698
}
7799

78-
pub fn resolve_names(&mut self, pid: u32) -> anyhow::Result<()> {
79-
let mut errors = 0;
100+
pub(crate) fn create_symbolizer_source<'a>(
101+
pid: u32,
102+
) -> blazesym::symbolize::source::Source<'a> {
80103
let mut process = blazesym::symbolize::source::Process::new(pid.into());
81104
// https://github.com/libbpf/blazesym/issues/518
82105
process.map_files = false;
83-
let src = blazesym::symbolize::source::Source::Process(process);
106+
blazesym::symbolize::source::Source::Process(process)
107+
}
108+
109+
pub(crate) fn create_normalizer() -> blazesym::normalize::Normalizer {
110+
blazesym::normalize::Normalizer::builder()
111+
.enable_vma_caching(true)
112+
.enable_build_ids(true)
113+
.enable_build_id_caching(true)
114+
.build()
115+
}
116+
117+
pub fn resolve_names(&mut self, pid: u32) -> anyhow::Result<()> {
118+
let src = Self::create_symbolizer_source(pid);
84119
let symbolizer = blazesym::symbolize::Symbolizer::new();
120+
self.resolve_names_impl(&symbolizer, &src)
121+
}
122+
123+
pub(crate) fn resolve_names_impl(
124+
&mut self,
125+
symbolizer: &blazesym::symbolize::Symbolizer,
126+
src: &blazesym::symbolize::source::Source,
127+
) -> anyhow::Result<()> {
128+
let mut errors = 0;
85129
self.stack
86-
.resolve_names(&src, &symbolizer)
130+
.resolve_names(src, symbolizer)
87131
.unwrap_or_else(|_| errors += 1);
88132

89133
for thread in &mut self.threads {
90134
thread
91135
.stack
92-
.resolve_names(&src, &symbolizer)
136+
.resolve_names(src, symbolizer)
93137
.unwrap_or_else(|_| errors += 1);
94138
}
95139
anyhow::ensure!(

datadog-crashtracker/src/crash_info/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,25 @@ impl CrashInfo {
7979
pub fn resolve_names(&mut self, pid: u32) -> anyhow::Result<()> {
8080
self.error.resolve_names(pid)
8181
}
82+
83+
pub fn enrich_callstacks(&mut self, pid: u32) -> anyhow::Result<()> {
84+
let src = ErrorData::create_symbolizer_source(pid);
85+
let normalizer = ErrorData::create_normalizer();
86+
let mut symbolizer = blazesym::symbolize::Symbolizer::new();
87+
let mut elf_resolvers = CachedElfResolvers::new(&mut symbolizer);
88+
89+
// We must call ips normalization first.
90+
// This will allow us to feed the symbolizer with the ELF Resolvers
91+
let rval1 = self
92+
.error
93+
.normalize_ips_impl(pid, &normalizer, &mut elf_resolvers);
94+
let rval2 = self.error.resolve_names_impl(&symbolizer, &src);
95+
anyhow::ensure!(
96+
rval1.is_ok() && rval2.is_ok(),
97+
"normalize_ips: {rval1:?}\tresolve_names: {rval2:?}"
98+
);
99+
Ok(())
100+
}
82101
}
83102

84103
impl CrashInfo {

datadog-crashtracker/src/crash_info/stacktrace.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,12 +412,13 @@ mod unix_test {
412412
let mut frame = StackFrame::new();
413413
frame.ip = Some(address);
414414

415+
let mut symbolizer = Symbolizer::new();
415416
let normalizer = Normalizer::new();
416417
frame
417418
.normalize_ip(
418419
&normalizer,
419420
Pid::from(std::process::id()),
420-
&mut CachedElfResolvers::default(),
421+
&mut CachedElfResolvers::new(&mut symbolizer),
421422
)
422423
.unwrap();
423424

@@ -446,12 +447,13 @@ mod unix_test {
446447
let mut frame = StackFrame::new();
447448
frame.ip = Some(address);
448449

450+
let mut symbolizer = blazesym::symbolize::Symbolizer::new();
449451
let normalizer = Normalizer::new();
450452
frame
451453
.normalize_ip(
452454
&normalizer,
453455
Pid::from(std::process::id()),
454-
&mut CachedElfResolvers::default(),
456+
&mut CachedElfResolvers::new(&mut symbolizer),
455457
)
456458
.unwrap();
457459

datadog-crashtracker/src/receiver/entry_points.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,7 @@ fn resolve_frames(
139139
.as_ref()
140140
.context("Unable to resolve frames: No PID specified")?
141141
.pid;
142-
let rval1 = crash_info.resolve_names(pid);
143-
let rval2 = crash_info.normalize_ips(pid);
144-
anyhow::ensure!(
145-
rval1.is_ok() && rval2.is_ok(),
146-
"resolve_names: {rval1:?}\tnormalize_ips: {rval2:?}"
147-
);
142+
crash_info.enrich_callstacks(pid)?;
148143
}
149144
Ok(())
150145
}

0 commit comments

Comments
 (0)