mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:58:13 +00:00
[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:
parent
1210d27678
commit
afed0f59e3
|
@ -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
|
||||
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue