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