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