Skip to content

Commit d2d99fc

Browse files
committed
Add SourceFilePathHandle.
The address result now contains SourceFilePathHandles instead of the actual SourceFilePath. The caller then has to resolve the handle to an actual SourceFilePath in a separate step. This achieves the following goals: - No string copies are allocated for the address result. - The address result is still "owned", i.e. it can be passed around without having a lifetime dependency on the SymbolMap. - The handle can be used as a hash map key, hashing the key is fast and independent of the string length. - It's easier for callers to do per-path work only once. - Making path strings that live longer than the SymbolMap is now an explicit operation. (e.g. this approach doesn't use Arc<str>) - Resolving paths often doesn't have to allocate strings either because the returned SourceFilePath can have a lifetime dependency on the SymbolMap, which means it can reference the file data whose mmap handle is owned by the SymbolMap. In practice this massively reduces the number of allocations + frees happening when handling the symbolication JSON API request.
1 parent 3558907 commit d2d99fc

File tree

31 files changed

+756
-337
lines changed

31 files changed

+756
-337
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

samply-api/src/api_file_path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::borrow::Cow;
22

33
use samply_symbols::SourceFilePath;
44

5-
pub fn to_api_file_path(file_path: &SourceFilePath) -> Cow<'_, str> {
5+
pub fn to_api_file_path<'a>(file_path: &'a SourceFilePath<'_>) -> Cow<'a, str> {
66
file_path
77
.special_path_str()
88
.unwrap_or_else(|| file_path.raw_path().into())

samply-api/src/lib.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ use debugid::DebugId;
148148
pub use samply_symbols;
149149
pub use samply_symbols::debugid;
150150
use samply_symbols::{FileAndPathHelper, SymbolManager};
151+
use serde::Serialize;
151152
use source::SourceApi;
152153
use symbolicate::SymbolicateApi;
153154

@@ -170,39 +171,48 @@ pub(crate) fn to_debug_id(breakpad_id: &str) -> Result<DebugId, samply_symbols::
170171
}
171172
}
172173

