blob: e1a641316951449dd5c1015fe809d4c5e378aa60 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// On Fuchsia, in lieu of the ELF dynamic symbol table consumed through dladdr,
// the Dart VM profiler consumes symbols produced by this tool, which have the
// format
//
// struct {
// uint32_t num_entries;
// struct {
// uint32_t offset;
// uint32_t size;
// uint32_t string_table_offset;
// } entries[num_entries];
// const char* string_table;
// }
//
// Entries are sorted by offset. String table entries are NUL-terminated.
//
// See also //third_party/dart/runtime/vm/native_symbol_fuchsia.cc
import "dart:convert";
import "dart:io";
import "dart:typed_data";
import "package:args/args.dart";
Future<void> main(List<String> args) async {
final parser = new ArgParser();
parser.addOption("nm", help: "Path to `nm` tool");
parser.addOption("binary", help: "Path to the ELF file to extract symbols from");
parser.addOption("output", help: "Path to output symbol table");
final usage = """
Usage: dart_profiler_symbols.dart [options]
Options:
${parser.usage};
""";
String nm;
String binary;
String output;
try {
final options = parser.parse(args);
nm = options["nm"];
if (nm == null) {
throw "Must specify --nm";
}
binary = options["binary"];
if (binary == null) {
throw "Must specify --binary";
}
output = options["output"];
if (output == null) {
throw "Must specify --output";
}
} catch (e) {
print("ERROR: $e\n");
print(usage);
exitCode = 1;
return;
}
await run(nm, binary, output);
}
class Symbol {
int offset;
int size;
String name;
}
Future<void> run(String nm, String binary, String output) async {
final args = ["--demangle", "--numeric-sort", "--print-size", binary];
final result = await Process.run(nm, args);
if (result.exitCode != 0) {
print(result.stdout);
print(result.stderr);
throw "Command failed: $nm $args";
}
var symbols = new List();
var regex = new RegExp("([0-9A-Za-z]+) ([0-9A-Za-z]+) (t|T|w|W) (.*)");
for (final line in result.stdout.split("\n")) {
var match = regex.firstMatch(line);
if (match == null) {
continue; // Ignore non-text symbols.
}
final symbol = new Symbol();
// Note that capture groups start at 1.
symbol.offset = int.parse(match[1], radix: 16);
symbol.size = int.parse(match[2], radix: 16);
symbol.name = match[4].split("(")[0];
if (symbol.name.startsWith("\$")) {
continue; // Ignore compiler/assembler temps.
}
symbols.add(symbol);
}
var nameTable = new BytesBuilder();
var binarySearchTable = new Uint32List(symbols.length * 3 + 1);
var binarySearchTableIndex = 0;
binarySearchTable[binarySearchTableIndex++] = symbols.length;
// Symbols are sorted by offset because of --numeric-sort.
for (var symbol in symbols) {
var nameOffset = nameTable.length;
nameTable.add(utf8.encode(symbol.name));
nameTable.addByte(0);
binarySearchTable[binarySearchTableIndex++] = symbol.offset;
binarySearchTable[binarySearchTableIndex++] = symbol.size;
binarySearchTable[binarySearchTableIndex++] = nameOffset;
}
var file = new File(output);
await file.parent.create(recursive: true);
var sink = file.openWrite();
sink.add(binarySearchTable.buffer.asUint8List());
sink.add(nameTable.takeBytes());
await sink.close();
}