The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / test / cctest / test-cpu-profiler.cc @ f230a1cf

History | View | Annotate | Download (49.2 KB)

1
// Copyright 2010 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
4
// met:
5
//
6
//     * Redistributions of source code must retain the above copyright
7
//       notice, this list of conditions and the following disclaimer.
8
//     * Redistributions in binary form must reproduce the above
9
//       copyright notice, this list of conditions and the following
10
//       disclaimer in the documentation and/or other materials provided
11
//       with the distribution.
12
//     * Neither the name of Google Inc. nor the names of its
13
//       contributors may be used to endorse or promote products derived
14
//       from this software without specific prior written permission.
15
//
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
//
28
// Tests of profiles generator and utilities.
29

    
30
#include "v8.h"
31
#include "cpu-profiler-inl.h"
32
#include "cctest.h"
33
#include "platform.h"
34
#include "smart-pointers.h"
35
#include "utils.h"
36
#include "../include/v8-profiler.h"
37
using i::CodeEntry;
38
using i::CpuProfile;
39
using i::CpuProfiler;
40
using i::CpuProfilesCollection;
41
using i::Heap;
42
using i::ProfileGenerator;
43
using i::ProfileNode;
44
using i::ProfilerEventsProcessor;
45
using i::ScopedVector;
46
using i::SmartPointer;
47
using i::TimeDelta;
48
using i::Vector;
49

    
50

    
51
TEST(StartStop) {
52
  i::Isolate* isolate = CcTest::i_isolate();
53
  CpuProfilesCollection profiles(isolate->heap());
54
  ProfileGenerator generator(&profiles);
55
  SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
56
          &generator, NULL, TimeDelta::FromMicroseconds(100)));
57
  processor->Start();
58
  processor->StopSynchronously();
59
}
60

    
61

    
62
static inline i::Address ToAddress(int n) {
63
  return reinterpret_cast<i::Address>(n);
64
}
65

    
66
static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
67
                                   i::Address frame1,
68
                                   i::Address frame2 = NULL,
69
                                   i::Address frame3 = NULL) {
70
  i::TickSample* sample = proc->StartTickSample();
71
  sample->pc = frame1;
72
  sample->tos = frame1;
73
  sample->frames_count = 0;
74
  if (frame2 != NULL) {
75
    sample->stack[0] = frame2;
76
    sample->frames_count = 1;
77
  }
78
  if (frame3 != NULL) {
79
    sample->stack[1] = frame3;
80
    sample->frames_count = 2;
81
  }
82
  proc->FinishTickSample();
83
}
84

    
85
namespace {
86

    
87
class TestSetup {
88
 public:
89
  TestSetup()
90
      : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
91
    i::FLAG_prof_browser_mode = false;
92
  }
93

    
94
  ~TestSetup() {
95
    i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
96
  }
97

    
98
 private:
99
  bool old_flag_prof_browser_mode_;
100
};
101

    
102
}  // namespace
103

    
104

    
105
i::Code* CreateCode(LocalContext* env) {
106
  static int counter = 0;
107
  i::EmbeddedVector<char, 256> script;
108
  i::EmbeddedVector<char, 32> name;
109

    
110
  i::OS::SNPrintF(name, "function_%d", ++counter);
111
  const char* name_start = name.start();
112
  i::OS::SNPrintF(script,
113
      "function %s() {\n"
114
           "var counter = 0;\n"
115
           "for (var i = 0; i < %d; ++i) counter += i;\n"
116
           "return '%s_' + counter;\n"
117
       "}\n"
118
       "%s();\n", name_start, counter, name_start, name_start);
119
  CompileRun(script.start());
120
  i::Handle<i::JSFunction> fun = v8::Utils::OpenHandle(
121
      *v8::Local<v8::Function>::Cast(
122
          (*env)->Global()->Get(v8_str(name_start))));
123
  return fun->code();
124
}
125

    
126

    
127
TEST(CodeEvents) {
128
  CcTest::InitializeVM();
129
  LocalContext env;
130
  i::Isolate* isolate = CcTest::i_isolate();
131
  i::Factory* factory = isolate->factory();
132
  TestSetup test_setup;
133

    
134
  i::HandleScope scope(isolate);
135

    
136
  i::Code* aaa_code = CreateCode(&env);
137
  i::Code* comment_code = CreateCode(&env);
138
  i::Code* args5_code = CreateCode(&env);
139
  i::Code* comment2_code = CreateCode(&env);
140
  i::Code* moved_code = CreateCode(&env);
141
  i::Code* args3_code = CreateCode(&env);
142
  i::Code* args4_code = CreateCode(&env);
143

    
144
  CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
145
  profiles->StartProfiling("", 1, false);
146
  ProfileGenerator generator(profiles);
147
  SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
148
          &generator, NULL, TimeDelta::FromMicroseconds(100)));
149
  processor->Start();
150
  CpuProfiler profiler(isolate, profiles, &generator, *processor);
151

    
152
  // Enqueue code creation events.
153
  const char* aaa_str = "aaa";
154
  i::Handle<i::String> aaa_name = factory->NewStringFromAscii(
155
      i::Vector<const char>(aaa_str, i::StrLength(aaa_str)));
156
  profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code, *aaa_name);
157
  profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code, "comment");
158
  profiler.CodeCreateEvent(i::Logger::STUB_TAG, args5_code, 5);
159
  profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code, "comment2");
160
  profiler.CodeMoveEvent(comment2_code->address(), moved_code->address());
161
  profiler.CodeCreateEvent(i::Logger::STUB_TAG, args3_code, 3);
162
  profiler.CodeCreateEvent(i::Logger::STUB_TAG, args4_code, 4);
163

    
164
  // Enqueue a tick event to enable code events processing.
165
  EnqueueTickSampleEvent(*processor, aaa_code->address());
166

    
167
  processor->StopSynchronously();
168

    
169
  // Check the state of profile generator.
170
  CodeEntry* aaa = generator.code_map()->FindEntry(aaa_code->address());
171
  CHECK_NE(NULL, aaa);
172
  CHECK_EQ(aaa_str, aaa->name());
173

    
174
  CodeEntry* comment = generator.code_map()->FindEntry(comment_code->address());
175
  CHECK_NE(NULL, comment);
176
  CHECK_EQ("comment", comment->name());
177

    
178
  CodeEntry* args5 = generator.code_map()->FindEntry(args5_code->address());
179
  CHECK_NE(NULL, args5);
180
  CHECK_EQ("5", args5->name());
181

    
182
  CHECK_EQ(NULL, generator.code_map()->FindEntry(comment2_code->address()));
183

    
184
  CodeEntry* comment2 = generator.code_map()->FindEntry(moved_code->address());
185
  CHECK_NE(NULL, comment2);
186
  CHECK_EQ("comment2", comment2->name());
187
}
188

    
189

    
190
template<typename T>
191
static int CompareProfileNodes(const T* p1, const T* p2) {
192
  return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
193
}
194

    
195

    
196
TEST(TickEvents) {
197
  TestSetup test_setup;
198
  LocalContext env;
199
  i::Isolate* isolate = CcTest::i_isolate();
200
  i::HandleScope scope(isolate);
201

    
202
  i::Code* frame1_code = CreateCode(&env);
203
  i::Code* frame2_code = CreateCode(&env);
204
  i::Code* frame3_code = CreateCode(&env);
205

    
206
  CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
207
  profiles->StartProfiling("", 1, false);
208
  ProfileGenerator generator(profiles);
209
  SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
210
          &generator, NULL, TimeDelta::FromMicroseconds(100)));
