mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
8776e15f60
Remove the runtime_kernel target and all uses of it in scripts or the test matrix. This runtime_kernel target is only an alias for the runtime target, now that kernel is the default. BUG=https://github.com/dart-lang/sdk/issues/46892 Change-Id: I00c0ecf8adbd7bff3fd3df48023a2976c5172066 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210000 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: William Hesse <whesse@google.com>
274 lines
13 KiB
Markdown
274 lines
13 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
|
|
$ python3 tools/build.py -a x64 -m release runtime 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 `decode`
|
|
from the package [native_stack_traces](https://pub.dev/packages/native_stack_traces). 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/native_stack_traces/bin/decode.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/native_stack_traces/bin/decode.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/native_stack_traces/bin/decode.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/native_stack_traces/bin/decode.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 `decode.dart` from the package [native_stack_traces](https://pub.dev/packages/native_stack_traces), 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.
|