1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/compiler/aot/precompiler_tracer.h"
6
7#include "vm/compiler/aot/precompiler.h"
8#include "vm/zone_text_buffer.h"
9
10namespace dart {
11
12#if defined(DART_PRECOMPILER)
13
14DEFINE_FLAG(charp,
15 trace_precompiler_to,
16 nullptr,
17 "Output machine readable precompilation trace into the given file");
18
19PrecompilerTracer* PrecompilerTracer::StartTracingIfRequested(
20 Precompiler* precompiler) {
21 if (FLAG_trace_precompiler_to != nullptr &&
22 Dart::file_write_callback() != nullptr &&
23 Dart::file_open_callback() != nullptr &&
24 Dart::file_close_callback() != nullptr) {
25 return new PrecompilerTracer(
26 precompiler, Dart::file_open_callback()(FLAG_trace_precompiler_to,
27 /*write=*/true));
28 }
29 return nullptr;
30}
31
32PrecompilerTracer::PrecompilerTracer(Precompiler* precompiler, void* stream)
33 : zone_(Thread::Current()->zone()),
34 precompiler_(precompiler),
35 stream_(stream),
36 strings_(HashTables::New<StringTable>(1024)),
37 entities_(HashTables::New<EntityTable>(1024)),
38 object_(Object::Handle()),
39 cls_(Class::Handle()) {
40 Write("{\"trace\":[\"R\",");
41}
42
43void PrecompilerTracer::Finalize() {
44 Write("\"E\"],");
45 WriteEntityTable();
46 Write(",");
47 WriteStringTable();
48 Write("}\n");
49 Dart::file_close_callback()(stream_);
50
51 strings_.Release();
52 entities_.Release();
53}
54
55void PrecompilerTracer::WriteEntityTable() {
56 Write("\"entities\":[");
57 const auto& entities_by_id =
58 Array::Handle(zone_, Array::New(entities_.NumOccupied()));
59
60 EntityTable::Iterator it(&entities_);
61 while (it.MoveNext()) {
62 object_ = entities_.GetPayload(it.Current(), 0);
63 const intptr_t index = Smi::Cast(object_).Value();
64 object_ = entities_.GetKey(it.Current());
65 entities_by_id.SetAt(index, object_);
66 }
67
68 auto& obj = Object::Handle(zone_);
69 auto& lib = Library::Handle(zone_);
70 auto& str = String::Handle(zone_);
71 for (intptr_t i = 0; i < entities_by_id.Length(); i++) {
72 if (i > 0) {
73 Write(",");
74 }
75 obj = entities_by_id.At(i);
76 if (obj.IsFunction()) {
77 const auto& fun = Function::Cast(obj);
78 cls_ = fun.Owner();
79 const intptr_t selector_id =
80 FLAG_use_bare_instructions && FLAG_use_table_dispatch
81 ? precompiler_->selector_map()->SelectorId(fun)
82 : -1;
83 Write("\"%c\",%" Pd ",%" Pd ",%" Pd "",
84 fun.IsDynamicFunction() ? 'F' : 'S', InternEntity(cls_),
85 InternString(NameForTrace(fun)), selector_id);
86 } else if (obj.IsField()) {
87 const auto& field = Field::Cast(obj);
88 cls_ = field.Owner();
89 str = field.name();
90 Write("\"V\",%" Pd ",%" Pd ",0", InternEntity(cls_), InternString(str));
91 } else if (obj.IsClass()) {
92 const auto& cls = Class::Cast(obj);
93 lib = cls.library();
94 str = lib.url();
95 const auto url_id = InternString(str);
96 str = cls.ScrubbedName();
97 const auto name_id = InternString(str);
98 Write("\"C\",%" Pd ",%" Pd ",0", url_id, name_id);
99 } else {
100 UNREACHABLE();
101 }
102 }
103 Write("]");
104}
105
106void PrecompilerTracer::WriteStringTable() {
107 Write("\"strings\":[");
108 GrowableArray<const char*> strings_by_id(strings_.NumOccupied());
109 strings_by_id.EnsureLength(strings_.NumOccupied(), nullptr);
110 StringTable::Iterator it(&strings_);
111 while (it.MoveNext()) {
112 object_ = strings_.GetPayload(it.Current(), 0);
113 const auto index = Smi::Cast(object_).Value();
114 object_ = strings_.GetKey(it.Current());
115 strings_by_id[index] = String::Cast(object_).ToCString();
116 }
117 auto comma = false;
118 for (auto str : strings_by_id) {
119 Write("%s\"%s\"", comma ? "," : "", str);
120 comma = true;
121 }
122 Write("]");
123}
124
125intptr_t PrecompilerTracer::InternString(const CString& cstr) {
126 object_ = Smi::New(strings_.NumOccupied());
127 object_ = strings_.InsertNewOrGetValue(cstr, object_);
128 return Smi::Cast(object_).Value();
129}
130
131intptr_t PrecompilerTracer::InternString(const String& str) {
132 object_ = Smi::New(strings_.NumOccupied());
133 object_ = strings_.InsertOrGetValue(str, object_);
134 return Smi::Cast(object_).Value();
135}
136
137intptr_t PrecompilerTracer::InternEntity(const Object& obj) {
138 ASSERT(obj.IsFunction() || obj.IsClass() || obj.IsField());
139 const auto num_occupied = entities_.NumOccupied();
140 object_ = Smi::New(num_occupied);
141 object_ = entities_.InsertOrGetValue(obj, object_);
142 const auto id = Smi::Cast(object_).Value();
143 if (id == num_occupied) {
144 cls_ = Class::null();
145 if (obj.IsFunction()) {
146 cls_ = Function::Cast(obj).Owner();
147 } else if (obj.IsField()) {
148 cls_ = Field::Cast(obj).Owner();
149 }
150 if (cls_.raw() != Class::null()) {
151 InternEntity(cls_);
152 }
153 }
154 return id;
155}
156
157PrecompilerTracer::CString PrecompilerTracer::NameForTrace(const Function& f) {
158 ZoneTextBuffer buffer(zone_);
159 f.PrintName(NameFormattingParams::DisambiguatedWithoutClassName(
160 Object::NameVisibility::kInternalName),
161 &buffer);
162 return {buffer.buffer(), buffer.length(),
163 String::Hash(buffer.buffer(), buffer.length())};
164}
165
166#endif // defined(DART_PRECOMPILER)
167
168} // namespace dart
169