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 | |
10 | namespace dart { |
11 | |
12 | #if defined(DART_PRECOMPILER) |
13 | |
14 | DEFINE_FLAG(charp, |
15 | trace_precompiler_to, |
16 | nullptr, |
17 | "Output machine readable precompilation trace into the given file" ); |
18 | |
19 | PrecompilerTracer* 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 | |
32 | PrecompilerTracer::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 | |
43 | void 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 | |
55 | void 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 | |
106 | void 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 | |
125 | intptr_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 | |
131 | intptr_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 | |
137 | intptr_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 | |
157 | PrecompilerTracer::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 | |