1 // Copyright 2013 the V8 project 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 #ifndef V8_PROFILER_HEAP_SNAPSHOT_GENERATOR_H_ 6 #define V8_PROFILER_HEAP_SNAPSHOT_GENERATOR_H_ 7 8 #include "include/v8-profiler.h" 9 #include "src/base/platform/time.h" 10 #include "src/objects.h" 11 #include "src/profiler/strings-storage.h" 12 13 namespace v8 { 14 namespace internal { 15 16 class AllocationTracker; 17 class AllocationTraceNode; 18 class HeapEntry; 19 class HeapIterator; 20 class HeapProfiler; 21 class HeapSnapshot; 22 class SnapshotFiller; 23 24 class HeapGraphEdge BASE_EMBEDDED { 25 public: 26 enum Type { 27 kContextVariable = v8::HeapGraphEdge::kContextVariable, 28 kElement = v8::HeapGraphEdge::kElement, 29 kProperty = v8::HeapGraphEdge::kProperty, 30 kInternal = v8::HeapGraphEdge::kInternal, 31 kHidden = v8::HeapGraphEdge::kHidden, 32 kShortcut = v8::HeapGraphEdge::kShortcut, 33 kWeak = v8::HeapGraphEdge::kWeak 34 }; 35 36 HeapGraphEdge(Type type, const char* name, int from, int to); 37 HeapGraphEdge(Type type, int index, int from, int to); 38 void ReplaceToIndexWithEntry(HeapSnapshot* snapshot); 39 type()40 Type type() const { return TypeField::decode(bit_field_); } index()41 int index() const { 42 DCHECK(type() == kElement || type() == kHidden); 43 return index_; 44 } name()45 const char* name() const { 46 DCHECK(type() == kContextVariable || type() == kProperty || 47 type() == kInternal || type() == kShortcut || type() == kWeak); 48 return name_; 49 } 50 INLINE(HeapEntry* from() const); to()51 HeapEntry* to() const { return to_entry_; } 52 53 INLINE(Isolate* isolate() const); 54 55 private: 56 INLINE(HeapSnapshot* snapshot() const); from_index()57 int from_index() const { return FromIndexField::decode(bit_field_); } 58 59 class TypeField : public BitField<Type, 0, 3> {}; 60 class FromIndexField : public BitField<int, 3, 29> {}; 61 uint32_t bit_field_; 62 union { 63 // During entries population |to_index_| is used for storing the index, 64 // afterwards it is replaced with a pointer to the entry. 65 int to_index_; 66 HeapEntry* to_entry_; 67 }; 68 union { 69 int index_; 70 const char* name_; 71 }; 72 }; 73 74 75 // HeapEntry instances represent an entity from the heap (or a special 76 // virtual node, e.g. root). 77 class HeapEntry BASE_EMBEDDED { 78 public: 79 enum Type { 80 kHidden = v8::HeapGraphNode::kHidden, 81 kArray = v8::HeapGraphNode::kArray, 82 kString = v8::HeapGraphNode::kString, 83 kObject = v8::HeapGraphNode::kObject, 84 kCode = v8::HeapGraphNode::kCode, 85 kClosure = v8::HeapGraphNode::kClosure, 86 kRegExp = v8::HeapGraphNode::kRegExp, 87 kHeapNumber = v8::HeapGraphNode::kHeapNumber, 88 kNative = v8::HeapGraphNode::kNative, 89 kSynthetic = v8::HeapGraphNode::kSynthetic, 90 kConsString = v8::HeapGraphNode::kConsString, 91 kSlicedString = v8::HeapGraphNode::kSlicedString, 92 kSymbol = v8::HeapGraphNode::kSymbol, 93 kSimdValue = v8::HeapGraphNode::kSimdValue 94 }; 95 static const int kNoEntry; 96 HeapEntry()97 HeapEntry() { } 98 HeapEntry(HeapSnapshot* snapshot, 99 Type type, 100 const char* name, 101 SnapshotObjectId id, 102 size_t self_size, 103 unsigned trace_node_id); 104 snapshot()105 HeapSnapshot* snapshot() { return snapshot_; } type()106 Type type() { return static_cast<Type>(type_); } name()107 const char* name() { return name_; } set_name(const char * name)108 void set_name(const char* name) { name_ = name; } id()109 SnapshotObjectId id() { return id_; } self_size()110 size_t self_size() { return self_size_; } trace_node_id()111 unsigned trace_node_id() const { return trace_node_id_; } 112 INLINE(int index() const); children_count()113 int children_count() const { return children_count_; } 114 INLINE(int set_children_index(int index)); add_child(HeapGraphEdge * edge)115 void add_child(HeapGraphEdge* edge) { 116 children_arr()[children_count_++] = edge; 117 } children()118 Vector<HeapGraphEdge*> children() { 119 return Vector<HeapGraphEdge*>(children_arr(), children_count_); } 120 INLINE(Isolate* isolate() const); 121 122 void SetIndexedReference( 123 HeapGraphEdge::Type type, int index, HeapEntry* entry); 124 void SetNamedReference( 125 HeapGraphEdge::Type type, const char* name, HeapEntry* entry); 126 127 void Print( 128 const char* prefix, const char* edge_name, int max_depth, int indent); 129 130 private: 131 INLINE(HeapGraphEdge** children_arr()); 132 const char* TypeAsString(); 133 134 unsigned type_: 4; 135 int children_count_: 28; 136 int children_index_; 137 size_t self_size_; 138 HeapSnapshot* snapshot_; 139 const char* name_; 140 SnapshotObjectId id_; 141 // id of allocation stack trace top node 142 unsigned trace_node_id_; 143 }; 144 145 146 // HeapSnapshot represents a single heap snapshot. It is stored in 147 // HeapProfiler, which is also a factory for 148 // HeapSnapshots. All HeapSnapshots share strings copied from JS heap 149 // to be able to return them even if they were collected. 150 // HeapSnapshotGenerator fills in a HeapSnapshot. 151 class HeapSnapshot { 152 public: 153 explicit HeapSnapshot(HeapProfiler* profiler); 154 void Delete(); 155 profiler()156 HeapProfiler* profiler() { return profiler_; } 157 size_t RawSnapshotSize() const; root()158 HeapEntry* root() { return &entries_[root_index_]; } gc_roots()159 HeapEntry* gc_roots() { return &entries_[gc_roots_index_]; } gc_subroot(int index)160 HeapEntry* gc_subroot(int index) { 161 return &entries_[gc_subroot_indexes_[index]]; 162 } entries()163 List<HeapEntry>& entries() { return entries_; } edges()164 List<HeapGraphEdge>& edges() { return edges_; } children()165 List<HeapGraphEdge*>& children() { return children_; } 166 void RememberLastJSObjectId(); max_snapshot_js_object_id()167 SnapshotObjectId max_snapshot_js_object_id() const { 168 return max_snapshot_js_object_id_; 169 } 170 171 HeapEntry* AddEntry(HeapEntry::Type type, 172 const char* name, 173 SnapshotObjectId id, 174 size_t size, 175 unsigned trace_node_id); 176 void AddSyntheticRootEntries(); 177 HeapEntry* GetEntryById(SnapshotObjectId id); 178 List<HeapEntry*>* GetSortedEntriesList(); 179 void FillChildren(); 180 181 void Print(int max_depth); 182 183 private: 184 HeapEntry* AddRootEntry(); 185 HeapEntry* AddGcRootsEntry(); 186 HeapEntry* AddGcSubrootEntry(int tag, SnapshotObjectId id); 187 188 HeapProfiler* profiler_; 189 int root_index_; 190 int gc_roots_index_; 191 int gc_subroot_indexes_[VisitorSynchronization::kNumberOfSyncTags]; 192 List<HeapEntry> entries_; 193 List<HeapGraphEdge> edges_; 194 List<HeapGraphEdge*> children_; 195 List<HeapEntry*> sorted_entries_; 196 SnapshotObjectId max_snapshot_js_object_id_; 197 198 friend class HeapSnapshotTester; 199 200 DISALLOW_COPY_AND_ASSIGN(HeapSnapshot); 201 }; 202 203 204 class HeapObjectsMap { 205 public: 206 struct TimeInterval { TimeIntervalTimeInterval207 explicit TimeInterval(SnapshotObjectId id) 208 : id(id), size(0), count(0), timestamp(base::TimeTicks::Now()) {} last_assigned_idTimeInterval209 SnapshotObjectId last_assigned_id() const { return id - kObjectIdStep; } 210 SnapshotObjectId id; 211 uint32_t size; 212 uint32_t count; 213 base::TimeTicks timestamp; 214 }; 215 216 explicit HeapObjectsMap(Heap* heap); 217 heap()218 Heap* heap() const { return heap_; } 219 220 SnapshotObjectId FindEntry(Address addr); 221 SnapshotObjectId FindOrAddEntry(Address addr, 222 unsigned int size, 223 bool accessed = true); 224 bool MoveObject(Address from, Address to, int size); 225 void UpdateObjectSize(Address addr, int size); last_assigned_id()226 SnapshotObjectId last_assigned_id() const { 227 return next_id_ - kObjectIdStep; 228 } 229 230 void StopHeapObjectsTracking(); 231 SnapshotObjectId PushHeapObjectsStats(OutputStream* stream, 232 int64_t* timestamp_us); samples()233 const List<TimeInterval>& samples() const { return time_intervals_; } 234 size_t GetUsedMemorySize() const; 235 236 SnapshotObjectId GenerateId(v8::RetainedObjectInfo* info); 237 238 static const int kObjectIdStep = 2; 239 static const SnapshotObjectId kInternalRootObjectId; 240 static const SnapshotObjectId kGcRootsObjectId; 241 static const SnapshotObjectId kGcRootsFirstSubrootId; 242 static const SnapshotObjectId kFirstAvailableObjectId; 243 244 int FindUntrackedObjects(); 245 246 void UpdateHeapObjectsMap(); 247 void RemoveDeadEntries(); 248 249 private: 250 struct EntryInfo { EntryInfoEntryInfo251 EntryInfo(SnapshotObjectId id, Address addr, unsigned int size) 252 : id(id), addr(addr), size(size), accessed(true) { } EntryInfoEntryInfo253 EntryInfo(SnapshotObjectId id, Address addr, unsigned int size, bool accessed) 254 : id(id), addr(addr), size(size), accessed(accessed) { } 255 SnapshotObjectId id; 256 Address addr; 257 unsigned int size; 258 bool accessed; 259 }; 260 261 SnapshotObjectId next_id_; 262 HashMap entries_map_; 263 List<EntryInfo> entries_; 264 List<TimeInterval> time_intervals_; 265 Heap* heap_; 266 267 DISALLOW_COPY_AND_ASSIGN(HeapObjectsMap); 268 }; 269 270 271 // A typedef for referencing anything that can be snapshotted living 272 // in any kind of heap memory. 273 typedef void* HeapThing; 274 275 276 // An interface that creates HeapEntries by HeapThings. 277 class HeapEntriesAllocator { 278 public: ~HeapEntriesAllocator()279 virtual ~HeapEntriesAllocator() { } 280 virtual HeapEntry* AllocateEntry(HeapThing ptr) = 0; 281 }; 282 283 284 // The HeapEntriesMap instance is used to track a mapping between 285 // real heap objects and their representations in heap snapshots. 286 class HeapEntriesMap { 287 public: 288 HeapEntriesMap(); 289 290 int Map(HeapThing thing); 291 void Pair(HeapThing thing, int entry); 292 293 private: Hash(HeapThing thing)294 static uint32_t Hash(HeapThing thing) { 295 return ComputeIntegerHash( 296 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(thing)), 297 v8::internal::kZeroHashSeed); 298 } 299 300 HashMap entries_; 301 302 friend class HeapObjectsSet; 303 304 DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap); 305 }; 306 307 308 class HeapObjectsSet { 309 public: 310 HeapObjectsSet(); 311 void Clear(); 312 bool Contains(Object* object); 313 void Insert(Object* obj); 314 const char* GetTag(Object* obj); 315 void SetTag(Object* obj, const char* tag); is_empty()316 bool is_empty() const { return entries_.occupancy() == 0; } 317 318 private: 319 HashMap entries_; 320 321 DISALLOW_COPY_AND_ASSIGN(HeapObjectsSet); 322 }; 323 324 325 class SnapshottingProgressReportingInterface { 326 public: ~SnapshottingProgressReportingInterface()327 virtual ~SnapshottingProgressReportingInterface() { } 328 virtual void ProgressStep() = 0; 329 virtual bool ProgressReport(bool force) = 0; 330 }; 331 332 333 // An implementation of V8 heap graph extractor. 334 class V8HeapExplorer : public HeapEntriesAllocator { 335 public: 336 V8HeapExplorer(HeapSnapshot* snapshot, 337 SnapshottingProgressReportingInterface* progress, 338 v8::HeapProfiler::ObjectNameResolver* resolver); 339 virtual ~V8HeapExplorer(); 340 virtual HeapEntry* AllocateEntry(HeapThing ptr); 341 int EstimateObjectsCount(HeapIterator* iterator); 342 bool IterateAndExtractReferences(SnapshotFiller* filler); 343 void TagGlobalObjects(); 344 void TagCodeObject(Code* code); 345 void TagBuiltinCodeObject(Code* code, const char* name); 346 HeapEntry* AddEntry(Address address, 347 HeapEntry::Type type, 348 const char* name, 349 size_t size); 350 351 static String* GetConstructorName(JSObject* object); 352 353 private: 354 typedef bool (V8HeapExplorer::*ExtractReferencesMethod)(int entry, 355 HeapObject* object); 356 357 void MarkVisitedField(HeapObject* obj, int offset); 358 359 HeapEntry* AddEntry(HeapObject* object); 360 HeapEntry* AddEntry(HeapObject* object, 361 HeapEntry::Type type, 362 const char* name); 363 364 const char* GetSystemEntryName(HeapObject* object); 365 366 template<V8HeapExplorer::ExtractReferencesMethod extractor> 367 bool IterateAndExtractSinglePass(); 368 369 bool ExtractReferencesPass1(int entry, HeapObject* obj); 370 bool ExtractReferencesPass2(int entry, HeapObject* obj); 371 void ExtractJSGlobalProxyReferences(int entry, JSGlobalProxy* proxy); 372 void ExtractJSObjectReferences(int entry, JSObject* js_obj); 373 void ExtractStringReferences(int entry, String* obj); 374 void ExtractSymbolReferences(int entry, Symbol* symbol); 375 void ExtractJSCollectionReferences(int entry, JSCollection* collection); 376 void ExtractJSWeakCollectionReferences(int entry, 377 JSWeakCollection* collection); 378 void ExtractContextReferences(int entry, Context* context); 379 void ExtractMapReferences(int entry, Map* map); 380 void ExtractSharedFunctionInfoReferences(int entry, 381 SharedFunctionInfo* shared); 382 void ExtractScriptReferences(int entry, Script* script); 383 void ExtractAccessorInfoReferences(int entry, AccessorInfo* accessor_info); 384 void ExtractAccessorPairReferences(int entry, AccessorPair* accessors); 385 void ExtractCodeCacheReferences(int entry, CodeCache* code_cache); 386 void ExtractCodeReferences(int entry, Code* code); 387 void ExtractBoxReferences(int entry, Box* box); 388 void ExtractCellReferences(int entry, Cell* cell); 389 void ExtractPropertyCellReferences(int entry, PropertyCell* cell); 390 void ExtractAllocationSiteReferences(int entry, AllocationSite* site); 391 void ExtractJSArrayBufferReferences(int entry, JSArrayBuffer* buffer); 392 void ExtractFixedArrayReferences(int entry, FixedArray* array); 393 void ExtractPropertyReferences(JSObject* js_obj, int entry); 394 void ExtractAccessorPairProperty(JSObject* js_obj, int entry, Name* key, 395 Object* callback_obj, int field_offset = -1); 396 void ExtractElementReferences(JSObject* js_obj, int entry); 397 void ExtractInternalReferences(JSObject* js_obj, int entry); 398 399 bool IsEssentialObject(Object* object); 400 void SetContextReference(HeapObject* parent_obj, 401 int parent, 402 String* reference_name, 403 Object* child, 404 int field_offset); 405 void SetNativeBindReference(HeapObject* parent_obj, 406 int parent, 407 const char* reference_name, 408 Object* child); 409 void SetElementReference(HeapObject* parent_obj, 410 int parent, 411 int index, 412 Object* child); 413 void SetInternalReference(HeapObject* parent_obj, 414 int parent, 415 const char* reference_name, 416 Object* child, 417 int field_offset = -1); 418 void SetInternalReference(HeapObject* parent_obj, 419 int parent, 420 int index, 421 Object* child, 422 int field_offset = -1); 423 void SetHiddenReference(HeapObject* parent_obj, 424 int parent, 425 int index, 426 Object* child); 427 void SetWeakReference(HeapObject* parent_obj, 428 int parent, 429 const char* reference_name, 430 Object* child_obj, 431 int field_offset); 432 void SetWeakReference(HeapObject* parent_obj, 433 int parent, 434 int index, 435 Object* child_obj, 436 int field_offset); 437 void SetPropertyReference(HeapObject* parent_obj, 438 int parent, 439 Name* reference_name, 440 Object* child, 441 const char* name_format_string = NULL, 442 int field_offset = -1); 443 void SetDataOrAccessorPropertyReference(PropertyKind kind, 444 JSObject* parent_obj, int parent, 445 Name* reference_name, Object* child, 446 const char* name_format_string = NULL, 447 int field_offset = -1); 448 449 void SetUserGlobalReference(Object* user_global); 450 void SetRootGcRootsReference(); 451 void SetGcRootsReference(VisitorSynchronization::SyncTag tag); 452 void SetGcSubrootReference( 453 VisitorSynchronization::SyncTag tag, bool is_weak, Object* child); 454 const char* GetStrongGcSubrootName(Object* object); 455 void TagObject(Object* obj, const char* tag); 456 void MarkAsWeakContainer(Object* object); 457 458 HeapEntry* GetEntry(Object* obj); 459 460 Heap* heap_; 461 HeapSnapshot* snapshot_; 462 StringsStorage* names_; 463 HeapObjectsMap* heap_object_map_; 464 SnapshottingProgressReportingInterface* progress_; 465 SnapshotFiller* filler_; 466 HeapObjectsSet objects_tags_; 467 HeapObjectsSet strong_gc_subroot_names_; 468 HeapObjectsSet user_roots_; 469 HeapObjectsSet weak_containers_; 470 v8::HeapProfiler::ObjectNameResolver* global_object_name_resolver_; 471 472 std::vector<bool> marks_; 473 474 friend class IndexedReferencesExtractor; 475 friend class RootsReferencesExtractor; 476 477 DISALLOW_COPY_AND_ASSIGN(V8HeapExplorer); 478 }; 479 480 481 class NativeGroupRetainedObjectInfo; 482 483 484 // An implementation of retained native objects extractor. 485 class NativeObjectsExplorer { 486 public: 487 NativeObjectsExplorer(HeapSnapshot* snapshot, 488 SnapshottingProgressReportingInterface* progress); 489 virtual ~NativeObjectsExplorer(); 490 int EstimateObjectsCount(); 491 bool IterateAndExtractReferences(SnapshotFiller* filler); 492 493 private: 494 void FillRetainedObjects(); 495 void FillImplicitReferences(); 496 List<HeapObject*>* GetListMaybeDisposeInfo(v8::RetainedObjectInfo* info); 497 void SetNativeRootReference(v8::RetainedObjectInfo* info); 498 void SetRootNativeRootsReference(); 499 void SetWrapperNativeReferences(HeapObject* wrapper, 500 v8::RetainedObjectInfo* info); 501 void VisitSubtreeWrapper(Object** p, uint16_t class_id); 502 InfoHash(v8::RetainedObjectInfo * info)503 static uint32_t InfoHash(v8::RetainedObjectInfo* info) { 504 return ComputeIntegerHash(static_cast<uint32_t>(info->GetHash()), 505 v8::internal::kZeroHashSeed); 506 } RetainedInfosMatch(void * key1,void * key2)507 static bool RetainedInfosMatch(void* key1, void* key2) { 508 return key1 == key2 || 509 (reinterpret_cast<v8::RetainedObjectInfo*>(key1))->IsEquivalent( 510 reinterpret_cast<v8::RetainedObjectInfo*>(key2)); 511 } INLINE(static bool StringsMatch (void * key1,void * key2))512 INLINE(static bool StringsMatch(void* key1, void* key2)) { 513 return strcmp(reinterpret_cast<char*>(key1), 514 reinterpret_cast<char*>(key2)) == 0; 515 } 516 517 NativeGroupRetainedObjectInfo* FindOrAddGroupInfo(const char* label); 518 519 Isolate* isolate_; 520 HeapSnapshot* snapshot_; 521 StringsStorage* names_; 522 bool embedder_queried_; 523 HeapObjectsSet in_groups_; 524 // RetainedObjectInfo* -> List<HeapObject*>* 525 HashMap objects_by_info_; 526 HashMap native_groups_; 527 HeapEntriesAllocator* synthetic_entries_allocator_; 528 HeapEntriesAllocator* native_entries_allocator_; 529 // Used during references extraction. 530 SnapshotFiller* filler_; 531 532 static HeapThing const kNativesRootObject; 533 534 friend class GlobalHandlesExtractor; 535 536 DISALLOW_COPY_AND_ASSIGN(NativeObjectsExplorer); 537 }; 538 539 540 class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { 541 public: 542 HeapSnapshotGenerator(HeapSnapshot* snapshot, 543 v8::ActivityControl* control, 544 v8::HeapProfiler::ObjectNameResolver* resolver, 545 Heap* heap); 546 bool GenerateSnapshot(); 547 548 private: 549 bool FillReferences(); 550 void ProgressStep(); 551 bool ProgressReport(bool force = false); 552 void SetProgressTotal(int iterations_count); 553 554 HeapSnapshot* snapshot_; 555 v8::ActivityControl* control_; 556 V8HeapExplorer v8_heap_explorer_; 557 NativeObjectsExplorer dom_explorer_; 558 // Mapping from HeapThing pointers to HeapEntry* pointers. 559 HeapEntriesMap entries_; 560 // Used during snapshot generation. 561 int progress_counter_; 562 int progress_total_; 563 Heap* heap_; 564 565 DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator); 566 }; 567 568 class OutputStreamWriter; 569 570 class HeapSnapshotJSONSerializer { 571 public: HeapSnapshotJSONSerializer(HeapSnapshot * snapshot)572 explicit HeapSnapshotJSONSerializer(HeapSnapshot* snapshot) 573 : snapshot_(snapshot), 574 strings_(StringsMatch), 575 next_node_id_(1), 576 next_string_id_(1), 577 writer_(NULL) { 578 } 579 void Serialize(v8::OutputStream* stream); 580 581 private: INLINE(static bool StringsMatch (void * key1,void * key2))582 INLINE(static bool StringsMatch(void* key1, void* key2)) { 583 return strcmp(reinterpret_cast<char*>(key1), 584 reinterpret_cast<char*>(key2)) == 0; 585 } 586 INLINE(static uint32_t StringHash (const void * string))587 INLINE(static uint32_t StringHash(const void* string)) { 588 const char* s = reinterpret_cast<const char*>(string); 589 int len = static_cast<int>(strlen(s)); 590 return StringHasher::HashSequentialString( 591 s, len, v8::internal::kZeroHashSeed); 592 } 593 594 int GetStringId(const char* s); entry_index(HeapEntry * e)595 int entry_index(HeapEntry* e) { return e->index() * kNodeFieldsCount; } 596 void SerializeEdge(HeapGraphEdge* edge, bool first_edge); 597 void SerializeEdges(); 598 void SerializeImpl(); 599 void SerializeNode(HeapEntry* entry); 600 void SerializeNodes(); 601 void SerializeSnapshot(); 602 void SerializeTraceTree(); 603 void SerializeTraceNode(AllocationTraceNode* node); 604 void SerializeTraceNodeInfos(); 605 void SerializeSamples(); 606 void SerializeString(const unsigned char* s); 607 void SerializeStrings(); 608 609 static const int kEdgeFieldsCount; 610 static const int kNodeFieldsCount; 611 612 HeapSnapshot* snapshot_; 613 HashMap strings_; 614 int next_node_id_; 615 int next_string_id_; 616 OutputStreamWriter* writer_; 617 618 friend class HeapSnapshotJSONSerializerEnumerator; 619 friend class HeapSnapshotJSONSerializerIterator; 620 621 DISALLOW_COPY_AND_ASSIGN(HeapSnapshotJSONSerializer); 622 }; 623 624 625 } // namespace internal 626 } // namespace v8 627 628 #endif // V8_PROFILER_HEAP_SNAPSHOT_GENERATOR_H_ 629