Skip to content

Commit 4a5afec

Browse files
authored
Some more breakpad sym file symbolication improvements (#678)
This should reduce memory usage when symbols come from breakpad sym files.
2 parents 477a90f + 734d15a commit 4a5afec

File tree

10 files changed

+506
-441
lines changed

10 files changed

+506
-441
lines changed
Lines changed: 16 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::collections::BTreeMap;
22

3-
use samply_symbols::{FrameDebugInfo, SourceFilePath, SourceFilePathHandle};
3+
use samply_symbols::{FrameDebugInfo, SourceFilePath, SourceFilePathHandle, SymbolInfo};
44

55
pub trait PathResolver {
66
fn resolve_source_file_path(&self, handle: SourceFilePathHandle) -> SourceFilePath<'_>;
@@ -13,71 +13,27 @@ impl PathResolver for () {
1313
}
1414

1515
pub struct AddressResult {
16-
pub symbol_address: u32,
17-
pub symbol_name: String,
18-
pub function_size: Option<u32>,
16+
pub symbol: SymbolInfo,
1917
pub inline_frames: Option<Vec<FrameDebugInfo>>,
2018
}
2119

22-
pub type AddressResults = BTreeMap<u32, Option<AddressResult>>;
23-
24-
pub struct LookedUpAddresses {
25-
pub address_results: AddressResults,
26-
pub symbol_count: u32,
27-
}
28-
29-
impl LookedUpAddresses {
30-
pub fn for_addresses(addresses: &[u32]) -> Self {
31-
LookedUpAddresses {
32-
address_results: addresses.iter().map(|&addr| (addr, None)).collect(),
33-
symbol_count: 0,
34-
}
35-
}
36-
37-
pub fn add_address_symbol(
38-
&mut self,
39-
address: u32,
40-
symbol_address: u32,
41-
symbol_name: String,
42-
function_size: Option<u32>,
43-
) {
44-
*self.address_results.get_mut(&address).unwrap() = Some(AddressResult {
45-
symbol_address,
46-
symbol_name,
47-
function_size,
20+
impl AddressResult {
21+
pub fn new(symbol: SymbolInfo) -> Self {
22+
Self {
23+
symbol,
4824
inline_frames: None,
49-
});
50-
}
51-
52-
pub fn add_address_debug_info(&mut self, address: u32, frames: Vec<FrameDebugInfo>) {
53-
let outer_function_name = frames.last().and_then(|f| f.function.as_deref());
54-
let entry = self.address_results.get_mut(&address).unwrap();
55-
56-
match entry {
57-
Some(address_result) => {
58-
// Overwrite the symbol name with the function name from the debug info.
59-
if let Some(name) = outer_function_name {
60-
address_result.symbol_name = name.to_string();
61-
}
62-
// Add the inline frame info.``
63-
address_result.inline_frames = Some(frames);
64-
}
65-
None => {
66-
// add_address_symbol has not been called for this address.
67-
// This happens when we only have debug info but no symbol for this address.
68-
// This is a rare case.
69-
*entry = Some(AddressResult {
70-
symbol_address: address, // TODO: Would be nice to get the actual function start address from addr2line
71-
symbol_name: outer_function_name
72-
.map_or_else(|| format!("0x{address:x}"), str::to_string),
73-
function_size: None,
74-
inline_frames: Some(frames),
75-
});
76-
}
7725
}
7826
}
7927

80-
pub fn set_total_symbol_count(&mut self, total_symbol_count: u32) {
81-
self.symbol_count = total_symbol_count;
28+
pub fn set_debug_info(&mut self, frames: Vec<FrameDebugInfo>) {
29+
let outer_function_name = frames.last().and_then(|f| f.function.as_ref());
30+
// Overwrite the symbol name with the function name from the debug info.
31+
if let Some(name) = outer_function_name {
32+
self.symbol.name = name.clone();
33+
}
34+
// Add the inline frame info.
35+
self.inline_frames = Some(frames);
8236
}
8337
}
38+
39+
pub type AddressResults = BTreeMap<u32, Option<AddressResult>>;

samply-api/src/symbolicate/mod.rs

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@ use std::collections::HashMap;
22
use std::sync::Arc;
33

44
use samply_symbols::{
5-
FileAndPathHelper, FramesLookupResult, LibraryInfo, LookupAddress, SourceFilePath,
6-
SourceFilePathHandle, SymbolManager, SymbolMap,
5+
AccessPatternHint, FileAndPathHelper, FramesLookupResult, LibraryInfo, LookupAddress,
6+
SourceFilePath, SourceFilePathHandle, SymbolManager, SymbolMap,
77
};
88

99
use crate::error::Error;
10-
use crate::symbolicate::looked_up_addresses::PathResolver;
10+
use crate::symbolicate::looked_up_addresses::{AddressResult, AddressResults, PathResolver};
1111
use crate::symbolicate::response_json::{PerLibResult, Response};
1212
use crate::to_debug_id;
1313

1414
pub mod looked_up_addresses;
1515
pub mod request_json;
1616
pub mod response_json;
1717

18-
use looked_up_addresses::LookedUpAddresses;
1918
use request_json::Lib;
2019

2120
impl<H: FileAndPathHelper> PathResolver for SymbolMap<H> {
@@ -63,7 +62,7 @@ impl<'a, H: FileAndPathHelper + 'static> SymbolicateApi<'a, H> {
6362
let mut symbolicated_addresses = HashMap::new();
6463
for (lib, addresses) in requested_addresses.into_iter() {
6564
let address_results = self
66-
.symbolicate_requested_addresses_for_lib(&lib, addresses)
65+
.symbolicate_requested_addresses_for_lib(&lib, &addresses)
6766
.await;
6867
symbolicated_addresses.insert(lib, address_results);
6968
}
@@ -73,48 +72,42 @@ impl<'a, H: FileAndPathHelper + 'static> SymbolicateApi<'a, H> {
7372
async fn symbolicate_requested_addresses_for_lib(
7473
&self,
7574
lib: &Lib,
76-
mut addresses: Vec<u32>,
75+
addresses: &[u32],
7776
) -> Result<PerLibResult<H>, samply_symbols::Error> {
78-
// Sort the addresses before the lookup, to have a higher chance of hitting
79-
// the same external file for subsequent addresses.
80-
addresses.sort_unstable();
81-
addresses.dedup();
82-
8377
let debug_id = to_debug_id(&lib.breakpad_id)?;
8478

85-
let mut external_addresses = Vec::new();
86-
87-
// Do the synchronous work first, and accumulate external_addresses which need
88-
// to be handled asynchronously. This allows us to group async file loads by
89-
// the external file.
90-
9179
let info = LibraryInfo {
9280
debug_name: Some(lib.debug_name.to_string()),
9381
debug_id: Some(debug_id),
9482
..Default::default()
9583
};
9684
let symbol_map = self.symbol_manager.load_symbol_map(&info).await?;
97-
let mut symbolication_result = LookedUpAddresses::for_addresses(&addresses);
98-
99-
symbolication_result.set_total_symbol_count(symbol_map.symbol_count() as u32);
100-
101-
for &address in &addresses {
102-
if let Some(address_info) = symbol_map.lookup_sync(LookupAddress::Relative(address)) {
103-
symbolication_result.add_address_symbol(
104-
address,
105-
address_info.symbol.address,
106-
address_info.symbol.name,
107-
address_info.symbol.size,
108-
);
109-
match address_info.frames {
110-
Some(FramesLookupResult::Available(frames)) => {
111-
symbolication_result.add_address_debug_info(address, frames)
112-
}
113-
Some(FramesLookupResult::External(ext_address)) => {
114-
external_addresses.push((address, ext_address));
115-
}
116-
None => {}
85+
86+
// Create a BTreeMap for the lookup results. This lets us iterate over the
87+
// addresses in ascending order - the sort happens when the map is created.
88+
let mut address_results: AddressResults =
89+
addresses.iter().map(|&addr| (addr, None)).collect();
90+
symbol_map.set_access_pattern_hint(AccessPatternHint::SequentialLookup);
91+
92+
// Do the synchronous work first, and accumulate external_addresses which need
93+
// to be handled asynchronously. This allows us to group async file loads by
94+
// the external file.
95+
let mut external_addresses = Vec::new();
96+
97+
for (&address, address_result) in &mut address_results {
98+
let Some(address_info) = symbol_map.lookup_sync(LookupAddress::Relative(address))
99+
else {
100+
continue;
101+
};
102+
*address_result = Some(AddressResult::new(address_info.symbol));
103+
match address_info.frames {
104+
Some(FramesLookupResult::Available(frames)) => {
105+
address_result.as_mut().unwrap().set_debug_info(frames)
106+
}
107+
Some(FramesLookupResult::External(ext_address)) => {
108+
external_addresses.push((address, ext_address));
117109
}
110+
None => {}
118111
}
119112
}
120113

@@ -126,12 +119,13 @@ impl<'a, H: FileAndPathHelper + 'static> SymbolicateApi<'a, H> {
126119

127120
for (address, ext_address) in external_addresses {
128121
if let Some(frames) = symbol_map.lookup_external(&ext_address).await {
129-
symbolication_result.add_address_debug_info(address, frames);
122+
let address_result = address_results.get_mut(&address).unwrap();
123+
address_result.as_mut().unwrap().set_debug_info(frames);
130124
}
131125
}
132126

133127
let outcome = PerLibResult {
134-
address_results: symbolication_result,
128+
address_results,
135129
symbol_map: Arc::new(symbol_map),
136130
};
137131

samply-api/src/symbolicate/response_json.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ use std::sync::Arc;
55
use samply_symbols::{FileAndPathHelper, FrameDebugInfo, SymbolMap};
66
use serde::ser::{SerializeMap, SerializeSeq};
77

8-
use super::looked_up_addresses::LookedUpAddresses;
98
use super::request_json::{Job, Lib, RequestFrame, RequestStack};
109
use crate::api_file_path::to_api_file_path;
1110
use crate::symbolicate::looked_up_addresses::{AddressResults, PathResolver};
1211
use crate::symbolicate::request_json::Request;
1312

1413
pub struct PerLibResult<H: FileAndPathHelper> {
15-
pub address_results: LookedUpAddresses,
14+
pub address_results: AddressResults,
1615
pub symbol_map: Arc<SymbolMap<H>>,
1716
}
1817

@@ -59,7 +58,7 @@ impl<H: FileAndPathHelper> serde::Serialize for Response<H> {
5958
}
6059

6160
pub struct PerLibResultRef<'a> {
62-
pub address_results: std::result::Result<&'a LookedUpAddresses, &'a samply_symbols::Error>,
61+
pub address_results: std::result::Result<&'a AddressResults, &'a samply_symbols::Error>,
6362
pub path_resolver: &'a dyn PathResolver,
6463
}
6564

@@ -106,9 +105,9 @@ impl<'a> serde::Serialize for Result<'a> {
106105
if let Some(per_lib_result) = self.per_lib_results.get(lib) {
107106
let module_key = format!("{}/{}", lib.debug_name, lib.breakpad_id);
108107
match per_lib_result.address_results {
109-
Ok(symbols) => {
108+
Ok(address_results) => {
110109
let module_results = PerModuleResultsRef {
111-
address_results: &symbols.address_results,
110+
address_results,
112111
path_resolver: per_lib_result.path_resolver,
113112
};
114113
symbols_by_module_index.insert(module_index as u32, module_results);
@@ -213,12 +212,12 @@ impl<'a> serde::Serialize for ResponseFrame<'a> {
213212
let address_result = symbol_map.address_results.get(&frame.address).unwrap();
214213
// But the result might still be None.
215214
if let Some(address_result) = address_result {
216-
map.serialize_entry("function", &address_result.symbol_name)?;
215+
map.serialize_entry("function", &address_result.symbol.name)?;
217216
map.serialize_entry(
218217
"function_offset",
219-
&SerializeAsHexStr(frame.address - address_result.symbol_address),
218+
&SerializeAsHexStr(frame.address - address_result.symbol.address),
220219
)?;
221-
if let Some(function_size) = address_result.function_size {
220+
if let Some(function_size) = address_result.symbol.size {
222221
map.serialize_entry("function_size", &SerializeAsHexStr(function_size))?;
223222
}
224223

0 commit comments

Comments
 (0)