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 <deque> 9 #include <unordered_map> 10 11 #include "include/v8-profiler.h" 12 #include "src/base/platform/time.h" 13 #include "src/objects.h" 14 #include "src/profiler/strings-storage.h" 15 16 namespace v8 { 17 namespace internal { 18 19 class AllocationTracker; 20 class AllocationTraceNode; 21 class HeapEntry; 22 class HeapIterator; 23 class HeapProfiler; 24 class HeapSnapshot; 25 class SnapshotFiller; 26 27 class HeapGraphEdge BASE_EMBEDDED { 28 public: 29 enum Type { 30 kContextVariable = v8::HeapGraphEdge::kContextVariable, 31 kElement = v8::HeapGraphEdge::kElement, 32 kProperty = v8::HeapGraphEdge::kProperty, 33 kInternal = v8::HeapGraphEdge::kInternal, 34 kHidden = v8::HeapGraphEdge::kHidden, 35 kShortcut = v8::HeapGraphEdge::kShortcut, 36 kWeak = v8::HeapGraphEdge::kWeak 37 }; 38 39 HeapGraphEdge(Type type, const char* name, int from, int to); 40 HeapGraphEdge(Type type, int index, int from, int to); 41 void ReplaceToIndexWithEntry(HeapSnapshot* snapshot); 42 type()43 Type type() const { return TypeField::decode(bit_field_); } index()44 int index() const { 45 DCHECK(type() == kElement || type() == kHidden); 46 return index_; 47 } name()48 const char* name() const { 49 DCHECK(type() == kContextVariable || type() == kProperty || 50 type() == kInternal || type() == kShortcut || type() == kWeak); 51 return name_; 52 } 53 INLINE(HeapEntry* from() const); to()54 HeapEntry* to() const { return to_entry_; } 55 56 INLINE(Isolate* isolate() const); 57 58 private: 59 INLINE(HeapSnapshot* snapshot() const); from_index()60 int from_index() const { return FromIndexField::decode(bit_field_); } 61 62 class TypeField : public BitField<Type, 0, 3> {}; 63 class FromIndexField : public BitField<int, 3, 29> {}; 64 uint32_t bit_field_; 65 union { 66 // During entries population |to_index_| is used for storing the index, 67 // afterwards it is replaced with a pointer to the entry. 68 int to_index_; 69 HeapEntry* to_entry_; 70 }; 71 union { 72 int index_; 73 const char* name_; 74 }; 75 }; 76 77 78 // HeapEntry instances represent an entity from the heap (or a special 79 // virtual node, e.g. root). 80 class HeapEntry BASE_EMBEDDED { 81 public: 82 enum Type { 83 kHidden = v8::HeapGraphNode::kHidden, 84 kArray = v8::HeapGraphNode::kArray, 85 kString = v8::HeapGraphNode::kString, 86 kObject = v8::HeapGraphNode::kObject, 87 kCode = v8::HeapGraphNode::kCode, 88 kClosure = v8::HeapGraphNode::kClosure, 89 kRegExp = v8::HeapGraphNode::kRegExp, 90 kHeapNumber = v8::HeapGraphNode::kHeapNumber, 91 kNative = v8::HeapGraphNode::kNative, 92 kSynthetic = v8::HeapGraphNode::kSynthetic, 93 kConsString = v8::HeapGraphNode::kConsString, 94 kSlicedString = v8::HeapGraphNode::kSlicedString, 95 kSymbol = v8::HeapGraphNode::kSymbol 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_begin() + children_count_++) = edge; 119 } child(int i)120 HeapGraphEdge* child(int i) { return *(children_begin() + i); } 121 INLINE(Isolate* isolate() const); 122 123 void SetIndexedReference( 124 HeapGraphEdge::Type type, int index, HeapEntry* entry); 125 void SetNamedReference( 126 HeapGraphEdge::Type type, const char* name, HeapEntry* entry); 127 128 void Print( 129 const char* prefix, const char* edge_name, int max_depth, int indent); 130 131 private: 132 INLINE(std::deque<HeapGraphEdge*>::iterator children_begin()); 133 INLINE(std::deque<HeapGraphEdge*>::iterator children_end()); 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 std::deque<HeapGraphEdge>& edges() { return edges_; } children()167 std::deque<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 std::deque<HeapGraphEdge> edges_; 196 std::deque<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 ExtractCellReferences(int entry, Cell* cell); 389 void ExtractWeakCellReferences(int entry, WeakCell* weak_cell); 390 void ExtractPropertyCellReferences(int entry, PropertyCell* cell); 391 void ExtractAllocationSiteReferences(int entry, AllocationSite* site); 392 void ExtractJSArrayBufferReferences(int entry, JSArrayBuffer* buffer); 393 void ExtractFixedArrayReferences(int entry, FixedArray* array); 394 void ExtractPropertyReferences(JSObject* js_obj, int entry); 395 void ExtractAccessorPairProperty(JSObject* js_obj, int entry, Name* key, 396 Object* callback_obj, int field_offset = -1); 397 void ExtractElementReferences(JSObject* js_obj, int entry); 398 void ExtractInternalReferences(JSObject* js_obj, int entry); 399 400 bool IsEssentialObject(Object* object); 401 bool IsEssentialHiddenReference(Object* parent, int field_offset); 402 403 void SetContextReference(HeapObject* parent_obj, 404 int parent, 405 String* reference_name, 406 Object* child, 407 int field_offset); 408 void SetNativeBindReference(HeapObject* parent_obj, 409 int parent, 410 const char* reference_name, 411 Object* child); 412 void SetElementReference(HeapObject* parent_obj, 413 int parent, 414 int index, 415 Object* child); 416 void SetInternalReference(HeapObject* parent_obj, 417 int parent, 418 const char* reference_name, 419 Object* child, 420 int field_offset = -1); 421 void SetInternalReference(HeapObject* parent_obj, 422 int parent, 423 int index, 424 Object* child, 425 int field_offset = -1); 426 void SetHiddenReference(HeapObject* parent_obj, int parent, int index, 427 Object* child, int field_offset); 428 void SetWeakReference(HeapObject* parent_obj, 429 int parent, 430 const char* reference_name, 431 Object* child_obj, 432 int field_offset); 433 void SetWeakReference(HeapObject* parent_obj, 434 int parent, 435 int index, 436 Object* child_obj, 437 int field_offset); 438 void SetPropertyReference(HeapObject* parent_obj, 439 int parent, 440 Name* reference_name, 441 Object* child, 442 const char* name_format_string = NULL, 443 int field_offset = -1); 444 void SetDataOrAccessorPropertyReference(PropertyKind kind, 445 JSObject* parent_obj, int parent, 446 Name* reference_name, Object* child, 447 const char* name_format_string = NULL, 448 int field_offset = -1); 449 450 void SetUserGlobalReference(Object* user_global); 451 void SetRootGcRootsReference(); 452 void SetGcRootsReference(VisitorSynchronization::SyncTag tag); 453 void SetGcSubrootReference( 454 VisitorSynchronization::SyncTag tag, bool is_weak, Object* child); 455 const char* GetStrongGcSubrootName(Object* object); 456 void TagObject(Object* obj, const char* tag); 457 void TagFixedArraySubType(const FixedArray* array, 458 FixedArraySubInstanceType type); 459 460 HeapEntry* GetEntry(Object* obj); 461 462 Heap* heap_; 463 HeapSnapshot* snapshot_; 464 StringsStorage* names_; 465 HeapObjectsMap* heap_object_map_; 466 SnapshottingProgressReportingInterface* progress_; 467 SnapshotFiller* filler_; 468 HeapObjectsSet objects_tags_; 469 HeapObjectsSet strong_gc_subroot_names_; 470 HeapObjectsSet user_roots_; 471 std::unordered_map<const FixedArray*, FixedArraySubInstanceType> array_types_; 472 v8::HeapProfiler::ObjectNameResolver* global_object_name_resolver_; 473 474 std::vector<bool> marks_; 475 476 friend class IndexedReferencesExtractor; 477 friend class RootsReferencesExtractor; 478 479 DISALLOW_COPY_AND_ASSIGN(V8HeapExplorer); 480 }; 481 482 483 class NativeGroupRetainedObjectInfo; 484 485 486 // An implementation of retained native objects extractor. 487 class NativeObjectsExplorer { 488 public: 489 NativeObjectsExplorer(HeapSnapshot* snapshot, 490 SnapshottingProgressReportingInterface* progress); 491 virtual ~NativeObjectsExplorer(); 492 int EstimateObjectsCount(); 493 bool IterateAndExtractReferences(SnapshotFiller* filler); 494 495 private: 496 void FillRetainedObjects(); 497 void FillEdges(); 498 List<HeapObject*>* GetListMaybeDisposeInfo(v8::RetainedObjectInfo* info); 499 void SetNativeRootReference(v8::RetainedObjectInfo* info); 500 void SetRootNativeRootsReference(); 501 void SetWrapperNativeReferences(HeapObject* wrapper, 502 v8::RetainedObjectInfo* info); 503 void VisitSubtreeWrapper(Object** p, uint16_t class_id); 504 InfoHash(v8::RetainedObjectInfo * info)505 static uint32_t InfoHash(v8::RetainedObjectInfo* info) { 506 return ComputeIntegerHash(static_cast<uint32_t>(info->GetHash()), 507 v8::internal::kZeroHashSeed); 508 } RetainedInfosMatch(void * key1,void * key2)509 static bool RetainedInfosMatch(void* key1, void* key2) { 510 return key1 == key2 || 511 (reinterpret_cast<v8::RetainedObjectInfo*>(key1))->IsEquivalent( 512 reinterpret_cast<v8::RetainedObjectInfo*>(key2)); 513 } INLINE(static bool StringsMatch (void * key1,void * key2))514 INLINE(static bool StringsMatch(void* key1, void* key2)) { 515 return strcmp(reinterpret_cast<char*>(key1), 516 reinterpret_cast<char*>(key2)) == 0; 517 } 518 519 NativeGroupRetainedObjectInfo* FindOrAddGroupInfo(const char* label); 520 521 Isolate* isolate_; 522 HeapSnapshot* snapshot_; 523 StringsStorage* names_; 524 bool embedder_queried_; 525 HeapObjectsSet in_groups_; 526 // RetainedObjectInfo* -> List<HeapObject*>* 527 base::CustomMatcherHashMap objects_by_info_; 528 base::CustomMatcherHashMap native_groups_; 529 HeapEntriesAllocator* synthetic_entries_allocator_; 530 HeapEntriesAllocator* native_entries_allocator_; 531 // Used during references extraction. 532 SnapshotFiller* filler_; 533 v8::HeapProfiler::RetainerEdges edges_; 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