Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support PPDB embedded sources #734

Merged
merged 12 commits into from
Dec 16, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
**Features**:

- `PortablePdbDebugSession` now returns files referenced in the Portable PDB file. ([#729](https://github.com/getsentry/symbolic/pull/729))
- `PortablePdbDebugSession` now returns source files embedded in the Portable PDB file. ([#734](https://github.com/getsentry/symbolic/pull/734))

**Breaking changes**:

Expand Down
2 changes: 1 addition & 1 deletion examples/dump_sources/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn write_object_sources(path: &Path, output_path: &Path) -> Result<(), Box<dyn s
for object in archive.objects() {
match object {
Ok(object) => {
let out = output_path.join(&format!("{}.zip", &object.debug_id()));
let out = output_path.join(format!("{}.zip", &object.debug_id()));
println!(" -> {}", out.display());
let writer = SourceBundleWriter::create(&out)?;
writer.write_object(&object, &path.file_name().unwrap().to_string_lossy())?;
Expand Down
2 changes: 1 addition & 1 deletion symbolic-debuginfo/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ impl<'data> FileInfo<'data> {
}

#[allow(clippy::ptr_arg)] // false positive https://github.com/rust-lang/rust-clippy/issues/9218
fn from_utf8_cow_lossy<'data>(input: &Cow<'data, [u8]>) -> Cow<'data, str> {
pub(crate) fn from_utf8_cow_lossy<'data>(input: &Cow<'data, [u8]>) -> Cow<'data, str> {
// See https://github.com/rust-lang/rust/issues/32669
match input {
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
Expand Down
35 changes: 29 additions & 6 deletions symbolic-debuginfo/src/ppdb.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Support for Portable PDB Objects.
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::iter;

use symbolic_common::{Arch, CodeId, DebugId};
use symbolic_ppdb::EmbeddedSource;
use symbolic_ppdb::{FormatError, PortablePdb};

use crate::base::*;
Expand Down Expand Up @@ -88,9 +90,7 @@ impl<'data: 'object, 'object> ObjectLike<'data, 'object> for PortablePdbObject<'

/// Constructs a debugging session.
fn debug_session(&self) -> Result<PortablePdbDebugSession<'data>, FormatError> {
Ok(PortablePdbDebugSession {
ppdb: self.ppdb.clone(),
})
PortablePdbDebugSession::new(&self.ppdb)
}

/// Determines whether this object contains stack unwinding information.
Expand All @@ -100,7 +100,10 @@ impl<'data: 'object, 'object> ObjectLike<'data, 'object> for PortablePdbObject<'

/// Determines whether this object contains embedded source.
fn has_sources(&self) -> bool {
false
match self.ppdb.get_embedded_sources() {
Ok(mut iter) => iter.any(|v| v.is_ok()),
Err(_) => false,
}
}

/// Determines whether this object is malformed and was only partially parsed.
Expand Down Expand Up @@ -138,9 +141,24 @@ impl fmt::Debug for PortablePdbObject<'_> {
/// A debug session for a Portable PDB object.
pub struct PortablePdbDebugSession<'data> {
ppdb: PortablePdb<'data>,
sources: HashMap<String, EmbeddedSource<'data>>,
}

impl<'data> PortablePdbDebugSession<'data> {
fn new(ppdb: &'_ PortablePdb<'data>) -> Result<Self, FormatError> {
let mut sources: HashMap<String, EmbeddedSource<'data>> = HashMap::new();
for source in ppdb.get_embedded_sources()? {
match source {
Ok(source) => sources.insert(source.get_path().into(), source),
Err(e) => return Err(e),
};
}
Ok(PortablePdbDebugSession {
ppdb: ppdb.clone(),
sources,
})
}

/// Returns an iterator over all functions in this debug file.
pub fn functions(&self) -> PortablePdbFunctionIterator<'_> {
iter::empty()
Expand All @@ -154,8 +172,13 @@ impl<'data> PortablePdbDebugSession<'data> {
/// Looks up a file's source contents by its full canonicalized path.
///
/// The given path must be canonicalized.
pub fn source_by_path(&self, _path: &str) -> Result<Option<Cow<'_, str>>, FormatError> {
Ok(None)
pub fn source_by_path(&self, path: &str) -> Result<Option<Cow<'_, str>>, FormatError> {
match self.sources.get(path) {
None => Ok(None),
Some(source) => source
.get_contents()
.map(|bytes| Some(from_utf8_cow_lossy(&bytes))),
}
}
}

Expand Down
43 changes: 43 additions & 0 deletions symbolic-debuginfo/tests/test_objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,49 @@ fn test_ppdb_functions() -> Result<(), Error> {
Ok(())
}

#[test]
fn test_ppdb_has_sources() -> Result<(), Error> {
{
let view = ByteView::open(fixture("windows/portable.pdb"))?;
let object = Object::parse(&view)?;
assert_eq!(object.has_sources(), false);
}
{
let view = ByteView::open(fixture("windows/Sentry.Samples.Console.Basic.pdb"))?;
let object = Object::parse(&view)?;
assert_eq!(object.has_sources(), true);
}
Ok(())
}

#[test]
fn test_ppdb_source_by_path() -> Result<(), Error> {
{
let view = ByteView::open(fixture("windows/portable.pdb"))?;
let object = Object::parse(&view)?;

let session = object.debug_session()?;
let source = session.source_by_path("foo/bar.cs").unwrap();
assert!(source.is_none());
}

{
let view = ByteView::open(fixture("windows/Sentry.Samples.Console.Basic.pdb"))?;
let object = Object::parse(&view)?;

let session = object.debug_session()?;
let source = session
.source_by_path(
"C:\\dev\\sentry-dotnet\\samples\\Sentry.Samples.Console.Basic\\Program.cs",
)
.unwrap();
let source_text = source.unwrap();
assert_eq!(source_text.len(), 204);
}

Ok(())
}

#[test]
fn test_wasm_symbols() -> Result<(), Error> {
let view = ByteView::open(fixture("wasm/simple.wasm"))?;
Expand Down
1 change: 1 addition & 0 deletions symbolic-ppdb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ symbolic-common = { version = "10.2.1", path = "../symbolic-common" }
watto = { version = "0.1.0", features = ["writer", "strings"] }
thiserror = "1.0.31"
uuid = "1.0.0"
flate2 = { version ="1.0.13", default-features = false, features = [ "rust_backend" ] }

[dev-dependencies]
symbolic-testutils = { path = "../symbolic-testutils" }
2 changes: 1 addition & 1 deletion symbolic-ppdb/src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub struct CacheError {
}

impl CacheError {
/// Creates a new SymCache error from a known kind of error as well as an
/// Creates a new CacheError from a known kind of error as well as an
/// arbitrary error payload.
pub(crate) fn new<E>(kind: CacheErrorKind, source: E) -> Self
where
Expand Down
Loading