211
  processor->Start();
212
  CpuProfiler profiler(isolate, profiles, &generator, *processor);
213

    
214
  profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
215
  profiler.CodeCreateEvent(i::Logger::STUB_TAG, frame2_code, 5);
216
  profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame3_code, "ddd");
217

    
218
  EnqueueTickSampleEvent(*processor, frame1_code->instruction_start());
219
  EnqueueTickSampleEvent(
220
      *processor,
221
      frame2_code->instruction_start() + frame2_code->ExecutableSize() / 2,
222
      frame1_code->instruction_start() + frame2_code->ExecutableSize() / 2);
223
  EnqueueTickSampleEvent(
224
      *processor,
225
      frame3_code->instruction_end() - 1,
226
      frame2_code->instruction_end() - 1,
227
      frame1_code->instruction_end() - 1);
228

    
229
  processor->StopSynchronously();
230
  CpuProfile* profile = profiles->StopProfiling("");
231
  CHECK_NE(NULL, profile);
232

    
233
  // Check call trees.
234
  const i::List<ProfileNode*>* top_down_root_children =
235
      profile->top_down()->root()->children();
236
  CHECK_EQ(1, top_down_root_children->length());
237
  CHECK_EQ("bbb", top_down_root_children->last()->entry()->name());
238
  const i::List<ProfileNode*>* top_down_bbb_children =
239
      top_down_root_children->last()->children();
240
  CHECK_EQ(1, top_down_bbb_children->length());
241
  CHECK_EQ("5", top_down_bbb_children->last()->entry()->name());
242
  const i::List<ProfileNode*>* top_down_stub_children =
243
      top_down_bbb_children->last()->children();
244
  CHECK_EQ(1, top_down_stub_children->length());
245
  CHECK_EQ("ddd", top_down_stub_children->last()->entry()->name());
246
  const i::List<ProfileNode*>* top_down_ddd_children =
247
      top_down_stub_children->last()->children();
248
  CHECK_EQ(0, top_down_ddd_children->length());
249
}
250

    
251

    
252
// http://crbug/51594
253
// This test must not crash.
254
TEST(CrashIfStoppingLastNonExistentProfile) {
255
  CcTest::InitializeVM();
256
  TestSetup test_setup;
257
  CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
258
  profiler->StartProfiling("1");
259
  profiler->StopProfiling("2");
260
  profiler->StartProfiling("1");
261
  profiler->StopProfiling("");
262
}
263

    
264

    
265
// http://code.google.com/p/v8/issues/detail?id=1398
266
// Long stacks (exceeding max frames limit) must not be erased.
267
TEST(Issue1398) {
268
  TestSetup test_setup;
269
  LocalContext env;
270
  i::Isolate* isolate = CcTest::i_isolate();
271
  i::HandleScope scope(isolate);
272

    
273
  i::Code* code = CreateCode(&env);
274

    
275
  CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
276
  profiles->StartProfiling("", 1, false);
277
  ProfileGenerator generator(profiles);
278
  SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
279
          &generator, NULL, TimeDelta::FromMicroseconds(100)));
280
  processor->Start();
281
  CpuProfiler profiler(isolate, profiles, &generator, *processor);
282

    
283
  profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
284

    
285
  i::TickSample* sample = processor->StartTickSample();
286
  sample->pc = code->address();
287
  sample->tos = 0;
288
  sample->frames_count = i::TickSample::kMaxFramesCount;
289
  for (int i = 0; i < sample->frames_count; ++i) {
290
    sample->stack[i] = code->address();
291
  }
292
  processor->FinishTickSample();
293

    
294
  processor->StopSynchronously();
295
  CpuProfile* profile = profiles->StopProfiling("");
296
  CHECK_NE(NULL, profile);
297

    
298
  int actual_depth = 0;
299
  const ProfileNode* node = profile->top_down()->root();
300
  while (node->children()->length() > 0) {
301
    node = node->children()->last();
302
    ++actual_depth;
303
  }
304

    
305
  CHECK_EQ(1 + i::TickSample::kMaxFramesCount, actual_depth);  // +1 for PC.
306
}
307

    
308

    
309
TEST(DeleteAllCpuProfiles) {
310
  CcTest::InitializeVM();
311
  TestSetup test_setup;
312
  CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
313
  CHECK_EQ(0, profiler->GetProfilesCount());
314
  profiler->DeleteAllProfiles();
315
  CHECK_EQ(0, profiler->GetProfilesCount());
316

    
317
  profiler->StartProfiling("1");
318
  profiler->StopProfiling("1");
319
  CHECK_EQ(1, profiler->GetProfilesCount());
320
  profiler->DeleteAllProfiles();
321
  CHECK_EQ(0, profiler->GetProfilesCount());
322
  profiler->StartProfiling("1");
323
  profiler->StartProfiling("2");
324
  profiler->StopProfiling("2");
325
  profiler->StopProfiling("1");
326
  CHECK_EQ(2, profiler->GetProfilesCount());
327
  profiler->DeleteAllProfiles();
328
  CHECK_EQ(0, profiler->GetProfilesCount());
329

    
330
  // Test profiling cancellation by the 'delete' command.
331
  profiler->StartProfiling("1");
332
  profiler->StartProfiling("2");
333
  CHECK_EQ(0, profiler->GetProfilesCount());
334
  profiler->DeleteAllProfiles();
335
  CHECK_EQ(0, profiler->GetProfilesCount());
336
}
337

    
338

    
339
static const v8::CpuProfile* FindCpuProfile(v8::CpuProfiler* profiler,
340
                                            unsigned uid) {
341
  int length = profiler->GetProfileCount();
342
  for (int i = 0; i < length; i++) {
343
    const v8::CpuProfile* profile = profiler->GetCpuProfile(i);
344
    if (profile->GetUid() == uid) {
345
      return profile;
346
    }
347
  }
348
  return NULL;
349
}
350

    
351

    
352
TEST(DeleteCpuProfile) {
353
  LocalContext env;
354
  v8::HandleScope scope(env->GetIsolate());
355
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
356

    
357
  CHECK_EQ(0, cpu_profiler->GetProfileCount());
358
  v8::Local<v8::String> name1 = v8::String::New("1");
359
  cpu_profiler->StartCpuProfiling(name1);
360
  const v8::CpuProfile* p1 = cpu_profiler->StopCpuProfiling(name1);
361
  CHECK_NE(NULL, p1);
362
  CHECK_EQ(1, cpu_profiler->GetProfileCount());
363
  unsigned uid1 = p1->GetUid();
364
  CHECK_EQ(p1, FindCpuProfile(cpu_profiler, uid1));
365
  const_cast<v8::CpuProfile*>(p1)->Delete();
366
  CHECK_EQ(0, cpu_profiler->GetProfileCount());
367
  CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1));
368

    
369
  v8::Local<v8::String> name2 = v8::String::New("2");
370
  cpu_profiler->StartCpuProfiling(name2);
