Skip to content

Commit aa0208a

Browse files
authored
Merge branch 'main' into server-url
2 parents 2607368 + a74a3c0 commit aa0208a

File tree

36 files changed

+808
-675
lines changed

36 files changed

+808
-675
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ members = [
55
"fxprof-processed-profile",
66
"gecko_profile",
77
"samply-api",
8+
"samply-debugid",
9+
"samply-object",
810
"samply-quota-manager",
911
"samply-symbols",
1012
"samply",

fxprof-processed-profile/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repository = "https://github.com/mstange/samply/"
1010
readme = "README.md"
1111

1212
[dependencies]
13-
bitflags = "2.5"
13+
bitflags = "2.10"
1414
serde_json = "1.0"
1515
serde = "1.0.204"
1616
serde_derive = "1.0.188"

samply-api/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ default = []
1414
send_futures = ["samply-symbols/send_futures"]
1515

1616
[dependencies]
17+
samply-debugid = { version = "0.1.0", path = "../samply-debugid" }
1718
samply-symbols = { version = "0.24.1", path = "../samply-symbols" }
1819
thiserror = "2"
1920
serde = "1.0.204"
2021
serde_derive = "1.0.188"
2122
serde_json = "1"
2223
yaxpeax-arch = { version = "0.3", default-features = false }
2324
yaxpeax-x86 = { version = "2", default-features = false, features = ["std", "fmt"] }
24-
yaxpeax-arm = { version = "0.3", default-features = false, features = ["std"] }
25+
yaxpeax-arm = { version = "0.4", default-features = false, features = ["std"] }
2526

2627
[dev-dependencies]
2728
memmap2 = "0.9.4"

samply-api/src/asm/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::str::FromStr;
22

3+
use samply_debugid::CodeId;
34
use samply_symbols::debugid::DebugId;
45
use samply_symbols::{
5-
object, CodeByteReadingError, CodeId, FileAndPathHelper, FileAndPathHelperError, LibraryInfo,
6+
object, CodeByteReadingError, FileAndPathHelper, FileAndPathHelperError, LibraryInfo,
67
LookupAddress, SymbolManager,
78
};
89
use yaxpeax_arch::{Arch, DecodeError, LengthedInstruction, Reader, U8Reader};

samply-debugid/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "samply-debugid"
3+
version = "0.1.0"
4+
authors = ["Markus Stange <[email protected]>"]
5+
license = "MIT OR Apache-2.0"
6+
edition = "2021"
7+
description = "Samply compatible debugids."
8+
repository = "https://github.com/mstange/samply/"
9+
readme = "README.md"
10+
11+
[dependencies.debugid]
12+
default-features = false
13+
version = "0.8.0"
14+
15+
[dependencies.uuid]
16+
default-features = false
17+
version = "1"

samply-debugid/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# samply-debugid
2+
3+
This crate allows generating [`debugid`s](https://crates.io/crates/debugid)
4+
that are compatible with `samply`. Useful for writing your own profiles to be
5+
symbolicated and displayed with `samply load`.

samply-debugid/src/codeid.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use std::str::FromStr;
2+
3+
use uuid::Uuid;
4+
5+
/// An enum carrying an identifier for a binary. This is stores the same information
6+
/// as a [`debugid::CodeId`], but without projecting it down to a string.
7+
///
8+
/// All types need to be treated rather differently, see their respective documentation.
9+
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
10+
pub enum CodeId {
11+
/// The code ID for a Windows PE file. When combined with the binary name,
12+
/// the code ID lets you obtain binaries from symbol servers. It is not useful
13+
/// on its own, it has to be paired with the binary name.
14+
///
15+
/// On Windows, a binary's code ID is distinct from its debug ID (= pdb GUID + age).
16+
/// If you have a binary file, you can get both the code ID and the debug ID
17+
/// from it. If you only have a PDB file, you usually *cannot* get the code ID of
18+
/// the corresponding binary from it.
19+
PeCodeId(PeCodeId),
20+
21+
/// The code ID for a macOS / iOS binary (mach-O). This is just the mach-O UUID.
22+
/// The mach-O UUID is shared between both the binary file and the debug file (dSYM),
23+
/// and it can be used on its own to find dSYMs using Spotlight.
24+
///
25+
/// The debug ID and the code ID contain the same information; the debug ID
26+
/// is literally just the UUID plus a zero at the end.
27+
MachoUuid(Uuid),
28+
29+
/// The code ID for a Linux ELF file. This is the "ELF build ID" (also called "GNU build ID").
30+
/// The build ID is usually 20 bytes, commonly written out as 40 hex chars.
31+
///
32+
/// It can be used to find debug files on the local file system or to download
33+
/// binaries or debug files from a `debuginfod` symbol server. it does not have to be
34+
/// paired with the binary name.
35+
///
36+
/// An ELF binary's code ID is more useful than its debug ID: The debug ID is truncated
37+
/// to 16 bytes (32 hex characters), whereas the code ID is the full ELF build ID.
38+
ElfBuildId(ElfBuildId),
39+
}
40+
41+
impl FromStr for CodeId {
42+
type Err = ();
43+
44+
fn from_str(s: &str) -> Result<Self, Self::Err> {
45+
if s.len() <= 17 {
46+
// 8 bytes timestamp + 1 to 8 bytes of image size
47+
Ok(CodeId::PeCodeId(PeCodeId::from_str(s)?))
48+
} else if s.len() == 32 && is_uppercase_hex(s) {
49+
// mach-O UUID
50+
Ok(CodeId::MachoUuid(Uuid::from_str(s).map_err(|_| ())?))
51+
} else {
52+
// ELF build ID. These are usually 40 hex characters (= 20 bytes).
53+
Ok(CodeId::ElfBuildId(ElfBuildId::from_str(s)?))
54+
}
55+
}
56+
}
57+
58+
fn is_uppercase_hex(s: &str) -> bool {
59+
s.chars()
60+
.all(|c| c.is_ascii_hexdigit() && (c.is_ascii_digit() || c.is_ascii_uppercase()))
61+
}
62+
63+
impl std::fmt::Display for CodeId {
64+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65+
match self {
66+
CodeId::PeCodeId(pe) => std::fmt::Display::fmt(pe, f),
67+
CodeId::MachoUuid(uuid) => f.write_fmt(format_args!("{:X}", uuid.simple())),
68+
CodeId::ElfBuildId(elf) => std::fmt::Display::fmt(elf, f),
69+
}
70+
}
71+
}
72+
73+
/// The code ID for a Windows PE file.
74+
///
75+
/// When combined with the binary name, the `PeCodeId` lets you obtain binaries from
76+
/// symbol servers. It is not useful on its own, it has to be paired with the binary name.
77+
///
78+
/// A Windows binary's `PeCodeId` is distinct from its debug ID (= pdb GUID + age).
79+
/// If you have a binary file, you can get both the `PeCodeId` and the debug ID
80+
/// from it. If you only have a PDB file, you usually *cannot* get the `PeCodeId` of
81+
/// the corresponding binary from it.
82+
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
83+
pub struct PeCodeId {
84+
pub timestamp: u32,
85+
pub image_size: u32,
86+
}
87+
88+
impl FromStr for PeCodeId {
89+
type Err = ();
90+
91+
fn from_str(s: &str) -> Result<Self, Self::Err> {
92+
if s.len() < 9 || s.len() > 16 {
93+
return Err(());
94+
}
95+
let timestamp = u32::from_str_radix(&s[..8], 16).map_err(|_| ())?;
96+
let image_size = u32::from_str_radix(&s[8..], 16).map_err(|_| ())?;
97+
Ok(Self {
98+
timestamp,
99+
image_size,
100+
})
101+
}
102+
}
103+
104+
impl std::fmt::Display for PeCodeId {
105+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106+
f.write_fmt(format_args!("{:08X}{:x}", self.timestamp, self.image_size))
107+
}
108+
}
109+
110+
/// The build ID for an ELF file (also called "GNU build ID").
111+
///
112+
/// The build ID can be used to find debug files on the local file system or to download
113+
/// binaries or debug files from a `debuginfod` symbol server. it does not have to be
114+
/// paired with the binary name.
115+
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
116+
pub struct ElfBuildId(pub Vec<u8>);
117+
118+
impl ElfBuildId {
119+
/// Create a new `ElfBuildId` from a slice of bytes (commonly a sha1 hash
120+
/// generated by the linker, i.e. 20 bytes).
121+
pub fn from_bytes(bytes: &[u8]) -> Self {
122+
Self(bytes.to_owned())
123+
}
124+
}
125+
126+
impl FromStr for ElfBuildId {
127+
type Err = ();
128+
129+
fn from_str(s: &str) -> Result<Self, Self::Err> {
130+
let byte_count = s.len() / 2;
131+
let mut bytes = Vec::with_capacity(byte_count);
132+
for i in 0..byte_count {
133+
let hex_byte = &s[i * 2..i * 2 + 2];
134+
let b = u8::from_str_radix(hex_byte, 16).map_err(|_| ())?;
135+
bytes.push(b);
136+
}
137+
Ok(Self(bytes))
138+
}
139+
}
140+
141+
impl std::fmt::Display for ElfBuildId {
142+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143+
for byte in &self.0 {
144+
f.write_fmt(format_args!("{byte:02x}"))?;
145+
}
146+
Ok(())
147+
}
148+
}
Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
use debugid::DebugId;
2-
use object::{Object, ObjectSection};
32
use uuid::Uuid;
43