173-
#[derive(serde_derive::Serialize)]
174-
pub struct QueryApiJsonResult(QueryApiJsonResultInner);
175-
176-
#[derive(serde_derive::Serialize)]
177-
#[serde(untagged)]
178-
enum QueryApiJsonResultInner {
179-
SymbolicateResponse(symbolicate::response_json::Response),
174+
pub enum QueryApiJsonResult<H: FileAndPathHelper> {
175+
SymbolicateResponse(symbolicate::response_json::Response<H>),
180176
SourceResponse(source::response_json::Response),
181177
AsmResponse(asm::response_json::Response),
182178
Err(Error),
183179
}
184180

185-
impl From<symbolicate::response_json::Response> for QueryApiJsonResult {
186-
fn from(value: symbolicate::response_json::Response) -> Self {
187-
Self(QueryApiJsonResultInner::SymbolicateResponse(value))
181+
impl<H: FileAndPathHelper> From<symbolicate::response_json::Response<H>> for QueryApiJsonResult<H> {
182+
fn from(value: symbolicate::response_json::Response<H>) -> Self {
183+
QueryApiJsonResult::SymbolicateResponse(value)
188184
}
189185
}
190186

191-
impl From<source::response_json::Response> for QueryApiJsonResult {
187+
impl<H: FileAndPathHelper> From<source::response_json::Response> for QueryApiJsonResult<H> {
192188
fn from(value: source::response_json::Response) -> Self {
193-
Self(QueryApiJsonResultInner::SourceResponse(value))
189+
QueryApiJsonResult::SourceResponse(value)
194190
}
195191
}
196192

197-
impl From<asm::response_json::Response> for QueryApiJsonResult {
193+
impl<H: FileAndPathHelper> From<asm::response_json::Response> for QueryApiJsonResult<H> {
198194
fn from(value: asm::response_json::Response) -> Self {
199-
Self(QueryApiJsonResultInner::AsmResponse(value))
195+
QueryApiJsonResult::AsmResponse(value)
200196
}
201197
}
202198

203-
impl From<Error> for QueryApiJsonResult {
199+
impl<H: FileAndPathHelper> From<Error> for QueryApiJsonResult<H> {
204200
fn from(value: Error) -> Self {
205-
Self(QueryApiJsonResultInner::Err(value))
201+
QueryApiJsonResult::Err(value)
202+
}
203+
}
204+
205+
impl<H: FileAndPathHelper> Serialize for QueryApiJsonResult<H> {
206+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
207+
where
208+
S: serde::Serializer,
209+
{
210+
match self {
211+
QueryApiJsonResult::SymbolicateResponse(response) => response.serialize(serializer),
212+
QueryApiJsonResult::SourceResponse(response) => response.serialize(serializer),
213+
QueryApiJsonResult::AsmResponse(response) => response.serialize(serializer),
214+
QueryApiJsonResult::Err(error) => error.serialize(serializer),
215+
}
206216
}
207217
}
208218

@@ -211,7 +221,7 @@ pub struct Api<'a, H: FileAndPathHelper> {
211221
symbol_manager: &'a SymbolManager<H>,
212222
}
213223

214-
impl<'a, H: FileAndPathHelper> Api<'a, H> {
224+
impl<'a, H: FileAndPathHelper + 'static> Api<'a, H> {
215225
/// Create a [`Api`] instance which uses the provided [`SymbolManager`].
216226
pub fn new(symbol_manager: &'a SymbolManager<H>) -> Self {
217227
Self { symbol_manager }
@@ -232,7 +242,11 @@ impl<'a, H: FileAndPathHelper> Api<'a, H> {
232242
/// symbol information for that address.
233243
/// - `/asm/v1`: Experimental API. Symbolicates an address and lets you read one of the files in the
234244
/// symbol information for that address.
235-
pub async fn query_api(self, request_url: &str, request_json_data: &str) -> QueryApiJsonResult {
245+
pub async fn query_api(
246+
self,
247+
request_url: &str,
248+
request_json_data: &str,
249+
) -> QueryApiJsonResult<H> {
236250
self.query_api_fallible(request_url, request_json_data)
237251
.await
238252
.unwrap_or_else(|e| e.into())
@@ -241,7 +255,7 @@ impl<'a, H: FileAndPathHelper> Api<'a, H> {
241255
self,
242256
request_url: &str,
243257
request_json_data: &str,
244-
) -> Result<QueryApiJsonResult, Error> {
258+
) -> Result<QueryApiJsonResult<H>, Error> {
245259
if request_url == "/symbolicate/v5" {
246260
let symbolicate_api = SymbolicateApi::new(self.symbol_manager);
247261
Ok(symbolicate_api

samply-api/src/source/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ impl<'a, H: FileAndPathHelper> SourceApi<'a, H> {
7373
let source_file_path = frames
7474
.into_iter()
7575
.filter_map(|frame| frame.file_path)
76+
.map(|path| symbol_map.resolve_source_file_path(path))
7677
.find(|file_path| to_api_file_path(file_path) == *requested_file)
7778
.ok_or(SourceError::InvalidPath)?;
7879

samply-api/src/symbolicate/looked_up_addresses.rs

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

3-
use samply_symbols::FrameDebugInfo;
3+
use samply_symbols::{FrameDebugInfo, SourceFilePath, SourceFilePathHandle};
4+
5+
pub trait PathResolver {
6+
fn resolve_source_file_path(&self, handle: SourceFilePathHandle) -> SourceFilePath<'_>;
7+
}
8+
9+
impl PathResolver for () {
10+
fn resolve_source_file_path(&self, _handle: SourceFilePathHandle) -> SourceFilePath<'_> {
11+
unreachable!()
12+
}
13+
}
414

515
pub struct AddressResult {
616
pub symbol_address: u32,

samply-api/src/symbolicate/mod.rs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
use std::collections::HashMap;
2+
use std::sync::Arc;
23

34
use samply_symbols::{
4-
FileAndPathHelper, FramesLookupResult, LibraryInfo, LookupAddress, SymbolManager,
5+
FileAndPathHelper, FramesLookupResult, LibraryInfo, LookupAddress, SourceFilePath,
6+
SourceFilePathHandle, SymbolManager, SymbolMap,
57
};
68

79
use crate::error::Error;
10+
use crate::symbolicate::looked_up_addresses::PathResolver;
11+
use crate::symbolicate::response_json::{PerLibResult, Response};
812
use crate::to_debug_id;
913

1014
pub mod looked_up_addresses;
@@ -14,11 +18,17 @@ pub mod response_json;
1418
use looked_up_addresses::LookedUpAddresses;
1519
use request_json::Lib;
1620

21+
impl<H: FileAndPathHelper> PathResolver for SymbolMap<H> {
22+
fn resolve_source_file_path(&self, handle: SourceFilePathHandle) -> SourceFilePath<'_> {
23+
self.resolve_source_file_path(handle)
24+
}
25+
}
26+
1727
pub struct SymbolicateApi<'a, H: FileAndPathHelper> {
1828
symbol_manager: &'a SymbolManager<H>,
1929
}
2030

21-
impl<'a, H: FileAndPathHelper> SymbolicateApi<'a, H> {
31+
impl<'a, H: FileAndPathHelper + 'static> SymbolicateApi<'a, H> {
2232
/// Create a [`SymbolicateApi`] instance which uses the provided [`SymbolManager`].
2333
pub fn new(symbol_manager: &'a SymbolManager<H>) -> Self {
2434
Self { symbol_manager }
@@ -27,26 +37,29 @@ impl<'a, H: FileAndPathHelper> SymbolicateApi<'a, H> {
2737
pub async fn query_api_json(
2838
&self,
2939
request_json: &str,
30-
) -> Result<response_json::Response, Error> {
40+
) -> Result<response_json::Response<H>, Error> {
3141
let request: request_json::Request = serde_json::from_str(request_json)?;
3242
self.query_api(request).await
3343
}
3444

3545
pub async fn query_api(
3646
&self,
3747
request: request_json::Request,
38-
) -> Result<response_json::Response, Error> {
48+
) -> Result<response_json::Response<H>, Error> {
3949
let requested_addresses = gather_requested_addresses(&request)?;
40-
let symbolicated_addresses = self
50+
let per_lib_results = self
4151
.symbolicate_requested_addresses(requested_addresses)
4252
.await;
43-
Ok(create_response(request, symbolicated_addresses))
53+
Ok(Response {
54+
request,
55+
per_lib_results,
56+
})
4457
}
4558

4659
async fn symbolicate_requested_addresses(
4760
&self,
4861
requested_addresses: HashMap<Lib, Vec<u32>>,
49-
) -> HashMap<Lib, Result<LookedUpAddresses, samply_symbols::Error>> {
62+
) -> HashMap<Lib, Result<PerLibResult<H>, samply_symbols::Error>> {
5063
let mut symbolicated_addresses = HashMap::new();
5164
for (lib, addresses) in requested_addresses.into_iter() {
5265
let address_results = self
@@ -61,15 +74,14 @@ impl<'a, H: FileAndPathHelper> SymbolicateApi<'a, H> {
6174
&self,
6275
lib: &Lib,
6376
mut addresses: Vec<u32>,
64-
) -> Result<LookedUpAddresses, samply_symbols::Error> {
77+
) -> Result<PerLibResult<H>, samply_symbols::Error> {
6578
// Sort the addresses before the lookup, to have a higher chance of hitting
6679
// the same external file for subsequent addresses.
6780
addresses.sort_unstable();
6881
addresses.dedup();
6982

7083
let debug_id = to_debug_id(&lib.breakpad_id)?;
7184

72-
let mut symbolication_result = LookedUpAddresses::for_addresses(&addresses);
7385
let mut external_addresses = Vec::new();
7486

7587
// Do the synchronous work first, and accumulate external_addresses which need
@@ -82,6 +94,7 @@ impl<'a, H: FileAndPathHelper> SymbolicateApi<'a, H> {
8294
..Default::default()
8395
};
8496
let symbol_map = self.symbol_manager.load_symbol_map(&info).await?;
97+
let mut symbolication_result = LookedUpAddresses::for_addresses(&addresses);
8598

8699
symbolication_result.set_total_symbol_count(symbol_map.symbol_count() as u32);
87100

@@ -117,7 +130,12 @@ impl<'a, H: FileAndPathHelper> SymbolicateApi<'a, H> {
117130
}
118131
}
119132

120-
Ok(symbolication_result)
133+
let outcome = PerLibResult {
134+
address_results: symbolication_result,
135+
symbol_map: Arc::new(symbol_map),
136+
};
137+
138+
Ok(outcome)
121139
}
122140
}
123141

@@ -147,15 +165,3 @@ fn gather_requested_addresses(
147165
}
148166
Ok(requested_addresses)
149167
}
150-
151-
fn create_response(
152-
request: request_json::Request,
153-
symbolicated_addresses: HashMap<Lib, Result<LookedUpAddresses, samply_symbols::Error>>,
154-
) -> response_json::Response {
155-
use response_json::{Response, Results};
156-
157-
Response(Results {
158-
request,
159-
symbolicated_addresses,
160-
})
161-
}

0 commit comments

Comments
 (0)