blob: 03dbd8efcd3f03cc3e4fdfc9c4b2dd637689882f [file] [log] [blame]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:typed_data';
import 'package:profiling/src/elf_utils.dart';
/// Symbols of a TEXT section of a binary indexed by their file offset.
class Symbols {
final Uint32List fileOffsets;
final List<String> names;
Symbols._(this.fileOffsets, this.names);
/// Given the [fileOffset] find a symbol it falls into.
///
/// We assume that symbol with index `i` starts at `fileOffsets[i]`
/// and ends at `fileOffset[i+1]`.
int? symbolIndex(int fileOffset) {
int lo = 0;
int hi = fileOffsets.length - 1;
while (lo <= hi) {
int mid = ((hi - lo + 1) >> 1) + lo;
if (fileOffset < fileOffsets[mid]) {
hi = mid - 1;
} else if ((mid != hi) && (fileOffset >= fileOffsets[mid + 1])) {
lo = mid + 1;
} else {
return mid;
}
}
return null;
}
String? lookupName(int fileOffset) {
final index = symbolIndex(fileOffset);
return index != null ? names[index] : null;
}
/// Try loading symbols from the binary at [path].
static Symbols? load(String path) {
try {
final loadingBias = loadingBiasOf(path);
final symbols = textSymbolsOf(path).toList(growable: false);
if (symbols.isEmpty) {
return null;
}
// Ensure symbols are sorted to be able to use binary search.
symbols.sort((a, b) => a.addr.compareTo(b.addr));
// `nm` prints virtual addresses - convert these to file offsets
// using loading bias.
final fileOffsets = Uint32List(symbols.length);
final names = List.generate(symbols.length, (i) => symbols[i].name);
for (var i = 0; i < symbols.length; i++) {
if (symbols[i].addr < loadingBias) {
throw StateError(
'unexpected: virtual address ${symbols[i].addr} of symbol '
'${symbols[i].name} is less than loading bias $loadingBias');
}
fileOffsets[i] = symbols[i].addr - loadingBias;
}
return Symbols._(fileOffsets, names);
} catch (_) {
print('failed to load symbols from $path');
return null;
}
}
}