371
  const v8::CpuProfile* p2 = cpu_profiler->StopCpuProfiling(name2);
372
  CHECK_NE(NULL, p2);
373
  CHECK_EQ(1, cpu_profiler->GetProfileCount());
374
  unsigned uid2 = p2->GetUid();
375
  CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
376
  CHECK_EQ(p2, FindCpuProfile(cpu_profiler, uid2));
377
  CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1));
378
  v8::Local<v8::String> name3 = v8::String::New("3");
379
  cpu_profiler->StartCpuProfiling(name3);
380
  const v8::CpuProfile* p3 = cpu_profiler->StopCpuProfiling(name3);
381
  CHECK_NE(NULL, p3);
382
  CHECK_EQ(2, cpu_profiler->GetProfileCount());
383
  unsigned uid3 = p3->GetUid();
384
  CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
385
  CHECK_EQ(p3, FindCpuProfile(cpu_profiler, uid3));
386
  CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1));
387
  const_cast<v8::CpuProfile*>(p2)->Delete();
388
  CHECK_EQ(1, cpu_profiler->GetProfileCount());
389
  CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid2));
390
  CHECK_EQ(p3, FindCpuProfile(cpu_profiler, uid3));
391
  const_cast<v8::CpuProfile*>(p3)->Delete();
392
  CHECK_EQ(0, cpu_profiler->GetProfileCount());
393
  CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid3));
394
  CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid2));
395
  CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1));
396
}
397

    
398

    
399
TEST(ProfileStartEndTime) {
400
  LocalContext env;
401
  v8::HandleScope scope(env->GetIsolate());
402
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
403

    
404
  v8::Local<v8::String> profile_name = v8::String::New("test");
405
  cpu_profiler->StartCpuProfiling(profile_name);
406
  const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name);
407
  CHECK(profile->GetStartTime() <= profile->GetEndTime());
408
}
409

    
410

    
411
static const v8::CpuProfile* RunProfiler(
412
    LocalContext& env, v8::Handle<v8::Function> function,
413
    v8::Handle<v8::Value> argv[], int argc,
414
    unsigned min_js_samples) {
415
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
416
  v8::Local<v8::String> profile_name = v8::String::New("my_profile");
417

    
418
  cpu_profiler->StartCpuProfiling(profile_name);
419

    
420
  i::Sampler* sampler =
421
      reinterpret_cast<i::Isolate*>(env->GetIsolate())->logger()->sampler();
422
  sampler->StartCountingSamples();
423
  do {
424
    function->Call(env->Global(), argc, argv);
425
  } while (sampler->js_and_external_sample_count() < min_js_samples);
426

    
427
  const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name);
428

    
429
  CHECK_NE(NULL, profile);
430
  // Dump collected profile to have a better diagnostic in case of failure.
431
  reinterpret_cast<i::CpuProfile*>(
432
      const_cast<v8::CpuProfile*>(profile))->Print();
433

    
434
  return profile;
435
}
436

    
437

    
438
static bool ContainsString(v8::Handle<v8::String> string,
439
                           const Vector<v8::Handle<v8::String> >& vector) {
440
  for (int i = 0; i < vector.length(); i++) {
441
    if (string->Equals(vector[i]))
442
      return true;
443
  }
444
  return false;
445
}
446

    
447

    
448
static void CheckChildrenNames(const v8::CpuProfileNode* node,
449
                               const Vector<v8::Handle<v8::String> >& names) {
450
  int count = node->GetChildrenCount();
451
  for (int i = 0; i < count; i++) {
452
    v8::Handle<v8::String> name = node->GetChild(i)->GetFunctionName();
453
    CHECK(ContainsString(name, names));
454
    // Check that there are no duplicates.
455
    for (int j = 0; j < count; j++) {
456
      if (j == i) continue;
457
      CHECK_NE(name, node->GetChild(j)->GetFunctionName());
458
    }
459
  }
460
}
461

    
462

    
463
static const v8::CpuProfileNode* FindChild(const v8::CpuProfileNode* node,
464
                                           const char* name) {
465
  int count = node->GetChildrenCount();
466
  v8::Handle<v8::String> nameHandle = v8::String::New(name);
467
  for (int i = 0; i < count; i++) {
468
    const v8::CpuProfileNode* child = node->GetChild(i);
469
    if (nameHandle->Equals(child->GetFunctionName())) return child;
470
  }
471
  return NULL;
472
}
473

    
474

    
475
static const v8::CpuProfileNode* GetChild(const v8::CpuProfileNode* node,
476
                                          const char* name) {
477
  const v8::CpuProfileNode* result = FindChild(node, name);
478
  if (!result) {
479
    char buffer[100];
480
    i::OS::SNPrintF(Vector<char>(buffer, ARRAY_SIZE(buffer)),
481
                    "Failed to GetChild: %s", name);
482
    FATAL(buffer);
483
  }
484
  return result;
485
}
486

    
487

    
488
static void CheckSimpleBranch(const v8::CpuProfileNode* node,
489
                              const char* names[], int length) {
490
  for (int i = 0; i < length; i++) {
491
    const char* name = names[i];
492
    node = GetChild(node, name);
493
    int expectedChildrenCount = (i == length - 1) ? 0 : 1;
494
    CHECK_EQ(expectedChildrenCount, node->GetChildrenCount());
495
  }
496
}
497

    
498

    
499
static const char* cpu_profiler_test_source = "function loop(timeout) {\n"
500
"  this.mmm = 0;\n"
501
"  var start = Date.now();\n"
502
"  while (Date.now() - start < timeout) {\n"
503
"    var n = 100*1000;\n"
504
"    while(n > 1) {\n"
505
"      n--;\n"
506
"      this.mmm += n * n * n;\n"
507
"    }\n"
508
"  }\n"
509
"}\n"
510
"function delay() { try { loop(10); } catch(e) { } }\n"
511
"function bar() { delay(); }\n"
512
"function baz() { delay(); }\n"
513
"function foo() {\n"
514
"    try {\n"
515
"       delay();\n"
516
"       bar();\n"
517
"       delay();\n"
518
"       baz();\n"
519
"    } catch (e) { }\n"
520
"}\n"
521
"function start(timeout) {\n"
522
"  var start = Date.now();\n"
523
"  do {\n"
524
"    foo();\n"
525
"    var duration = Date.now() - start;\n"
526
"  } while (duration < timeout);\n"
527
"  return duration;\n"
528
"}\n";
529

    
530

    
531
// Check that the profile tree for the script above will look like the
532
// following:
533
//
534
// [Top down]:
535
//  1062     0   (root) [-1]
536
//  1054     0    start [-1]
537
//  1054     1      foo [-1]
538
//   265     0        baz [-1]
539
//   265     1          delay [-1]
540
//   264   264            loop [-1]
541
//   525     3        delay [-1]
542
//   522   522          loop [-1]
543
//   263     0        bar [-1]
544
//   263     1          delay [-1]
545
//   262   262            loop [-1]
546
//     2     2    (program) [-1]
547
//     6     6    (garbage collector) [-1]
548
TEST(CollectCpuProfile) {
549
  LocalContext env;
550
  v8::HandleScope scope(env->GetIsolate());
551

    
552
  v8::Script::Compile(v8::String::New(cpu_profiler_test_source))->Run();
553
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
554
      env->Global()->Get(v8::String::New("start")));
