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 
TEST(StartStop)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 
EnqueueTickSampleEvent(ProfilerEventsProcessor * proc,i::Address frame1,i::Address frame2=NULL,i::Address frame3=NULL)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:
TestSetup()86   TestSetup()
87       : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
88     i::FLAG_prof_browser_mode = false;
89   }
90 
~TestSetup()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 
CreateCode(LocalContext * env)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 
TEST(CodeEvents)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>
CompareProfileNodes(const T * p1,const T * p2)187 static int CompareProfileNodes(const T* p1, const T* p2) {
188   return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
189 }
190 
191 
TEST(TickEvents)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.
TEST(CrashIfStoppingLastNonExistentProfile)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.
TEST(Issue1398)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 
TEST(DeleteAllCpuProfiles)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 
FindCpuProfile(v8::CpuProfiler * v8profiler,const v8::CpuProfile * v8profile)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 
TEST(DeleteCpuProfile)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 
TEST(ProfileStartEndTime)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 
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)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 
ContainsString(v8::Handle<v8::String> string,const Vector<v8::Handle<v8::String>> & vector)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 
CheckChildrenNames(const v8::CpuProfileNode * node,const Vector<v8::Handle<v8::String>> & names)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 
FindChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)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 
GetChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)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 
CheckSimpleBranch(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * names[],int length)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]
TEST(CollectCpuProfile)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).
TEST(HotDeoptNoFrameEntry)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 
TEST(CollectCpuProfileSamples)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
TEST(SampleWhenFrameIsNotSetup)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:
TestApiCallbacks(int min_duration_ms)766   explicit TestApiCallbacks(int min_duration_ms)
767       : min_duration_ms_(min_duration_ms),
768         is_warming_up_(false) {}
769 
Getter(v8::Local<v8::String> name,const v8::PropertyCallbackInfo<v8::Value> & info)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 
Setter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)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 
Callback(const v8::FunctionCallbackInfo<v8::Value> & info)783   static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
784     TestApiCallbacks* data = fromInfo(info);
785     data->Wait();
786   }
787 
set_warming_up(bool value)788   void set_warming_up(bool value) { is_warming_up_ = value; }
789 
790  private:
Wait()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>
fromInfo(const T & info)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.
TEST(NativeAccessorUninitializedIC)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.
TEST(NativeAccessorMonomorphicIC)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 
TEST(NativeMethodUninitializedIC)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 
TEST(NativeMethodMonomorphicIC)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 
TEST(BoundFunctionCall)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 static const char* call_function_test_source = "function bar(iterations) {\n"
1068 "}\n"
1069 "function start(duration) {\n"
1070 "  var start = Date.now();\n"
1071 "  while (Date.now() - start < duration) {\n"
1072 "    try {\n"
1073 "      bar.call(this, 10 * 1000);\n"
1074 "    } catch(e) {}\n"
1075 "  }\n"
1076 "}";
1077 
1078 
1079 // Test that if we sampled thread when it was inside FunctionCall buitin then
1080 // its caller frame will be '(unresolved function)' as we have no reliable way
1081 // to resolve it.
1082 //
1083 // [Top down]:
1084 //    96     0   (root) [-1] #1
1085 //     1     1    (garbage collector) [-1] #4
1086 //     5     0    (unresolved function) [-1] #5
1087 //     5     5      call [-1] #6
1088 //    71    70    start [-1] #3
1089 //     1     1      bar [-1] #7
1090 //    19    19    (program) [-1] #2
TEST(FunctionCallSample)1091 TEST(FunctionCallSample) {
1092   LocalContext env;
1093   v8::HandleScope scope(env->GetIsolate());
1094 
1095   // Collect garbage that might have be generated while installing extensions.
1096   CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1097 
1098   v8::Script::Compile(v8::String::NewFromUtf8(
1099                           env->GetIsolate(), call_function_test_source))->Run();
1100   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1101       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1102 
1103   int32_t duration_ms = 100;
1104   v8::Handle<v8::Value> args[] = {
1105     v8::Integer::New(env->GetIsolate(), duration_ms)
1106   };
1107   v8::CpuProfile* profile =
1108       RunProfiler(env.local(), function, args, arraysize(args), 100);
1109 
1110   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1111   {
1112     ScopedVector<v8::Handle<v8::String> > names(4);
1113     names[0] = v8::String::NewFromUtf8(
1114         env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1115     names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1116                                        ProfileGenerator::kProgramEntryName);
1117     names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1118     names[3] = v8::String::NewFromUtf8(
1119         env->GetIsolate(), i::ProfileGenerator::kUnresolvedFunctionName);
1120     // Don't allow |bar| and |call| nodes to be at the top level.
1121     CheckChildrenNames(root, names);
1122   }
1123 
1124   // In case of GC stress tests all samples may be in GC phase and there
1125   // won't be |start| node in the profiles.
1126   bool is_gc_stress_testing =
1127       (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction;
1128   const v8::CpuProfileNode* startNode =
1129       FindChild(env->GetIsolate(), root, "start");
1130   CHECK(is_gc_stress_testing || startNode);
1131   if (startNode) {
1132     ScopedVector<v8::Handle<v8::String> > names(2);
1133     names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1134     names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1135     CheckChildrenNames(startNode, names);
1136   }
1137 
1138   const v8::CpuProfileNode* unresolvedNode = FindChild(
1139       env->GetIsolate(), root, i::ProfileGenerator::kUnresolvedFunctionName);
1140   if (unresolvedNode) {
1141     ScopedVector<v8::Handle<v8::String> > names(1);
1142     names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1143     CheckChildrenNames(unresolvedNode, names);
1144   }
1145 
1146   profile->Delete();
1147 }
1148 
1149 
1150 static const char* function_apply_test_source = "function bar(iterations) {\n"
1151 "}\n"
1152 "function test() {\n"
1153 "  bar.apply(this, [10 * 1000]);\n"
1154 "}\n"
1155 "function start(duration) {\n"
1156 "  var start = Date.now();\n"
1157 "  while (Date.now() - start < duration) {\n"
1158 "    try {\n"
1159 "      test();\n"
1160 "    } catch(e) {}\n"
1161 "  }\n"
1162 "}";
1163 
1164 
1165 // [Top down]:
1166 //    94     0   (root) [-1] #0 1
1167 //     2     2    (garbage collector) [-1] #0 7
1168 //    82    49    start [-1] #16 3
1169 //     1     0      (unresolved function) [-1] #0 8
1170 //     1     1        apply [-1] #0 9
1171 //    32    21      test [-1] #16 4
1172 //     2     2        bar [-1] #16 6
1173 //     9     9        apply [-1] #0 5
1174 //    10    10    (program) [-1] #0 2
TEST(FunctionApplySample)1175 TEST(FunctionApplySample) {
1176   LocalContext env;
1177   v8::HandleScope scope(env->GetIsolate());
1178 
1179   v8::Script::Compile(
1180       v8::String::NewFromUtf8(env->GetIsolate(), function_apply_test_source))
1181       ->Run();
1182   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1183       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1184 
1185   int32_t duration_ms = 100;
1186   v8::Handle<v8::Value> args[] = {
1187     v8::Integer::New(env->GetIsolate(), duration_ms)
1188   };
1189 
1190   v8::CpuProfile* profile =
1191       RunProfiler(env.local(), function, args, arraysize(args), 100);
1192 
1193   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1194   {
1195     ScopedVector<v8::Handle<v8::String> > names(3);
1196     names[0] = v8::String::NewFromUtf8(
1197         env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1198     names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1199                                        ProfileGenerator::kProgramEntryName);
1200     names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1201     // Don't allow |test|, |bar| and |apply| nodes to be at the top level.
1202     CheckChildrenNames(root, names);
1203   }
1204 
1205   const v8::CpuProfileNode* startNode =
1206       FindChild(env->GetIsolate(), root, "start");
1207   if (startNode) {
1208     {
1209       ScopedVector<v8::Handle<v8::String> > names(2);
1210       names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "test");
1211       names[1] = v8::String::NewFromUtf8(
1212           env->GetIsolate(), ProfileGenerator::kUnresolvedFunctionName);
1213       CheckChildrenNames(startNode, names);
1214     }
1215 
1216     const v8::CpuProfileNode* testNode =
1217         FindChild(env->GetIsolate(), startNode, "test");
1218     if (testNode) {
1219       ScopedVector<v8::Handle<v8::String> > names(3);
1220       names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1221       names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1222       // apply calls "get length" before invoking the function itself
1223       // and we may get hit into it.
1224       names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "get length");
1225       CheckChildrenNames(testNode, names);
1226     }
1227 
1228     if (const v8::CpuProfileNode* unresolvedNode =
1229             FindChild(env->GetIsolate(), startNode,
1230                       ProfileGenerator::kUnresolvedFunctionName)) {
1231       ScopedVector<v8::Handle<v8::String> > names(1);
1232       names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1233       CheckChildrenNames(unresolvedNode, names);
1234       GetChild(env->GetIsolate(), unresolvedNode, "apply");
1235     }
1236   }
1237 
1238   profile->Delete();
1239 }
1240 
1241 
1242 static const char* cpu_profiler_deep_stack_test_source =
1243 "function foo(n) {\n"
1244 "  if (n)\n"
1245 "    foo(n - 1);\n"
1246 "  else\n"
1247 "    startProfiling('my_profile');\n"
1248 "}\n"
1249 "function start() {\n"
1250 "  foo(250);\n"
1251 "}\n";
1252 
1253 
1254 // Check a deep stack
1255 //
1256 // [Top down]:
1257 //    0  (root) 0 #1
1258 //    2    (program) 0 #2
1259 //    0    start 21 #3 no reason
1260 //    0      foo 21 #4 no reason
1261 //    0        foo 21 #5 no reason
1262 //                ....
1263 //    0          foo 21 #253 no reason
1264 //    1            startProfiling 0 #254
TEST(CpuProfileDeepStack)1265 TEST(CpuProfileDeepStack) {
1266   v8::HandleScope scope(CcTest::isolate());
1267   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1268   v8::Context::Scope context_scope(env);
1269 
1270   v8::Script::Compile(v8::String::NewFromUtf8(
1271       env->GetIsolate(), cpu_profiler_deep_stack_test_source))->Run();
1272   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1273       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1274 
1275   v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1276   v8::Local<v8::String> profile_name =
1277       v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1278   function->Call(env->Global(), 0, NULL);
1279   v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1280   CHECK_NE(NULL, profile);
1281   // Dump collected profile to have a better diagnostic in case of failure.
1282   reinterpret_cast<i::CpuProfile*>(profile)->Print();
1283 
1284   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1285   {
1286     ScopedVector<v8::Handle<v8::String> > names(3);
1287     names[0] = v8::String::NewFromUtf8(
1288         env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1289     names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1290                                        ProfileGenerator::kProgramEntryName);
1291     names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1292     CheckChildrenNames(root, names);
1293   }
1294 
1295   const v8::CpuProfileNode* node =
1296       GetChild(env->GetIsolate(), root, "start");
1297   for (int i = 0; i < 250; ++i) {
1298     node = GetChild(env->GetIsolate(), node, "foo");
1299   }
1300   // TODO(alph):
1301   // In theory there must be one more 'foo' and a 'startProfiling' nodes,
1302   // but due to unstable top frame extraction these might be missing.
1303 
1304   profile->Delete();
1305 }
1306 
1307 
1308 static const char* js_native_js_test_source =
1309     "function foo() {\n"
1310     "  startProfiling('my_profile');\n"
1311     "}\n"
1312     "function bar() {\n"
1313     "  try { foo(); } catch(e) {}\n"
1314     "}\n"
1315     "function start() {\n"
1316     "  try {\n"
1317     "    CallJsFunction(bar);\n"
1318     "  } catch(e) {}\n"
1319     "}";
1320 
CallJsFunction(const v8::FunctionCallbackInfo<v8::Value> & info)1321 static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1322   v8::Handle<v8::Function> function = info[0].As<v8::Function>();
1323   v8::Handle<v8::Value> argv[] = { info[1] };
1324   function->Call(info.This(), arraysize(argv), argv);
1325 }
1326 
1327 
1328 // [Top down]:
1329 //    58     0   (root) #0 1
1330 //     2     2    (program) #0 2
1331 //    56     1    start #16 3
1332 //    55     0      CallJsFunction #0 4
1333 //    55     1        bar #16 5
1334 //    54    54          foo #16 6
TEST(JsNativeJsSample)1335 TEST(JsNativeJsSample) {
1336   v8::HandleScope scope(CcTest::isolate());
1337   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1338   v8::Context::Scope context_scope(env);
1339 
1340   v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1341       env->GetIsolate(), CallJsFunction);
1342   v8::Local<v8::Function> func = func_template->GetFunction();
1343   func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1344   env->Global()->Set(
1345       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1346 
1347   v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
1348                                               js_native_js_test_source))->Run();
1349   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1350       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1351 
1352   v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1353 
1354   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1355   {
1356     ScopedVector<v8::Handle<v8::String> > names(3);
1357     names[0] = v8::String::NewFromUtf8(
1358         env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1359     names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1360                                        ProfileGenerator::kProgramEntryName);
1361     names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1362     CheckChildrenNames(root, names);
1363   }
1364 
1365   const v8::CpuProfileNode* startNode =
1366       GetChild(env->GetIsolate(), root, "start");
1367   CHECK_EQ(1, startNode->GetChildrenCount());
1368   const v8::CpuProfileNode* nativeFunctionNode =
1369       GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1370 
1371   CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1372   const v8::CpuProfileNode* barNode =
1373       GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1374 
1375   CHECK_EQ(1, barNode->GetChildrenCount());
1376   GetChild(env->GetIsolate(), barNode, "foo");
1377 
1378   profile->Delete();
1379 }
1380 
1381 
1382 static const char* js_native_js_runtime_js_test_source =
1383     "function foo() {\n"
1384     "  startProfiling('my_profile');\n"
1385     "}\n"
1386     "var bound = foo.bind(this);\n"
1387     "function bar() {\n"
1388     "  try { bound(); } catch(e) {}\n"
1389     "}\n"
1390     "function start() {\n"
1391     "  try {\n"
1392     "    CallJsFunction(bar);\n"
1393     "  } catch(e) {}\n"
1394     "}";
1395 
1396 
1397 // [Top down]:
1398 //    57     0   (root) #0 1
1399 //    55     1    start #16 3
1400 //    54     0      CallJsFunction #0 4
1401 //    54     3        bar #16 5
1402 //    51    51          foo #16 6
1403 //     2     2    (program) #0 2
TEST(JsNativeJsRuntimeJsSample)1404 TEST(JsNativeJsRuntimeJsSample) {
1405   v8::HandleScope scope(CcTest::isolate());
1406   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1407   v8::Context::Scope context_scope(env);
1408 
1409   v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1410       env->GetIsolate(), CallJsFunction);
1411   v8::Local<v8::Function> func = func_template->GetFunction();
1412   func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1413   env->Global()->Set(
1414       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1415 
1416   v8::Script::Compile(
1417       v8::String::NewFromUtf8(env->GetIsolate(),
1418                               js_native_js_runtime_js_test_source))->Run();
1419   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1420       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1421 
1422   v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1423 
1424   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1425   ScopedVector<v8::Handle<v8::String> > names(3);
1426   names[0] = v8::String::NewFromUtf8(
1427       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1428   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1429                                      ProfileGenerator::kProgramEntryName);
1430   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1431   CheckChildrenNames(root, names);
1432 
1433   const v8::CpuProfileNode* startNode =
1434       GetChild(env->GetIsolate(), root, "start");
1435   CHECK_EQ(1, startNode->GetChildrenCount());
1436   const v8::CpuProfileNode* nativeFunctionNode =
1437       GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1438 
1439   CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1440   const v8::CpuProfileNode* barNode =
1441       GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1442 
1443   // The child is in fact a bound foo.
1444   // A bound function has a wrapper that may make calls to
1445   // other functions e.g. "get length".
1446   CHECK_LE(1, barNode->GetChildrenCount());
1447   CHECK_GE(2, barNode->GetChildrenCount());
1448   GetChild(env->GetIsolate(), barNode, "foo");
1449 
1450   profile->Delete();
1451 }
1452 
1453 
CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value> & info)1454 static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1455   v8::base::OS::Print("In CallJsFunction2\n");
1456   CallJsFunction(info);
1457 }
1458 
1459 
1460 static const char* js_native1_js_native2_js_test_source =
1461     "function foo() {\n"
1462     "  try {\n"
1463     "    startProfiling('my_profile');\n"
1464     "  } catch(e) {}\n"
1465     "}\n"
1466     "function bar() {\n"
1467     "  CallJsFunction2(foo);\n"
1468     "}\n"
1469     "function start() {\n"
1470     "  try {\n"
1471     "    CallJsFunction1(bar);\n"
1472     "  } catch(e) {}\n"
1473     "}";
1474 
1475 
1476 // [Top down]:
1477 //    57     0   (root) #0 1
1478 //    55     1    start #16 3
1479 //    54     0      CallJsFunction1 #0 4
1480 //    54     0        bar #16 5
1481 //    54     0          CallJsFunction2 #0 6
1482 //    54    54            foo #16 7
1483 //     2     2    (program) #0 2
TEST(JsNative1JsNative2JsSample)1484 TEST(JsNative1JsNative2JsSample) {
1485   v8::HandleScope scope(CcTest::isolate());
1486   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1487   v8::Context::Scope context_scope(env);
1488 
1489   v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1490       env->GetIsolate(), CallJsFunction);
1491   v8::Local<v8::Function> func1 = func_template->GetFunction();
1492   func1->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"));
1493   env->Global()->Set(
1494       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"), func1);
1495 
1496   v8::Local<v8::Function> func2 = v8::FunctionTemplate::New(
1497       env->GetIsolate(), CallJsFunction2)->GetFunction();
1498   func2->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"));
1499   env->Global()->Set(
1500       v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"), func2);
1501 
1502   v8::Script::Compile(
1503       v8::String::NewFromUtf8(env->GetIsolate(),
1504                               js_native1_js_native2_js_test_source))->Run();
1505   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1506       env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1507 
1508   v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1509 
1510   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1511   ScopedVector<v8::Handle<v8::String> > names(3);
1512   names[0] = v8::String::NewFromUtf8(
1513       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1514   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1515                                      ProfileGenerator::kProgramEntryName);
1516   names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1517   CheckChildrenNames(root, names);
1518 
1519   const v8::CpuProfileNode* startNode =
1520       GetChild(env->GetIsolate(), root, "start");
1521   CHECK_EQ(1, startNode->GetChildrenCount());
1522   const v8::CpuProfileNode* nativeNode1 =
1523       GetChild(env->GetIsolate(), startNode, "CallJsFunction1");
1524 
1525   CHECK_EQ(1, nativeNode1->GetChildrenCount());
1526   const v8::CpuProfileNode* barNode =
1527       GetChild(env->GetIsolate(), nativeNode1, "bar");
1528 
1529   CHECK_EQ(1, barNode->GetChildrenCount());
1530   const v8::CpuProfileNode* nativeNode2 =
1531       GetChild(env->GetIsolate(), barNode, "CallJsFunction2");
1532 
1533   CHECK_EQ(1, nativeNode2->GetChildrenCount());
1534   GetChild(env->GetIsolate(), nativeNode2, "foo");
1535 
1536   profile->Delete();
1537 }
1538 
1539 
1540 // [Top down]:
1541 //     6     0   (root) #0 1
1542 //     3     3    (program) #0 2
1543 //     3     3    (idle) #0 3
TEST(IdleTime)1544 TEST(IdleTime) {
1545   LocalContext env;
1546   v8::HandleScope scope(env->GetIsolate());
1547   v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1548 
1549   v8::Local<v8::String> profile_name =
1550       v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1551   cpu_profiler->StartProfiling(profile_name);
1552 
1553   i::Isolate* isolate = CcTest::i_isolate();
1554   i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
1555   processor->AddCurrentStack(isolate);
1556 
1557   cpu_profiler->SetIdle(true);
1558 
1559   for (int i = 0; i < 3; i++) {
1560     processor->AddCurrentStack(isolate);
1561   }
1562 
1563   cpu_profiler->SetIdle(false);
1564   processor->AddCurrentStack(isolate);
1565 
1566 
1567   v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1568   CHECK_NE(NULL, profile);
1569   // Dump collected profile to have a better diagnostic in case of failure.
1570   reinterpret_cast<i::CpuProfile*>(profile)->Print();
1571 
1572   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1573   ScopedVector<v8::Handle<v8::String> > names(3);
1574   names[0] = v8::String::NewFromUtf8(
1575       env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1576   names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1577                                      ProfileGenerator::kProgramEntryName);
1578   names[2] = v8::String::NewFromUtf8(env->GetIsolate(),
1579                                      ProfileGenerator::kIdleEntryName);
1580   CheckChildrenNames(root, names);
1581 
1582   const v8::CpuProfileNode* programNode =
1583       GetChild(env->GetIsolate(), root, ProfileGenerator::kProgramEntryName);
1584   CHECK_EQ(0, programNode->GetChildrenCount());
1585   CHECK_GE(programNode->GetHitCount(), 3);
1586 
1587   const v8::CpuProfileNode* idleNode =
1588       GetChild(env->GetIsolate(), root, ProfileGenerator::kIdleEntryName);
1589   CHECK_EQ(0, idleNode->GetChildrenCount());
1590   CHECK_GE(idleNode->GetHitCount(), 3);
1591 
1592   profile->Delete();
1593 }
1594 
1595 
CheckFunctionDetails(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name,const char * script_name,int script_id,int line,int column)1596 static void CheckFunctionDetails(v8::Isolate* isolate,
1597                                  const v8::CpuProfileNode* node,
1598                                  const char* name, const char* script_name,
1599                                  int script_id, int line, int column) {
1600   CHECK_EQ(v8::String::NewFromUtf8(isolate, name),
1601            node->GetFunctionName());
1602   CHECK_EQ(v8::String::NewFromUtf8(isolate, script_name),
1603            node->GetScriptResourceName());
1604   CHECK_EQ(script_id, node->GetScriptId());
1605   CHECK_EQ(line, node->GetLineNumber());
1606   CHECK_EQ(column, node->GetColumnNumber());
1607 }
1608 
1609 
TEST(FunctionDetails)1610 TEST(FunctionDetails) {
1611   v8::HandleScope scope(CcTest::isolate());
1612   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1613   v8::Context::Scope context_scope(env);
1614 
1615   v8::Handle<v8::Script> script_a = CompileWithOrigin(
1616           "    function foo\n() { try { bar(); } catch(e) {} }\n"
1617           " function bar() { startProfiling(); }\n",
1618           "script_a");
1619   script_a->Run();
1620   v8::Handle<v8::Script> script_b = CompileWithOrigin(
1621           "\n\n   function baz() { try { foo(); } catch(e) {} }\n"
1622           "\n\nbaz();\n"
1623           "stopProfiling();\n",
1624           "script_b");
1625   script_b->Run();
1626   const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1627   const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1628   reinterpret_cast<ProfileNode*>(
1629       const_cast<v8::CpuProfileNode*>(current))->Print(0);
1630   // The tree should look like this:
1631   //  0   (root) 0 #1
1632   //  0    "" 19 #2 no reason script_b:1
1633   //  0      baz 19 #3 TryCatchStatement script_b:3
1634   //  0        foo 18 #4 TryCatchStatement script_a:2
1635   //  1          bar 18 #5 no reason script_a:3
1636   const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1637   const v8::CpuProfileNode* script = GetChild(env->GetIsolate(), root, "");
1638   CheckFunctionDetails(env->GetIsolate(), script, "", "script_b",
1639                        script_b->GetUnboundScript()->GetId(), 1, 1);
1640   const v8::CpuProfileNode* baz = GetChild(env->GetIsolate(), script, "baz");
1641   CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b",
1642                        script_b->GetUnboundScript()->GetId(), 3, 16);
1643   const v8::CpuProfileNode* foo = GetChild(env->GetIsolate(), baz, "foo");
1644   CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a",
1645                        script_a->GetUnboundScript()->GetId(), 2, 1);
1646   const v8::CpuProfileNode* bar = GetChild(env->GetIsolate(), foo, "bar");
1647   CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a",
1648                        script_a->GetUnboundScript()->GetId(), 3, 14);
1649 }
1650 
1651 
TEST(DontStopOnFinishedProfileDelete)1652 TEST(DontStopOnFinishedProfileDelete) {
1653   v8::HandleScope scope(CcTest::isolate());
1654   v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1655   v8::Context::Scope context_scope(env);
1656   v8::Isolate* isolate = env->GetIsolate();
1657 
1658   v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler();
1659   i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
1660 
1661   CHECK_EQ(0, iprofiler->GetProfilesCount());
1662   v8::Handle<v8::String> outer = v8::String::NewFromUtf8(isolate, "outer");
1663   profiler->StartProfiling(outer);
1664   CHECK_EQ(0, iprofiler->GetProfilesCount());
1665 
1666   v8::Handle<v8::String> inner = v8::String::NewFromUtf8(isolate, "inner");
1667   profiler->StartProfiling(inner);
1668   CHECK_EQ(0, iprofiler->GetProfilesCount());
1669 
1670   v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
1671   CHECK(inner_profile);
1672   CHECK_EQ(1, iprofiler->GetProfilesCount());
1673   inner_profile->Delete();
1674   inner_profile = NULL;
1675   CHECK_EQ(0, iprofiler->GetProfilesCount());
1676 
1677   v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
1678   CHECK(outer_profile);
1679   CHECK_EQ(1, iprofiler->GetProfilesCount());
1680   outer_profile->Delete();
1681   outer_profile = NULL;
1682   CHECK_EQ(0, iprofiler->GetProfilesCount());
1683 }
1684