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