555

    
556
  int32_t profiling_interval_ms = 200;
557
  v8::Handle<v8::Value> args[] = { v8::Integer::New(profiling_interval_ms) };
558
  const v8::CpuProfile* profile =
559
      RunProfiler(env, function, args, ARRAY_SIZE(args), 200);
560
  function->Call(env->Global(), ARRAY_SIZE(args), args);
561

    
562
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
563

    
564
  ScopedVector<v8::Handle<v8::String> > names(3);
565
  names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
566
  names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
567
  names[2] = v8::String::New("start");
568
  CheckChildrenNames(root, names);
569

    
570
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
571
  CHECK_EQ(1, startNode->GetChildrenCount());
572

    
573
  const v8::CpuProfileNode* fooNode = GetChild(startNode, "foo");
574
  CHECK_EQ(3, fooNode->GetChildrenCount());
575

    
576
  const char* barBranch[] = { "bar", "delay", "loop" };
577
  CheckSimpleBranch(fooNode, barBranch, ARRAY_SIZE(barBranch));
578
  const char* bazBranch[] = { "baz", "delay", "loop" };
579
  CheckSimpleBranch(fooNode, bazBranch, ARRAY_SIZE(bazBranch));
580
  const char* delayBranch[] = { "delay", "loop" };
581
  CheckSimpleBranch(fooNode, delayBranch, ARRAY_SIZE(delayBranch));
582

    
583
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
584
  cpu_profiler->DeleteAllCpuProfiles();
585
}
586

    
587

    
588

    
589
static const char* cpu_profiler_test_source2 = "function loop() {}\n"
590
"function delay() { loop(); }\n"
591
"function start(count) {\n"
592
"  var k = 0;\n"
593
"  do {\n"
594
"    delay();\n"
595
"  } while (++k < count*100*1000);\n"
596
"}\n";
597

    
598
// Check that the profile tree doesn't contain unexpected traces:
599
//  - 'loop' can be called only by 'delay'
600
//  - 'delay' may be called only by 'start'
601
// The profile will look like the following:
602
//
603
// [Top down]:
604
//   135     0   (root) [-1] #1
605
//   121    72    start [-1] #3
606
//    49    33      delay [-1] #4
607
//    16    16        loop [-1] #5
608
//    14    14    (program) [-1] #2
609
TEST(SampleWhenFrameIsNotSetup) {
610
  LocalContext env;
611
  v8::HandleScope scope(env->GetIsolate());
612

    
613
  v8::Script::Compile(v8::String::New(cpu_profiler_test_source2))->Run();
614
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
615
      env->Global()->Get(v8::String::New("start")));
616

    
617
  int32_t repeat_count = 100;
618
#if defined(USE_SIMULATOR)
619
  // Simulators are much slower.
620
  repeat_count = 1;
621
#endif
622
  v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) };
623
  const v8::CpuProfile* profile =
624
      RunProfiler(env, function, args, ARRAY_SIZE(args), 100);
625

    
626
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
627

    
628
  ScopedVector<v8::Handle<v8::String> > names(3);
629
  names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
630
  names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
631
  names[2] = v8::String::New("start");
632
  CheckChildrenNames(root, names);
633

    
634
  const v8::CpuProfileNode* startNode = FindChild(root, "start");
635
  // On slow machines there may be no meaningfull samples at all, skip the
636
  // check there.
637
  if (startNode && startNode->GetChildrenCount() > 0) {
638
    CHECK_EQ(1, startNode->GetChildrenCount());
639
    const v8::CpuProfileNode* delayNode = GetChild(startNode, "delay");
640
    if (delayNode->GetChildrenCount() > 0) {
641
      CHECK_EQ(1, delayNode->GetChildrenCount());
642
      GetChild(delayNode, "loop");
643
    }
644
  }
645

    
646
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
647
  cpu_profiler->DeleteAllCpuProfiles();
648
}
649

    
650

    
651
static const char* native_accessor_test_source = "function start(count) {\n"
652
"  for (var i = 0; i < count; i++) {\n"
653
"    var o = instance.foo;\n"
654
"    instance.foo = o + 1;\n"
655
"  }\n"
656
"}\n";
657

    
658

    
659
class TestApiCallbacks {
660
 public:
661
  explicit TestApiCallbacks(int min_duration_ms)
662
      : min_duration_ms_(min_duration_ms),
663
        is_warming_up_(false) {}
664

    
665
  static void Getter(v8::Local<v8::String> name,
666
                     const v8::PropertyCallbackInfo<v8::Value>& info) {
667
    TestApiCallbacks* data = fromInfo(info);
668
    data->Wait();
669
  }
670

    
671
  static void Setter(v8::Local<v8::String> name,
672
                     v8::Local<v8::Value> value,
673
                     const v8::PropertyCallbackInfo<void>& info) {
674
    TestApiCallbacks* data = fromInfo(info);
675
    data->Wait();
676
  }
677

    
678
  static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
679
    TestApiCallbacks* data = fromInfo(info);
680
    data->Wait();
681
  }
682

    
683
  void set_warming_up(bool value) { is_warming_up_ = value; }
684

    
685
 private:
686
  void Wait() {
687
    if (is_warming_up_) return;
688
    double start = i::OS::TimeCurrentMillis();
689
    double duration = 0;
690
    while (duration < min_duration_ms_) {
691
      i::OS::Sleep(1);
692
      duration = i::OS::TimeCurrentMillis() - start;
693
    }
694
  }
695

    
696
  template<typename T>
697
  static TestApiCallbacks* fromInfo(const T& info) {
698
    void* data = v8::External::Cast(*info.Data())->Value();
699
    return reinterpret_cast<TestApiCallbacks*>(data);
700
  }
701

    
702
  int min_duration_ms_;
703
  bool is_warming_up_;
704
};
705

    
706

    
707
// Test that native accessors are properly reported in the CPU profile.
708
// This test checks the case when the long-running accessors are called
709
// only once and the optimizer doesn't have chance to change the invocation
710
// code.
711
TEST(NativeAccessorUninitializedIC) {
712
  LocalContext env;
713
  v8::HandleScope scope(env->GetIsolate());
714

    
715

    
716
  v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
717
  v8::Local<v8::ObjectTemplate> instance_template =
718
      func_template->InstanceTemplate();
719

    
720
  TestApiCallbacks accessors(100);
721
  v8::Local<v8::External> data = v8::External::New(&accessors);
722
  instance_template->SetAccessor(
723
      v8::String::New("foo"), &TestApiCallbacks::Getter,
724
      &TestApiCallbacks::Setter, data);
725
  v8::Local<v8::Function> func = func_template->GetFunction();
726
  v8::Local<v8::Object> instance = func->NewInstance();
727
  env->Global()->Set(v8::String::New("instance"), instance);
728

    
729
  v8::Script::Compile(v8::String::New(native_accessor_test_source))->Run();
730
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
731
      env->Global()->Get(v8::String::New("start")));
732

    
733
  int32_t repeat_count = 1;
734
  v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) };
735
  const v8::CpuProfile* profile =
736
      RunProfiler(env, function, args, ARRAY_SIZE(args), 180);
