| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // @dart = 2.9 |
| |
| // 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"; |
| import "package:path/path.dart" as path; |
| |
| 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 buildIdDir; |
| String buildIdScript; |
| String nm; |
| String binary; |
| String output; |
| |
| try { |
| final options = parser.parse(args); |
| nm = options["nm"]; |
| if (nm == null) { |
| throw "Must specify --nm"; |
| } |
| if (!FileSystemEntity.isFileSync(nm)) { |
| throw "Cannot find $nm"; |
| } |
| binary = options["binary"]; |
| if (binary == null) { |
| throw "Must specify --binary"; |
| } |
| if (!FileSystemEntity.isFileSync(binary)) { |
| throw "Cannot find $binary"; |
| } |
| output = options["output"]; |
| if (output == null) { |
| throw "Must specify --output"; |
| } |
| } catch (e) { |
| print("ERROR: $e\n"); |
| print(usage); |
| exitCode = 1; |
| return; |
| } |
| |
| await run(buildIdDir, buildIdScript, nm, binary, output); |
| } |
| |
| class Symbol { |
| int offset; |
| int size; |
| String name; |
| } |
| |
| Future<void> run(String buildIdDir, String buildIdScript, String nm, |
| String binary, String output) async { |
| final unstrippedFile = binary; |
| final args = ["--demangle", "--numeric-sort", "--print-size", unstrippedFile]; |
| 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); |
| } |
| |
| if (symbols.isEmpty) { |
| throw "$unstrippedFile has no symbols"; |
| } |
| |
| 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(); |
| } |