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