737

    
738
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
739
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
740
  GetChild(startNode, "get foo");
741
  GetChild(startNode, "set foo");
742

    
743
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
744
  cpu_profiler->DeleteAllCpuProfiles();
745
}
746

    
747

    
748
// Test that native accessors are properly reported in the CPU profile.
749
// This test makes sure that the accessors are called enough times to become
750
// hot and to trigger optimizations.
751
TEST(NativeAccessorMonomorphicIC) {
752
  LocalContext env;
753
  v8::HandleScope scope(env->GetIsolate());
754

    
755

    
756
  v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
757
  v8::Local<v8::ObjectTemplate> instance_template =
758
      func_template->InstanceTemplate();
759

    
760
  TestApiCallbacks accessors(1);
761
  v8::Local<v8::External> data = v8::External::New(&accessors);
762
  instance_template->SetAccessor(
763
      v8::String::New("foo"), &TestApiCallbacks::Getter,
764
      &TestApiCallbacks::Setter, data);
765
  v8::Local<v8::Function> func = func_template->GetFunction();
766
  v8::Local<v8::Object> instance = func->NewInstance();
767
  env->Global()->Set(v8::String::New("instance"), instance);
768

    
769
  v8::Script::Compile(v8::String::New(native_accessor_test_source))->Run();
770
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
771
      env->Global()->Get(v8::String::New("start")));
772

    
773
  {
774
    // Make sure accessors ICs are in monomorphic state before starting
775
    // profiling.
776
    accessors.set_warming_up(true);
777
    int32_t warm_up_iterations = 3;
778
    v8::Handle<v8::Value> args[] = { v8::Integer::New(warm_up_iterations) };
779
    function->Call(env->Global(), ARRAY_SIZE(args), args);
780
    accessors.set_warming_up(false);
781
  }
782

    
783
  int32_t repeat_count = 100;
784
  v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) };
785
  const v8::CpuProfile* profile =
786
      RunProfiler(env, function, args, ARRAY_SIZE(args), 200);
787

    
788
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
789
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
790
  GetChild(startNode, "get foo");
791
  GetChild(startNode, "set foo");
792

    
793
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
794
  cpu_profiler->DeleteAllCpuProfiles();
795
}
796

    
797

    
798
static const char* native_method_test_source = "function start(count) {\n"
799
"  for (var i = 0; i < count; i++) {\n"
800
"    instance.fooMethod();\n"
801
"  }\n"
802
"}\n";
803

    
804

    
805
TEST(NativeMethodUninitializedIC) {
806
  LocalContext env;
807
  v8::HandleScope scope(env->GetIsolate());
808

    
809
  TestApiCallbacks callbacks(100);
810
  v8::Local<v8::External> data = v8::External::New(&callbacks);
811

    
812
  v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
813
  func_template->SetClassName(v8::String::New("Test_InstanceCostructor"));
814
  v8::Local<v8::ObjectTemplate> proto_template =
815
      func_template->PrototypeTemplate();
816
  v8::Local<v8::Signature> signature = v8::Signature::New(func_template);
817
  proto_template->Set(v8::String::New("fooMethod"), v8::FunctionTemplate::New(
818
      &TestApiCallbacks::Callback, data, signature, 0));
819

    
820
  v8::Local<v8::Function> func = func_template->GetFunction();
821
  v8::Local<v8::Object> instance = func->NewInstance();
822
  env->Global()->Set(v8::String::New("instance"), instance);
823

    
824
  v8::Script::Compile(v8::String::New(native_method_test_source))->Run();
825
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
826
      env->Global()->Get(v8::String::New("start")));
827

    
828
  int32_t repeat_count = 1;
829
  v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) };
830
  const v8::CpuProfile* profile =
831
      RunProfiler(env, function, args, ARRAY_SIZE(args), 100);
832

    
833
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
834
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
835
  GetChild(startNode, "fooMethod");
836

    
837
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
838
  cpu_profiler->DeleteAllCpuProfiles();
839
}
840

    
841

    
842
TEST(NativeMethodMonomorphicIC) {
843
  LocalContext env;
844
  v8::HandleScope scope(env->GetIsolate());
845

    
846
  TestApiCallbacks callbacks(1);
847
  v8::Local<v8::External> data = v8::External::New(&callbacks);
848

    
849
  v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
850
  func_template->SetClassName(v8::String::New("Test_InstanceCostructor"));
851
  v8::Local<v8::ObjectTemplate> proto_template =
852
      func_template->PrototypeTemplate();
853
  v8::Local<v8::Signature> signature = v8::Signature::New(func_template);
854
  proto_template->Set(v8::String::New("fooMethod"), v8::FunctionTemplate::New(
855
      &TestApiCallbacks::Callback, data, signature, 0));
856

    
857
  v8::Local<v8::Function> func = func_template->GetFunction();
858
  v8::Local<v8::Object> instance = func->NewInstance();
859
  env->Global()->Set(v8::String::New("instance"), instance);
860

    
861
  v8::Script::Compile(v8::String::New(native_method_test_source))->Run();
862
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
863
      env->Global()->Get(v8::String::New("start")));
864
  {
865
    // Make sure method ICs are in monomorphic state before starting
866
    // profiling.
867
    callbacks.set_warming_up(true);
868
    int32_t warm_up_iterations = 3;
869
    v8::Handle<v8::Value> args[] = { v8::Integer::New(warm_up_iterations) };
870
    function->Call(env->Global(), ARRAY_SIZE(args), args);
871
    callbacks.set_warming_up(false);
872
  }
873

    
874
  int32_t repeat_count = 100;
875
  v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) };
876
  const v8::CpuProfile* profile =
877
      RunProfiler(env, function, args, ARRAY_SIZE(args), 100);
878

    
879
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
880
  GetChild(root, "start");
881
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
882
  GetChild(startNode, "fooMethod");
883

    
884
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
885
  cpu_profiler->DeleteAllCpuProfiles();
886
}
887

    
888

    
889
static const char* bound_function_test_source = "function foo(iterations) {\n"
890
"  var r = 0;\n"
891
"  for (var i = 0; i < iterations; i++) { r += i; }\n"
892
"  return r;\n"
893
"}\n"
894
"function start(duration) {\n"
895
"  var callback = foo.bind(this);\n"
896
"  var start = Date.now();\n"
897
"  while (Date.now() - start < duration) {\n"
898
"    callback(10 * 1000);\n"
899
"  }\n"
900
"}";
901

    
902

    
903
TEST(BoundFunctionCall) {
904
  LocalContext env;
905
  v8::HandleScope scope(env->GetIsolate());
906

    
907
  v8::Script::Compile(v8::String::New(bound_function_test_source))->Run();
908
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
909
      env->Global()->Get(v8::String::New("start")));
910

    
911
  int32_t duration_ms = 100;
912
  v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) };
913
  const v8::CpuProfile* profile =
914
      RunProfiler(env, function, args, ARRAY_SIZE(args), 100);
915

    
916
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
917
  ScopedVector<v8::Handle<v8::String> > names(3);
918
  names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
919
  names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
920
  names[2] = v8::String::New("start");
921
  // Don't allow |foo| node to be at the top level.
922
  CheckChildrenNames(root, names);
923

    
924
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
925
  GetChild(startNode, "foo");
