blob: ecb6919874a6334cfda3d81c6a13a1252e953713 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "heap-profiler.h"
31#include "string-stream.h"
32
33namespace v8 {
34namespace internal {
35
36
37#ifdef ENABLE_LOGGING_AND_PROFILING
38namespace {
39
40// Clusterizer is a set of helper functions for converting
41// object references into clusters.
42class Clusterizer : public AllStatic {
43 public:
44 static JSObjectsCluster Clusterize(HeapObject* obj) {
45 return Clusterize(obj, true);
46 }
47 static void InsertIntoTree(JSObjectsClusterTree* tree,
48 HeapObject* obj, bool fine_grain);
49 static void InsertReferenceIntoTree(JSObjectsClusterTree* tree,
50 const JSObjectsCluster& cluster) {
51 InsertIntoTree(tree, cluster, 0);
52 }
53
54 private:
55 static JSObjectsCluster Clusterize(HeapObject* obj, bool fine_grain);
56 static int CalculateNetworkSize(JSObject* obj);
57 static int GetObjectSize(HeapObject* obj) {
58 return obj->IsJSObject() ?
59 CalculateNetworkSize(JSObject::cast(obj)) : obj->Size();
60 }
61 static void InsertIntoTree(JSObjectsClusterTree* tree,
62 const JSObjectsCluster& cluster, int size);
63};
64
65
66JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) {
67 if (obj->IsJSObject()) {
68 JSObject* js_obj = JSObject::cast(obj);
69 String* constructor = JSObject::cast(js_obj)->constructor_name();
70 // Differentiate Object and Array instances.
71 if (fine_grain && (constructor == Heap::Object_symbol() ||
72 constructor == Heap::Array_symbol())) {
73 return JSObjectsCluster(constructor, obj);
74 } else {
75 return JSObjectsCluster(constructor);
76 }
77 } else if (obj->IsString()) {
78 return JSObjectsCluster(Heap::String_symbol());
79 }
80 return JSObjectsCluster();
81}
82
83
84void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree,
85 HeapObject* obj, bool fine_grain) {
86 JSObjectsCluster cluster = Clusterize(obj, fine_grain);
87 if (cluster.is_null()) return;
88 InsertIntoTree(tree, cluster, GetObjectSize(obj));
89}
90
91
92void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree,
93 const JSObjectsCluster& cluster, int size) {
94 JSObjectsClusterTree::Locator loc;
95 tree->Insert(cluster, &loc);
96 NumberAndSizeInfo number_and_size = loc.value();
97 number_and_size.increment_number(1);
98 number_and_size.increment_bytes(size);
99 loc.set_value(number_and_size);
100}
101
102
103int Clusterizer::CalculateNetworkSize(JSObject* obj) {
104 int size = obj->Size();
105 // If 'properties' and 'elements' are non-empty (thus, non-shared),
106 // take their size into account.
107 if (FixedArray::cast(obj->properties())->length() != 0) {
108 size += obj->properties()->Size();
109 }
110 if (FixedArray::cast(obj->elements())->length() != 0) {
111 size += obj->elements()->Size();
112 }
113 return size;
114}
115
116
117// A helper class for recording back references.
118class ReferencesExtractor : public ObjectVisitor {
119 public:
120 ReferencesExtractor(const JSObjectsCluster& cluster,
121 RetainerHeapProfile* profile)
122 : cluster_(cluster),
123 profile_(profile),
124 inside_array_(false) {
125 }
126
127 void VisitPointer(Object** o) {
128 if ((*o)->IsJSObject() || (*o)->IsString()) {
129 profile_->StoreReference(cluster_, HeapObject::cast(*o));
130 } else if ((*o)->IsFixedArray() && !inside_array_) {
131 // Traverse one level deep for data members that are fixed arrays.
132 // This covers the case of 'elements' and 'properties' of JSObject,
133 // and function contexts.
134 inside_array_ = true;
135 FixedArray::cast(*o)->Iterate(this);
136 inside_array_ = false;
137 }
138 }
139
140 void VisitPointers(Object** start, Object** end) {
141 for (Object** p = start; p < end; p++) VisitPointer(p);
142 }
143
144 private:
145 const JSObjectsCluster& cluster_;
146 RetainerHeapProfile* profile_;
147 bool inside_array_;
148};
149
150
151// A printer interface implementation for the Retainers profile.
152class RetainersPrinter : public RetainerHeapProfile::Printer {
153 public:
154 void PrintRetainers(const JSObjectsCluster& cluster,
155 const StringStream& retainers) {
156 HeapStringAllocator allocator;
157 StringStream stream(&allocator);
158 cluster.Print(&stream);
159 LOG(HeapSampleJSRetainersEvent(
160 *(stream.ToCString()), *(retainers.ToCString())));
161 }
162};
163
164
165// Visitor for printing a cluster tree.
166class ClusterTreePrinter BASE_EMBEDDED {
167 public:
168 explicit ClusterTreePrinter(StringStream* stream) : stream_(stream) {}
169 void Call(const JSObjectsCluster& cluster,
170 const NumberAndSizeInfo& number_and_size) {
171 Print(stream_, cluster, number_and_size);
172 }
173 static void Print(StringStream* stream,
174 const JSObjectsCluster& cluster,
175 const NumberAndSizeInfo& number_and_size);
176
177 private:
178 StringStream* stream_;
179};
180
181
182void ClusterTreePrinter::Print(StringStream* stream,
183 const JSObjectsCluster& cluster,
184 const NumberAndSizeInfo& number_and_size) {
185 stream->Put(',');
186 cluster.Print(stream);
187 stream->Add(";%d", number_and_size.number());
188}
189
190
191// Visitor for printing a retainer tree.
192class SimpleRetainerTreePrinter BASE_EMBEDDED {
193 public:
194 explicit SimpleRetainerTreePrinter(RetainerHeapProfile::Printer* printer)
195 : printer_(printer) {}
196 void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
197
198 private:
199 RetainerHeapProfile::Printer* printer_;
200};
201
202
203void SimpleRetainerTreePrinter::Call(const JSObjectsCluster& cluster,
204 JSObjectsClusterTree* tree) {
205 HeapStringAllocator allocator;
206 StringStream stream(&allocator);
207 ClusterTreePrinter retainers_printer(&stream);
208 tree->ForEach(&retainers_printer);
209 printer_->PrintRetainers(cluster, stream);
210}
211
212
213// Visitor for aggregating references count of equivalent clusters.
214class RetainersAggregator BASE_EMBEDDED {
215 public:
216 RetainersAggregator(ClustersCoarser* coarser, JSObjectsClusterTree* dest_tree)
217 : coarser_(coarser), dest_tree_(dest_tree) {}
218 void Call(const JSObjectsCluster& cluster,
219 const NumberAndSizeInfo& number_and_size);
220
221 private:
222 ClustersCoarser* coarser_;
223 JSObjectsClusterTree* dest_tree_;
224};
225
226
227void RetainersAggregator::Call(const JSObjectsCluster& cluster,
228 const NumberAndSizeInfo& number_and_size) {
229 JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster);
230 if (eq.is_null()) eq = cluster;
231 JSObjectsClusterTree::Locator loc;
232 dest_tree_->Insert(eq, &loc);
233 NumberAndSizeInfo aggregated_number = loc.value();
234 aggregated_number.increment_number(number_and_size.number());
235 loc.set_value(aggregated_number);
236}
237
238
239// Visitor for printing retainers tree. Aggregates equivalent retainer clusters.
240class AggregatingRetainerTreePrinter BASE_EMBEDDED {
241 public:
242 AggregatingRetainerTreePrinter(ClustersCoarser* coarser,
243 RetainerHeapProfile::Printer* printer)
244 : coarser_(coarser), printer_(printer) {}
245 void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
246
247 private:
248 ClustersCoarser* coarser_;
249 RetainerHeapProfile::Printer* printer_;
250};
251
252
253void AggregatingRetainerTreePrinter::Call(const JSObjectsCluster& cluster,
254 JSObjectsClusterTree* tree) {
255 if (!coarser_->GetCoarseEquivalent(cluster).is_null()) return;
256 JSObjectsClusterTree dest_tree_;
257 RetainersAggregator retainers_aggregator(coarser_, &dest_tree_);
258 tree->ForEach(&retainers_aggregator);
259 HeapStringAllocator allocator;
260 StringStream stream(&allocator);
261 ClusterTreePrinter retainers_printer(&stream);
262 dest_tree_.ForEach(&retainers_printer);
263 printer_->PrintRetainers(cluster, stream);
264}
265
266
267// A helper class for building a retainers tree, that aggregates
268// all equivalent clusters.
269class RetainerTreeAggregator BASE_EMBEDDED {
270 public:
271 explicit RetainerTreeAggregator(ClustersCoarser* coarser)
272 : coarser_(coarser) {}
273 void Process(JSObjectsRetainerTree* input_tree) {
274 input_tree->ForEach(this);
275 }
276 void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
277 JSObjectsRetainerTree& output_tree() { return output_tree_; }
278
279 private:
280 ClustersCoarser* coarser_;
281 JSObjectsRetainerTree output_tree_;
282};
283
284
285void RetainerTreeAggregator::Call(const JSObjectsCluster& cluster,
286 JSObjectsClusterTree* tree) {
287 JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster);
288 if (eq.is_null()) return;
289 JSObjectsRetainerTree::Locator loc;
290 if (output_tree_.Insert(eq, &loc)) {
291 loc.set_value(new JSObjectsClusterTree());
292 }
293 RetainersAggregator retainers_aggregator(coarser_, loc.value());
294 tree->ForEach(&retainers_aggregator);
295}
296
297} // namespace
298
299
300const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
301const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
302
303
304ConstructorHeapProfile::ConstructorHeapProfile()
305 : zscope_(DELETE_ON_EXIT) {
306}
307
308
309void ConstructorHeapProfile::Call(const JSObjectsCluster& cluster,
310 const NumberAndSizeInfo& number_and_size) {
311 HeapStringAllocator allocator;
312 StringStream stream(&allocator);
313 cluster.Print(&stream);
314 LOG(HeapSampleJSConstructorEvent(*(stream.ToCString()),
315 number_and_size.number(),
316 number_and_size.bytes()));
317}
318
319
320void ConstructorHeapProfile::CollectStats(HeapObject* obj) {
321 Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false);
322}
323
324
325void ConstructorHeapProfile::PrintStats() {
326 js_objects_info_tree_.ForEach(this);
327}
328
329
330void JSObjectsCluster::Print(StringStream* accumulator) const {
331 ASSERT(!is_null());
332 if (constructor_ == FromSpecialCase(ROOTS)) {
333 accumulator->Add("(roots)");
334 } else if (constructor_ == FromSpecialCase(GLOBAL_PROPERTY)) {
335 accumulator->Add("(global property)");
336 } else if (constructor_ == FromSpecialCase(SELF)) {
337 accumulator->Add("(self)");
338 } else {
339 SmartPointer<char> s_name(
340 constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
341 accumulator->Add("%s", (*s_name)[0] != '\0' ? *s_name : "(anonymous)");
342 if (instance_ != NULL) {
343 accumulator->Add(":%p", static_cast<void*>(instance_));
344 }
345 }
346}
347
348
349void JSObjectsCluster::DebugPrint(StringStream* accumulator) const {
350 if (!is_null()) {
351 Print(accumulator);
352 } else {
353 accumulator->Add("(null cluster)");
354 }
355}
356
357
358inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs(
359 const JSObjectsCluster& cluster_)
360 : cluster(cluster_), refs(kInitialBackrefsListCapacity) {
361}
362
363
364inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs(
365 const ClustersCoarser::ClusterBackRefs& src)
366 : cluster(src.cluster), refs(src.refs.capacity()) {
367 refs.AddAll(src.refs);
368}
369
370
371inline ClustersCoarser::ClusterBackRefs&
372 ClustersCoarser::ClusterBackRefs::operator=(
373 const ClustersCoarser::ClusterBackRefs& src) {
374 if (this == &src) return *this;
375 cluster = src.cluster;
376 refs.Clear();
377 refs.AddAll(src.refs);
378 return *this;
379}
380
381
382inline int ClustersCoarser::ClusterBackRefs::Compare(
383 const ClustersCoarser::ClusterBackRefs& a,
384 const ClustersCoarser::ClusterBackRefs& b) {
385 int cmp = JSObjectsCluster::CompareConstructors(a.cluster, b.cluster);
386 if (cmp != 0) return cmp;
387 if (a.refs.length() < b.refs.length()) return -1;
388 if (a.refs.length() > b.refs.length()) return 1;
389 for (int i = 0; i < a.refs.length(); ++i) {
390 int cmp = JSObjectsCluster::Compare(a.refs[i], b.refs[i]);
391 if (cmp != 0) return cmp;
392 }
393 return 0;
394}
395
396
397ClustersCoarser::ClustersCoarser()
398 : zscope_(DELETE_ON_EXIT),
399 sim_list_(ClustersCoarser::kInitialSimilarityListCapacity),
400 current_pair_(NULL),
401 current_set_(NULL),
402 self_(NULL) {
403}
404
405
406void ClustersCoarser::Call(const JSObjectsCluster& cluster,
407 JSObjectsClusterTree* tree) {
408 if (!cluster.can_be_coarsed()) return;
409 ClusterBackRefs pair(cluster);
410 ASSERT(current_pair_ == NULL);
411 current_pair_ = &pair;
412 current_set_ = new JSObjectsRetainerTree();
413 self_ = &cluster;
414 tree->ForEach(this);
415 sim_list_.Add(pair);
416 current_pair_ = NULL;
417 current_set_ = NULL;
418 self_ = NULL;
419}
420
421
422void ClustersCoarser::Call(const JSObjectsCluster& cluster,
423 const NumberAndSizeInfo& number_and_size) {
424 ASSERT(current_pair_ != NULL);
425 ASSERT(current_set_ != NULL);
426 ASSERT(self_ != NULL);
427 JSObjectsRetainerTree::Locator loc;
428 if (JSObjectsCluster::Compare(*self_, cluster) == 0) {
429 current_pair_->refs.Add(JSObjectsCluster(JSObjectsCluster::SELF));
430 return;
431 }
432 JSObjectsCluster eq = GetCoarseEquivalent(cluster);
433 if (!eq.is_null()) {
434 if (current_set_->Find(eq, &loc)) return;
435 current_pair_->refs.Add(eq);
436 current_set_->Insert(eq, &loc);
437 } else {
438 current_pair_->refs.Add(cluster);
439 }
440}
441
442
443void ClustersCoarser::Process(JSObjectsRetainerTree* tree) {
444 int last_eq_clusters = -1;
445 for (int i = 0; i < kMaxPassesCount; ++i) {
446 sim_list_.Clear();
447 const int curr_eq_clusters = DoProcess(tree);
448 // If no new cluster equivalents discovered, abort processing.
449 if (last_eq_clusters == curr_eq_clusters) break;
450 last_eq_clusters = curr_eq_clusters;
451 }
452}
453
454
455int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) {
456 tree->ForEach(this);
457 sim_list_.Iterate(ClusterBackRefs::SortRefsIterator);
458 sim_list_.Sort(ClusterBackRefsCmp);
459 return FillEqualityTree();
460}
461
462
463JSObjectsCluster ClustersCoarser::GetCoarseEquivalent(
464 const JSObjectsCluster& cluster) {
465 if (!cluster.can_be_coarsed()) return JSObjectsCluster();
466 EqualityTree::Locator loc;
467 return eq_tree_.Find(cluster, &loc) ? loc.value() : JSObjectsCluster();
468}
469
470
471bool ClustersCoarser::HasAnEquivalent(const JSObjectsCluster& cluster) {
472 // Return true for coarsible clusters that have a non-identical equivalent.
473 if (!cluster.can_be_coarsed()) return false;
474 JSObjectsCluster eq = GetCoarseEquivalent(cluster);
475 return !eq.is_null() && JSObjectsCluster::Compare(cluster, eq) != 0;
476}
477
478
479int ClustersCoarser::FillEqualityTree() {
480 int eq_clusters_count = 0;
481 int eq_to = 0;
482 bool first_added = false;
483 for (int i = 1; i < sim_list_.length(); ++i) {
484 if (ClusterBackRefs::Compare(sim_list_[i], sim_list_[eq_to]) == 0) {
485 EqualityTree::Locator loc;
486 if (!first_added) {
487 // Add self-equivalence, if we have more than one item in this
488 // equivalence class.
489 eq_tree_.Insert(sim_list_[eq_to].cluster, &loc);
490 loc.set_value(sim_list_[eq_to].cluster);
491 first_added = true;
492 }
493 eq_tree_.Insert(sim_list_[i].cluster, &loc);
494 loc.set_value(sim_list_[eq_to].cluster);
495 ++eq_clusters_count;
496 } else {
497 eq_to = i;
498 first_added = false;
499 }
500 }
501 return eq_clusters_count;
502}
503
504
505const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey;
506const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue;
507const JSObjectsRetainerTreeConfig::Key JSObjectsRetainerTreeConfig::kNoKey;
508const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue =
509 NULL;
510
511
512RetainerHeapProfile::RetainerHeapProfile()
513 : zscope_(DELETE_ON_EXIT) {
514 JSObjectsCluster roots(JSObjectsCluster::ROOTS);
515 ReferencesExtractor extractor(roots, this);
516 Heap::IterateRoots(&extractor);
517}
518
519
520void RetainerHeapProfile::StoreReference(const JSObjectsCluster& cluster,
521 HeapObject* ref) {
522 JSObjectsCluster ref_cluster = Clusterizer::Clusterize(ref);
523 JSObjectsRetainerTree::Locator ref_loc;
524 if (retainers_tree_.Insert(ref_cluster, &ref_loc)) {
525 ref_loc.set_value(new JSObjectsClusterTree());
526 }
527 JSObjectsClusterTree* referenced_by = ref_loc.value();
528 Clusterizer::InsertReferenceIntoTree(referenced_by, cluster);
529}
530
531
532void RetainerHeapProfile::CollectStats(HeapObject* obj) {
533 if (obj->IsJSObject()) {
534 const JSObjectsCluster cluster = Clusterizer::Clusterize(obj);
535 ReferencesExtractor extractor(cluster, this);
536 obj->Iterate(&extractor);
537 } else if (obj->IsJSGlobalPropertyCell()) {
538 JSObjectsCluster global_prop(JSObjectsCluster::GLOBAL_PROPERTY);
539 ReferencesExtractor extractor(global_prop, this);
540 obj->Iterate(&extractor);
541 }
542}
543
544
545void RetainerHeapProfile::DebugPrintStats(
546 RetainerHeapProfile::Printer* printer) {
547 coarser_.Process(&retainers_tree_);
548 // Print clusters that have no equivalents, aggregating their retainers.
549 AggregatingRetainerTreePrinter agg_printer(&coarser_, printer);
550 retainers_tree_.ForEach(&agg_printer);
551 // Now aggregate clusters that have equivalents...
552 RetainerTreeAggregator aggregator(&coarser_);
553 aggregator.Process(&retainers_tree_);
554 // ...and print them.
555 SimpleRetainerTreePrinter s_printer(printer);
556 aggregator.output_tree().ForEach(&s_printer);
557}
558
559
560void RetainerHeapProfile::PrintStats() {
561 RetainersPrinter printer;
562 DebugPrintStats(&printer);
563}
564
565
566//
567// HeapProfiler class implementation.
568//
569void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) {
570 InstanceType type = obj->map()->instance_type();
571 ASSERT(0 <= type && type <= LAST_TYPE);
572 info[type].increment_number(1);
573 info[type].increment_bytes(obj->Size());
574}
575
576
577void HeapProfiler::WriteSample() {
578 LOG(HeapSampleBeginEvent("Heap", "allocated"));
579 LOG(HeapSampleStats(
580 "Heap", "allocated", Heap::Capacity(), Heap::SizeOfObjects()));
581
582 HistogramInfo info[LAST_TYPE+1];
583#define DEF_TYPE_NAME(name) info[name].set_name(#name);
584 INSTANCE_TYPE_LIST(DEF_TYPE_NAME)
585#undef DEF_TYPE_NAME
586
587 ConstructorHeapProfile js_cons_profile;
588 RetainerHeapProfile js_retainer_profile;
589 HeapIterator iterator;
590 while (iterator.has_next()) {
591 HeapObject* obj = iterator.next();
592 CollectStats(obj, info);
593 js_cons_profile.CollectStats(obj);
594 js_retainer_profile.CollectStats(obj);
595 }
596
597 // Lump all the string types together.
598 int string_number = 0;
599 int string_bytes = 0;
600#define INCREMENT_SIZE(type, size, name, camel_name) \
601 string_number += info[type].number(); \
602 string_bytes += info[type].bytes();
603 STRING_TYPE_LIST(INCREMENT_SIZE)
604#undef INCREMENT_SIZE
605 if (string_bytes > 0) {
606 LOG(HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes));
607 }
608
609 for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) {
610 if (info[i].bytes() > 0) {
611 LOG(HeapSampleItemEvent(info[i].name(), info[i].number(),
612 info[i].bytes()));
613 }
614 }
615
616 js_cons_profile.PrintStats();
617 js_retainer_profile.PrintStats();
618
619 LOG(HeapSampleEndEvent("Heap", "allocated"));
620}
621
622
623#endif // ENABLE_LOGGING_AND_PROFILING
624
625
626} } // namespace v8::internal