Skip to content

Commit

Permalink
[flutter_runner] Generate symbols for the Dart VM profiler (flutter#1…
Browse files Browse the repository at this point in the history
…2048)

Ported from the original implementation in the Topaz tree.
  • Loading branch information
jason-simmons authored Sep 10, 2019
1 parent 954f198 commit bfa43e1
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 2 deletions.
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_pool.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/profiler_symbols/dart_profiler_symbols.dart
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/files.cc
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/files.h
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/handle_exception.cc
Expand Down
26 changes: 24 additions & 2 deletions shell/platform/fuchsia/flutter/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ template("jit_runner") {
]

if (!product) {
deps += [ engine_observatory_target ]
deps += [
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_jit_runner",
engine_observatory_target,
]
}

binary = "flutter_jit${product_suffix}_runner"
Expand All @@ -119,6 +122,14 @@ template("jit_runner") {
path = rebase_path(engine_observatory_archive_file)
dest = "observatory.tar"
},
{
path = rebase_path(
get_label_info(
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_jit_runner",
"target_gen_dir") +
"/flutter_jit_runner.dartprofilersymbols")
dest = "flutter_jit_runner.dartprofilersymbols"
},
]
}

Expand Down Expand Up @@ -169,7 +180,10 @@ template("aot_runner") {
]

if (!product) {
deps += [ engine_observatory_target ]
deps += [
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_aot_runner",
engine_observatory_target,
]
}

meta_dir = "$flutter_root/shell/platform/fuchsia/flutter/meta"
Expand All @@ -189,6 +203,14 @@ template("aot_runner") {
path = rebase_path(engine_observatory_archive_file)
dest = "observatory.tar"
},
{
path = rebase_path(
get_label_info(
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_aot_runner",
"target_gen_dir") +
"/flutter_aot_runner.dartprofilersymbols")
dest = "flutter_aot_runner.dartprofilersymbols"
},
]
}

Expand Down
24 changes: 24 additions & 0 deletions shell/platform/fuchsia/flutter/runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "flutter/fml/make_copyable.h"
#include "flutter/lib/ui/text/font_collection.h"
#include "flutter/runtime/dart_vm.h"
#include "runtime/dart/utils/files.h"
#include "runtime/dart/utils/vmo.h"
#include "runtime/dart/utils/vmservice_object.h"
#include "third_party/icu/source/common/unicode/udata.h"
Expand Down Expand Up @@ -87,6 +88,19 @@ static void SetThreadName(const std::string& thread_name) {
thread_name.size());
}

#if !defined(DART_PRODUCT)
// Register native symbol information for the Dart VM's profiler.
static void RegisterProfilerSymbols(const char* symbols_path,
const char* dso_name) {
std::string* symbols = new std::string();
if (dart_utils::ReadFileToString(symbols_path, symbols)) {
Dart_AddSymbols(dso_name, symbols->data(), symbols->size());
} else {
FML_LOG(ERROR) << "Failed to load " << symbols_path;
}
}
#endif // !defined(DART_PRODUCT)

Runner::Runner(async::Loop* loop)
: loop_(loop), runner_context_(RunnerContext::CreateFromStartupInfo()) {
#if !defined(DART_PRODUCT)
Expand All @@ -110,6 +124,16 @@ Runner::Runner(async::Loop* loop)

runner_context_->AddPublicService<fuchsia::sys::Runner>(
std::bind(&Runner::RegisterApplication, this, std::placeholders::_1));

#if !defined(DART_PRODUCT)
if (Dart_IsPrecompiledRuntime()) {
RegisterProfilerSymbols("pkg/data/flutter_aot_runner.dartprofilersymbols",
"");
} else {
RegisterProfilerSymbols("pkg/data/flutter_jit_runner.dartprofilersymbols",
"");
}
#endif // !defined(DART_PRODUCT)
}

Runner::~Runner() {
Expand Down
56 changes: 56 additions & 0 deletions shell/platform/fuchsia/runtime/dart/profiler_symbols/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# 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.

import("//third_party/dart/build/dart/dart_action.gni")
import("$flutter_root/tools/fuchsia/clang.gni")

template("generate_dart_profiler_symbols") {
assert(defined(invoker.library_label), "Must define 'library_label'")
assert(defined(invoker.library_path), "Must define 'library_path'")
assert(defined(invoker.output), "Must define 'output'")

prebuilt_dart_action(target_name) {
deps = [
invoker.library_label,
]
inputs = [
invoker.library_path,
]
outputs = [
invoker.output,
]

script = "dart_profiler_symbols.dart"

packages = rebase_path("//third_party/dart/.packages")

args = [
"--nm",
rebase_path("//fuchsia/toolchain/$host_os/bin/llvm-nm"),
"--binary",
rebase_path(invoker.library_path),
"--output",
rebase_path(invoker.output),
]
}
}

generate_dart_profiler_symbols("dart_jit_runner") {
library_label =
"$flutter_root/shell/platform/fuchsia/dart_runner:dart_jit_runner_bin"
library_path = "${root_out_dir}/exe.unstripped/dart_jit_runner"
output = "${target_gen_dir}/dart_jit_runner.dartprofilersymbols"
}

generate_dart_profiler_symbols("flutter_jit_runner") {
library_label = "$flutter_root/shell/platform/fuchsia/flutter:jit"
library_path = "${root_out_dir}/exe.unstripped/flutter_jit_runner"
output = "${target_gen_dir}/flutter_jit_runner.dartprofilersymbols"
}

generate_dart_profiler_symbols("flutter_aot_runner") {
library_label = "$flutter_root/shell/platform/fuchsia/flutter:aot"
library_path = "${root_out_dir}/exe.unstripped/flutter_aot_runner"
output = "${target_gen_dir}/flutter_aot_runner.dartprofilersymbols"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// 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.

// 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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 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.

name: profiler_symbols
version: 0
description: Extracts a minimal symbols table for the Dart VM profiler
author: Dart Team <[email protected]>

0 comments on commit bfa43e1

Please sign in to comment.