926

    
927
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
928
  cpu_profiler->DeleteAllCpuProfiles();
929
}
930

    
931

    
932
static const char* call_function_test_source = "function bar(iterations) {\n"
933
"}\n"
934
"function start(duration) {\n"
935
"  var start = Date.now();\n"
936
"  while (Date.now() - start < duration) {\n"
937
"    try {\n"
938
"      bar.call(this, 10 * 1000);\n"
939
"    } catch(e) {}\n"
940
"  }\n"
941
"}";
942

    
943

    
944
// Test that if we sampled thread when it was inside FunctionCall buitin then
945
// its caller frame will be '(unresolved function)' as we have no reliable way
946
// to resolve it.
947
//
948
// [Top down]:
949
//    96     0   (root) [-1] #1
950
//     1     1    (garbage collector) [-1] #4
951
//     5     0    (unresolved function) [-1] #5
952
//     5     5      call [-1] #6
953
//    71    70    start [-1] #3
954
//     1     1      bar [-1] #7
955
//    19    19    (program) [-1] #2
956
TEST(FunctionCallSample) {
957
  LocalContext env;
958
  v8::HandleScope scope(env->GetIsolate());
959

    
960
  // Collect garbage that might have be generated while installing extensions.
961
  CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
962

    
963
  v8::Script::Compile(v8::String::New(call_function_test_source))->Run();
964
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
965
      env->Global()->Get(v8::String::New("start")));
966

    
967
  int32_t duration_ms = 100;
968
  v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) };
969
  const v8::CpuProfile* profile =
970
      RunProfiler(env, function, args, ARRAY_SIZE(args), 100);
971

    
972
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
973
  {
974
    ScopedVector<v8::Handle<v8::String> > names(4);
975
    names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
976
    names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
977
    names[2] = v8::String::New("start");
978
    names[3] = v8::String::New(i::ProfileGenerator::kUnresolvedFunctionName);
979
    // Don't allow |bar| and |call| nodes to be at the top level.
980
    CheckChildrenNames(root, names);
981
  }
982

    
983
  // In case of GC stress tests all samples may be in GC phase and there
984
  // won't be |start| node in the profiles.
985
  bool is_gc_stress_testing =
986
      (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction;
987
  const v8::CpuProfileNode* startNode = FindChild(root, "start");
988
  CHECK(is_gc_stress_testing || startNode);
989
  if (startNode) {
990
    ScopedVector<v8::Handle<v8::String> > names(2);
991
    names[0] = v8::String::New("bar");
992
    names[1] = v8::String::New("call");
993
    CheckChildrenNames(startNode, names);
994
  }
995

    
996
  const v8::CpuProfileNode* unresolvedNode =
997
      FindChild(root, i::ProfileGenerator::kUnresolvedFunctionName);
998
  if (unresolvedNode) {
999
    ScopedVector<v8::Handle<v8::String> > names(1);
1000
    names[0] = v8::String::New("call");
1001
    CheckChildrenNames(unresolvedNode, names);
1002
  }
1003

    
1004
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1005
  cpu_profiler->DeleteAllCpuProfiles();
1006
}
1007

    
1008

    
1009
static const char* function_apply_test_source = "function bar(iterations) {\n"
1010
"}\n"
1011
"function test() {\n"
1012
"  bar.apply(this, [10 * 1000]);\n"
1013
"}\n"
1014
"function start(duration) {\n"
1015
"  var start = Date.now();\n"
1016
"  while (Date.now() - start < duration) {\n"
1017
"    try {\n"
1018
"      test();\n"
1019
"    } catch(e) {}\n"
1020
"  }\n"
1021
"}";
1022

    
1023

    
1024
// [Top down]:
1025
//    94     0   (root) [-1] #0 1
1026
//     2     2    (garbage collector) [-1] #0 7
1027
//    82    49    start [-1] #16 3
1028
//     1     0      (unresolved function) [-1] #0 8
1029
//     1     1        apply [-1] #0 9
1030
//    32    21      test [-1] #16 4
1031
//     2     2        bar [-1] #16 6
1032
//     9     9        apply [-1] #0 5
1033
//    10    10    (program) [-1] #0 2
1034
TEST(FunctionApplySample) {
1035
  LocalContext env;
1036
  v8::HandleScope scope(env->GetIsolate());
1037

    
1038
  v8::Script::Compile(v8::String::New(function_apply_test_source))->Run();
1039
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1040
      env->Global()->Get(v8::String::New("start")));
1041

    
1042
  int32_t duration_ms = 100;
1043
  v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) };
1044

    
1045
  const v8::CpuProfile* profile =
1046
      RunProfiler(env, function, args, ARRAY_SIZE(args), 100);
1047

    
1048
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1049
  {
1050
    ScopedVector<v8::Handle<v8::String> > names(3);
1051
    names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
1052
    names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
1053
    names[2] = v8::String::New("start");
1054
    // Don't allow |test|, |bar| and |apply| nodes to be at the top level.
1055
    CheckChildrenNames(root, names);
1056
  }
1057

    
1058
  const v8::CpuProfileNode* startNode = FindChild(root, "start");
1059
  if (startNode) {
1060
    {
1061
      ScopedVector<v8::Handle<v8::String> > names(2);
1062
      names[0] = v8::String::New("test");
1063
      names[1] = v8::String::New(ProfileGenerator::kUnresolvedFunctionName);
1064
      CheckChildrenNames(startNode, names);
1065
    }
1066

    
1067
    const v8::CpuProfileNode* testNode = FindChild(startNode, "test");
1068
    if (testNode) {
1069
      ScopedVector<v8::Handle<v8::String> > names(2);
1070
      names[0] = v8::String::New("bar");
1071
      names[1] = v8::String::New("apply");
1072
      CheckChildrenNames(testNode, names);
1073
    }
1074

    
1075
    if (const v8::CpuProfileNode* unresolvedNode =
1076
            FindChild(startNode, ProfileGenerator::kUnresolvedFunctionName)) {
1077
      ScopedVector<v8::Handle<v8::String> > names(1);
1078
      names[0] = v8::String::New("apply");
1079
      CheckChildrenNames(unresolvedNode, names);
1080
      GetChild(unresolvedNode, "apply");
1081
    }
1082
  }
1083

    
1084
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1085
  cpu_profiler->DeleteAllCpuProfiles();
1086
}
1087

    
1088

    
1089
static const char* js_native_js_test_source =
1090
"var is_profiling = false;\n"
1091
"function foo(iterations) {\n"
1092
"  if (!is_profiling) {\n"
1093
"    is_profiling = true;\n"
1094
"    startProfiling('my_profile');\n"
1095
"  }\n"
1096
"  var r = 0;\n"
1097
"  for (var i = 0; i < iterations; i++) { r += i; }\n"
1098
"  return r;\n"
1099
"}\n"
1100
"function bar(iterations) {\n"
1101
"  try { foo(iterations); } catch(e) {}\n"
1102
"}\n"
1103
"function start(duration) {\n"
1104
"  var start = Date.now();\n"
1105
"  while (Date.now() - start < duration) {\n"
1106
"    try {\n"
1107
"      CallJsFunction(bar, 10 * 1000);\n"
1108
"    } catch(e) {}\n"
1109
"  }\n"
1110
"}";
1111

    
1112
static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1113
  v8::Handle<v8::Function> function = info[0].As<v8::Function>();
