• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2010 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 // Tests of profiles generator and utilities.
29 
30 #include "src/v8.h"
31 
32 #include "include/v8-profiler.h"
33 #include "src/cpu-profiler-inl.h"
34 #include "src/platform.h"
35 #include "src/smart-pointers.h"
36 #include "src/utils.h"
37 #include "test/cctest/cctest.h"
38 #include "test/cctest/profiler-extension.h"
39 using i::CodeEntry;
40 using i::CpuProfile;
41 using i::CpuProfiler;
42 using i::CpuProfilesCollection;
43 using i::Heap;
44 using i::ProfileGenerator;
45 using i::ProfileNode;
46 using i::ProfilerEventsProcessor;
47 using i::ScopedVector;
48 using i::SmartPointer;
49 using i::TimeDelta;
50 using i::Vector;
51 
52 
TEST(StartStop)53 TEST(StartStop) {
54   i::Isolate* isolate = CcTest::i_isolate();
55   CpuProfilesCollection profiles(isolate->heap());
56   ProfileGenerator generator(&profiles);
57   SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
58           &generator, NULL, TimeDelta::FromMicroseconds(100)));
59   processor->Start();
60   processor->StopSynchronously();
61 }
62 
63 
EnqueueTickSampleEvent(ProfilerEventsProcessor * proc,i::Address frame1,i::Address frame2=NULL,i::Address frame3=NULL)64 static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
65                                    i::Address frame1,
66                                    i::Address frame2 = NULL,
67                                    i::Address frame3 = NULL) {
68   i::TickSample* sample = proc->StartTickSample();
69   sample->pc = frame1;
70   sample->tos = frame1;
71   sample->frames_count = 0;
72   if (frame2 != NULL) {
73     sample->stack[0] = frame2;
74     sample->frames_count = 1;
75   }
76   if (frame3 != NULL) {
77     sample->stack[1] = frame3;
78     sample->frames_count = 2;
79   }
80   proc->FinishTickSample();
81 }
82 
83 namespace {
84 
85 class TestSetup {
86  public:
TestSetup()87   TestSetup()
88       : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
89     i::FLAG_prof_browser_mode = false;
90   }
91 
~TestSetup()92   ~TestSetup() {
93     i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
94   }
95 
96  private:
97   bool old_flag_prof_browser_mode_;
98 };
99 
100 }  // namespace
101 
102 
CreateCode(LocalContext * env)103 i::Code* CreateCode(LocalContext* env) {
104   static int counter = 0;
105   i::EmbeddedVector<char, 256> script;
106   i::EmbeddedVector<char, 32> name;
107 
108   i::SNPrintF(name, "function_%d", ++counter);
109   const char* name_start = name.start();
110   i::SNPrintF(script,
111       "function %s() {\n"
112            "var counter = 0;\n"
113            "for (var i = 0; i < %d; ++i) counter += i;\n"
114            "return '%s_' + counter;\n"
115        "}\n"
116        "%s();\n", name_start, counter, name_start, name_start);
117   CompileRun(script.start());
118   i::Handle<i::JSFunction> fun = v8::Utils::OpenHandle(
119       *v8::Local<v8::Function>::Cast(
120           (*env)->Global()->Get(v8_str(name_start))));
121   return fun->code();
122 }
123 
124 
TEST(CodeEvents)125 TEST(CodeEvents) {
126   CcTest::InitializeVM();
127   LocalContext env;
128   i::Isolate* isolate = CcTest::i_isolate();
129   i::Factory* factory = isolate->factory();
130   TestSetup test_setup;
131 
132   i::HandleScope scope(isolate);
133 
134   i::Code* aaa_code = CreateCode(&env);
135   i::Code* comment_code = CreateCode(&env);
136   i::Code* args5_code = CreateCode(&env);
137   i::Code* comment2_code = CreateCode(&env);
138   i::Code* moved_code = CreateCode(&env);
139   i::Code* args3_code = CreateCode(&env);
140   i::Code* args4_code = CreateCode(&env);
141 
142   CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
143   profiles->StartProfiling("", false);
144   ProfileGenerator generator(profiles);
145   SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
146           &generator, NULL, TimeDelta::FromMicroseconds(100)));
147   processor->Start();
148   CpuProfiler profiler(isolate, profiles, &generator, processor.get());
149 
150   // Enqueue code creation events.
151   const char* aaa_str = "aaa";
152   i::Handle<i::String> aaa_name = factory->NewStringFromAsciiChecked(aaa_str);
153   profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code, *aaa_name);
154   profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code, "comment");
155   profiler.CodeCreateEvent(i::Logger::STUB_TAG, args5_code, 5);
156   profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code, "comment2");
157   profiler.CodeMoveEvent(comment2_code->address(), moved_code->address());
158   profiler.CodeCreateEvent(i::Logger::STUB_TAG, args3_code, 3);
159   profiler.CodeCreateEvent(i::Logger::STUB_TAG, args4_code, 4);
160 
161   // Enqueue a tick event to enable code events processing.
162   EnqueueTickSampleEvent(processor.get(), aaa_code->address());
163 
164   processor->StopSynchronously();
165 
166   // Check the state of profile generator.
167   CodeEntry* aaa = generator.code_map()->FindEntry(aaa_code->address());
168   CHECK_NE(NULL, aaa);
169   CHECK_EQ(aaa_str, aaa->name());
170 
171   CodeEntry* comment = generator.code_map()->FindEntry(comment_code->address());
172   CHECK_NE(NULL, comment);
173   CHECK_EQ("comment", comment->name());
174 
175   CodeEntry* args5 = generator.code_map()->FindEntry(args5_code->address());
176   CHECK_NE(NULL, args5);
177   CHECK_EQ("5", args5->name());
178 
179   CHECK_EQ(NULL, generator.code_map()->FindEntry(comment2_code->address()));
180 
181   CodeEntry* comment2 = generator.code_map()->FindEntry(moved_code->address());
182   CHECK_NE(NULL, comment2);
183   CHECK_EQ("comment2", comment2->name());
184 }
185 
186 
187 template<typename T>
CompareProfileNodes(const T * p1,const T * p2)188 static int CompareProfileNodes(const T* p1, const T* p2) {
189   return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
190 }
191 
192 
TEST(TickEvents)193 TEST(TickEvents) {
194   TestSetup test_setup;
195   LocalContext env;
196   i::Isolate* isolate = CcTest::i_isolate();
197   i::HandleScope scope(isolate);
198 
199   i::Code* frame1_code = CreateCode(&env);
200   i::Code* frame2_code = CreateCode(&env);
201   i::Code* frame3_code = CreateCode(&env);
202 
203   CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
204   profiles->StartProfiling("", false);
205   ProfileGenerator generator(profiles);
206   SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
207           &generator, NULL, TimeDelta::FromMicroseconds(100)));
208   processor->Start();
209   CpuProfiler profiler(isolate, profiles, &generator, processor.get());
210 
211   profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
212   profiler.CodeCreateEvent(i::Logger::STUB_TAG, frame2_code, 5);
213   profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame3_code, "ddd");
214 
215   EnqueueTickSampleEvent(processor.get(), frame1_code->instruction_start());
216   EnqueueTickSampleEvent(
217       processor.get(),
218       frame2_code->instruction_start() + frame2_code->ExecutableSize() / 2,
219       frame1_code->instruction_start() + frame2_code->ExecutableSize() / 2);
220   EnqueueTickSampleEvent(
221       processor.get(),
222       frame3_code->instruction_end() - 1,
223       frame2_code->instruction_end() - 1,
224       frame1_code->instruction_end() - 1);
225 
226   processor->StopSynchronously();
227   CpuProfile* profile = profiles->StopProfiling("");
228   CHECK_NE(NULL, profile);
229 
230   // Check call trees.
231   const i::List<ProfileNode*>* top_down_root_children =
232       profile->top_down()->root()->children();
233   CHECK_EQ(1, top_down_root_children->length());
234   CHECK_EQ("bbb", top_down_root_children->last()->entry()->name());
235   const i::List<ProfileNode*>* top_down_bbb_children =
236       top_down_root_children->last()->children();
237   CHECK_EQ(1, top_down_bbb_children->length());
238   CHECK_EQ("5", top_down_bbb_children->last()->entry()->name());
239   const i::List<ProfileNode*>* top_down_stub_children =
240       top_down_bbb_children->last()->children();
241   CHECK_EQ(1, top_down_stub_children->length());
242   CHECK_EQ("ddd", top_down_stub_children->last()->entry()->name());
243   const i::List<ProfileNode*>* top_down_ddd_children =
244       top_down_stub_children->last()->children();
245   CHECK_EQ(0, top_down_ddd_children->length());
246 }
247 
248 
249 // http://crbug/51594
250 // This test must not crash.
TEST(CrashIfStoppingLastNonExistentProfile)251 TEST(CrashIfStoppingLastNonExistentProfile) {
252   CcTest::InitializeVM();
253   TestSetup test_setup;
254   CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
255   profiler->StartProfiling("1");
256   profiler->StopProfiling("2");
257   profiler->StartProfiling("1");
258   profiler->StopProfiling("");
259 }
260 
261 
262 // http://code.google.com/p/v8/issues/detail?id=1398
263 // Long stacks (exceeding max frames limit) must not be erased.
TEST(Issue1398)264 TEST(Issue1398) {
265   TestSetup test_setup;
266   LocalContext env;
267   i::Isolate* isolate = CcTest::i_isolate();
268   i::HandleScope scope(isolate);
269 
270   i::Code* code = CreateCode(&env);
271 
272   CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
273   profiles->StartProfiling("", false);
274   ProfileGenerator generator(profiles);
275   SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
276           &generator, NULL, TimeDelta::FromMicroseconds(100)));
277   processor->Start();
278   CpuProfiler profiler(isolate, profiles, &generator, processor.get());
279 
280   profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
281 
282   i::TickSample* sample = processor->StartTickSample();
283   sample->pc = code->address();
284   sample->tos = 0;
285   sample->frames_count = i::TickSample::kMaxFramesCount;
286   for (int i = 0; i < sample->frames_count; ++i) {
287     sample->stack[i] = code->address();
288   }
289   processor->FinishTickSample();
290 
291   processor->StopSynchronously();
292   CpuProfile* profile = profiles->StopProfiling("");
293   CHECK_NE(NULL, profile);
294 
295   int actual_depth = 0;
296   const ProfileNode* node = profile->top_down()->root();
297   while (node->children()->length() > 0) {
298     node = node->children()->last();
299     ++actual_depth;
300   }
301 
302   CHECK_EQ(1 + i::TickSample::kMaxFramesCount, actual_depth);  // +1 for PC.
303 }
304 
305 
TEST(DeleteAllCpuProfiles)306 TEST(DeleteAllCpuProfiles) {
307   CcTest::InitializeVM();
308   TestSetup test_setup;
309   CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
310   CHECK_EQ(0, profiler->GetProfilesCount());
311   profiler->DeleteAllProfiles();
312   CHECK_EQ(0, profiler->GetProfilesCount());
313 
314   profiler->StartProfiling("1");
315   profiler->StopProfiling("1");
316   CHECK_EQ(1, profiler->GetProfilesCount());
317   profiler->DeleteAllProfiles();
318   CHECK_EQ(0, profiler->GetProfilesCount());
319   profiler->StartProfiling("1");
320   profiler->StartProfiling("2");
321   profiler->StopProfiling("2");
322   profiler->StopProfiling("1");
323   CHECK_EQ(2, profiler->GetProfilesCount());
324   profiler->DeleteAllProfiles();
325   CHECK_EQ(0, profiler->GetProfilesCount());
326 
327   // Test profiling cancellation by the 'delete' command.
328   profiler->StartProfiling("1");
329   profiler->StartProfiling("2");
330   CHECK_EQ(0, profiler->GetProfilesCount());
331   profiler->DeleteAllProfiles();
332   CHECK_EQ(0, profiler->GetProfilesCount());
333 }
334 
335 
FindCpuProfile(v8::CpuProfiler * v8profiler,const v8::CpuProfile * v8profile)336 static bool FindCpuProfile(v8::CpuProfiler* v8profiler,
337                            const v8::CpuProfile* v8profile) {
338   i::CpuProfiler* profiler = reinterpret_cast<i::CpuProfiler*>(v8profiler);
339   const i::CpuProfile* profile =
340       reinterpret_cast<const i::CpuProfile*>(v8profile);
341   int length = profiler->GetProfilesCount();
342   for (int i = 0; i < length; i++) {
343     if (profile == profiler->GetProfile(i))
344       return true;
345   }
346   return false;
347 }
348 
349 
TEST(DeleteCpuProfile)350 TEST(DeleteCpuProfile) {
351   LocalContext env;
352   v8::HandleScope scope(env->GetIsolate());
353   v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
354   i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(cpu_profiler);
355 
356   CHECK_EQ(0, iprofiler->GetProfilesCount());
357   v8::Local<v8::String> name1 = v8::String::NewFromUtf8(env->GetIsolate(), "1");
358   cpu_profiler->StartProfiling(name1);
359   v8::CpuProfile* p1 = cpu_profiler->StopProfiling(name1);
360   CHECK_NE(NULL, p1);
361   CHECK_EQ(1, iprofiler->GetProfilesCount());
362   CHECK(FindCpuProfile(cpu_profiler, p1));
363   p1->Delete();
364   CHECK_EQ(0, iprofiler->GetProfilesCount());
365 
366   v8::Local<v8::String> name2 = v8::String::NewFromUtf8(env->GetIsolate(), "2");
367   cpu_profiler->StartProfiling(name2);
368   v8::CpuProfile* p2 = cpu_profiler->StopProfiling(name2);
369   CHECK_NE(NULL, p2);
370   CHECK_EQ(1, iprofiler->GetProfilesCount());
371   CHECK(FindCpuProfile(cpu_profiler, p2));
372   v8::Local<v8::String> name3 = v8::String::NewFromUtf8(env->GetIsolate(), "3");
373   cpu_profiler->StartProfiling(name3);
374   v8::CpuProfile* p3 = cpu_profiler->StopProfiling(name3);
375   CHECK_NE(NULL, p3);
376   CHECK_EQ(2, iprofiler->GetProfilesCount());
377   CHECK_NE(p2, p3);
378   CHECK(FindCpuProfile(cpu_profiler, p3));
379   CHECK(FindCpuProfile(cpu_profiler, p2));
380   p2->Delete();
381   CHECK_EQ(1, iprofiler->GetProfilesCount());
382   CHECK(!FindCpuProfile(cpu_profiler, p2));
383   CHECK(FindCpuProfile(cpu_profiler, p3));
384   p3->Delete();
385   CHECK_EQ(0, iprofiler->GetProfilesCount());
386 }
387 
388 
TEST(ProfileStartEndTime)389 TEST(ProfileStartEndTime) {
390   LocalContext env;
391   v8::HandleScope scope(env->GetIsolate());
392   v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
393 
394   v8::Local<v8::String> profile_name =
395       v8::String::NewFromUtf8(env->GetIsolate(), "test");
396   cpu_profiler->StartProfiling(profile_name);
397   const v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
398   CHECK(profile->GetStartTime() <= profile->GetEndTime());
399 }
400 
401 
RunProfiler(v8::Handle<v8::Context> env,v8::Handle<v8::Function> function,v8::Handle<v8::Value> argv[],int argc,unsigned min_js_samples,bool collect_samples=false)402 static v8::CpuProfile* RunProfiler(
403     v8::Handle<v8::Context> env, v8::Handle<v8::Function> function,
404     v8::Handle<v8::Value> argv[], int argc,
405     unsigned min_js_samples, bool collect_samples = false) {
406   v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
407   v8::Local<v8::String> profile_name =
408       v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
409 
410   cpu_profiler->StartProfiling(profile_name, collect_samples);
411 
412   i::Sampler* sampler =
413       reinterpret_cast<i::Isolate*>(env->GetIsolate())->logger()->sampler();
414   sampler->StartCountingSamples();
415   do {
416     function->Call(env->Global(), argc, argv);
417   } while (sampler->js_and_external_sample_count() < min_js_samples);
418 
419   v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
420 
421   CHECK_NE(NULL, profile);
422   // Dump collected profile to have a better diagnostic in case of failure.
423   reinterpret_cast<i::CpuProfile*>(profile)->Print();
424 
425   return profile;
426 }
427 
428 
ContainsString(v8::Handle<v8::String> string,const Vector<v8::Handle<v8::String>> & vector)429 static bool ContainsString(v8::Handle<v8::String> string,
430                            const Vector<v8::Handle<v8::String> >& vector) {
431   for (int i = 0; i < vector.length(); i++) {
432     if (string->Equals(vector[i]))
433       return true;
434   }
435   return false;
436 }
437 
438 
CheckChildrenNames(const v8::CpuProfileNode * node,const Vector<v8::Handle<v8::String>> & names)439 static void CheckChildrenNames(const v8::CpuProfileNode* node,
440                                const Vector<v8::Handle<v8::String> >& names) {
441   int count = node->GetChildrenCount();
442   for (int i = 0; i < count; i++) {
443     v8::Handle<v8::String> name = node->GetChild(i)->GetFunctionName();
444     CHECK(ContainsString(name, names));
445     // Check that there are no duplicates.
446     for (int j = 0; j < count; j++) {
447       if (j == i) continue;
448       CHECK_NE(name, node->GetChild(j)->GetFunctionName());
449     }
450   }
451 }
452 
453 
FindChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)454 static const v8::CpuProfileNode* FindChild(v8::Isolate* isolate,
455                                            const v8::CpuProfileNode* node,
456                                            const char* name) {
457   int count = node->GetChildrenCount();
458   v8::Handle<v8::String> nameHandle = v8::String::NewFromUtf8(isolate, name);
459   for (int i = 0; i < count; i++) {
460     const v8::CpuProfileNode* child = node->GetChild(i);
461     if (nameHandle->Equals(child->GetFunctionName())) return child;
462   }
463   return NULL;
464 }
465 
466 
GetChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)467 static const v8::CpuProfileNode* GetChild(v8::Isolate* isolate,
468                                           const v8::CpuProfileNode* node,
469                                           const char* name) {
470   const v8::CpuProfileNode* result = FindChild(isolate, node, name);
471   if (!result) {
472     char buffer[100];
473     i::SNPrintF(Vector<char>(buffer, ARRAY_SIZE(buffer)),
474                 "Failed to GetChild: %s", name);
475     FATAL(buffer);
476   }
477   return result;
478 }
479 
480 
CheckSimpleBranch(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * names[],int length)481 static void CheckSimpleBranch(v8::Isolate* isolate,
482                               const v8::CpuProfileNode* node,
483                               const char* names[], int length) {
484   for (int i = 0; i < length; i++) {
485     const char* name = names[i];
486     node = GetChild(isolate, node, name);
487     int expectedChildrenCount = (i == length - 1) ? 0 : 1;
488     CHECK_EQ(expectedChildrenCount, node->GetChildrenCount());
489   }
490 }
491 
492 
493 static const char* cpu_profiler_test_source = "function loop(timeout) {\n"
494 "  this.mmm = 0;\n"
495 "  var start = Date.now();\n"
496 "  while (Date.now() - start < timeout) {\n"
497 "    var n = 100*1000;\n"
498 "    while(n > 1) {\n"
499 "      n--;\n"
500 "      this.mmm += n * n * n;\n"
501 "    }\n"
502 "  }\n"
503 "}\n"
504 "function delay() { try { loop(10); } catch(e) { } }\n"
505 "function bar() { delay(); }\n"
506 "function baz() { delay(); }\n"
507 "function foo() {\n"
508 "    try {\n"
509 "       delay();\n"
510 "       bar();\n"
511 "       delay();\n"
512 "       baz();\n"
513 "    } catch (e) { }\n"
514 "}\n"
515 "function start(timeout) {\n"
516 "  var start = Date.now();\n"
517 "  do {\n"
518 "    foo();\n"
519 "    var duration = Date.now() - start;\n"
520 "  } while (duration < timeout);\n"
521 "  return duration;\n"
522 "}\n";
523 
524 
525 // Check that the profile tree for the script above will look like the
526 // following:
527 //
528 // [Top down]:
529 //  1062     0   (root) [-1]
530 //  1054     0    start [-1]
531 //  1054     1      foo [-1]
532 //   265     0        baz [-1]
533 //   265     1          delay [-1]
534 //   264   264            loop [-1]
535 //   525     3        delay [-1]
536 //   522   522          loop [-1]
537 //   263     0        bar [-1]
538 //   263     1          delay [-1]
539 //   262   262            loop [-1]
540 //     2     2    (program) [-1]
541 //     6     6    (garbage collector) [-1]
TEST(CollectCpuProfile)542 TEST(CollectCpuProfile) {
543   LocalContext env;
544   v8::HandleScope scope(env->GetIsolate());
545 
546   v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
547                                               cpu_profiler_test_source))->Run();
548   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
549       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
550 
551   int32_t profiling_interval_ms = 200;
552   v8::Handle<v8::Value> args[] = {
553     v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
554   };
555   v8::CpuProfile* profile =
556       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
557   function->Call(env->Global(), ARRAY_SIZE(args), args);
558 
559   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
560 
561   ScopedVector<v8::Handle<v8::String> > names(3);
562   names[0] = v8::String::NewFromUtf8(
563       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
564   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
565                                      ProfileGenerator::kProgramEntryName);
566   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
567   CheckChildrenNames(root, names);
568 
569   const v8::CpuProfileNode* startNode =
570       GetChild(env->GetIsolate(), root, "start");
571   CHECK_EQ(1, startNode->GetChildrenCount());
572 
573   const v8::CpuProfileNode* fooNode =
574       GetChild(env->GetIsolate(), startNode, "foo");
575   CHECK_EQ(3, fooNode->GetChildrenCount());
576 
577   const char* barBranch[] = { "bar", "delay", "loop" };
578   CheckSimpleBranch(env->GetIsolate(), fooNode, barBranch,
579                     ARRAY_SIZE(barBranch));
580   const char* bazBranch[] = { "baz", "delay", "loop" };
581   CheckSimpleBranch(env->GetIsolate(), fooNode, bazBranch,
582                     ARRAY_SIZE(bazBranch));
583   const char* delayBranch[] = { "delay", "loop" };
584   CheckSimpleBranch(env->GetIsolate(), fooNode, delayBranch,
585                     ARRAY_SIZE(delayBranch));
586 
587   profile->Delete();
588 }
589 
590 
591 static const char* hot_deopt_no_frame_entry_test_source =
592 "function foo(a, b) {\n"
593 "    try {\n"
594 "      return a + b;\n"
595 "    } catch (e) { }\n"
596 "}\n"
597 "function start(timeout) {\n"
598 "  var start = Date.now();\n"
599 "  do {\n"
600 "    for (var i = 1; i < 1000; ++i) foo(1, i);\n"
601 "    var duration = Date.now() - start;\n"
602 "  } while (duration < timeout);\n"
603 "  return duration;\n"
604 "}\n";
605 
606 // Check that the profile tree for the script above will look like the
607 // following:
608 //
609 // [Top down]:
610 //  1062     0  (root) [-1]
611 //  1054     0    start [-1]
612 //  1054     1      foo [-1]
613 //     2     2    (program) [-1]
614 //     6     6    (garbage collector) [-1]
615 //
616 // The test checks no FP ranges are present in a deoptimized funcion.
617 // If 'foo' has no ranges the samples falling into the prologue will miss the
618 // 'start' function on the stack, so 'foo' will be attached to the (root).
TEST(HotDeoptNoFrameEntry)619 TEST(HotDeoptNoFrameEntry) {
620   LocalContext env;
621   v8::HandleScope scope(env->GetIsolate());
622 
623   v8::Script::Compile(v8::String::NewFromUtf8(
624       env->GetIsolate(),
625       hot_deopt_no_frame_entry_test_source))->Run();
626   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
627       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
628 
629   int32_t profiling_interval_ms = 200;
630   v8::Handle<v8::Value> args[] = {
631     v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
632   };
633   v8::CpuProfile* profile =
634       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
635   function->Call(env->Global(), ARRAY_SIZE(args), args);
636 
637   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
638 
639   ScopedVector<v8::Handle<v8::String> > names(3);
640   names[0] = v8::String::NewFromUtf8(
641       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
642   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
643                                      ProfileGenerator::kProgramEntryName);
644   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
645   CheckChildrenNames(root, names);
646 
647   const v8::CpuProfileNode* startNode =
648       GetChild(env->GetIsolate(), root, "start");
649   CHECK_EQ(1, startNode->GetChildrenCount());
650 
651   GetChild(env->GetIsolate(), startNode, "foo");
652 
653   profile->Delete();
654 }
655 
656 
TEST(CollectCpuProfileSamples)657 TEST(CollectCpuProfileSamples) {
658   LocalContext env;
659   v8::HandleScope scope(env->GetIsolate());
660 
661   v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
662                                               cpu_profiler_test_source))->Run();
663   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
664       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
665 
666   int32_t profiling_interval_ms = 200;
667   v8::Handle<v8::Value> args[] = {
668     v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
669   };
670   v8::CpuProfile* profile =
671       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200, true);
672 
673   CHECK_LE(200, profile->GetSamplesCount());
674   uint64_t end_time = profile->GetEndTime();
675   uint64_t current_time = profile->GetStartTime();
676   CHECK_LE(current_time, end_time);
677   for (int i = 0; i < profile->GetSamplesCount(); i++) {
678     CHECK_NE(NULL, profile->GetSample(i));
679     uint64_t timestamp = profile->GetSampleTimestamp(i);
680     CHECK_LE(current_time, timestamp);
681     CHECK_LE(timestamp, end_time);
682     current_time = timestamp;
683   }
684 
685   profile->Delete();
686 }
687 
688 
689 static const char* cpu_profiler_test_source2 = "function loop() {}\n"
690 "function delay() { loop(); }\n"
691 "function start(count) {\n"
692 "  var k = 0;\n"
693 "  do {\n"
694 "    delay();\n"
695 "  } while (++k < count*100*1000);\n"
696 "}\n";
697 
698 // Check that the profile tree doesn't contain unexpected traces:
699 //  - 'loop' can be called only by 'delay'
700 //  - 'delay' may be called only by 'start'
701 // The profile will look like the following:
702 //
703 // [Top down]:
704 //   135     0   (root) [-1] #1
705 //   121    72    start [-1] #3
706 //    49    33      delay [-1] #4
707 //    16    16        loop [-1] #5
708 //    14    14    (program) [-1] #2
TEST(SampleWhenFrameIsNotSetup)709 TEST(SampleWhenFrameIsNotSetup) {
710   LocalContext env;
711   v8::HandleScope scope(env->GetIsolate());
712 
713   v8::Script::Compile(v8::String::NewFromUtf8(
714                           env->GetIsolate(), cpu_profiler_test_source2))->Run();
715   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
716       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
717 
718   int32_t repeat_count = 100;
719 #if defined(USE_SIMULATOR)
720   // Simulators are much slower.
721   repeat_count = 1;
722 #endif
723   v8::Handle<v8::Value> args[] = {
724     v8::Integer::New(env->GetIsolate(), repeat_count)
725   };
726   v8::CpuProfile* profile =
727       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
728 
729   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
730 
731   ScopedVector<v8::Handle<v8::String> > names(3);
732   names[0] = v8::String::NewFromUtf8(
733       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
734   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
735                                      ProfileGenerator::kProgramEntryName);
736   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
737   CheckChildrenNames(root, names);
738 
739   const v8::CpuProfileNode* startNode =
740       FindChild(env->GetIsolate(), root, "start");
741   // On slow machines there may be no meaningfull samples at all, skip the
742   // check there.
743   if (startNode && startNode->GetChildrenCount() > 0) {
744     CHECK_EQ(1, startNode->GetChildrenCount());
745     const v8::CpuProfileNode* delayNode =
746         GetChild(env->GetIsolate(), startNode, "delay");
747     if (delayNode->GetChildrenCount() > 0) {
748       CHECK_EQ(1, delayNode->GetChildrenCount());
749       GetChild(env->GetIsolate(), delayNode, "loop");
750     }
751   }
752 
753   profile->Delete();
754 }
755 
756 
757 static const char* native_accessor_test_source = "function start(count) {\n"
758 "  for (var i = 0; i < count; i++) {\n"
759 "    var o = instance.foo;\n"
760 "    instance.foo = o + 1;\n"
761 "  }\n"
762 "}\n";
763 
764 
765 class TestApiCallbacks {
766  public:
TestApiCallbacks(int min_duration_ms)767   explicit TestApiCallbacks(int min_duration_ms)
768       : min_duration_ms_(min_duration_ms),
769         is_warming_up_(false) {}
770 
Getter(v8::Local<v8::String> name,const v8::PropertyCallbackInfo<v8::Value> & info)771   static void Getter(v8::Local<v8::String> name,
772                      const v8::PropertyCallbackInfo<v8::Value>& info) {
773     TestApiCallbacks* data = fromInfo(info);
774     data->Wait();
775   }
776 
Setter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)777   static void Setter(v8::Local<v8::String> name,
778                      v8::Local<v8::Value> value,
779                      const v8::PropertyCallbackInfo<void>& info) {
780     TestApiCallbacks* data = fromInfo(info);
781     data->Wait();
782   }
783 
Callback(const v8::FunctionCallbackInfo<v8::Value> & info)784   static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
785     TestApiCallbacks* data = fromInfo(info);
786     data->Wait();
787   }
788 
set_warming_up(bool value)789   void set_warming_up(bool value) { is_warming_up_ = value; }
790 
791  private:
Wait()792   void Wait() {
793     if (is_warming_up_) return;
794     double start = i::OS::TimeCurrentMillis();
795     double duration = 0;
796     while (duration < min_duration_ms_) {
797       i::OS::Sleep(1);
798       duration = i::OS::TimeCurrentMillis() - start;
799     }
800   }
801 
802   template<typename T>
fromInfo(const T & info)803   static TestApiCallbacks* fromInfo(const T& info) {
804     void* data = v8::External::Cast(*info.Data())->Value();
805     return reinterpret_cast<TestApiCallbacks*>(data);
806   }
807 
808   int min_duration_ms_;
809   bool is_warming_up_;
810 };
811 
812 
813 // Test that native accessors are properly reported in the CPU profile.
814 // This test checks the case when the long-running accessors are called
815 // only once and the optimizer doesn't have chance to change the invocation
816 // code.
TEST(NativeAccessorUninitializedIC)817 TEST(NativeAccessorUninitializedIC) {
818   LocalContext env;
819   v8::Isolate* isolate = env->GetIsolate();
820   v8::HandleScope scope(isolate);
821 
822   v8::Local<v8::FunctionTemplate> func_template =
823       v8::FunctionTemplate::New(isolate);
824   v8::Local<v8::ObjectTemplate> instance_template =
825       func_template->InstanceTemplate();
826 
827   TestApiCallbacks accessors(100);
828   v8::Local<v8::External> data =
829       v8::External::New(isolate, &accessors);
830   instance_template->SetAccessor(
831       v8::String::NewFromUtf8(isolate, "foo"),
832       &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data);
833   v8::Local<v8::Function> func = func_template->GetFunction();
834   v8::Local<v8::Object> instance = func->NewInstance();
835   env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
836                      instance);
837 
838   v8::Script::Compile(
839       v8::String::NewFromUtf8(isolate, native_accessor_test_source))
840       ->Run();
841   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
842       env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
843 
844   int32_t repeat_count = 1;
845   v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
846   v8::CpuProfile* profile =
847       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 180);
848 
849   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
850   const v8::CpuProfileNode* startNode =
851       GetChild(isolate, root, "start");
852   GetChild(isolate, startNode, "get foo");
853   GetChild(isolate, startNode, "set foo");
854 
855   profile->Delete();
856 }
857 
858 
859 // Test that native accessors are properly reported in the CPU profile.
860 // This test makes sure that the accessors are called enough times to become
861 // hot and to trigger optimizations.
TEST(NativeAccessorMonomorphicIC)862 TEST(NativeAccessorMonomorphicIC) {
863   LocalContext env;
864   v8::Isolate* isolate = env->GetIsolate();
865   v8::HandleScope scope(isolate);
866 
867   v8::Local<v8::FunctionTemplate> func_template =
868       v8::FunctionTemplate::New(isolate);
869   v8::Local<v8::ObjectTemplate> instance_template =
870       func_template->InstanceTemplate();
871 
872   TestApiCallbacks accessors(1);
873   v8::Local<v8::External> data =
874       v8::External::New(isolate, &accessors);
875   instance_template->SetAccessor(
876       v8::String::NewFromUtf8(isolate, "foo"),
877       &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data);
878   v8::Local<v8::Function> func = func_template->GetFunction();
879   v8::Local<v8::Object> instance = func->NewInstance();
880   env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
881                      instance);
882 
883   v8::Script::Compile(
884       v8::String::NewFromUtf8(isolate, native_accessor_test_source))
885       ->Run();
886   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
887       env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
888 
889   {
890     // Make sure accessors ICs are in monomorphic state before starting
891     // profiling.
892     accessors.set_warming_up(true);
893     int32_t warm_up_iterations = 3;
894     v8::Handle<v8::Value> args[] = {
895       v8::Integer::New(isolate, warm_up_iterations)
896     };
897     function->Call(env->Global(), ARRAY_SIZE(args), args);
898     accessors.set_warming_up(false);
899   }
900 
901   int32_t repeat_count = 100;
902   v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
903   v8::CpuProfile* profile =
904       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
905 
906   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
907   const v8::CpuProfileNode* startNode =
908       GetChild(isolate, root, "start");
909   GetChild(isolate, startNode, "get foo");
910   GetChild(isolate, startNode, "set foo");
911 
912   profile->Delete();
913 }
914 
915 
916 static const char* native_method_test_source = "function start(count) {\n"
917 "  for (var i = 0; i < count; i++) {\n"
918 "    instance.fooMethod();\n"
919 "  }\n"
920 "}\n";
921 
922 
TEST(NativeMethodUninitializedIC)923 TEST(NativeMethodUninitializedIC) {
924   LocalContext env;
925   v8::Isolate* isolate = env->GetIsolate();
926   v8::HandleScope scope(isolate);
927 
928   TestApiCallbacks callbacks(100);
929   v8::Local<v8::External> data =
930       v8::External::New(isolate, &callbacks);
931 
932   v8::Local<v8::FunctionTemplate> func_template =
933       v8::FunctionTemplate::New(isolate);
934   func_template->SetClassName(
935       v8::String::NewFromUtf8(isolate, "Test_InstanceCostructor"));
936   v8::Local<v8::ObjectTemplate> proto_template =
937       func_template->PrototypeTemplate();
938   v8::Local<v8::Signature> signature =
939       v8::Signature::New(isolate, func_template);
940   proto_template->Set(v8::String::NewFromUtf8(isolate, "fooMethod"),
941                       v8::FunctionTemplate::New(isolate,
942                                                 &TestApiCallbacks::Callback,
943                                                 data, signature, 0));
944 
945   v8::Local<v8::Function> func = func_template->GetFunction();
946   v8::Local<v8::Object> instance = func->NewInstance();
947   env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
948                      instance);
949 
950   v8::Script::Compile(v8::String::NewFromUtf8(
951                           isolate, native_method_test_source))->Run();
952   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
953       env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
954 
955   int32_t repeat_count = 1;
956   v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
957   v8::CpuProfile* profile =
958       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
959 
960   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
961   const v8::CpuProfileNode* startNode =
962       GetChild(isolate, root, "start");
963   GetChild(isolate, startNode, "fooMethod");
964 
965   profile->Delete();
966 }
967 
968 
TEST(NativeMethodMonomorphicIC)969 TEST(NativeMethodMonomorphicIC) {
970   LocalContext env;
971   v8::Isolate* isolate = env->GetIsolate();
972   v8::HandleScope scope(isolate);
973 
974   TestApiCallbacks callbacks(1);
975   v8::Local<v8::External> data =
976       v8::External::New(isolate, &callbacks);
977 
978   v8::Local<v8::FunctionTemplate> func_template =
979       v8::FunctionTemplate::New(isolate);
980   func_template->SetClassName(
981       v8::String::NewFromUtf8(isolate, "Test_InstanceCostructor"));
982   v8::Local<v8::ObjectTemplate> proto_template =
983       func_template->PrototypeTemplate();
984   v8::Local<v8::Signature> signature =
985       v8::Signature::New(isolate, func_template);
986   proto_template->Set(v8::String::NewFromUtf8(isolate, "fooMethod"),
987                       v8::FunctionTemplate::New(isolate,
988                                                 &TestApiCallbacks::Callback,
989                                                 data, signature, 0));
990 
991   v8::Local<v8::Function> func = func_template->GetFunction();
992   v8::Local<v8::Object> instance = func->NewInstance();
993   env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
994                      instance);
995 
996   v8::Script::Compile(v8::String::NewFromUtf8(
997                           isolate, native_method_test_source))->Run();
998   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
999       env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
1000   {
1001     // Make sure method ICs are in monomorphic state before starting
1002     // profiling.
1003     callbacks.set_warming_up(true);
1004     int32_t warm_up_iterations = 3;
1005     v8::Handle<v8::Value> args[] = {
1006       v8::Integer::New(isolate, warm_up_iterations)
1007     };
1008     function->Call(env->Global(), ARRAY_SIZE(args), args);
1009     callbacks.set_warming_up(false);
1010   }
1011 
1012   int32_t repeat_count = 100;
1013   v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
1014   v8::CpuProfile* profile =
1015       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1016 
1017   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1018   GetChild(isolate, root, "start");
1019   const v8::CpuProfileNode* startNode =
1020       GetChild(isolate, root, "start");
1021   GetChild(isolate, startNode, "fooMethod");
1022 
1023   profile->Delete();
1024 }
1025 
1026 
1027 static const char* bound_function_test_source = "function foo(iterations) {\n"
1028 "  var r = 0;\n"
1029 "  for (var i = 0; i < iterations; i++) { r += i; }\n"
1030 "  return r;\n"
1031 "}\n"
1032 "function start(duration) {\n"
1033 "  var callback = foo.bind(this);\n"
1034 "  var start = Date.now();\n"
1035 "  while (Date.now() - start < duration) {\n"
1036 "    callback(10 * 1000);\n"
1037 "  }\n"
1038 "}";
1039 
1040 
TEST(BoundFunctionCall)1041 TEST(BoundFunctionCall) {
1042   LocalContext env;
1043   v8::HandleScope scope(env->GetIsolate());
1044 
1045   v8::Script::Compile(
1046       v8::String::NewFromUtf8(env->GetIsolate(), bound_function_test_source))
1047       ->Run();
1048   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1049       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1050 
1051   int32_t duration_ms = 100;
1052   v8::Handle<v8::Value> args[] = {
1053     v8::Integer::New(env->GetIsolate(), duration_ms)
1054   };
1055   v8::CpuProfile* profile =
1056       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1057 
1058   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1059   ScopedVector<v8::Handle<v8::String> > names(3);
1060   names[0] = v8::String::NewFromUtf8(
1061       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1062   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1063                                      ProfileGenerator::kProgramEntryName);
1064   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1065   // Don't allow |foo| node to be at the top level.
1066   CheckChildrenNames(root, names);
1067 
1068   const v8::CpuProfileNode* startNode =
1069       GetChild(env->GetIsolate(), root, "start");
1070   GetChild(env->GetIsolate(), startNode, "foo");
1071 
1072   profile->Delete();
1073 }
1074 
1075 
1076 static const char* call_function_test_source = "function bar(iterations) {\n"
1077 "}\n"
1078 "function start(duration) {\n"
1079 "  var start = Date.now();\n"
1080 "  while (Date.now() - start < duration) {\n"
1081 "    try {\n"
1082 "      bar.call(this, 10 * 1000);\n"
1083 "    } catch(e) {}\n"
1084 "  }\n"
1085 "}";
1086 
1087 
1088 // Test that if we sampled thread when it was inside FunctionCall buitin then
1089 // its caller frame will be '(unresolved function)' as we have no reliable way
1090 // to resolve it.
1091 //
1092 // [Top down]:
1093 //    96     0   (root) [-1] #1
1094 //     1     1    (garbage collector) [-1] #4
1095 //     5     0    (unresolved function) [-1] #5
1096 //     5     5      call [-1] #6
1097 //    71    70    start [-1] #3
1098 //     1     1      bar [-1] #7
1099 //    19    19    (program) [-1] #2
TEST(FunctionCallSample)1100 TEST(FunctionCallSample) {
1101   LocalContext env;
1102   v8::HandleScope scope(env->GetIsolate());
1103 
1104   // Collect garbage that might have be generated while installing extensions.
1105   CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1106 
1107   v8::Script::Compile(v8::String::NewFromUtf8(
1108                           env->GetIsolate(), call_function_test_source))->Run();
1109   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1110       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1111 
1112   int32_t duration_ms = 100;
1113   v8::Handle<v8::Value> args[] = {
1114     v8::Integer::New(env->GetIsolate(), duration_ms)
1115   };
1116   v8::CpuProfile* profile =
1117       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1118 
1119   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1120   {
1121     ScopedVector<v8::Handle<v8::String> > names(4);
1122     names[0] = v8::String::NewFromUtf8(
1123         env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1124     names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1125                                        ProfileGenerator::kProgramEntryName);
1126     names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1127     names[3] = v8::String::NewFromUtf8(
1128         env->GetIsolate(), i::ProfileGenerator::kUnresolvedFunctionName);
1129     // Don't allow |bar| and |call| nodes to be at the top level.
1130     CheckChildrenNames(root, names);
1131   }
1132 
1133   // In case of GC stress tests all samples may be in GC phase and there
1134   // won't be |start| node in the profiles.
1135   bool is_gc_stress_testing =
1136       (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction;
1137   const v8::CpuProfileNode* startNode =
1138       FindChild(env->GetIsolate(), root, "start");
1139   CHECK(is_gc_stress_testing || startNode);
1140   if (startNode) {
1141     ScopedVector<v8::Handle<v8::String> > names(2);
1142     names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1143     names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1144     CheckChildrenNames(startNode, names);
1145   }
1146 
1147   const v8::CpuProfileNode* unresolvedNode = FindChild(
1148       env->GetIsolate(), root, i::ProfileGenerator::kUnresolvedFunctionName);
1149   if (unresolvedNode) {
1150     ScopedVector<v8::Handle<v8::String> > names(1);
1151     names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1152     CheckChildrenNames(unresolvedNode, names);
1153   }
1154 
1155   profile->Delete();
1156 }
1157 
1158 
1159 static const char* function_apply_test_source = "function bar(iterations) {\n"
1160 "}\n"
1161 "function test() {\n"
1162 "  bar.apply(this, [10 * 1000]);\n"
1163 "}\n"
1164 "function start(duration) {\n"
1165 "  var start = Date.now();\n"
1166 "  while (Date.now() - start < duration) {\n"
1167 "    try {\n"
1168 "      test();\n"
1169 "    } catch(e) {}\n"
1170 "  }\n"
1171 "}";
1172 
1173 
1174 // [Top down]:
1175 //    94     0   (root) [-1] #0 1
1176 //     2     2    (garbage collector) [-1] #0 7
1177 //    82    49    start [-1] #16 3
1178 //     1     0      (unresolved function) [-1] #0 8
1179 //     1     1        apply [-1] #0 9
1180 //    32    21      test [-1] #16 4
1181 //     2     2        bar [-1] #16 6
1182 //     9     9        apply [-1] #0 5
1183 //    10    10    (program) [-1] #0 2
TEST(FunctionApplySample)1184 TEST(FunctionApplySample) {
1185   LocalContext env;
1186   v8::HandleScope scope(env->GetIsolate());
1187 
1188   v8::Script::Compile(
1189       v8::String::NewFromUtf8(env->GetIsolate(), function_apply_test_source))
1190       ->Run();
1191   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1192       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1193 
1194   int32_t duration_ms = 100;
1195   v8::Handle<v8::Value> args[] = {
1196     v8::Integer::New(env->GetIsolate(), duration_ms)
1197   };
1198 
1199   v8::CpuProfile* profile =
1200       RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1201 
1202   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1203   {
1204     ScopedVector<v8::Handle<v8::String> > names(3);
1205     names[0] = v8::String::NewFromUtf8(
1206         env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1207     names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1208                                        ProfileGenerator::kProgramEntryName);
1209     names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1210     // Don't allow |test|, |bar| and |apply| nodes to be at the top level.
1211     CheckChildrenNames(root, names);
1212   }
1213 
1214   const v8::CpuProfileNode* startNode =
1215       FindChild(env->GetIsolate(), root, "start");
1216   if (startNode) {
1217     {
1218       ScopedVector<v8::Handle<v8::String> > names(2);
1219       names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "test");
1220       names[1] = v8::String::NewFromUtf8(
1221           env->GetIsolate(), ProfileGenerator::kUnresolvedFunctionName);
1222       CheckChildrenNames(startNode, names);
1223     }
1224 
1225     const v8::CpuProfileNode* testNode =
1226         FindChild(env->GetIsolate(), startNode, "test");
1227     if (testNode) {
1228       ScopedVector<v8::Handle<v8::String> > names(3);
1229       names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1230       names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1231       // apply calls "get length" before invoking the function itself
1232       // and we may get hit into it.
1233       names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "get length");
1234       CheckChildrenNames(testNode, names);
1235     }
1236 
1237     if (const v8::CpuProfileNode* unresolvedNode =
1238             FindChild(env->GetIsolate(), startNode,
1239                       ProfileGenerator::kUnresolvedFunctionName)) {
1240       ScopedVector<v8::Handle<v8::String> > names(1);
1241       names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1242       CheckChildrenNames(unresolvedNode, names);
1243       GetChild(env->GetIsolate(), unresolvedNode, "apply");
1244     }
1245   }
1246 
1247   profile->Delete();
1248 }
1249 
1250 
1251 static const char* js_native_js_test_source =
1252 "var is_profiling = false;\n"
1253 "function foo(iterations) {\n"
1254 "  if (!is_profiling) {\n"
1255 "    is_profiling = true;\n"
1256 "    startProfiling('my_profile');\n"
1257 "  }\n"
1258 "  var r = 0;\n"
1259 "  for (var i = 0; i < iterations; i++) { r += i; }\n"
1260 "  return r;\n"
1261 "}\n"
1262 "function bar(iterations) {\n"
1263 "  try { foo(iterations); } catch(e) {}\n"
1264 "}\n"
1265 "function start(duration) {\n"
1266 "  var start = Date.now();\n"
1267 "  while (Date.now() - start < duration) {\n"
1268 "    try {\n"
1269 "      CallJsFunction(bar, 10 * 1000);\n"
1270 "    } catch(e) {}\n"
1271 "  }\n"
1272 "}";
1273 
CallJsFunction(const v8::FunctionCallbackInfo<v8::Value> & info)1274 static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1275   v8::Handle<v8::Function> function = info[0].As<v8::Function>();
1276   v8::Handle<v8::Value> argv[] = { info[1] };
1277   function->Call(info.This(), ARRAY_SIZE(argv), argv);
1278 }
1279 
1280 
1281 // [Top down]:
1282 //    58     0   (root) #0 1
1283 //     2     2    (program) #0 2
1284 //    56     1    start #16 3
1285 //    55     0      CallJsFunction #0 4
1286 //    55     1        bar #16 5
1287 //    54    54          foo #16 6
TEST(JsNativeJsSample)1288 TEST(JsNativeJsSample) {
1289   v8::HandleScope scope(CcTest::isolate());
1290   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1291   v8::Context::Scope context_scope(env);
1292 
1293   v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1294       env->GetIsolate(), CallJsFunction);
1295   v8::Local<v8::Function> func = func_template->GetFunction();
1296   func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1297   env->Global()->Set(
1298       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1299 
1300   v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
1301                                               js_native_js_test_source))->Run();
1302   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1303       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1304 
1305   int32_t duration_ms = 20;
1306   v8::Handle<v8::Value> args[] = {
1307     v8::Integer::New(env->GetIsolate(), duration_ms)
1308   };
1309   v8::CpuProfile* profile =
1310       RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1311 
1312   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1313   {
1314     ScopedVector<v8::Handle<v8::String> > names(3);
1315     names[0] = v8::String::NewFromUtf8(
1316         env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1317     names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1318                                        ProfileGenerator::kProgramEntryName);
1319     names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1320     CheckChildrenNames(root, names);
1321   }
1322 
1323   const v8::CpuProfileNode* startNode =
1324       GetChild(env->GetIsolate(), root, "start");
1325   CHECK_EQ(1, startNode->GetChildrenCount());
1326   const v8::CpuProfileNode* nativeFunctionNode =
1327       GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1328 
1329   CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1330   const v8::CpuProfileNode* barNode =
1331       GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1332 
1333   CHECK_EQ(1, barNode->GetChildrenCount());
1334   GetChild(env->GetIsolate(), barNode, "foo");
1335 
1336   profile->Delete();
1337 }
1338 
1339 
1340 static const char* js_native_js_runtime_js_test_source =
1341 "var is_profiling = false;\n"
1342 "function foo(iterations) {\n"
1343 "  if (!is_profiling) {\n"
1344 "    is_profiling = true;\n"
1345 "    startProfiling('my_profile');\n"
1346 "  }\n"
1347 "  var r = 0;\n"
1348 "  for (var i = 0; i < iterations; i++) { r += i; }\n"
1349 "  return r;\n"
1350 "}\n"
1351 "var bound = foo.bind(this);\n"
1352 "function bar(iterations) {\n"
1353 "  try { bound(iterations); } catch(e) {}\n"
1354 "}\n"
1355 "function start(duration) {\n"
1356 "  var start = Date.now();\n"
1357 "  while (Date.now() - start < duration) {\n"
1358 "    try {\n"
1359 "      CallJsFunction(bar, 10 * 1000);\n"
1360 "    } catch(e) {}\n"
1361 "  }\n"
1362 "}";
1363 
1364 
1365 // [Top down]:
1366 //    57     0   (root) #0 1
1367 //    55     1    start #16 3
1368 //    54     0      CallJsFunction #0 4
1369 //    54     3        bar #16 5
1370 //    51    51          foo #16 6
1371 //     2     2    (program) #0 2
TEST(JsNativeJsRuntimeJsSample)1372 TEST(JsNativeJsRuntimeJsSample) {
1373   v8::HandleScope scope(CcTest::isolate());
1374   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1375   v8::Context::Scope context_scope(env);
1376 
1377   v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1378       env->GetIsolate(), CallJsFunction);
1379   v8::Local<v8::Function> func = func_template->GetFunction();
1380   func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1381   env->Global()->Set(
1382       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1383 
1384   v8::Script::Compile(
1385       v8::String::NewFromUtf8(env->GetIsolate(),
1386                               js_native_js_runtime_js_test_source))->Run();
1387   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1388       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1389 
1390   int32_t duration_ms = 20;
1391   v8::Handle<v8::Value> args[] = {
1392     v8::Integer::New(env->GetIsolate(), duration_ms)
1393   };
1394   v8::CpuProfile* profile =
1395       RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1396 
1397   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1398   ScopedVector<v8::Handle<v8::String> > names(3);
1399   names[0] = v8::String::NewFromUtf8(
1400       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1401   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1402                                      ProfileGenerator::kProgramEntryName);
1403   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1404   CheckChildrenNames(root, names);
1405 
1406   const v8::CpuProfileNode* startNode =
1407       GetChild(env->GetIsolate(), root, "start");
1408   CHECK_EQ(1, startNode->GetChildrenCount());
1409   const v8::CpuProfileNode* nativeFunctionNode =
1410       GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1411 
1412   CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1413   const v8::CpuProfileNode* barNode =
1414       GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1415 
1416   // The child is in fact a bound foo.
1417   // A bound function has a wrapper that may make calls to
1418   // other functions e.g. "get length".
1419   CHECK_LE(1, barNode->GetChildrenCount());
1420   CHECK_GE(2, barNode->GetChildrenCount());
1421   GetChild(env->GetIsolate(), barNode, "foo");
1422 
1423   profile->Delete();
1424 }
1425 
1426 
CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value> & info)1427 static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1428   CallJsFunction(info);
1429 }
1430 
1431 
1432 static const char* js_native1_js_native2_js_test_source =
1433 "var is_profiling = false;\n"
1434 "function foo(iterations) {\n"
1435 "  if (!is_profiling) {\n"
1436 "    is_profiling = true;\n"
1437 "    startProfiling('my_profile');\n"
1438 "  }\n"
1439 "  var r = 0;\n"
1440 "  for (var i = 0; i < iterations; i++) { r += i; }\n"
1441 "  return r;\n"
1442 "}\n"
1443 "function bar(iterations) {\n"
1444 "  CallJsFunction2(foo, iterations);\n"
1445 "}\n"
1446 "function start(duration) {\n"
1447 "  var start = Date.now();\n"
1448 "  while (Date.now() - start < duration) {\n"
1449 "    try {\n"
1450 "      CallJsFunction1(bar, 10 * 1000);\n"
1451 "    } catch(e) {}\n"
1452 "  }\n"
1453 "}";
1454 
1455 
1456 // [Top down]:
1457 //    57     0   (root) #0 1
1458 //    55     1    start #16 3
1459 //    54     0      CallJsFunction1 #0 4
1460 //    54     0        bar #16 5
1461 //    54     0          CallJsFunction2 #0 6
1462 //    54    54            foo #16 7
1463 //     2     2    (program) #0 2
TEST(JsNative1JsNative2JsSample)1464 TEST(JsNative1JsNative2JsSample) {
1465   v8::HandleScope scope(CcTest::isolate());
1466   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1467   v8::Context::Scope context_scope(env);
1468 
1469   v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1470       env->GetIsolate(), CallJsFunction);
1471   v8::Local<v8::Function> func1 = func_template->GetFunction();
1472   func1->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"));
1473   env->Global()->Set(
1474       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"), func1);
1475 
1476   v8::Local<v8::Function> func2 = v8::FunctionTemplate::New(
1477       env->GetIsolate(), CallJsFunction2)->GetFunction();
1478   func2->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"));
1479   env->Global()->Set(
1480       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"), func2);
1481 
1482   v8::Script::Compile(
1483       v8::String::NewFromUtf8(env->GetIsolate(),
1484                               js_native1_js_native2_js_test_source))->Run();
1485   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1486       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1487 
1488   int32_t duration_ms = 20;
1489   v8::Handle<v8::Value> args[] = {
1490     v8::Integer::New(env->GetIsolate(), duration_ms)
1491   };
1492   v8::CpuProfile* profile =
1493       RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1494 
1495   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1496   ScopedVector<v8::Handle<v8::String> > names(3);
1497   names[0] = v8::String::NewFromUtf8(
1498       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1499   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1500                                      ProfileGenerator::kProgramEntryName);
1501   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1502   CheckChildrenNames(root, names);
1503 
1504   const v8::CpuProfileNode* startNode =
1505       GetChild(env->GetIsolate(), root, "start");
1506   CHECK_EQ(1, startNode->GetChildrenCount());
1507   const v8::CpuProfileNode* nativeNode1 =
1508       GetChild(env->GetIsolate(), startNode, "CallJsFunction1");
1509 
1510   CHECK_EQ(1, nativeNode1->GetChildrenCount());
1511   const v8::CpuProfileNode* barNode =
1512       GetChild(env->GetIsolate(), nativeNode1, "bar");
1513 
1514   CHECK_EQ(1, barNode->GetChildrenCount());
1515   const v8::CpuProfileNode* nativeNode2 =
1516       GetChild(env->GetIsolate(), barNode, "CallJsFunction2");
1517 
1518   CHECK_EQ(1, nativeNode2->GetChildrenCount());
1519   GetChild(env->GetIsolate(), nativeNode2, "foo");
1520 
1521   profile->Delete();
1522 }
1523 
1524 
1525 // [Top down]:
1526 //     6     0   (root) #0 1
1527 //     3     3    (program) #0 2
1528 //     3     3    (idle) #0 3
TEST(IdleTime)1529 TEST(IdleTime) {
1530   LocalContext env;
1531   v8::HandleScope scope(env->GetIsolate());
1532   v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1533 
1534   v8::Local<v8::String> profile_name =
1535       v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1536   cpu_profiler->StartProfiling(profile_name);
1537 
1538   i::Isolate* isolate = CcTest::i_isolate();
1539   i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
1540   processor->AddCurrentStack(isolate);
1541 
1542   cpu_profiler->SetIdle(true);
1543 
1544   for (int i = 0; i < 3; i++) {
1545     processor->AddCurrentStack(isolate);
1546   }
1547 
1548   cpu_profiler->SetIdle(false);
1549   processor->AddCurrentStack(isolate);
1550 
1551 
1552   v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1553   CHECK_NE(NULL, profile);
1554   // Dump collected profile to have a better diagnostic in case of failure.
1555   reinterpret_cast<i::CpuProfile*>(profile)->Print();
1556 
1557   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1558   ScopedVector<v8::Handle<v8::String> > names(3);
1559   names[0] = v8::String::NewFromUtf8(
1560       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1561   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1562                                      ProfileGenerator::kProgramEntryName);
1563   names[2] = v8::String::NewFromUtf8(env->GetIsolate(),
1564                                      ProfileGenerator::kIdleEntryName);
1565   CheckChildrenNames(root, names);
1566 
1567   const v8::CpuProfileNode* programNode =
1568       GetChild(env->GetIsolate(), root, ProfileGenerator::kProgramEntryName);
1569   CHECK_EQ(0, programNode->GetChildrenCount());
1570   CHECK_GE(programNode->GetHitCount(), 3);
1571 
1572   const v8::CpuProfileNode* idleNode =
1573       GetChild(env->GetIsolate(), root, ProfileGenerator::kIdleEntryName);
1574   CHECK_EQ(0, idleNode->GetChildrenCount());
1575   CHECK_GE(idleNode->GetHitCount(), 3);
1576 
1577   profile->Delete();
1578 }
1579 
1580 
CheckFunctionDetails(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name,const char * script_name,int script_id,int line,int column)1581 static void CheckFunctionDetails(v8::Isolate* isolate,
1582                                  const v8::CpuProfileNode* node,
1583                                  const char* name, const char* script_name,
1584                                  int script_id, int line, int column) {
1585   CHECK_EQ(v8::String::NewFromUtf8(isolate, name),
1586            node->GetFunctionName());
1587   CHECK_EQ(v8::String::NewFromUtf8(isolate, script_name),
1588            node->GetScriptResourceName());
1589   CHECK_EQ(script_id, node->GetScriptId());
1590   CHECK_EQ(line, node->GetLineNumber());
1591   CHECK_EQ(column, node->GetColumnNumber());
1592 }
1593 
1594 
TEST(FunctionDetails)1595 TEST(FunctionDetails) {
1596   v8::HandleScope scope(CcTest::isolate());
1597   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1598   v8::Context::Scope context_scope(env);
1599 
1600   v8::Handle<v8::Script> script_a = CompileWithOrigin(
1601           "    function foo\n() { try { bar(); } catch(e) {} }\n"
1602           " function bar() { startProfiling(); }\n",
1603           "script_a");
1604   script_a->Run();
1605   v8::Handle<v8::Script> script_b = CompileWithOrigin(
1606           "\n\n   function baz() { try { foo(); } catch(e) {} }\n"
1607           "\n\nbaz();\n"
1608           "stopProfiling();\n",
1609           "script_b");
1610   script_b->Run();
1611   const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1612   const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1613   reinterpret_cast<ProfileNode*>(
1614       const_cast<v8::CpuProfileNode*>(current))->Print(0);
1615   // The tree should look like this:
1616   //  0   (root) 0 #1
1617   //  0    (anonymous function) 19 #2 no reason script_b:1
1618   //  0      baz 19 #3 TryCatchStatement script_b:3
1619   //  0        foo 18 #4 TryCatchStatement script_a:2
1620   //  1          bar 18 #5 no reason script_a:3
1621   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1622   const v8::CpuProfileNode* script = GetChild(env->GetIsolate(), root,
1623       ProfileGenerator::kAnonymousFunctionName);
1624   CheckFunctionDetails(env->GetIsolate(), script,
1625                        ProfileGenerator::kAnonymousFunctionName, "script_b",
1626                        script_b->GetUnboundScript()->GetId(), 1, 1);
1627   const v8::CpuProfileNode* baz = GetChild(env->GetIsolate(), script, "baz");
1628   CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b",
1629                        script_b->GetUnboundScript()->GetId(), 3, 16);
1630   const v8::CpuProfileNode* foo = GetChild(env->GetIsolate(), baz, "foo");
1631   CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a",
1632                        script_a->GetUnboundScript()->GetId(), 2, 1);
1633   const v8::CpuProfileNode* bar = GetChild(env->GetIsolate(), foo, "bar");
1634   CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a",
1635                        script_a->GetUnboundScript()->GetId(), 3, 14);
1636 }
1637 
1638 
TEST(DontStopOnFinishedProfileDelete)1639 TEST(DontStopOnFinishedProfileDelete) {
1640   v8::HandleScope scope(CcTest::isolate());
1641   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1642   v8::Context::Scope context_scope(env);
1643   v8::Isolate* isolate = env->GetIsolate();
1644 
1645   v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler();
1646   i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
1647 
1648   CHECK_EQ(0, iprofiler->GetProfilesCount());
1649   v8::Handle<v8::String> outer = v8::String::NewFromUtf8(isolate, "outer");
1650   profiler->StartProfiling(outer);
1651   CHECK_EQ(0, iprofiler->GetProfilesCount());
1652 
1653   v8::Handle<v8::String> inner = v8::String::NewFromUtf8(isolate, "inner");
1654   profiler->StartProfiling(inner);
1655   CHECK_EQ(0, iprofiler->GetProfilesCount());
1656 
1657   v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
1658   CHECK(inner_profile);
1659   CHECK_EQ(1, iprofiler->GetProfilesCount());
1660   inner_profile->Delete();
1661   inner_profile = NULL;
1662   CHECK_EQ(0, iprofiler->GetProfilesCount());
1663 
1664   v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
1665   CHECK(outer_profile);
1666   CHECK_EQ(1, iprofiler->GetProfilesCount());
1667   outer_profile->Delete();
1668   outer_profile = NULL;
1669   CHECK_EQ(0, iprofiler->GetProfilesCount());
1670 }
1671