fxprof_processed_profile/library_info.rs
1use debugid::DebugId;
2use serde::{ser::SerializeMap, Serialize, Serializer};
3use std::sync::Arc;
4
5/// A library ("binary" / "module" / "DSO") which is loaded into a process.
6/// This can be the main executable file or a dynamic library, or any other
7/// mapping of executable memory.
8///
9/// Library information makes after-the-fact symbolication possible: The
10/// profile JSON contains raw code addresses, and then the symbols for these
11/// addresses get resolved later.
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct LibraryInfo {
14 /// The name of this library that should be displayed in the profiler.
15 /// Usually this is the filename of the binary, but it could also be any other
16 /// name, such as "\[kernel.kallsyms\]" or "\[vdso\]" or "JIT".
17 pub name: String,
18 /// The debug name of this library which should be used when looking up symbols.
19 /// On Windows this is the filename of the PDB file, on other platforms it's
20 /// usually the same as the filename of the binary.
21 pub debug_name: String,
22 /// The absolute path to the binary file.
23 pub path: String,
24 /// The absolute path to the debug file. On Linux and macOS this is the same as
25 /// the path to the binary file. On Windows this is the path to the PDB file.
26 pub debug_path: String,
27 /// The debug ID of the library. This lets symbolication confirm that it's
28 /// getting symbols for the right file, and it can sometimes allow obtaining a
29 /// symbol file from a symbol server.
30 pub debug_id: DebugId,
31 /// The code ID of the library. This lets symbolication confirm that it's
32 /// getting symbols for the right file, and it can sometimes allow obtaining a
33 /// symbol file from a symbol server.
34 pub code_id: Option<String>,
35 /// An optional string with the CPU arch of this library, for example "x86_64",
36 /// "arm64", or "arm64e". This is used for macOS system libraries in the dyld
37 /// shared cache, in order to avoid loading the wrong cache files, as a
38 /// performance optimization. In the past, this was also used to find the
39 /// correct sub-binary in a mach-O fat binary. But we now use the debug_id for that
40 /// purpose.
41 pub arch: Option<String>,
42 /// An optional symbol table, for "pre-symbolicating" stack frames.
43 ///
44 /// Usually, symbolication is something that should happen asynchronously,
45 /// because it can be very slow, so the regular way to use the profiler is to
46 /// store only frame addresses and no symbols in the profile JSON, and perform
47 /// symbolication only once the profile is loaded in the Firefox Profiler UI.
48 ///
49 /// However, sometimes symbols are only available during recording and are not
50 /// easily accessible afterwards. One such example the symbol table of the
51 /// Linux kernel: Users with root privileges can access the symbol table of the
52 /// currently-running kernel via `/proc/kallsyms`, but we don't want to have
53 /// to run the local symbol server with root privileges. So it's easier to
54 /// resolve kernel symbols when generating the profile JSON.
55 ///
56 /// This way of symbolicating does not support file names, line numbers, or
57 /// inline frames. It is intended for relatively "small" symbol tables for which
58 /// an address lookup is fast.
59 pub symbol_table: Option<Arc<SymbolTable>>,
60}
61
62impl Serialize for LibraryInfo {
63 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
64 let breakpad_id = self.debug_id.breakpad().to_string();
65 let code_id = self.code_id.as_ref().map(|cid| cid.to_string());
66 let mut map = serializer.serialize_map(None)?;
67 map.serialize_entry("name", &self.name)?;
68 map.serialize_entry("path", &self.path)?;
69 map.serialize_entry("debugName", &self.debug_name)?;
70 map.serialize_entry("debugPath", &self.debug_path)?;
71 map.serialize_entry("breakpadId", &breakpad_id)?;
72 map.serialize_entry("codeId", &code_id)?;
73 map.serialize_entry("arch", &self.arch)?;
74 map.end()
75 }
76}
77
78/// A symbol table which contains a list of [`Symbol`]s, used in [`LibraryInfo`].
79#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub struct SymbolTable {
81 symbols: Vec<Symbol>,
82}
83
84impl SymbolTable {
85 /// Create a [`SymbolTable`] from a list of [`Symbol`]s.
86 pub fn new(mut symbols: Vec<Symbol>) -> Self {
87 symbols.sort();
88 symbols.dedup_by_key(|symbol| symbol.address);
89 Self { symbols }
90 }
91
92 /// Look up the symbol for an address. This address is relative to the library's base address.
93 pub fn lookup(&self, address: u32) -> Option<&Symbol> {
94 let index = match self
95 .symbols
96 .binary_search_by_key(&address, |symbol| symbol.address)
97 {
98 Ok(i) => i,
99 Err(0) => return None,
100 Err(next_i) => next_i - 1,
101 };
102 let symbol = &self.symbols[index];
103 match symbol.size {
104 Some(size) if address < symbol.address.saturating_add(size) => Some(symbol),
105 Some(_size) => None,
106 None => Some(symbol),
107 }
108 }
109}
110
111/// A single symbol from a [`SymbolTable`].
112#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
113pub struct Symbol {
114 /// The symbol's address, as a "relative address", i.e. relative to the library's base address.
115 pub address: u32,
116 /// The symbol's size, if known. This is often just set based on the address of the next symbol.
117 pub size: Option<u32>,
118 /// The symbol name.
119 pub name: String,
120}