1114
  v8::Handle<v8::Value> argv[] = { info[1] };
1115
  function->Call(info.This(), ARRAY_SIZE(argv), argv);
1116
}
1117

    
1118

    
1119
// [Top down]:
1120
//    58     0   (root) #0 1
1121
//     2     2    (program) #0 2
1122
//    56     1    start #16 3
1123
//    55     0      CallJsFunction #0 4
1124
//    55     1        bar #16 5
1125
//    54    54          foo #16 6
1126
TEST(JsNativeJsSample) {
1127
  const char* extensions[] = { "v8/profiler" };
1128
  v8::ExtensionConfiguration config(1, extensions);
1129
  LocalContext env(&config);
1130
  v8::HandleScope scope(env->GetIsolate());
1131

    
1132
  v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1133
      CallJsFunction);
1134
  v8::Local<v8::Function> func = func_template->GetFunction();
1135
  func->SetName(v8::String::New("CallJsFunction"));
1136
  env->Global()->Set(v8::String::New("CallJsFunction"), func);
1137

    
1138
  v8::Script::Compile(v8::String::New(js_native_js_test_source))->Run();
1139
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1140
      env->Global()->Get(v8::String::New("start")));
1141

    
1142
  int32_t duration_ms = 20;
1143
  v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) };
1144
  const v8::CpuProfile* profile =
1145
      RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1146

    
1147
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1148
  {
1149
    ScopedVector<v8::Handle<v8::String> > names(3);
1150
    names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
1151
    names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
1152
    names[2] = v8::String::New("start");
1153
    CheckChildrenNames(root, names);
1154
  }
1155

    
1156
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
1157
  CHECK_EQ(1, startNode->GetChildrenCount());
1158
  const v8::CpuProfileNode* nativeFunctionNode =
1159
      GetChild(startNode, "CallJsFunction");
1160

    
1161
  CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1162
  const v8::CpuProfileNode* barNode = GetChild(nativeFunctionNode, "bar");
1163

    
1164
  CHECK_EQ(1, barNode->GetChildrenCount());
1165
  GetChild(barNode, "foo");
1166

    
1167
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1168
  cpu_profiler->DeleteAllCpuProfiles();
1169
}
1170

    
1171

    
1172
static const char* js_native_js_runtime_js_test_source =
1173
"var is_profiling = false;\n"
1174
"function foo(iterations) {\n"
1175
"  if (!is_profiling) {\n"
1176
"    is_profiling = true;\n"
1177
"    startProfiling('my_profile');\n"
1178
"  }\n"
1179
"  var r = 0;\n"
1180
"  for (var i = 0; i < iterations; i++) { r += i; }\n"
1181
"  return r;\n"
1182
"}\n"
1183
"var bound = foo.bind(this);\n"
1184
"function bar(iterations) {\n"
1185
"  try { bound(iterations); } catch(e) {}\n"
1186
"}\n"
1187
"function start(duration) {\n"
1188
"  var start = Date.now();\n"
1189
"  while (Date.now() - start < duration) {\n"
1190
"    try {\n"
1191
"      CallJsFunction(bar, 10 * 1000);\n"
1192
"    } catch(e) {}\n"
1193
"  }\n"
1194
"}";
1195

    
1196

    
1197
// [Top down]:
1198
//    57     0   (root) #0 1
1199
//    55     1    start #16 3
1200
//    54     0      CallJsFunction #0 4
1201
//    54     3        bar #16 5
1202
//    51    51          foo #16 6
1203
//     2     2    (program) #0 2
1204
TEST(JsNativeJsRuntimeJsSample) {
1205
  const char* extensions[] = { "v8/profiler" };
1206
  v8::ExtensionConfiguration config(1, extensions);
1207
  LocalContext env(&config);
1208
  v8::HandleScope scope(env->GetIsolate());
1209

    
1210
  v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1211
      CallJsFunction);
1212
  v8::Local<v8::Function> func = func_template->GetFunction();
1213
  func->SetName(v8::String::New("CallJsFunction"));
1214
  env->Global()->Set(v8::String::New("CallJsFunction"), func);
1215

    
1216
  v8::Script::Compile(v8::String::New(js_native_js_runtime_js_test_source))->
1217
      Run();
1218
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1219
      env->Global()->Get(v8::String::New("start")));
1220

    
1221
  int32_t duration_ms = 20;
1222
  v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) };
1223
  const v8::CpuProfile* profile =
1224
      RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1225

    
1226
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1227
  ScopedVector<v8::Handle<v8::String> > names(3);
1228
  names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
1229
  names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
1230
  names[2] = v8::String::New("start");
1231
  CheckChildrenNames(root, names);
1232

    
1233
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
1234
  CHECK_EQ(1, startNode->GetChildrenCount());
1235
  const v8::CpuProfileNode* nativeFunctionNode =
1236
      GetChild(startNode, "CallJsFunction");
1237

    
1238
  CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1239
  const v8::CpuProfileNode* barNode = GetChild(nativeFunctionNode, "bar");
1240

    
1241
  CHECK_EQ(1, barNode->GetChildrenCount());
1242
  GetChild(barNode, "foo");
1243

    
1244
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1245
  cpu_profiler->DeleteAllCpuProfiles();
1246
}
1247

    
1248

    
1249
static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1250
  CallJsFunction(info);
1251
}
1252

    
1253

    
1254
static const char* js_native1_js_native2_js_test_source =
1255
"var is_profiling = false;\n"
1256
"function foo(iterations) {\n"
1257
"  if (!is_profiling) {\n"
1258
"    is_profiling = true;\n"
1259
"    startProfiling('my_profile');\n"
1260
"  }\n"
1261
"  var r = 0;\n"
1262
"  for (var i = 0; i < iterations; i++) { r += i; }\n"
1263
"  return r;\n"
1264
"}\n"
1265
"function bar(iterations) {\n"
1266
"  CallJsFunction2(foo, iterations);\n"
1267
"}\n"
1268
"function start(duration) {\n"
1269
"  var start = Date.now();\n"
1270
"  while (Date.now() - start < duration) {\n"
1271
"    try {\n"
1272
"      CallJsFunction1(bar, 10 * 1000);\n"
1273
"    } catch(e) {}\n"
1274
"  }\n"
1275
"}";
1276

    
1277

    
1278
// [Top down]:
1279
//    57     0   (root) #0 1
1280
//    55     1    start #16 3
1281
//    54     0      CallJsFunction1 #0 4
1282
//    54     0        bar #16 5
1283
//    54     0          CallJsFunction2 #0 6
1284
//    54    54            foo #16 7
1285
//     2     2    (program) #0 2
1286
TEST(JsNative1JsNative2JsSample) {
1287
  const char* extensions[] = { "v8/profiler" };
1288
  v8::ExtensionConfiguration config(1, extensions);
1289
  LocalContext env(&config);
1290
  v8::HandleScope scope(env->GetIsolate());
1291

    
1292
  v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1293
      CallJsFunction);
1294
  v8::Local<v8::Function> func1 = func_template->GetFunction();
1295
  func1->SetName(v8::String::New("CallJsFunction1"));
1296
  env->Global()->Set(v8::String::New("CallJsFunction1"), func1);