5-
use crate::shared::{CodeId, ElfBuildId};
6-
74
pub trait DebugIdExt {
85
/// Creates a DebugId from some identifier. The identifier could be
96
/// an ELF build ID, or a hash derived from the text section.
@@ -62,54 +59,3 @@ impl DebugIdExt for DebugId {
6259
DebugId::from_identifier(&hash, little_endian)
6360
}
6461
}
65-
66-
/// Tries to obtain a DebugId for an object. This uses the build ID, if available,
67-
/// and falls back to hashing the first page of the text section otherwise.
68-
/// Returns None on failure.
69-
pub fn debug_id_for_object<'data>(obj: &impl Object<'data>) -> Option<DebugId> {
70-
// Windows
71-
if let Ok(Some(pdb_info)) = obj.pdb_info() {
72-
return Some(DebugId::from_guid_age(&pdb_info.guid(), pdb_info.age()).unwrap());
73-
}
74-
75-
// ELF
76-
if let Ok(Some(build_id)) = obj.build_id() {
77-
return Some(DebugId::from_identifier(build_id, obj.is_little_endian()));
78-
}
79-
80-
// mach-O
81-
if let Ok(Some(uuid)) = obj.mach_uuid() {
82-
return Some(DebugId::from_uuid(Uuid::from_bytes(uuid)));
83-
}
84-
85-
// We were not able to locate a build ID, so fall back to creating a synthetic
86-
// identifier from a hash of the first page of the ".text" (program code) section.
87-
if let Some(section) = obj.section_by_name(".text") {
88-
let data_len = section.size().min(4096);
89-
if let Ok(Some(first_page_data)) = section.data_range(section.address(), data_len) {
90-
return Some(DebugId::from_text_first_page(
91-
first_page_data,
92-
obj.is_little_endian(),
93-
));
94-
}
95-
}
96-
97-
None
98-
}
99-
100-
/// Tries to obtain a CodeId for an object.
101-
///
102-
/// This currently only handles mach-O and ELF.
103-
pub fn code_id_for_object<'data>(obj: &impl Object<'data>) -> Option<CodeId> {
104-
// ELF
105-
if let Ok(Some(build_id)) = obj.build_id() {
106-
return Some(CodeId::ElfBuildId(ElfBuildId::from_bytes(build_id)));
107-
}
108-
109-
// mach-O
110-
if let Ok(Some(uuid)) = obj.mach_uuid() {
111-
return Some(CodeId::MachoUuid(Uuid::from_bytes(uuid)));
112-
}
113-
114-
None
115-
}

samply-debugid/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod codeid;
2+
mod debugid;
3+
4+
pub use codeid::{CodeId, ElfBuildId, PeCodeId};
5+
pub use debugid::DebugIdExt;

0 commit comments

Comments
 (0)