Skip to content

Commit bfa43e1

Browse files
[flutter_runner] Generate symbols for the Dart VM profiler (#12048)
Ported from the original implementation in the Topaz tree.
1 parent 954f198 commit bfa43e1

File tree

7 files changed

+259
-2
lines changed

7 files changed

+259
-2
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc
970970
FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_pool.h
971971
FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc
972972
FILE: ../../../flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h
973+
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/profiler_symbols/dart_profiler_symbols.dart
973974
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/files.cc
974975
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/files.h
975976
FILE: ../../../flutter/shell/platform/fuchsia/runtime/dart/utils/handle_exception.cc

shell/platform/fuchsia/flutter/BUILD.gn

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ template("jit_runner") {
9999
]
100100

101101
if (!product) {
102-
deps += [ engine_observatory_target ]
102+
deps += [
103+
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_jit_runner",
104+
engine_observatory_target,
105+
]
103106
}
104107

105108
binary = "flutter_jit${product_suffix}_runner"
@@ -119,6 +122,14 @@ template("jit_runner") {
119122
path = rebase_path(engine_observatory_archive_file)
120123
dest = "observatory.tar"
121124
},
125+
{
126+
path = rebase_path(
127+
get_label_info(
128+
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_jit_runner",
129+
"target_gen_dir") +
130+
"/flutter_jit_runner.dartprofilersymbols")
131+
dest = "flutter_jit_runner.dartprofilersymbols"
132+
},
122133
]
123134
}
124135

@@ -169,7 +180,10 @@ template("aot_runner") {
169180
]
170181

171182
if (!product) {
172-
deps += [ engine_observatory_target ]
183+
deps += [
184+
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_aot_runner",
185+
engine_observatory_target,
186+
]
173187
}
174188

175189
meta_dir = "$flutter_root/shell/platform/fuchsia/flutter/meta"
@@ -189,6 +203,14 @@ template("aot_runner") {
189203
path = rebase_path(engine_observatory_archive_file)
190204
dest = "observatory.tar"
191205
},
206+
{
207+
path = rebase_path(
208+
get_label_info(
209+
"$flutter_root/shell/platform/fuchsia/runtime/dart/profiler_symbols:flutter_aot_runner",
210+
"target_gen_dir") +
211+
"/flutter_aot_runner.dartprofilersymbols")
212+
dest = "flutter_aot_runner.dartprofilersymbols"
213+
},
192214
]
193215
}
194216

shell/platform/fuchsia/flutter/runner.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "flutter/fml/make_copyable.h"
1717
#include "flutter/lib/ui/text/font_collection.h"
1818
#include "flutter/runtime/dart_vm.h"
19+
#include "runtime/dart/utils/files.h"
1920
#include "runtime/dart/utils/vmo.h"
2021
#include "runtime/dart/utils/vmservice_object.h"
2122
#include "third_party/icu/source/common/unicode/udata.h"
@@ -87,6 +88,19 @@ static void SetThreadName(const std::string& thread_name) {
8788
thread_name.size());
8889
}
8990

91+
#if !defined(DART_PRODUCT)
92+
// Register native symbol information for the Dart VM's profiler.
93+
static void RegisterProfilerSymbols(const char* symbols_path,
94+
const char* dso_name) {
95+
std::string* symbols = new std::string();
96+
if (dart_utils::ReadFileToString(symbols_path, symbols)) {
97+
Dart_AddSymbols(dso_name, symbols->data(), symbols->size());
98+
} else {
99+
FML_LOG(ERROR) << "Failed to load " << symbols_path;
100+
}
101+
}
102+
#endif // !defined(DART_PRODUCT)
103+
90104
Runner::Runner(async::Loop* loop)
91105
: loop_(loop), runner_context_(RunnerContext::CreateFromStartupInfo()) {
92106
#if !defined(DART_PRODUCT)
@@ -110,6 +124,16 @@ Runner::Runner(async::Loop* loop)
110124

111125
runner_context_->AddPublicService<fuchsia::sys::Runner>(
112126
std::bind(&Runner::RegisterApplication, this, std::placeholders::_1));
127+
128+
#if !defined(DART_PRODUCT)
129+
if (Dart_IsPrecompiledRuntime()) {
130+
RegisterProfilerSymbols("pkg/data/flutter_aot_runner.dartprofilersymbols",
131+
"");
132+
} else {
133+
RegisterProfilerSymbols("pkg/data/flutter_jit_runner.dartprofilersymbols",
134+
"");
135+
}
136+
#endif // !defined(DART_PRODUCT)
113137
}
114138

