| 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 | |