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