[native_stack_traces] Allow static symbol offsets as locations.

Static symbol offsets from Dart stack traces can now be used directly
as locations for the find command.

Change-Id: Ie2971099cbc33b871f8e0e213865854b5ee6890b
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-product-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/142362
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Teagan Strickland 2020-04-06 13:11:41 +00:00 committed by commit-bot@chromium.org
parent 1210d27678
commit afed0f59e3
6 changed files with 138 additions and 74 deletions

View file

@ -1,5 +1,13 @@
# Changelog
## 0.3.2
- The `find` command can now look up addresses given as offsets from static
symbols, not just hexadecimal virtual or absolute addresses.
- Integer inputs (addresses or offsets) without an '0x' prefix or hexadecimal
digits will now be parsed as decimal unless the `-x`/`--force_hexadecimal`
flag is used.
## 0.3.1
- Uses dynamic symbol information embedded in stack frame lines when available.
@ -15,7 +23,7 @@
## 0.2.1
- Added static method Dwarf.fromBuffer.
- Added static method `Dwarf.fromBuffer`.
## 0.2.0

View file

@ -34,6 +34,10 @@ final ArgParser _findParser =
_createBaseDebugParser(ArgParser(allowTrailingOptions: true))
..addMultiOption('location',
abbr: 'l', help: 'PC address to find', valueHelp: 'PC')
..addFlag('force_hexadecimal',
abbr: 'x',
negatable: false,
help: 'Always parse integers as hexadecimal')
..addOption('vm_start',
help: 'Absolute address for start of VM instructions',
valueHelp: 'PC')
@ -88,19 +92,30 @@ final String _findUsage = '''
Usage: convert_stack_traces find [options] <PC> ...
The find command looks up program counter (PC) addresses, either given as
arguments on the command line or via the -l option. For each PC address,
it outputs the file, function, and line number information if found.
arguments on the command line or via the -l/--location option. For each
successful PC lookup, it outputs the call information in one of two formats:
PC addresses are expected to be hexadecimal numbers with or without an initial
"0x" marker.
- If the location corresponds to a call site in Dart source code, the call
information includes the file, function, and line number information.
- If it corresponds to a Dart VM stub, the call information includes the dynamic
symbol name for the instructions payload and an offset into that payload.
The -l option may be provided multiple times, or a single use of the -l option
may be given multiple arguments separated by commas.
By default, PC addresses are assumed to be virtual addresses valid for the
given debugging information. To find absolute PC addresses, use both the
--vm_start and --isolate_start arguments tp provide the absolute addresses of
the VM and isolate instructions sections.
PC addresses can be provided in one of two formats:
- An integer, e.g. 0x2a3f or 15049
- A static symbol in the VM snapshot plus an integer offset, e.g.,
_kDartIsolateSnapshotInstructions+1523 or _kDartVMSnapshotInstructions+0x403f
Integers without an "0x" prefix that do not includes hexadecimal digits are
assumed to be decimal unless the -x/--force_hexadecimal flag is used.
By default, integer PC addresses are assumed to be virtual addresses valid for
the given debugging information. Otherwise, use both the --vm_start and
--isolate_start arguments to provide the appropriate starting addresses of the
VM and isolate instructions sections.
Options shared by all commands:
${_argParser.usage}
@ -159,67 +174,74 @@ Dwarf _loadFromFile(String original, Function(String) usageError) {
}
void find(ArgResults options) {
final bool verbose = options['verbose'];
final bool forceHexadecimal = options['force_hexadecimal'];
void usageError(String message) => errorWithUsage(message, command: 'find');
int convertAddress(String s) => int.tryParse(s, radix: 16);
int parseIntAddress(String s) {
if (!forceHexadecimal && !s.startsWith("0x")) {
final decimal = int.tryParse(s);
if (decimal != null) return decimal;
}
return int.tryParse(s.startsWith("0x") ? s.substring(2) : s, radix: 16);
}
PCOffset convertAddress(StackTraceHeader header, String s) {
final parsedOffset = tryParseSymbolOffset(s, forceHexadecimal);
if (parsedOffset != null) return parsedOffset;
final address = parseIntAddress(s);
if (address != null) return header.offsetOf(address);
return null;
}
final dwarf = _loadFromFile(options['debug'], usageError);
if (dwarf == null) {
return;
}
if (dwarf == null) return;
if (options['dump_debug_file_contents']) {
print(dwarf.dumpFileInfo());
}
final verbose = options['verbose'];
int vm_start;
if ((options['vm_start'] == null) != (options['isolate_start'] == null)) {
return usageError("need both VM start and isolate start");
}
int vmStart = dwarf.vmStartAddress;
if (options['vm_start'] != null) {
vm_start = convertAddress(options['vm_start']);
if (vm_start == null) {
vmStart = parseIntAddress(options['vm_start']);
if (vmStart == null) {
return usageError('could not parse VM start address '
'${options['vm_start']}');
}
}
int isolate_start;
int isolateStart = dwarf.isolateStartAddress;
if (options['isolate_start'] != null) {
isolate_start = convertAddress(options['isolate_start']);
if (isolate_start == null) {
isolateStart = parseIntAddress(options['isolate_start']);
if (isolateStart == null) {
return usageError('could not parse isolate start address '
'${options['isolate_start']}');
}
}
if ((vm_start == null) != (isolate_start == null)) {
return usageError("need both VM start and isolate start");
}
final header = StackTraceHeader(isolateStart, vmStart);
final locations = <int>[];
for (final s in options['location'] + options.rest) {
final location = convertAddress(s);
if (location == null) {
return usageError('could not parse PC address ${s}');
}
final locations = <PCOffset>[];
for (final String s in options['location'] + options.rest) {
final location = convertAddress(header, s);
if (location == null) return usageError('could not parse PC address ${s}');
locations.add(location);
}
if (locations.isEmpty) return usageError('no PC addresses to find');
// Used to approximate how many hex digits we should have in the final output.
final maxDigits = options['location']
.fold(0, ((acc, s) => s.startsWith('0x') ? s.length - 2 : s.length));
Iterable<int> addresses = locations;
if (vm_start != null) {
final header = StackTraceHeader(isolate_start, vm_start);
addresses =
locations.map((l) => header.offsetOf(l).virtualAddressIn(dwarf));
}
for (final addr in addresses) {
for (final offset in locations) {
final addr = dwarf.virtualAddressOf(offset);
final frames = dwarf
.callInfoFor(addr, includeInternalFrames: verbose)
?.map((CallInfo c) => " " + c.toString());
final addrString = addr > 0
? "0x" + addr.toRadixString(16).padLeft(maxDigits, '0')
: addr.toString();
final addrString =
addr > 0 ? "0x" + addr.toRadixString(16) : addr.toString();
print("For virtual address ${addrString}:");
if (frames == null) {
print(" Invalid virtual address.");

View file

@ -3,6 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
export 'src/convert.dart'
show collectPCOffsets, DwarfStackTraceDecoder, StackTraceHeader;
show
collectPCOffsets,
tryParseSymbolOffset,
DwarfStackTraceDecoder,
StackTraceHeader;
export 'src/dwarf.dart'
show CallInfo, DartCallInfo, StubCallInfo, Dwarf, PCOffset;

View file

@ -55,42 +55,66 @@ class StackTraceHeader {
/// - The virtual address of the program counter, if the snapshot was
/// loaded as a dynamic library, otherwise not present.
/// - The location of the virtual address, which is one of the following:
/// - A dynamic symbol name, a plus sign, and a hexadecimal offset.
/// - A dynamic symbol name, a plus sign, and an integer offset.
/// - The path to the snapshot, if it was loaded as a dynamic library,
/// otherwise the string "<unknown>".
const _symbolREString = r'(?:(?<symbol>' +
const _symbolOffsetREString = r'(?<symbol>' +
constants.vmSymbolName +
r'|' +
constants.isolateSymbolName +
r')\+0x(?<offset>[\da-f]+))';
r')\+(?<offset>(?:0x)?[\da-f]+)';
final _symbolOffsetRE = RegExp(_symbolOffsetREString);
final _traceLineRE = RegExp(
r' #(\d{2}) abs (?<address>[\da-f]+)(?: virt ([\da-f]+))? (?:' +
_symbolREString +
r'|.*)$');
r' #(\d{2}) abs (?<address>[\da-f]+)(?: virt ([\da-f]+))? (?<rest>.*)$');
/// Parses strings of the format <static symbol>+<integer offset>, where
/// <static symbol> is one of the static symbols used for Dart instruction
/// sections.
///
/// Unless forceHexadecimal is true, an integer offset without a "0x" prefix or
/// any hexdecimal digits will be parsed as decimal.
///
/// Returns null if the string is not of the expected format.
PCOffset tryParseSymbolOffset(String s, [bool forceHexadecimal = false]) {
final match = _symbolOffsetRE.firstMatch(s);
if (match == null) return null;
final symbolString = match.namedGroup('symbol');
final offsetString = match.namedGroup('offset');
int offset;
if (!forceHexadecimal && !offsetString.startsWith("0x")) {
offset = int.tryParse(offsetString);
}
if (offset == null) {
final digits = offsetString.startsWith("0x")
? offsetString.substring(2)
: offsetString;
offset = int.tryParse(digits, radix: 16);
}
if (offset == null) return null;
switch (symbolString) {
case constants.vmSymbolName:
return PCOffset(offset, InstructionsSection.vm);
case constants.isolateSymbolName:
return PCOffset(offset, InstructionsSection.isolate);
default:
break;
}
return null;
}
PCOffset _retrievePCOffset(StackTraceHeader header, RegExpMatch match) {
if (match == null) return null;
// Try using the symbol information first, since we don't need the header
final restString = match.namedGroup('rest');
// Try checking for symbol information first, since we don't need the header
// information to translate it.
final symbolString = match.namedGroup('symbol');
final offsetString = match.namedGroup('offset');
if (symbolString != null && offsetString != null) {
final offset = int.tryParse(offsetString, radix: 16);
if (offset != null) {
switch (symbolString) {
case constants.vmSymbolName:
return PCOffset(offset, InstructionsSection.vm);
case constants.isolateSymbolName:
return PCOffset(offset, InstructionsSection.isolate);
default:
break;
}
}
if (restString.isNotEmpty) {
final offset = tryParseSymbolOffset(restString);
if (offset != null) return offset;
}
// If we're parsing the absolute address, we can only convert it into
// a PCOffset if we saw the instructions line of the stack trace header.
final addressString = match.namedGroup('address');
if (addressString != null && header != null) {
if (header != null) {
final addressString = match.namedGroup('address');
final address = int.tryParse(addressString, radix: 16);
return header.offsetOf(address);
}

View file

@ -1132,7 +1132,7 @@ class StubCallInfo extends CallInfo {
}
@override
String toString() => "${name} + ${offset}";
String toString() => "${name}+0x${offset.toRadixString(16)}";
}
/// The instructions section in which a program counter address is located.
@ -1175,11 +1175,17 @@ class Dwarf {
final Map<int, _AbbreviationsTable> _abbreviationTables;
final DebugInfo _debugInfo;
final LineNumberInfo _lineNumberInfo;
final int _vmStartAddress;
final int _isolateStartAddress;
/// Virtual address of the start of the VM instructions section in the DWARF
/// information.
final int vmStartAddress;
/// Virtual address of the start of the isolate instructions section in the
/// DWARF information.
final int isolateStartAddress;
Dwarf._(this._elf, this._abbreviationTables, this._debugInfo,
this._lineNumberInfo, this._vmStartAddress, this._isolateStartAddress);
this._lineNumberInfo, this.vmStartAddress, this.isolateStartAddress);
/// Attempts to load the DWARF debugging information from the reader.
///
@ -1269,9 +1275,9 @@ class Dwarf {
int virtualAddressOf(PCOffset pcOffset) {
switch (pcOffset.section) {
case InstructionsSection.vm:
return pcOffset.offset + _vmStartAddress;
return pcOffset.offset + vmStartAddress;
case InstructionsSection.isolate:
return pcOffset.offset + _isolateStartAddress;
return pcOffset.offset + isolateStartAddress;
default:
throw "Unexpected value for instructions section";
}

View file

@ -1,6 +1,6 @@
name: native_stack_traces
description: Utilities for working with non-symbolic stack traces.
version: 0.3.1
version: 0.3.2
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces