64e256f7ce
We only check the expected stack starting from the call to foo in main, so we no longer depend on what is captured in the stacktrace for how main is invoked. Also removes the old status lines for this test, which are no longer applicable, and cleans up the documentation a little. Fixes https://github.com/dart-lang/sdk/issues/39766. Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try Change-Id: I66746f5a5169bce425bfd1f01a6a912b062e99c7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/128737 Reviewed-by: Teagan Strickland <sstrickl@google.com> Reviewed-by: Samir Jindel <sjindel@google.com> Commit-Queue: Teagan Strickland <sstrickl@google.com> Commit-Queue: Samir Jindel <sjindel@google.com>
275 lines
12 KiB
Markdown
275 lines
12 KiB
Markdown
# Using and interpreting DWARF stack traces
|
|
|
|
The Dart VM AOT compiler can encode code source mapping information as DWARF
|
|
debugging information instead of using in-snapshot `CodeSourceMap` objects.
|
|
Unlike `CodeSourceMap` objects, this DWARF information can then be stripped
|
|
from snapshots provided to users, which lowers the binary size.
|
|
|
|
## Turning on DWARF encoding of code source mapping information
|
|
|
|
To output code source mapping information as DWARF debugging information,
|
|
use the `--dwarf-stack-traces` flag. By default, generated assembly or ELF
|
|
snapshots include the DWARF information directly.
|
|
|
|
> **Note**: DWARF debugging information is _not_ obfuscated when using the
|
|
> `--obfuscate` flag. To avoid unobfuscated information leaking to users
|
|
> when using `--dwarf-stack-traces`, use the `--strip` option when creating
|
|
> ELF snapshots.
|
|
|
|
To output DWARF debugging information to a separate file that can be saved
|
|
as an artifact for later debugging, use the `--save-debugging-info=<...>` flag.
|
|
The generated file is not guaranteed to be any specific format and may change in
|
|
the future. See
|
|
[Translating DWARF stack traces](#translating-dwarf-stack-traces) for how to use
|
|
this file.
|
|
|
|
## Differences when using DWARF code source mapping information
|
|
|
|
When using DWARF code source mapping information, stack traces only include PC
|
|
address information for frames. This means that a developer will need the
|
|
generated DWARF information to get function, file, and line number information
|
|
for each frame.
|
|
|
|
Take the following example program, saved as `throws.dart` in the root of a
|
|
Dart SDK checkout:
|
|
```dart
|
|
@pragma('vm:prefer-inline')
|
|
void bar() => throw null;
|
|
|
|
@pragma('vm:never-inline')
|
|
void foo() => bar();
|
|
|
|
void main() => foo();
|
|
```
|
|
|
|
Below is the result of running the file both without and with
|
|
`--dwarf-stack-traces` in a 64-bit Linux development environment:
|
|
|
|
```bash
|
|
$ python tools/build.py -a x64 -m release runtime_kernel runtime_precompiled
|
|
|
|
$ pkg/vm/tool/gen_kernel --platform out/ReleaseX64/vm_platform_strong.dill -o throws.dill throws.dart
|
|
|
|
$ out/ReleaseX64/gen_snapshot --snapshot_kind=app-aot-elf --elf=snapshot.so throws.dill
|
|
|
|
# Here, we save the debugging information to a separate file debug.data as well
|
|
# as including it in the generated ELF snapshot for future examples.
|
|
$ out/ReleaseX64/gen_snapshot --dwarf-stack-traces --save-debugging-info=debug.data --snapshot_kind=app-aot-elf --elf=dwarf_snapshot.so throws.dill
|
|
|
|
$ out/ReleaseX64/dart_precompiled_runtime snapshot.so
|
|
Unhandled exception:
|
|
Throw of null.
|
|
#0 bar (file:///.../sdk/throws.dart:2)
|
|
#1 foo (file:///.../sdk/throws.dart:5)
|
|
#2 main (file:///.../sdk/throws.dart:7)
|
|
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:307)
|
|
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174)
|
|
|
|
$ out/ReleaseX64/dart_precompiled_runtime dwarf_snapshot.so
|
|
Unhandled exception:
|
|
Throw of null.
|
|
Warning: This VM has been configured to produce stack traces that violate the Dart standard.
|
|
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
|
|
pid: 148677, tid: 139648739401792, name Dart_Initialize
|
|
isolate_instructions: 7f028072e000 vm_instructions: 0
|
|
#00 abs 00007f028098381e virt 000000000025c81e /.../sdk/snapshot.so
|
|
#01 abs 00007f0280983742 virt 000000000025c742 /.../sdk/snapshot.so
|
|
#02 abs 00007f02809837d5 virt 000000000025c7d5 /.../sdk/snapshot.so
|
|
#03 abs 00007f028099d8af virt 00000000002768af /.../sdk/snapshot.so
|
|
#04 abs 00007f02808f42ff virt 00000000001cd2ff /.../sdk/snapshot.so
|
|
#05 abs 00007f028099d7a2 virt 00000000002767a2 /.../sdk/snapshot.so
|
|
#06 abs 00007f028086df98 virt 0000000000146f98 /.../sdk/snapshot.so
|
|
```
|
|
|
|
For a DWARF-based stack trace, we are guaranteed to have an absolute PC
|
|
address for each frame (the hexadecimal number following `abs`) as well as the
|
|
absolute addresses for the start of the isolate instructions and the VM
|
|
instructions (the line starting with `isolate_instructions`). This information
|
|
is used by our tool and libraries for converting DWARF stack traces, described
|
|
later.
|
|
|
|
If we are running from a snapshot which is a native format for dynamic libraries
|
|
(e.g., ELF on Linux or Mach-O on Mac OS X), then we will also have a virtual PC
|
|
address for each frame (the hexadecimal number following `virt`). If we have
|
|
appropriate DWARF information for the snapshot (e.g., part of the unstripped
|
|
ELF snapshot on Linux, or a separately generated .DSYM package when compiling
|
|
the generated assembly on Mac OS X), we can use the virtual address along with
|
|
the DWARF information to get back function, file, and line number information
|
|
using native tools:
|
|
|
|
```bash
|
|
# Virtual address from frame #00
|
|
$ addr2line -f -i -e dwarf_snapshot.so 000000000025c81e
|
|
bar
|
|
file:///.../sdk/throws.dart:2
|
|
foo
|
|
file:///.../sdk/throws.dart:5
|
|
|
|
# Virtual address from frame #01
|
|
$ addr2line -f -i -e dwarf_snapshot.so 000000000025c742
|
|
Precompiled____main_6919
|
|
file:///.../dart/sdk/throws.dart:7
|
|
```
|
|
|
|
However, as seen here, the information may not be exactly as expected from the
|
|
non-DWARF stack trace. In addition, the DWARF stack trace may include frames
|
|
that are internal to the Dart VM and would not normally be provided in
|
|
non-DWARF stack traces. Note that there are seven frames in the DWARF stack
|
|
trace above, but only 5 in the non-DWARF stack trace. Frame `#02` happens
|
|
to correspond to one of these elided frames:
|
|
|
|
```bash
|
|
# Virtual address from frame #02
|
|
$ addr2line -f -i -e dwarf_snapshot.so 000000000025c7d5
|
|
Precompiled____main_main_6920
|
|
file:///.../sdk/throws.dart:?
|
|
```
|
|
|
|
## Translating DWARF stack traces
|
|
|
|
To ease translation of DWARF stack traces, we provide a platform-independent
|
|
tool and libraries. They can translate DWARF stack traces using the DWARF
|
|
debugging information contained in unstripped ELF snapshots or saved separately
|
|
using `--save-debugging-info=<...>`. For most uses, the tool should suffice, but
|
|
the libraries it uses are also available for integration into Dart-based
|
|
workflows.
|
|
|
|
### Using the stack trace converter tool
|
|
|
|
A simple way to translate DWARF stack traces is to use the tool
|
|
`pkg/vm/bin/convert_stack_traces.dart`. The tool has one required
|
|
argument `-e`, which takes the name of the file containing DWARF
|
|
debugging information as an input. This can either be an
|
|
unstripped ELF snapshot or a file generated by `--save-debugging-info=<...>`.
|
|
|
|
Using the earlier example, we can run the snapshot and convert any generated
|
|
stack traces as follows:
|
|
|
|
```bash
|
|
# Using the unstripped ELF snapshot and piping all output to the tool's stdin.
|
|
$ out/ReleaseX64/dart_precompiled_runtime dwarf_snapshot.so |& out/ReleaseX64/dart pkg/vm/bin/convert_stack_traces.dart -e dwarf_snapshot.so
|
|
Unhandled exception:
|
|
Throw of null.
|
|
Warning: This VM has been configured to produce stack traces that violate the Dart standard.
|
|
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
|
|
pid: 158029, tid: 140495595888704, name Dart_Initialize
|
|
isolate_instructions: 7fc7ad076000 vm_instructions: 0
|
|
#0 bar (file:///.../sdk/throws.dart:2)
|
|
#1 foo (file:///.../sdk/throws.dart:5)
|
|
#2 main (file:///.../sdk/throws.dart:7)
|
|
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:307)
|
|
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174)
|
|
|
|
# Using the separately saved debugging information and piping all output to the tool's stdin.
|
|
$ out/ReleaseX64/dart_precompiled_runtime dwarf_snapshot.so |& out/ReleaseX64/dart pkg/vm/bin/convert_stack_traces.dart -e debug.data
|
|
Unhandled exception:
|
|
Throw of null.
|
|
Warning: This VM has been configured to produce stack traces that violate the Dart standard.
|
|
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
|
|
pid: 158242, tid: 139697980215360, name Dart_Initialize
|
|
isolate_instructions: 7f0df76e1000 vm_instructions: 0
|
|
#0 bar (file:///.../sdk/throws.dart:2)
|
|
#1 foo (file:///.../sdk/throws.dart:5)
|
|
#2 main (file:///.../sdk/throws.dart:7)
|
|
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:307)
|
|
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174)
|
|
|
|
# Saving all output to the file "output.txt".
|
|
$ out/ReleaseX64/dart_precompiled_runtime dwarf_snapshot.so >output.txt 2>&1
|
|
|
|
# Reading the input to convert from the file "output.txt" instead of stdin.
|
|
$ out/ReleaseX64/dart pkg/vm/bin/convert_stack_traces.dart -e debug.data -i output.txt
|
|
Unhandled exception:
|
|
Throw of null.
|
|
Warning: This VM has been configured to produce stack traces that violate the Dart standard.
|
|
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
|
|
pid: 159139, tid: 139991440654400, name Dart_Initialize
|
|
isolate_instructions: 7f524b090000 vm_instructions: 0
|
|
#0 bar (file:///.../sdk/throws.dart:2)
|
|
#1 foo (file:///.../sdk/throws.dart:5)
|
|
#2 main (file:///.../sdk/throws.dart:7)
|
|
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:307)
|
|
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174)
|
|
|
|
# Output the converted input to the file "converted.txt" instead of stdout.
|
|
$ out/ReleaseX64/dart pkg/vm/bin/convert_stack_traces.dart -e debug.data -i output.txt -o converted.txt
|
|
|
|
$ cat converted.txt
|
|
Unhandled exception:
|
|
Throw of null.
|
|
Warning: This VM has been configured to produce stack traces that violate the Dart standard.
|
|
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
|
|
pid: 159139, tid: 139991440654400, name Dart_Initialize
|
|
isolate_instructions: 7f524b090000 vm_instructions: 0
|
|
#0 bar (file:///.../sdk/throws.dart:2)
|
|
#1 foo (file:///.../sdk/throws.dart:5)
|
|
#2 main (file:///.../sdk/throws.dart:7)
|
|
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:307)
|
|
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174)
|
|
```
|
|
|
|
> **Note**: As seen here, only lines that contain stack trace frames are
|
|
> converted. In particular, we do not strip the extra stack trace header lines
|
|
> that are only part of DWARF-based stack traces.
|
|
|
|
### Using the stack trace converter libraries
|
|
|
|
This section describes two different libraries used to retrieve and convert
|
|
information associated with DWARF-based stack traces:
|
|
|
|
* `package:vm/dwarf/dwarf.dart`, for 'Dwarf' and 'CallInfo' objects
|
|
* `package:vm/dwarf/convert.dart`, for `PCOffset` objects and various operations on stack traces
|
|
|
|
#### DWARF debugging information
|
|
|
|
A `Dwarf` object represents the DWARF debugging information from either
|
|
unstripped ELF snapshots or a file generated by `--save-debugging-info=<...>`.
|
|
|
|
The `Dwarf.fromFile` factory takes a filename and returns a 'Dwarf' object,
|
|
if the given file exists and is a recognized format that contains DWARF
|
|
information.
|
|
|
|
#### Call site information
|
|
|
|
A `CallInfo` object represents a call site in the code corresponding to a
|
|
particular virtual address and contains the function name, file name, and line
|
|
number for the call site and whether the code for the call site has been
|
|
inlined at this use.
|
|
|
|
To look up the call information associated with a particular virtual address,
|
|
use `Dwarf::callInfo`. If the virtual address is outside the range of those
|
|
generated by the DWARF line number program(s), then it returns `null`, otherwise
|
|
it returns an iterable of `CallInfo` objects. If the optional
|
|
`includeInternalFrames` argument is false (the default), then the iterable can
|
|
be empty if the virtual address points to code that does not correspond to
|
|
user or library code, like generated function prologues.
|
|
|
|
#### Converting stack traces
|
|
|
|
To convert a stream of lines that may include DWARF stack traces, use
|
|
the stack transformer `DwarfStackTraceDecoder`. Its constructor takes a `Dwarf`
|
|
object, and the transformer, like `convert_stack_traces.dart`, only changes
|
|
lines that correspond to stack trace frames.
|
|
|
|
> **Note**: The stack transformer assumes that lines are not combined or broken
|
|
> across `String`s in the input stream. If this is not already guaranteed,
|
|
> transform the stream with `LineSplitter` from `dart:convert` prior to
|
|
> transforming it with a `DwarfStackTraceDecoder`.
|
|
|
|
#### Extracting information from stack traces
|
|
|
|
A `PCOffset` object represents the PC address information extracted from a
|
|
DWARF-based stack trace frame. It contains whether the PC address comes from
|
|
the VM or isolate instructions section, and the offset of the PC address in
|
|
that section.
|
|
|
|
The `PCOffset::virtualAddress` method takes a `Dwarf` object and returns
|
|
a compatible virtual address for use with methods on that `Dwarf` object.
|
|
|
|
The function `collectPCOffsets` extracts `PCOffset`s for the frames of a stack
|
|
trace.
|
|
|
|
> **Note**: Since the absolute addresses of the VM and isolate instruction
|
|
> sections are part of the stack frame header, `collectPCOffsets` can only
|
|
> extract information from _complete_ stack traces.
|