Skip to content

Commit

Permalink
ref: Split DWARF line records according to inlinees
Browse files Browse the repository at this point in the history
  • Loading branch information
Swatinem committed Jul 25, 2022
1 parent 40a0e39 commit 65a71bf
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 29 deletions.
17 changes: 16 additions & 1 deletion symbolic-debuginfo/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ impl<'data> Deref for FileEntry<'data> {
}

/// File and line number mapping for an instruction address.
#[derive(Clone)]
#[derive(Clone, Eq, PartialEq)]
pub struct LineInfo<'data> {
/// The instruction address relative to the image base (load address).
pub address: u64,
Expand All @@ -538,6 +538,21 @@ pub struct LineInfo<'data> {
pub line: u64,
}

#[cfg(test)]
impl LineInfo<'static> {
pub(crate) fn new(address: u64, size: u64, file: &[u8], line: u64) -> LineInfo {
LineInfo {
address,
size: Some(size),
file: FileInfo {
name: file,
dir: &[],
},
line,
}
}
}

impl fmt::Debug for LineInfo<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("LineInfo");
Expand Down
193 changes: 169 additions & 24 deletions symbolic-debuginfo/src/function_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,36 @@ impl<'s> FunctionBuilder<'s> {
}

// Process the line.
if let Some(line) = next_line.take() {
if let Some(mut line) = next_line.take() {
stack.flush_address(line.address);

// If the line record exceeds the size of the innermost inlinee on the stack,
// split the line record apart and continue with the second half, which can then
// fall into another inlinee. We do this only for *inlinees*, and not for the
// outer function which is on the bottom of that stack.
// The `linux/crash.debug` fixture contains a case where a line record goes beyond
// the outermost function. In that case we just continue as normal without splitting.
let split_line = if stack.stack.len() > 1 {
line.size.and_then(|size| {
let line_end = line.address.saturating_add(size);
let last_inlinee_addr = stack.last_mut().end_address();
if last_inlinee_addr < line_end {
let mut split_line = line.clone();
split_line.address = last_inlinee_addr;
split_line.size = Some(line_end - last_inlinee_addr);
line.size = Some(last_inlinee_addr - line.address);

Some(split_line)
} else {
None
}
})
} else {
None
};

stack.last_mut().lines.push(line);
next_line = line_iter.next();
next_line = split_line.or_else(|| line_iter.next());
continue;
}

Expand Down Expand Up @@ -257,10 +283,7 @@ mod tests {
let func = builder.finish();

assert_eq!(func.name.as_str(), "foo");
assert_eq!(func.lines.len(), 1);
assert_eq!(func.lines[0].address, 0x10);
assert_eq!(func.lines[0].file.name_str(), "foo.c");
assert_eq!(func.lines[0].line, 1);
assert_eq!(&func.lines, &[LineInfo::new(0x10, 0x30, b"foo.c", 1)]);
}

#[test]
Expand All @@ -269,6 +292,17 @@ mod tests {
// 0x20 - 0x40: bar in bar.c on line 1
// - inlined into: foo in foo.c on line 2
let mut builder = FunctionBuilder::new(Name::from("foo"), &[], 0x10, 0x30);
builder.add_inlinee(
1,
Name::from("bar"),
0x20,
0x20,
FileInfo {
name: b"foo.c",
dir: &[],
},
2,
);
builder.add_leaf_line(
0x10,
Some(0x10),
Expand All @@ -278,43 +312,154 @@ mod tests {
},
1,
);
builder.add_leaf_line(
0x20,
Some(0x20),
FileInfo {
name: b"bar.c",
dir: &[],
},
1,
);
let func = builder.finish();

// the outer function has two line records, one for itself, the other for the inlined call
assert_eq!(func.name.as_str(), "foo");
assert_eq!(
&func.lines,
&[
LineInfo::new(0x10, 0x10, b"foo.c", 1),
LineInfo::new(0x20, 0x20, b"foo.c", 2)
]
);

assert_eq!(func.inlinees.len(), 1);
assert_eq!(func.inlinees[0].name.as_str(), "bar");
assert_eq!(
&func.inlinees[0].lines,
&[LineInfo::new(0x20, 0x20, b"bar.c", 1)]
);
}

#[test]
fn test_longer_line_record() {
// Consider the following code:
//
// ```
// | fn parent() {
// 1 | child1();
// 2 | child2();
// | }
// 1 | fn child1() { child2() }
// 1 | fn child2() {}
// ```
//
// we assume here that we transitively inline `child2` all the way into `parent`.
// but we only have a single line record for that whole chunk of code,
// even though the inlining hierarchy specifies two different call sites
//
// addr: 0x10 0x20 0x30 0x40 0x50
// v v v v v
// # DWARF hierarchy
// parent: |-------------------|
// child1: |----| (called from parent.c line 1)
// child2: |----| (called from child1.c line 1)
// |----| (called from parent.c line 2)
// # line records
// |----| |----| (parent.c line 1)
// |---------| (child2.c line 1)

let mut builder = FunctionBuilder::new(Name::from("parent"), &[], 0x10, 0x40);
builder.add_inlinee(
1,
Name::from("bar"),
Name::from("child1"),
0x20,
0x10,
FileInfo {
name: b"parent.c",
dir: &[],
},
1,
);
builder.add_inlinee(
2,
Name::from("child2"),
0x20,
0x10,
FileInfo {
name: b"foo.c",
name: b"child1.c",
dir: &[],
},
1,
);
builder.add_inlinee(
1,
Name::from("child2"),
0x30,
0x10,
FileInfo {
name: b"parent.c",
dir: &[],
},
2,
);
builder.add_leaf_line(
0x10,
Some(0x10),
FileInfo {
name: b"parent.c",
dir: &[],
},
1,
);
builder.add_leaf_line(
0x20,
Some(0x20),
FileInfo {
name: b"bar.c",
name: b"child2.c",
dir: &[],
},
1,
);
builder.add_leaf_line(
0x40,
Some(0x10),
FileInfo {
name: b"parent.c",
dir: &[],
},
1,
);
let func = builder.finish();

// the outer function has two line records, one for itself, the other for the inlined call
assert_eq!(func.name.as_str(), "foo");
assert_eq!(func.lines.len(), 2);
assert_eq!(func.lines[0].address, 0x10);
assert_eq!(func.lines[0].file.name_str(), "foo.c");
assert_eq!(func.lines[0].line, 1);
assert_eq!(func.lines[1].address, 0x20);
assert_eq!(func.lines[1].file.name_str(), "foo.c");
assert_eq!(func.lines[1].line, 2);
assert_eq!(func.name.as_str(), "parent");
assert_eq!(
&func.lines,
&[
LineInfo::new(0x10, 0x10, b"parent.c", 1),
LineInfo::new(0x20, 0x10, b"parent.c", 1),
LineInfo::new(0x30, 0x10, b"parent.c", 2),
LineInfo::new(0x40, 0x10, b"parent.c", 1),
]
);

assert_eq!(func.inlinees.len(), 1);
assert_eq!(func.inlinees[0].name.as_str(), "bar");
assert_eq!(func.inlinees[0].lines.len(), 1);
assert_eq!(func.inlinees[0].lines[0].address, 0x20);
assert_eq!(func.inlinees[0].lines[0].file.name_str(), "bar.c");
assert_eq!(func.inlinees[0].lines[0].line, 1);
assert_eq!(func.inlinees.len(), 2);
assert_eq!(func.inlinees[0].name.as_str(), "child1");
assert_eq!(
&func.inlinees[0].lines,
&[LineInfo::new(0x20, 0x10, b"child1.c", 1),]
);
assert_eq!(func.inlinees[0].inlinees.len(), 1);
assert_eq!(func.inlinees[0].inlinees[0].name.as_str(), "child2");
assert_eq!(
&func.inlinees[0].inlinees[0].lines,
&[LineInfo::new(0x20, 0x10, b"child2.c", 1),]
);

assert_eq!(func.inlinees[1].name.as_str(), "child2");
assert_eq!(
&func.inlinees[1].lines,
&[LineInfo::new(0x30, 0x10, b"child2.c", 1),]
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: symbolic-debuginfo/tests/test_objects.rs
assertion_line: 150
expression: "FunctionsDebug(&functions[..10], 0)"
---

Expand Down Expand Up @@ -83,6 +84,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0xefa: minidump_file_writer.cc:174 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0xf1f: minidump_file_writer.cc:173 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0xf23: minidump_file_writer.cc:174 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0xf67: minidump_file_writer.cc:328 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0xf6b: minidump_file_writer.cc:166 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0xf72: minidump_file_writer.cc:167 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0xf76: minidump_file_writer.cc:175 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
Expand Down Expand Up @@ -125,6 +127,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x1049: minidump_file_writer.cc:201 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0x1067: minidump_file_writer.cc:200 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0x106b: minidump_file_writer.cc:201 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0x10b3: minidump_file_writer.cc:328 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0x10b7: minidump_file_writer.cc:195 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0x10ba: minidump_file_writer.cc:196 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
0x10c0: minidump_file_writer.cc:202 (/Users/travis/build/getsentry/breakpad-tools/deps/breakpad/src/client)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: symbolic-debuginfo/tests/test_objects.rs
assertion_line: 247
expression: "FunctionsDebug(&functions[..10], 0)"
---

Expand Down Expand Up @@ -166,6 +167,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x1cdc: basic_string.h:383 (/usr/include/c++/5/bits)

> 0x1cdc: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderC4EPcRKS3_ (0x4)
0x1cdc: basic_string.h:109 (/usr/include/c++/5/bits)

> 0x1ce0: _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC4ERKS4_ (0x7)
0x1ce0: basic_string.h:400 (/usr/include/c++/5/bits)
Expand Down Expand Up @@ -259,6 +261,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x20c6: exception_handler.cc:318 (../deps/breakpad/src/client/linux/handler)
0x20cd: exception_handler.cc:319 (../deps/breakpad/src/client/linux/handler)
0x20e0: exception_handler.cc:315 (../deps/breakpad/src/client/linux/handler)
0x20ec: exception_handler.cc:199 (../deps/breakpad/src/client/linux/handler)

> 0x20e0: InstallDefaultHandler (0xc)
0x20e0: exception_handler.cc:199 (../deps/breakpad/src/client/linux/handler)
Expand Down Expand Up @@ -314,6 +317,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x2122: stl_vector.h:548 (/usr/include/c++/5/bits)

> 0x2122: _ZN9__gnu_cxx17__normal_iteratorIPPN15google_breakpad16ExceptionHandlerESt6vectorIS3_SaIS3_EEEC4ERKS4_ (0x4)
0x2122: stl_iterator.h:741 (/usr/include/c++/5/bits)

> 0x2126: _ZSt4findIN9__gnu_cxx17__normal_iteratorIPPN15google_breakpad16ExceptionHandlerESt6vectorIS4_SaIS4_EEEES4_ET_SA_SA_RKT0_ (0xaa)
0x2126: stl_algo.h:3791 (/usr/include/c++/5/bits)
Expand Down Expand Up @@ -555,8 +559,10 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x2350: stl_iterator.h:763 (/usr/include/c++/5/bits)

> 0x2360: _ZN9__gnu_cxx17__normal_iteratorIPPN15google_breakpad16ExceptionHandlerESt6vectorIS3_SaIS3_EEEppEv (0x10)
0x2360: stl_iterator.h:763 (/usr/include/c++/5/bits)

> 0x2370: _ZN9__gnu_cxx17__normal_iteratorIPPN15google_breakpad16ExceptionHandlerESt6vectorIS3_SaIS3_EEEppEv (0x9)
0x2370: stl_iterator.h:763 (/usr/include/c++/5/bits)

> 0x2382: _ZN9__gnu_cxx17__normal_iteratorIPPN15google_breakpad16ExceptionHandlerESt6vectorIS3_SaIS3_EEEppEv (0x4)
0x2382: stl_iterator.h:763 (/usr/include/c++/5/bits)
Expand Down Expand Up @@ -610,8 +616,10 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x23ed: stl_iterator.h:763 (/usr/include/c++/5/bits)

> 0x23f6: _ZN9__gnu_cxx17__normal_iteratorIPPN15google_breakpad16ExceptionHandlerESt6vectorIS3_SaIS3_EEEppEv (0x9)
0x23f6: stl_iterator.h:763 (/usr/include/c++/5/bits)

> 0x23ff: _ZN9__gnu_cxx17__normal_iteratorIPPN15google_breakpad16ExceptionHandlerESt6vectorIS3_SaIS3_EEEppEv (0x9)
0x23ff: stl_iterator.h:763 (/usr/include/c++/5/bits)

> 0x2408: RestoreAlternateStackLocked (0x24)
0x2408: exception_handler.cc:176 (../deps/breakpad/src/client/linux/handler)
Expand Down Expand Up @@ -716,6 +724,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x2996: exception_handler.cc:508 (../deps/breakpad/src/client/linux/handler)
0x299d: exception_handler.cc:568 (../deps/breakpad/src/client/linux/handler)
0x29a5: exception_handler.cc:505 (../deps/breakpad/src/client/linux/handler)
0x29e6: memory_allocator.h:143 (../deps/breakpad/src/common)

> 0x2534: _ZNK15google_breakpad16ExceptionHandler14IsOutOfProcessEv (0x4)
0x2534: exception_handler.h:205 (../deps/breakpad/src/client/linux/handler)
Expand Down Expand Up @@ -794,6 +803,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x2738: linux_syscall_support.h:3357 (../deps/third_party/lss)

> 0x275c: sys_close (0x10)
0x275c: linux_syscall_support.h:3357 (../deps/third_party/lss)

> 0x276e: sys_close (0x1a)
0x276e: linux_syscall_support.h:3357 (../deps/third_party/lss)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: symbolic-debuginfo/tests/test_objects.rs
assertion_line: 396
expression: "FunctionsDebug(&functions[..10], 0)"
---

Expand Down Expand Up @@ -100,6 +101,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0xefa: minidump_file_writer.cc:174 (../deps/breakpad/src/client)
0xf1f: minidump_file_writer.cc:173 (../deps/breakpad/src/client)
0xf23: minidump_file_writer.cc:174 (../deps/breakpad/src/client)
0xf67: minidump_file_writer.cc:328 (../deps/breakpad/src/client)
0xf6b: minidump_file_writer.cc:166 (../deps/breakpad/src/client)
0xf72: minidump_file_writer.cc:167 (../deps/breakpad/src/client)
0xf76: minidump_file_writer.cc:175 (../deps/breakpad/src/client)
Expand Down Expand Up @@ -146,6 +148,7 @@ expression: "FunctionsDebug(&functions[..10], 0)"
0x1049: minidump_file_writer.cc:201 (../deps/breakpad/src/client)
0x1067: minidump_file_writer.cc:200 (../deps/breakpad/src/client)
0x106b: minidump_file_writer.cc:201 (../deps/breakpad/src/client)
0x10b3: minidump_file_writer.cc:328 (../deps/breakpad/src/client)
0x10b7: minidump_file_writer.cc:195 (../deps/breakpad/src/client)
0x10ba: minidump_file_writer.cc:196 (../deps/breakpad/src/client)
0x10c0: minidump_file_writer.cc:202 (../deps/breakpad/src/client)
Expand Down
Loading

0 comments on commit 65a71bf

Please sign in to comment.