1297

    
1298
  v8::Local<v8::Function> func2 = v8::FunctionTemplate::New(
1299
      CallJsFunction2)->GetFunction();
1300
  func2->SetName(v8::String::New("CallJsFunction2"));
1301
  env->Global()->Set(v8::String::New("CallJsFunction2"), func2);
1302

    
1303
  v8::Script::Compile(v8::String::New(js_native1_js_native2_js_test_source))->
1304
      Run();
1305
  v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1306
      env->Global()->Get(v8::String::New("start")));
1307

    
1308
  int32_t duration_ms = 20;
1309
  v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) };
1310
  const v8::CpuProfile* profile =
1311
      RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1312

    
1313
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1314
  ScopedVector<v8::Handle<v8::String> > names(3);
1315
  names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
1316
  names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
1317
  names[2] = v8::String::New("start");
1318
  CheckChildrenNames(root, names);
1319

    
1320
  const v8::CpuProfileNode* startNode = GetChild(root, "start");
1321
  CHECK_EQ(1, startNode->GetChildrenCount());
1322
  const v8::CpuProfileNode* nativeNode1 =
1323
      GetChild(startNode, "CallJsFunction1");
1324

    
1325
  CHECK_EQ(1, nativeNode1->GetChildrenCount());
1326
  const v8::CpuProfileNode* barNode = GetChild(nativeNode1, "bar");
1327

    
1328
  CHECK_EQ(1, barNode->GetChildrenCount());
1329
  const v8::CpuProfileNode* nativeNode2 = GetChild(barNode, "CallJsFunction2");
1330

    
1331
  CHECK_EQ(1, nativeNode2->GetChildrenCount());
1332
  GetChild(nativeNode2, "foo");
1333

    
1334
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1335
  cpu_profiler->DeleteAllCpuProfiles();
1336
}
1337

    
1338

    
1339
// [Top down]:
1340
//     6     0   (root) #0 1
1341
//     3     3    (program) #0 2
1342
//     3     3    (idle) #0 3
1343
TEST(IdleTime) {
1344
  LocalContext env;
1345
  v8::HandleScope scope(env->GetIsolate());
1346
  v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1347

    
1348
  v8::Local<v8::String> profile_name = v8::String::New("my_profile");
1349
  cpu_profiler->StartCpuProfiling(profile_name);
1350

    
1351
  i::Isolate* isolate = CcTest::i_isolate();
1352
  i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
1353
  processor->AddCurrentStack(isolate);
1354

    
1355
  cpu_profiler->SetIdle(true);
1356

    
1357
  for (int i = 0; i < 3; i++) {
1358
    processor->AddCurrentStack(isolate);
1359
  }
1360

    
1361
  cpu_profiler->SetIdle(false);
1362
  processor->AddCurrentStack(isolate);
1363

    
1364

    
1365
  const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name);
1366
  CHECK_NE(NULL, profile);
1367
  // Dump collected profile to have a better diagnostic in case of failure.
1368
  reinterpret_cast<i::CpuProfile*>(
1369
      const_cast<v8::CpuProfile*>(profile))->Print();
1370

    
1371
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1372
  ScopedVector<v8::Handle<v8::String> > names(3);
1373
  names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName);
1374
  names[1] = v8::String::New(ProfileGenerator::kProgramEntryName);
1375
  names[2] = v8::String::New(ProfileGenerator::kIdleEntryName);
1376
  CheckChildrenNames(root, names);
1377

    
1378
  const v8::CpuProfileNode* programNode =
1379
      GetChild(root, ProfileGenerator::kProgramEntryName);
1380
  CHECK_EQ(0, programNode->GetChildrenCount());
1381
  CHECK_GE(programNode->GetHitCount(), 3);
1382

    
1383
  const v8::CpuProfileNode* idleNode =
1384
      GetChild(root, ProfileGenerator::kIdleEntryName);
1385
  CHECK_EQ(0, idleNode->GetChildrenCount());
1386
  CHECK_GE(idleNode->GetHitCount(), 3);
1387

    
1388
  cpu_profiler->DeleteAllCpuProfiles();
1389
}
1390

    
1391

    
1392
static void CheckFunctionDetails(const v8::CpuProfileNode* node,
1393
    const char* name, const char* script_name, int script_id,
1394
    int line, int column) {
1395
  CHECK_EQ(v8::String::New(name), node->GetFunctionName());
1396
  CHECK_EQ(v8::String::New(script_name), node->GetScriptResourceName());
1397
  CHECK_EQ(script_id, node->GetScriptId());
1398
  CHECK_EQ(line, node->GetLineNumber());
1399
  CHECK_EQ(column, node->GetColumnNumber());
1400
}
1401

    
1402

    
1403
TEST(FunctionDetails) {
1404
  const char* extensions[] = { "v8/profiler" };
1405
  v8::ExtensionConfiguration config(1, extensions);
1406
  LocalContext env(&config);
1407
  v8::HandleScope handleScope(env->GetIsolate());
1408

    
1409
  v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler();
1410
  CHECK_EQ(0, profiler->GetProfileCount());
1411
  v8::Handle<v8::Script> script_a = v8::Script::Compile(v8::String::New(
1412
      "    function foo\n() { try { bar(); } catch(e) {} }\n"
1413
      " function bar() { startProfiling(); }\n"), v8::String::New("script_a"));
1414
  script_a->Run();
1415
  v8::Handle<v8::Script> script_b = v8::Script::Compile(v8::String::New(
1416
      "\n\n   function baz() { try { foo(); } catch(e) {} }\n"
1417
      "\n\nbaz();\n"
1418
      "stopProfiling();\n"), v8::String::New("script_b"));
1419
  script_b->Run();
1420
  CHECK_EQ(1, profiler->GetProfileCount());
1421
  const v8::CpuProfile* profile = profiler->GetCpuProfile(0);
1422
  const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1423
  reinterpret_cast<ProfileNode*>(
1424
      const_cast<v8::CpuProfileNode*>(current))->Print(0);
1425
  // The tree should look like this:
1426
  //  0   (root) 0 #1
1427
  //  0    (anonymous function) 19 #2 no reason script_b:1
1428
  //  0      baz 19 #3 TryCatchStatement script_b:3
1429
  //  0        foo 18 #4 TryCatchStatement script_a:2
1430
  //  1          bar 18 #5 no reason script_a:3
1431
  const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1432
  const v8::CpuProfileNode* script = GetChild(root,
1433
      ProfileGenerator::kAnonymousFunctionName);
1434
  CheckFunctionDetails(script, ProfileGenerator::kAnonymousFunctionName,
1435
      "script_b", script_b->GetId(), 1, 1);
1436
  const v8::CpuProfileNode* baz = GetChild(script, "baz");
1437
  CheckFunctionDetails(baz, "baz", "script_b", script_b->GetId(), 3, 16);
1438
  const v8::CpuProfileNode* foo = GetChild(baz, "foo");
1439
  CheckFunctionDetails(foo, "foo", "script_a", script_a->GetId(), 2, 1);
1440
  const v8::CpuProfileNode* bar = GetChild(foo, "bar");
1441
  CheckFunctionDetails(bar, "bar", "script_a", script_a->GetId(), 3, 14);
1442
}