From 075204bd8c87d066945c82fbd9b9a429df20e943 Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Wed, 26 Jun 2024 17:42:02 +0200 Subject: [PATCH 1/8] Support for DWARFv5 embedded source code extension --- Cargo.lock | 111 +++++++++--------- Cargo.toml | 3 +- symbolic-debuginfo/src/base.rs | 19 ++- symbolic-debuginfo/src/dwarf.rs | 35 +++++- symbolic-debuginfo/src/elf.rs | 8 +- symbolic-debuginfo/src/wasm.rs | 7 +- symbolic-debuginfo/tests/test_objects.rs | 35 +++++- .../fixtures/wasm/embed-source.wasm | Bin 0 -> 1021 bytes 8 files changed, 155 insertions(+), 63 deletions(-) create mode 100755 symbolic-testutils/fixtures/wasm/embed-source.wasm diff --git a/Cargo.lock b/Cargo.lock index 8f488300f..a418cfd0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,9 +190,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line 0.22.0", "cc", @@ -235,9 +235,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -315,9 +315,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.99" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" dependencies = [ "jobserver", "libc", @@ -378,18 +378,18 @@ checksum = "b0fc239e0f6cb375d2402d48afb92f76f5404fd1df208a41930ec81eda078bea" [[package]] name = "clap" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -512,7 +512,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "libc", "mio", @@ -584,9 +584,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", @@ -618,9 +618,9 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elementtree" @@ -771,8 +771,7 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gimli" version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9" +source = "git+https://github.com/gimli-rs/gimli.git?rev=aca0c4275e073da77a0c6e506ba9f4fb3c3aa6ca#aca0c4275e073da77a0c6e506ba9f4fb3c3aa6ca" dependencies = [ "fallible-iterator 0.3.0", "stable_deref_trait", @@ -1007,9 +1006,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128" @@ -1053,9 +1052,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchers" @@ -1074,9 +1073,9 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1113,7 +1112,7 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4d14bcca0fd3ed165a03000480aaa364c6860c34e900cb2dafdf3b95340e77" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "debugid", "num-derive", "num-traits", @@ -1182,9 +1181,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -1207,7 +1206,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4c25a3bb7d880e8eceab4822f3141ad0700d20f025991c1f03bd3d00219a5fc" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1251,9 +1250,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -1297,9 +1296,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1507,9 +1506,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1520,7 +1519,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "hex", ] @@ -1615,11 +1614,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1693,7 +1692,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1796,9 +1795,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", @@ -2057,7 +2056,7 @@ version = "0.113.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f7cbdc8d3f31a863224649258942a537be44e37cb8f5413da63b51159779b9" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "is-macro", "num-bigint", "phf", @@ -2070,9 +2069,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.144.2" +version = "0.144.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31adf4599e8de70f3b754dfc34ec2ab09fa6841d79a9f4a888250a404eae7030" +checksum = "fc0b4193b9c127db1990a5a08111aafe0122bc8b138646807c63f2a6521b7da4" dependencies = [ "either", "new_debug_unreachable", @@ -2339,9 +2338,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -2439,9 +2438,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -2538,9 +2537,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90" +checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" dependencies = [ "serde", "stable_deref_trait", @@ -2601,9 +2600,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2618,9 +2617,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "sha1_smol", ] @@ -2714,7 +2713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07035cc9a9b41e62d3bb3a3815a66ab87c993c06fe1cf6b2a3f2a18499d937db" dependencies = [ "ahash", - "bitflags 2.5.0", + "bitflags 2.6.0", "hashbrown", "indexmap", "semver 1.0.23", @@ -3025,9 +3024,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.11+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index e8f9c20a4..337d887c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,8 @@ fallible-iterator = "0.3.0" flate2 = { version = "1.0.25", default-features = false, features = [ "rust_backend", ] } -gimli = { version = "0.30.0", default-features = false, features = [ +# FIXME: replace with crates.io release once available (PR #728) +gimli = { git = "https://github.com/gimli-rs/gimli.git", rev = "aca0c4275e073da77a0c6e506ba9f4fb3c3aa6ca", default-features = false, features = [ "read", "std", "fallible-iterator", diff --git a/symbolic-debuginfo/src/base.rs b/symbolic-debuginfo/src/base.rs index e6644076e..9f9efc907 100644 --- a/symbolic-debuginfo/src/base.rs +++ b/symbolic-debuginfo/src/base.rs @@ -450,13 +450,19 @@ pub struct FileInfo<'data> { name: Cow<'data, [u8]>, /// Path to the file. dir: Cow<'data, [u8]>, + /// Source code + source: Option>, } impl<'data> FileInfo<'data> { /// Creates a `FileInfo` with a given directory and the file name. #[cfg(feature = "dwarf")] - pub fn new(dir: Cow<'data, [u8]>, name: Cow<'data, [u8]>) -> Self { - FileInfo { name, dir } + pub fn new( + dir: Cow<'data, [u8]>, + name: Cow<'data, [u8]>, + source: Option>, + ) -> Self { + FileInfo { name, dir, source } } /// Creates a `FileInfo` from a joined path by trying to split it. @@ -470,6 +476,7 @@ impl<'data> FileInfo<'data> { Some(dir) => Cow::Borrowed(dir), None => Cow::default(), }, + source: None, } } @@ -485,6 +492,7 @@ impl<'data> FileInfo<'data> { Some(dir) => Cow::Owned(dir.to_vec()), None => Cow::default(), }, + source: None, } } @@ -493,6 +501,7 @@ impl<'data> FileInfo<'data> { FileInfo { name: Cow::Borrowed(name), dir: Cow::default(), + source: None, } } @@ -506,6 +515,11 @@ impl<'data> FileInfo<'data> { from_utf8_cow_lossy(&self.dir) } + /// The embedded source code as an UTF-8 string. + pub fn source_str(&self) -> Option> { + self.source.as_ref().map(from_utf8_cow_lossy) + } + /// The full path to the file, relative to the compilation directory. pub fn path_str(&self) -> String { let joined = join_path(&self.dir_str(), &self.name_str()); @@ -832,6 +846,7 @@ mod tests { FileInfo::new( Cow::Borrowed(dir.as_bytes()), Cow::Borrowed(name.as_bytes()), + None, ) } diff --git a/symbolic-debuginfo/src/dwarf.rs b/symbolic-debuginfo/src/dwarf.rs index 2e41bc823..f1343de2a 100644 --- a/symbolic-debuginfo/src/dwarf.rs +++ b/symbolic-debuginfo/src/dwarf.rs @@ -703,6 +703,14 @@ impl<'d, 'a> DwarfUnit<'d, 'a> { self.bcsymbolmap, self.inner.slice_value(file.path_name()).unwrap_or_default(), )), + file.source().and_then(|source| { + let unit_ref = self.inner.unit.unit_ref(&self.inner.info); + match unit_ref.attr_string(source) { + Ok(source) if source.is_empty() => None, + Err(_) => None, + Ok(source) => Some(Cow::Borrowed(source.slice())), + } + }), ) } @@ -1348,10 +1356,35 @@ impl<'data> DwarfDebugSession<'data> { } /// See [DebugSession::source_by_path] for more information. + /// + /// Note that this does not load additional sources from disk and only works with sources + /// embedded directly in the debug information (DW_LNCT_LLVM_source) pub fn source_by_path( &self, - _path: &str, + path: &str, ) -> Result>, DwarfError> { + // First: see if there's an exact match for the provided path + for file in self.files() { + let file = file?; + if path != file.info.path_str() { + continue; + } + if let Some(contents) = file.source_str() { + return Ok(Some(SourceFileDescriptor::new_embedded(contents, None))); + } + } + + // If not, we'll try matching with the relative paths + for file in self.files() { + let file = file?; + if path != file.info.name_str() { + continue; + } + if let Some(contents) = file.source_str() { + return Ok(Some(SourceFileDescriptor::new_embedded(contents, None))); + } + } + Ok(None) } } diff --git a/symbolic-debuginfo/src/elf.rs b/symbolic-debuginfo/src/elf.rs index 6ddb2f298..345644630 100644 --- a/symbolic-debuginfo/src/elf.rs +++ b/symbolic-debuginfo/src/elf.rs @@ -537,7 +537,13 @@ impl<'data> ElfObject<'data> { /// Determines whether this object contains embedded source. pub fn has_sources(&self) -> bool { - false + // TODO: can we determine if we have sources without opening a debug session? + let Ok(session) = self.debug_session() else { + return false; + }; + session + .files() + .any(|f| f.is_ok_and(|f| f.source_str().is_some())) } /// Determines whether this object is malformed and was only partially parsed diff --git a/symbolic-debuginfo/src/wasm.rs b/symbolic-debuginfo/src/wasm.rs index 1454f2f49..583bf0a03 100644 --- a/symbolic-debuginfo/src/wasm.rs +++ b/symbolic-debuginfo/src/wasm.rs @@ -131,7 +131,12 @@ impl<'data> WasmObject<'data> { /// Determines whether this object contains embedded source. pub fn has_sources(&self) -> bool { - false + match self.debug_session() { + Ok(session) => session + .files() + .any(|f| f.is_ok_and(|f| f.source_str().is_some())), + Err(_) => false, + } } /// Determines whether this object is malformed and was only partially parsed diff --git a/symbolic-debuginfo/tests/test_objects.rs b/symbolic-debuginfo/tests/test_objects.rs index 6372f0a18..1ab1d7086 100644 --- a/symbolic-debuginfo/tests/test_objects.rs +++ b/symbolic-debuginfo/tests/test_objects.rs @@ -1,4 +1,4 @@ -use std::{env, ffi::CString, fmt, io::BufWriter}; +use std::{collections::HashMap, env, ffi::CString, fmt, io::BufWriter}; use symbolic_common::ByteView; use symbolic_debuginfo::{ @@ -871,6 +871,8 @@ fn test_wasm_symbols() -> Result<(), Error> { Some("bda18fd85d4a4eb893022d6bfad846b1".into()) ); + assert!(!object.has_sources()); + let symbols = object.symbol_map(); insta::assert_debug_snapshot!("wasm_symbols", SymbolsDebug(&symbols)); @@ -893,3 +895,34 @@ fn test_wasm_line_program() -> Result<(), Error> { Ok(()) } + +#[test] +fn test_wasm_has_sources() -> Result<(), Error> { + let view = ByteView::open(fixture("wasm/embed-source.wasm"))?; + let object = Object::parse(&view)?; + + assert!(object.has_sources()); + + let session = object.debug_session()?; + + let files_with_source = session + .files() + .filter_map(|x| { + let file_entry = x.ok()?; + let source = file_entry.source_str()?; + Some((file_entry.name_str(), source)) + }) + .collect::>(); + + insta::assert_debug_snapshot!(files_with_source, @r###" + { + "foo.c": "int main(void) { return 42; }\n", + } + "###); + + assert!(session.source_by_path("foo/bar.c").unwrap().is_none()); + assert!(session.source_by_path("foo.c").unwrap().is_some()); + assert!(session.source_by_path("/tmp/foo.c").unwrap().is_some()); + + Ok(()) +} diff --git a/symbolic-testutils/fixtures/wasm/embed-source.wasm b/symbolic-testutils/fixtures/wasm/embed-source.wasm new file mode 100755 index 0000000000000000000000000000000000000000..dfe3186e0fe9cd8126523d7123d6bfd19086dcf9 GIT binary patch literal 1021 zcmZuwUr!T35TDt*D}|%<03#40Tmy|DQmg_dkN}N||6WXt55|{e+uPQI-d%Hh<&Va+ zL`5F`0*0^QyYZ{|#tZu9i?c@wA#roFJ2yM?o8Rutf=~eh01g`4*nw@J4?AoYZ|)1_ za}|n~dK)FY70W$e?w9*paa7~-z)t{-z6>G*MAO0+B3cFl%j8%GD_>7cy8{)l`$kzN zkU2UA&X&J6tFbX z!%<1hqk3J*M2*v~MRJ2=h(zP|lcZyL;3~im6Ie=u76if%p&mu08q}I1Y`A+eR(=$^ z<%gwvC89TeNmqDYtoc}k`dS0-T<$deP`>HyY|u)j|2ve^OWH}PVOpzT;xx-!Q%76V zsAOea8VWFULavIGQc`5t$ji`phm$zSlMv$*;g zb9<5R&Aad2SSIZ_beHe1yB`JsexMnOK;kj>!2;)g(2C+jdi?(lO`Y9qXdLF+C*U>F zZ1P$X#ftS>*2r@n#eTyNMU(5?rjz>mSJ9T`wS(X=a}C7PAbk%QP(1a1ZI`zqqAz2d ze)8f~$rFhvrT>Im;{4*F@TyP}R`g5r+BGT<%j!zCMMI+Ts9c6YO<5`>y~XMwT4^0K qz53nF(-UjRgx95@Tak(_ Date: Tue, 16 Jul 2024 10:55:05 +0200 Subject: [PATCH 2/8] switch to new gimli release --- Cargo.lock | 7 ++++--- Cargo.toml | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a418cfd0c..5db70e3d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -770,8 +770,9 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gimli" -version = "0.30.0" -source = "git+https://github.com/gimli-rs/gimli.git?rev=aca0c4275e073da77a0c6e506ba9f4fb3c3aa6ca#aca0c4275e073da77a0c6e506ba9f4fb3c3aa6ca" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" dependencies = [ "fallible-iterator 0.3.0", "stable_deref_trait", @@ -2210,7 +2211,7 @@ dependencies = [ "elsa", "fallible-iterator 0.3.0", "flate2", - "gimli 0.30.0", + "gimli 0.31.0", "goblin", "insta", "lazy_static", diff --git a/Cargo.toml b/Cargo.toml index 337d887c4..948b65fd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,7 @@ fallible-iterator = "0.3.0" flate2 = { version = "1.0.25", default-features = false, features = [ "rust_backend", ] } -# FIXME: replace with crates.io release once available (PR #728) -gimli = { git = "https://github.com/gimli-rs/gimli.git", rev = "aca0c4275e073da77a0c6e506ba9f4fb3c3aa6ca", default-features = false, features = [ +gimli = { version = "0.31.0", default-features = false, features = [ "read", "std", "fallible-iterator", From bef6a8ffe768e2b1cc31255424d530008e2c75ff Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Tue, 16 Jul 2024 12:11:14 +0200 Subject: [PATCH 3/8] FileInfo::new: avoid semver breakage, add second constructor --- symbolic-debuginfo/src/base.rs | 9 +++++++-- symbolic-debuginfo/src/dwarf.rs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/symbolic-debuginfo/src/base.rs b/symbolic-debuginfo/src/base.rs index 9f9efc907..1b20314cf 100644 --- a/symbolic-debuginfo/src/base.rs +++ b/symbolic-debuginfo/src/base.rs @@ -457,7 +457,13 @@ pub struct FileInfo<'data> { impl<'data> FileInfo<'data> { /// Creates a `FileInfo` with a given directory and the file name. #[cfg(feature = "dwarf")] - pub fn new( + pub fn new(dir: Cow<'data, [u8]>, name: Cow<'data, [u8]>) -> Self { + Self::with_source(dir, name, None) + } + + /// Creates a `FileInfo` with a given directory, the file name, and optional source code. + #[cfg(feature = "dwarf")] + pub fn with_source( dir: Cow<'data, [u8]>, name: Cow<'data, [u8]>, source: Option>, @@ -846,7 +852,6 @@ mod tests { FileInfo::new( Cow::Borrowed(dir.as_bytes()), Cow::Borrowed(name.as_bytes()), - None, ) } diff --git a/symbolic-debuginfo/src/dwarf.rs b/symbolic-debuginfo/src/dwarf.rs index f1343de2a..f22265921 100644 --- a/symbolic-debuginfo/src/dwarf.rs +++ b/symbolic-debuginfo/src/dwarf.rs @@ -692,7 +692,7 @@ impl<'d, 'a> DwarfUnit<'d, 'a> { line_program: &LineNumberProgramHeader<'d>, file: &LineProgramFileEntry<'d>, ) -> FileInfo<'d> { - FileInfo::new( + FileInfo::with_source( Cow::Borrowed(resolve_byte_name( self.bcsymbolmap, file.directory(line_program) From 8a6a330da8df82a3eb4eb5f7c513c1e5a53bb34f Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Tue, 16 Jul 2024 12:12:21 +0200 Subject: [PATCH 4/8] fix clippy lint --- symbolic-debuginfo/src/dwarf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/symbolic-debuginfo/src/dwarf.rs b/symbolic-debuginfo/src/dwarf.rs index f22265921..46ffa06d3 100644 --- a/symbolic-debuginfo/src/dwarf.rs +++ b/symbolic-debuginfo/src/dwarf.rs @@ -704,7 +704,7 @@ impl<'d, 'a> DwarfUnit<'d, 'a> { self.inner.slice_value(file.path_name()).unwrap_or_default(), )), file.source().and_then(|source| { - let unit_ref = self.inner.unit.unit_ref(&self.inner.info); + let unit_ref = self.inner.unit.unit_ref(self.inner.info); match unit_ref.attr_string(source) { Ok(source) if source.is_empty() => None, Err(_) => None, From bc259e7ac3b86a11c1b748f3139d0fe6adea947e Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Tue, 16 Jul 2024 12:13:49 +0200 Subject: [PATCH 5/8] add changelog entry --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 869ec8af1..fd8275773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Unreleased + +**Features** + +- Support for DWARFv5 embedded source code extension ([#849](https://github.com/getsentry/symbolic/pull/849)) + + ## 12.9.2 - Downgrade and pin `zip` to fix SourceBundles with >64k files ([#846](https://github.com/getsentry/symbolic/pull/846)) From d95448e0a21cdc37892831538140d8225e7ed5dc Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Tue, 16 Jul 2024 14:53:20 +0200 Subject: [PATCH 6/8] refactor ElfObject::has_sources --- symbolic-debuginfo/src/elf.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/symbolic-debuginfo/src/elf.rs b/symbolic-debuginfo/src/elf.rs index 345644630..266c69b88 100644 --- a/symbolic-debuginfo/src/elf.rs +++ b/symbolic-debuginfo/src/elf.rs @@ -537,13 +537,14 @@ impl<'data> ElfObject<'data> { /// Determines whether this object contains embedded source. pub fn has_sources(&self) -> bool { - // TODO: can we determine if we have sources without opening a debug session? - let Ok(session) = self.debug_session() else { - return false; - }; - session - .files() - .any(|f| f.is_ok_and(|f| f.source_str().is_some())) + // Note: It'd be great to be able to determine this without iterating over all file entries. + // Unfortunately, though, this seems to be the only correct implementation. + match self.debug_session() { + Ok(session) => session + .files() + .any(|f| f.is_ok_and(|f| f.source_str().is_some())), + Err(_) => false, + } } /// Determines whether this object is malformed and was only partially parsed From 8e3749f9160869130e1d153ffb3bdd9ed5706615 Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Tue, 16 Jul 2024 14:54:16 +0200 Subject: [PATCH 7/8] DwarfDebugSession::source_by_path: do lookups by absolute path --- CHANGELOG.md | 1 - symbolic-debuginfo/src/dwarf.rs | 47 ++++++++++++------------ symbolic-debuginfo/tests/test_objects.rs | 7 ++-- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd8275773..7225297e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ - Support for DWARFv5 embedded source code extension ([#849](https://github.com/getsentry/symbolic/pull/849)) - ## 12.9.2 - Downgrade and pin `zip` to fix SourceBundles with >64k files ([#846](https://github.com/getsentry/symbolic/pull/846)) diff --git a/symbolic-debuginfo/src/dwarf.rs b/symbolic-debuginfo/src/dwarf.rs index 46ffa06d3..765ee858e 100644 --- a/symbolic-debuginfo/src/dwarf.rs +++ b/symbolic-debuginfo/src/dwarf.rs @@ -10,7 +10,7 @@ //! [`PeObject`]: ../pe/struct.PeObject.html use std::borrow::Cow; -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashMap}; use std::error::Error; use std::fmt; use std::marker::PhantomData; @@ -1303,6 +1303,9 @@ impl std::iter::FusedIterator for DwarfUnitIterator<'_> {} pub struct DwarfDebugSession<'data> { cell: SelfCell>, DwarfInfo<'data>>, bcsymbolmap: Option>>, + // We store the "lookup path" for each entry here to avoid lifetime issues w.r.t + // HashMap<_, SourceFileDescriptor<'data>>, as we can't construct this in a method call. + sources_path_to_file_idx: OnceCell>, } impl<'data> DwarfDebugSession<'data> { @@ -1324,6 +1327,7 @@ impl<'data> DwarfDebugSession<'data> { Ok(DwarfDebugSession { cell, bcsymbolmap: None, + sources_path_to_file_idx: OnceCell::default(), }) } @@ -1356,36 +1360,33 @@ impl<'data> DwarfDebugSession<'data> { } /// See [DebugSession::source_by_path] for more information. + /// This lookup returns entries that match a given [FileEntry::abs_path_str]. /// /// Note that this does not load additional sources from disk and only works with sources - /// embedded directly in the debug information (DW_LNCT_LLVM_source) + /// embedded directly in the debug information (DW_LNCT_LLVM_source). pub fn source_by_path( &self, path: &str, ) -> Result>, DwarfError> { - // First: see if there's an exact match for the provided path - for file in self.files() { - let file = file?; - if path != file.info.path_str() { - continue; - } - if let Some(contents) = file.source_str() { - return Ok(Some(SourceFileDescriptor::new_embedded(contents, None))); - } - } - - // If not, we'll try matching with the relative paths - for file in self.files() { - let file = file?; - if path != file.info.name_str() { - continue; - } - if let Some(contents) = file.source_str() { - return Ok(Some(SourceFileDescriptor::new_embedded(contents, None))); + // Construct / fetch a lookup table to avoid scanning and comparing each file's path in this + // operation: + let sources = self.sources_path_to_file_idx.get_or_init(|| { + let mut res = HashMap::new(); + for (i, file) in self.files().enumerate() { + if let Ok(file) = file { + if let Some(_contents) = file.source_str() { + res.insert(file.abs_path_str(), i); + } + } } - } + res + }); - Ok(None) + Ok(sources.get(path).map(|&idx| { + // These unwraps hold by construction above + let file = self.files().nth(idx).unwrap().unwrap(); + SourceFileDescriptor::new_embedded(file.source_str().unwrap(), None) + })) } } diff --git a/symbolic-debuginfo/tests/test_objects.rs b/symbolic-debuginfo/tests/test_objects.rs index 1ab1d7086..c98ee9976 100644 --- a/symbolic-debuginfo/tests/test_objects.rs +++ b/symbolic-debuginfo/tests/test_objects.rs @@ -910,18 +910,19 @@ fn test_wasm_has_sources() -> Result<(), Error> { .filter_map(|x| { let file_entry = x.ok()?; let source = file_entry.source_str()?; - Some((file_entry.name_str(), source)) + Some((file_entry.abs_path_str(), source)) }) .collect::>(); insta::assert_debug_snapshot!(files_with_source, @r###" { - "foo.c": "int main(void) { return 42; }\n", + "/tmp/foo.c": "int main(void) { return 42; }\n", } "###); assert!(session.source_by_path("foo/bar.c").unwrap().is_none()); - assert!(session.source_by_path("foo.c").unwrap().is_some()); + assert!(session.source_by_path("foo.c").unwrap().is_none()); + // source_by_path matches the absolute file path assert!(session.source_by_path("/tmp/foo.c").unwrap().is_some()); Ok(()) From 9eb475ccb7f9aac8dfdfa8e4eef4cdce2f616a7b Mon Sep 17 00:00:00 2001 From: Mrmaxmeier Date: Tue, 16 Jul 2024 14:58:20 +0200 Subject: [PATCH 8/8] refactoring --- symbolic-debuginfo/src/dwarf.rs | 2 +- symbolic-symcache/src/lookup.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/symbolic-debuginfo/src/dwarf.rs b/symbolic-debuginfo/src/dwarf.rs index 765ee858e..8c35fe0ac 100644 --- a/symbolic-debuginfo/src/dwarf.rs +++ b/symbolic-debuginfo/src/dwarf.rs @@ -1374,7 +1374,7 @@ impl<'data> DwarfDebugSession<'data> { let mut res = HashMap::new(); for (i, file) in self.files().enumerate() { if let Ok(file) = file { - if let Some(_contents) = file.source_str() { + if file.source_str().is_some() { res.insert(file.abs_path_str(), i); } } diff --git a/symbolic-symcache/src/lookup.rs b/symbolic-symcache/src/lookup.rs index e7635d750..9c1a4f4a9 100644 --- a/symbolic-symcache/src/lookup.rs +++ b/symbolic-symcache/src/lookup.rs @@ -228,9 +228,8 @@ impl<'data> Iterator for Functions<'data> { } } - function.map(|f| { + function.inspect(|_f| { self.function_idx += 1; - f }) } } @@ -266,9 +265,8 @@ impl<'data> Iterator for Files<'data> { type Item = File<'data>; fn next(&mut self) -> Option { - self.cache.get_file(self.file_idx).map(|f| { + self.cache.get_file(self.file_idx).inspect(|_f| { self.file_idx += 1; - f }) } }