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