115139
Runner::~Runner() {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
import("//third_party/dart/build/dart/dart_action.gni")
6+
import("$flutter_root/tools/fuchsia/clang.gni")
7+
8+
template("generate_dart_profiler_symbols") {
9+
assert(defined(invoker.library_label), "Must define 'library_label'")
10+
assert(defined(invoker.library_path), "Must define 'library_path'")
11+
assert(defined(invoker.output), "Must define 'output'")
12+
13+
prebuilt_dart_action(target_name) {
14+
deps = [
15+
invoker.library_label,
16+
]
17+
inputs = [
18+
invoker.library_path,
19+
]
20+
outputs = [
21+
invoker.output,
22+
]
23+
24+
script = "dart_profiler_symbols.dart"
25+
26+
packages = rebase_path("//third_party/dart/.packages")
27+
28+
args = [
29+
"--nm",
30+
rebase_path("//fuchsia/toolchain/$host_os/bin/llvm-nm"),
31+
"--binary",
32+
rebase_path(invoker.library_path),
33+
"--output",
34+
rebase_path(invoker.output),
35+
]
36+
}
37+
}
38+
39+
generate_dart_profiler_symbols("dart_jit_runner") {
40+
library_label =
41+
"$flutter_root/shell/platform/fuchsia/dart_runner:dart_jit_runner_bin"
42+
library_path = "${root_out_dir}/exe.unstripped/dart_jit_runner"
43+
output = "${target_gen_dir}/dart_jit_runner.dartprofilersymbols"
44+
}
45+
46+
generate_dart_profiler_symbols("flutter_jit_runner") {
47+
library_label = "$flutter_root/shell/platform/fuchsia/flutter:jit"
48+
library_path = "${root_out_dir}/exe.unstripped/flutter_jit_runner"
49+
output = "${target_gen_dir}/flutter_jit_runner.dartprofilersymbols"
50+
}
51+
52+
generate_dart_profiler_symbols("flutter_aot_runner") {
53+
library_label = "$flutter_root/shell/platform/fuchsia/flutter:aot"
54+
library_path = "${root_out_dir}/exe.unstripped/flutter_aot_runner"
55+
output = "${target_gen_dir}/flutter_aot_runner.dartprofilersymbols"
56+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// On Fuchsia, in lieu of the ELF dynamic symbol table consumed through dladdr,
6+
// the Dart VM profiler consumes symbols produced by this tool, which have the
7+
// format
8+
//
9+
// struct {
10+
// uint32_t num_entries;
11+
// struct {
12+
// uint32_t offset;
13+
// uint32_t size;
14+
// uint32_t string_table_offset;
15+
// } entries[num_entries];
16+
// const char* string_table;
17+
// }
18+
//
19+
// Entries are sorted by offset. String table entries are NUL-terminated.
20+
//
21+
// See also //third_party/dart/runtime/vm/native_symbol_fuchsia.cc
22+
23+
import "dart:convert";
24+
import "dart:io";
25+
import "dart:typed_data";
26+
27+
import "package:args/args.dart";
28+
import "package:path/path.dart" as path;
29+
30+
Future<void> main(List<String> args) async {
31+
final parser = new ArgParser();
32+
parser.addOption("nm", help: "Path to `nm` tool");
33+
parser.addOption("binary",
34+
help: "Path to the ELF file to extract symbols from");
35+
parser.addOption("output", help: "Path to output symbol table");
36+
final usage = """
37+
Usage: dart_profiler_symbols.dart [options]
38+
39+
Options:
40+
${parser.usage};
41+
""";
42+
43+
String buildIdDir;
44+
String buildIdScript;
45+
String nm;
46+
String binary;
47+
String output;
48+
49+
try {
50+
final options = parser.parse(args);
51+
nm = options["nm"];
52+
if (nm == null) {
53+
throw "Must specify --nm";
54+
}
55+
if (!FileSystemEntity.isFileSync(nm)) {
56+
throw "Cannot find $nm";
57+
}
58+
binary = options["binary"];
59+
if (binary == null) {
60+
throw "Must specify --binary";
61+
}
62+
if (!FileSystemEntity.isFileSync(binary)) {
63+
throw "Cannot find $binary";
64+
}
65+
output = options["output"];
66+
if (output == null) {
67+
throw "Must specify --output";
68+
}
69+
} catch (e) {
70+
print("ERROR: $e\n");
71+
print(usage);
72+
exitCode = 1;
73+
return;
74+
}
75+
76+
await run(buildIdDir, buildIdScript, nm, binary, output);
77+
}
78+
79+
class Symbol {
80+
int offset;
81+
int size;
82+
String name;
83+
}
84+
85+
Future<void> run(String buildIdDir, String buildIdScript, String nm,
86+
String binary, String output) async {
87+
final unstrippedFile = binary;
88+
final args = ["--demangle", "--numeric-sort", "--print-size", unstrippedFile];
89+
final result = await Process.run(nm, args);
90+
if (result.exitCode != 0) {
91+
print(result.stdout);
92+
print(result.stderr);
93+
throw "Command failed: $nm $args";
94+
}
95+
96+
var symbols = new List();
97+
98+
var regex = new RegExp("([0-9A-Za-z]+) ([0-9A-Za-z]+) (t|T|w|W) (.*)");
99+
for (final line in result.stdout.split("\n")) {
100+
var match = regex.firstMatch(line);
101+
if (match == null) {
102+
continue; // Ignore non-text symbols.
103+
}
104+
105+
final symbol = new Symbol();
106+
107+
// Note that capture groups start at 1.
108+
symbol.offset = int.parse(match[1], radix: 16);
109+
symbol.size = int.parse(match[2], radix: 16);
110+
symbol.name = match[4].split("(")[0];
111+
112+
if (symbol.name.startsWith("\$")) {
113+
continue; // Ignore compiler/assembler temps.
114+
}
115+
116+
symbols.add(symbol);
117+
}
118+
119+
if (symbols.isEmpty) {
120+
throw "$unstrippedFile has no symbols";
121+
}
122+
123+
var nameTable = new BytesBuilder();
124+
var binarySearchTable = new Uint32List(symbols.length * 3 + 1);
125+
var binarySearchTableIndex = 0;
126+
binarySearchTable[binarySearchTableIndex++] = symbols.length;
127+
// Symbols are sorted by offset because of --numeric-sort.
128+
for (var symbol in symbols) {
129+
var nameOffset = nameTable.length;
130+
nameTable.add(utf8.encode(symbol.name));
131+
nameTable.addByte(0);
132+
binarySearchTable[binarySearchTableIndex++] = symbol.offset;
133+
binarySearchTable[binarySearchTableIndex++] = symbol.size;
134+
binarySearchTable[binarySearchTableIndex++] = nameOffset;
135+
}
136+
137+
var file = new File(output);
138+
await file.parent.create(recursive: true);
139+
var sink = file.openWrite();
140+
sink.add(binarySearchTable.buffer.asUint8List());
141+
sink.add(nameTable.takeBytes());
142+
await sink.close();
143+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
name: profiler_symbols
6+
version: 0
7+
description: Extracts a minimal symbols table for the Dart VM profiler
8+
author: Dart Team <[email protected]>

0 commit comments

Comments
 (0)