1 | // Copyright (c) 2018, 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 | #if defined(DART_PRECOMPILER) |
6 | |
7 | #include "vm/v8_snapshot_writer.h" |
8 | |
9 | #include "vm/dart.h" |
10 | #include "vm/os.h" |
11 | |
12 | namespace dart { |
13 | |
14 | const char* ZoneString(Zone* Z, const char* str) { |
15 | const intptr_t len = strlen(str) + 1; |
16 | char* dest = Z->Alloc<char>(len); |
17 | snprintf(dest, len, "%s" , str); |
18 | return dest; |
19 | } |
20 | |
21 | V8SnapshotProfileWriter::V8SnapshotProfileWriter(Zone* zone) |
22 | : zone_(zone), |
23 | node_types_(zone_), |
24 | edge_types_(zone_), |
25 | strings_(zone), |
26 | roots_(zone_) { |
27 | node_types_.Insert({"Unknown" , kUnknown}); |
28 | node_types_.Insert({"ArtificialRoot" , kArtificialRoot}); |
29 | |
30 | edge_types_.Insert({"context" , kContext}); |
31 | edge_types_.Insert({"element" , kElement}); |
32 | edge_types_.Insert({"property" , kProperty}); |
33 | edge_types_.Insert({"internal" , kInternal}); |
34 | |
35 | strings_.Insert({"<unknown>" , kUnknownString}); |
36 | strings_.Insert({"<artificial root>" , kArtificialRootString}); |
37 | |
38 | nodes_.Insert({ArtificialRootId(), |
39 | { |
40 | kArtificialRoot, |
41 | kArtificialRootString, |
42 | ArtificialRootId(), |
43 | 0, |
44 | nullptr, |
45 | 0, |
46 | }}); |
47 | } |
48 | |
49 | void V8SnapshotProfileWriter::SetObjectTypeAndName(ObjectId object_id, |
50 | const char* type, |
51 | const char* name) { |
52 | ASSERT(type != nullptr); |
53 | NodeInfo* info = EnsureId(object_id); |
54 | |
55 | if (!node_types_.HasKey(type)) { |
56 | node_types_.Insert({ZoneString(zone_, type), node_types_.Size()}); |
57 | } |
58 | |
59 | intptr_t type_id = node_types_.LookupValue(type); |
60 | ASSERT(info->type == kUnknown || info->type == type_id); |
61 | info->type = type_id; |
62 | if (name != nullptr) { |
63 | info->name = EnsureString(name); |
64 | } else { |
65 | info->name = |
66 | EnsureString(OS::SCreate(zone_, "Unnamed [%s] %s" , type, "(nil)" )); |
67 | } |
68 | } |
69 | |
70 | void V8SnapshotProfileWriter::AttributeBytesTo(ObjectId object_id, |
71 | size_t num_bytes) { |
72 | EnsureId(object_id)->self_size += num_bytes; |
73 | } |
74 | |
75 | void V8SnapshotProfileWriter::AttributeReferenceTo(ObjectId object_id, |
76 | Reference reference) { |
77 | EnsureId(reference.to_object_id); |
78 | NodeInfo* info = EnsureId(object_id); |
79 | |
80 | ASSERT(reference.offset_or_name >= 0); |
81 | info->edges->Add({ |
82 | static_cast<intptr_t>(reference.reference_type == Reference::kElement |
83 | ? kElement |
84 | : kProperty), |
85 | reference.offset_or_name, |
86 | reference.to_object_id, |
87 | }); |
88 | ++edge_count_; |
89 | } |
90 | |
91 | V8SnapshotProfileWriter::NodeInfo V8SnapshotProfileWriter::DefaultNode( |
92 | ObjectId object_id) { |
93 | return { |
94 | kUnknown, |
95 | kUnknownString, |
96 | object_id, |
97 | 0, |
98 | new (zone_) ZoneGrowableArray<EdgeInfo>(zone_, 0), |
99 | -1, |
100 | }; |
101 | } |
102 | |
103 | const V8SnapshotProfileWriter::NodeInfo& |
104 | V8SnapshotProfileWriter::ArtificialRoot() { |
105 | return nodes_.Lookup(ArtificialRootId())->value; |
106 | } |
107 | |
108 | V8SnapshotProfileWriter::NodeInfo* V8SnapshotProfileWriter::EnsureId( |
109 | ObjectId object_id) { |
110 | if (!nodes_.HasKey(object_id)) { |
111 | NodeInfo info = DefaultNode(object_id); |
112 | nodes_.Insert({object_id, info}); |
113 | } |
114 | return &nodes_.Lookup(object_id)->value; |
115 | } |
116 | |
117 | intptr_t V8SnapshotProfileWriter::EnsureString(const char* str) { |
118 | if (!strings_.HasKey(str)) { |
119 | strings_.Insert({ZoneString(zone_, str), strings_.Size()}); |
120 | return strings_.Size() - 1; |
121 | } |
122 | return strings_.LookupValue(str); |
123 | } |
124 | |
125 | void V8SnapshotProfileWriter::WriteNodeInfo(JSONWriter* writer, |
126 | const NodeInfo& info) { |
127 | writer->PrintValue(info.type); |
128 | writer->PrintValue(info.name); |
129 | writer->PrintValue(NodeIdFor(info.id)); |
130 | writer->PrintValue(info.self_size); |
131 | // The artificial root has 'nullptr' edges, it actually points to all the |
132 | // roots. |
133 | writer->PrintValue64(info.edges != nullptr ? info.edges->length() |
134 | : roots_.Size()); |
135 | writer->PrintNewline(); |
136 | } |
137 | |
138 | void V8SnapshotProfileWriter::WriteEdgeInfo(JSONWriter* writer, |
139 | const EdgeInfo& info) { |
140 | writer->PrintValue64(info.type); |
141 | writer->PrintValue64(info.name_or_index); |
142 | writer->PrintValue64(nodes_.LookupValue(info.to_node).offset); |
143 | writer->PrintNewline(); |
144 | } |
145 | |
146 | void V8SnapshotProfileWriter::AddRoot(ObjectId object_id, |
147 | const char* name /*= nullptr*/) { |
148 | EnsureId(object_id); |
149 | // HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js) |
150 | // assumes that the root does not have more than one edge to any other node |
151 | // (most likely an oversight). |
152 | if (roots_.HasKey(object_id)) return; |
153 | |
154 | ObjectIdToNodeInfoTraits::Pair pair; |
155 | pair.key = object_id; |
156 | pair.value = NodeInfo{ |
157 | 0, name != nullptr ? EnsureString(name) : -1, object_id, 0, nullptr, 0}; |
158 | roots_.Insert(pair); |
159 | } |
160 | |
161 | void V8SnapshotProfileWriter::WriteStringsTable( |
162 | JSONWriter* writer, |
163 | const DirectChainedHashMap<StringToIntMapTraits>& map) { |
164 | const char** strings = zone_->Alloc<const char*>(map.Size()); |
165 | StringToIntMapTraits::Pair* pair = nullptr; |
166 | auto it = map.GetIterator(); |
167 | while ((pair = it.Next()) != nullptr) { |
168 | ASSERT(pair->value >= 0 && pair->value < map.Size()); |
169 | strings[pair->value] = pair->key; |
170 | } |
171 | for (intptr_t i = 0; i < map.Size(); ++i) { |
172 | writer->PrintValue(strings[i]); |
173 | writer->PrintNewline(); |
174 | } |
175 | } |
176 | |
177 | void V8SnapshotProfileWriter::Write(JSONWriter* writer) { |
178 | writer->OpenObject(); |
179 | |
180 | writer->OpenObject("snapshot" ); |
181 | { |
182 | writer->OpenObject("meta" ); |
183 | |
184 | { |
185 | writer->OpenArray("node_fields" ); |
186 | writer->PrintValue("type" ); |
187 | writer->PrintValue("name" ); |
188 | writer->PrintValue("id" ); |
189 | writer->PrintValue("self_size" ); |
190 | writer->PrintValue("edge_count" ); |
191 | writer->CloseArray(); |
192 | } |
193 | |
194 | { |
195 | writer->OpenArray("node_types" ); |
196 | { |
197 | writer->OpenArray(); |
198 | WriteStringsTable(writer, node_types_); |
199 | writer->CloseArray(); |
200 | } |
201 | writer->CloseArray(); |
202 | } |
203 | |
204 | { |
205 | writer->OpenArray("edge_fields" ); |
206 | writer->PrintValue("type" ); |
207 | writer->PrintValue("name_or_index" ); |
208 | writer->PrintValue("to_node" ); |
209 | writer->CloseArray(); |
210 | } |
211 | |
212 | { |
213 | writer->OpenArray("edge_types" ); |
214 | { |
215 | writer->OpenArray(); |
216 | WriteStringsTable(writer, edge_types_); |
217 | writer->CloseArray(); |
218 | } |
219 | writer->CloseArray(); |
220 | } |
221 | |
222 | writer->CloseObject(); |
223 | |
224 | writer->PrintProperty64("node_count" , nodes_.Size()); |
225 | writer->PrintProperty64("edge_count" , edge_count_ + roots_.Size()); |
226 | } |
227 | writer->CloseObject(); |
228 | |
229 | { |
230 | writer->OpenArray("nodes" ); |
231 | // Write the artificial root node. |
232 | WriteNodeInfo(writer, ArtificialRoot()); |
233 | intptr_t offset = kNumNodeFields; |
234 | ObjectIdToNodeInfoTraits::Pair* entry = nullptr; |
235 | auto it = nodes_.GetIterator(); |
236 | while ((entry = it.Next()) != nullptr) { |
237 | ASSERT(entry->key == entry->value.id); |
238 | if (entry->value.id == ArtificialRootId()) { |
239 | continue; // Written separately above. |
240 | } |
241 | entry->value.offset = offset; |
242 | WriteNodeInfo(writer, entry->value); |
243 | offset += kNumNodeFields; |
244 | } |
245 | writer->CloseArray(); |
246 | } |
247 | |
248 | { |
249 | writer->OpenArray("edges" ); |
250 | |
251 | // Write references from the artificial root to the actual roots. |
252 | ObjectIdToNodeInfoTraits::Pair* entry = nullptr; |
253 | auto roots_it = roots_.GetIterator(); |
254 | for (int i = 0; (entry = roots_it.Next()) != nullptr; ++i) { |
255 | if (entry->value.name != -1) { |
256 | WriteEdgeInfo(writer, {kProperty, entry->value.name, entry->key}); |
257 | } else { |
258 | WriteEdgeInfo(writer, {kInternal, i, entry->key}); |
259 | } |
260 | } |
261 | |
262 | auto nodes_it = nodes_.GetIterator(); |
263 | while ((entry = nodes_it.Next()) != nullptr) { |
264 | if (entry->value.edges == nullptr) { |
265 | continue; // Artificial root, its edges are written separately above. |
266 | } |
267 | |
268 | for (intptr_t i = 0; i < entry->value.edges->length(); ++i) { |
269 | WriteEdgeInfo(writer, entry->value.edges->At(i)); |
270 | } |
271 | } |
272 | |
273 | writer->CloseArray(); |
274 | } |
275 | |
276 | { |
277 | writer->OpenArray("strings" ); |
278 | WriteStringsTable(writer, strings_); |
279 | writer->CloseArray(); |
280 | } |
281 | |
282 | writer->CloseObject(); |
283 | } |
284 | |
285 | void V8SnapshotProfileWriter::Write(const char* filename) { |
286 | JSONWriter json; |
287 | Write(&json); |
288 | |
289 | auto file_open = Dart::file_open_callback(); |
290 | auto file_write = Dart::file_write_callback(); |
291 | auto file_close = Dart::file_close_callback(); |
292 | if ((file_open == nullptr) || (file_write == nullptr) || |
293 | (file_close == nullptr)) { |
294 | OS::PrintErr("Could not access file callbacks to write snapshot profile." ); |
295 | return; |
296 | } |
297 | |
298 | auto file = file_open(filename, /*write=*/true); |
299 | if (file == nullptr) { |
300 | OS::PrintErr("Failed to open file %s\n" , filename); |
301 | } else { |
302 | char* output = nullptr; |
303 | intptr_t output_length = 0; |
304 | json.Steal(&output, &output_length); |
305 | file_write(output, output_length, file); |
306 | free(output); |
307 | file_close(file); |
308 | } |
309 | } |
310 | |
311 | } // namespace dart |
312 | |
313 | #endif |
314 | |