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::NewFromUtf8(env->GetIsolate(), "1"); 359 cpu_profiler->StartCpuProfiling(name1); 360 const v8::CpuProfile* p1 = cpu_profiler->StopCpuProfiling(name1); 361 CHECK_NE(NULL, p1); 362 CHECK_EQ(1, cpu_profiler->GetProfileCount()); 363 unsigned uid1 = p1->GetUid(); 364 CHECK_EQ(p1, FindCpuProfile(cpu_profiler, uid1)); 365 const_cast<v8::CpuProfile*>(p1)->Delete(); 366 CHECK_EQ(0, cpu_profiler->GetProfileCount()); 367 CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1)); 368 369 v8::Local<v8::String> name2 = v8::String::NewFromUtf8(env->GetIsolate(), "2"); 370 cpu_profiler->StartCpuProfiling(name2); 371 const v8::CpuProfile* p2 = cpu_profiler->StopCpuProfiling(name2); 372 CHECK_NE(NULL, p2); 373 CHECK_EQ(1, cpu_profiler->GetProfileCount()); 374 unsigned uid2 = p2->GetUid(); 375 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2)); 376 CHECK_EQ(p2, FindCpuProfile(cpu_profiler, uid2)); 377 CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1)); 378 v8::Local<v8::String> name3 = v8::String::NewFromUtf8(env->GetIsolate(), "3"); 379 cpu_profiler->StartCpuProfiling(name3); 380 const v8::CpuProfile* p3 = cpu_profiler->StopCpuProfiling(name3); 381 CHECK_NE(NULL, p3); 382 CHECK_EQ(2, cpu_profiler->GetProfileCount()); 383 unsigned uid3 = p3->GetUid(); 384 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3)); 385 CHECK_EQ(p3, FindCpuProfile(cpu_profiler, uid3)); 386 CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1)); 387 const_cast<v8::CpuProfile*>(p2)->Delete(); 388 CHECK_EQ(1, cpu_profiler->GetProfileCount()); 389 CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid2)); 390 CHECK_EQ(p3, FindCpuProfile(cpu_profiler, uid3)); 391 const_cast<v8::CpuProfile*>(p3)->Delete(); 392 CHECK_EQ(0, cpu_profiler->GetProfileCount()); 393 CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid3)); 394 CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid2)); 395 CHECK_EQ(NULL, FindCpuProfile(cpu_profiler, uid1)); 396 } 397 398 399 TEST(ProfileStartEndTime) { 400 LocalContext env; 401 v8::HandleScope scope(env->GetIsolate()); 402 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 403 404 v8::Local<v8::String> profile_name = 405 v8::String::NewFromUtf8(env->GetIsolate(), "test"); 406 cpu_profiler->StartCpuProfiling(profile_name); 407 const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name); 408 CHECK(profile->GetStartTime() <= profile->GetEndTime()); 409 } 410 411 412 static const v8::CpuProfile* RunProfiler( 413 LocalContext& env, v8::Handle<v8::Function> function, 414 v8::Handle<v8::Value> argv[], int argc, 415 unsigned min_js_samples) { 416 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 417 v8::Local<v8::String> profile_name = 418 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile"); 419 420 cpu_profiler->StartCpuProfiling(profile_name); 421 422 i::Sampler* sampler = 423 reinterpret_cast<i::Isolate*>(env->GetIsolate())->logger()->sampler(); 424 sampler->StartCountingSamples(); 425 do { 426 function->Call(env->Global(), argc, argv); 427 } while (sampler->js_and_external_sample_count() < min_js_samples); 428 429 const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name); 430 431 CHECK_NE(NULL, profile); 432 // Dump collected profile to have a better diagnostic in case of failure. 433 reinterpret_cast<i::CpuProfile*>( 434 const_cast<v8::CpuProfile*>(profile))->Print(); 435 436 return profile; 437 } 438 439 440 static bool ContainsString(v8::Handle<v8::String> string, 441 const Vector<v8::Handle<v8::String> >& vector) { 442 for (int i = 0; i < vector.length(); i++) { 443 if (string->Equals(vector[i])) 444 return true; 445 } 446 return false; 447 } 448 449 450 static void CheckChildrenNames(const v8::CpuProfileNode* node, 451 const Vector<v8::Handle<v8::String> >& names) { 452 int count = node->GetChildrenCount(); 453 for (int i = 0; i < count; i++) { 454 v8::Handle<v8::String> name = node->GetChild(i)->GetFunctionName(); 455 CHECK(ContainsString(name, names)); 456 // Check that there are no duplicates. 457 for (int j = 0; j < count; j++) { 458 if (j == i) continue; 459 CHECK_NE(name, node->GetChild(j)->GetFunctionName()); 460 } 461 } 462 } 463 464 465 static const v8::CpuProfileNode* FindChild(v8::Isolate* isolate, 466 const v8::CpuProfileNode* node, 467 const char* name) { 468 int count = node->GetChildrenCount(); 469 v8::Handle<v8::String> nameHandle = v8::String::NewFromUtf8(isolate, name); 470 for (int i = 0; i < count; i++) { 471 const v8::CpuProfileNode* child = node->GetChild(i); 472 if (nameHandle->Equals(child->GetFunctionName())) return child; 473 } 474 return NULL; 475 } 476 477 478 static const v8::CpuProfileNode* GetChild(v8::Isolate* isolate, 479 const v8::CpuProfileNode* node, 480 const char* name) { 481 const v8::CpuProfileNode* result = FindChild(isolate, node, name); 482 if (!result) { 483 char buffer[100]; 484 i::OS::SNPrintF(Vector<char>(buffer, ARRAY_SIZE(buffer)), 485 "Failed to GetChild: %s", name); 486 FATAL(buffer); 487 } 488 return result; 489 } 490 491 492 static void CheckSimpleBranch(v8::Isolate* isolate, 493 const v8::CpuProfileNode* node, 494 const char* names[], int length) { 495 for (int i = 0; i < length; i++) { 496 const char* name = names[i]; 497 node = GetChild(isolate, node, name); 498 int expectedChildrenCount = (i == length - 1) ? 0 : 1; 499 CHECK_EQ(expectedChildrenCount, node->GetChildrenCount()); 500 } 501 } 502 503 504 static const char* cpu_profiler_test_source = "function loop(timeout) {\n" 505 " this.mmm = 0;\n" 506 " var start = Date.now();\n" 507 " while (Date.now() - start < timeout) {\n" 508 " var n = 100*1000;\n" 509 " while(n > 1) {\n" 510 " n--;\n" 511 " this.mmm += n * n * n;\n" 512 " }\n" 513 " }\n" 514 "}\n" 515 "function delay() { try { loop(10); } catch(e) { } }\n" 516 "function bar() { delay(); }\n" 517 "function baz() { delay(); }\n" 518 "function foo() {\n" 519 " try {\n" 520 " delay();\n" 521 " bar();\n" 522 " delay();\n" 523 " baz();\n" 524 " } catch (e) { }\n" 525 "}\n" 526 "function start(timeout) {\n" 527 " var start = Date.now();\n" 528 " do {\n" 529 " foo();\n" 530 " var duration = Date.now() - start;\n" 531 " } while (duration < timeout);\n" 532 " return duration;\n" 533 "}\n"; 534 535 536 // Check that the profile tree for the script above will look like the 537 // following: 538 // 539 // [Top down]: 540 // 1062 0 (root) [-1] 541 // 1054 0 start [-1] 542 // 1054 1 foo [-1] 543 // 265 0 baz [-1] 544 // 265 1 delay [-1] 545 // 264 264 loop [-1] 546 // 525 3 delay [-1] 547 // 522 522 loop [-1] 548 // 263 0 bar [-1] 549 // 263 1 delay [-1] 550 // 262 262 loop [-1] 551 // 2 2 (program) [-1] 552 // 6 6 (garbage collector) [-1] 553 TEST(CollectCpuProfile) { 554 LocalContext env; 555 v8::HandleScope scope(env->GetIsolate()); 556 557 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(), 558 cpu_profiler_test_source))->Run(); 559 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 560 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 561 562 int32_t profiling_interval_ms = 200; 563 v8::Handle<v8::Value> args[] = { v8::Integer::New(profiling_interval_ms) }; 564 const v8::CpuProfile* profile = 565 RunProfiler(env, function, args, ARRAY_SIZE(args), 200); 566 function->Call(env->Global(), ARRAY_SIZE(args), args); 567 568 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 569 570 ScopedVector<v8::Handle<v8::String> > names(3); 571 names[0] = v8::String::NewFromUtf8( 572 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 573 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 574 ProfileGenerator::kProgramEntryName); 575 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 576 CheckChildrenNames(root, names); 577 578 const v8::CpuProfileNode* startNode = 579 GetChild(env->GetIsolate(), root, "start"); 580 CHECK_EQ(1, startNode->GetChildrenCount()); 581 582 const v8::CpuProfileNode* fooNode = 583 GetChild(env->GetIsolate(), startNode, "foo"); 584 CHECK_EQ(3, fooNode->GetChildrenCount()); 585 586 const char* barBranch[] = { "bar", "delay", "loop" }; 587 CheckSimpleBranch(env->GetIsolate(), fooNode, barBranch, 588 ARRAY_SIZE(barBranch)); 589 const char* bazBranch[] = { "baz", "delay", "loop" }; 590 CheckSimpleBranch(env->GetIsolate(), fooNode, bazBranch, 591 ARRAY_SIZE(bazBranch)); 592 const char* delayBranch[] = { "delay", "loop" }; 593 CheckSimpleBranch(env->GetIsolate(), fooNode, delayBranch, 594 ARRAY_SIZE(delayBranch)); 595 596 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 597 cpu_profiler->DeleteAllCpuProfiles(); 598 } 599 600 601 602 static const char* cpu_profiler_test_source2 = "function loop() {}\n" 603 "function delay() { loop(); }\n" 604 "function start(count) {\n" 605 " var k = 0;\n" 606 " do {\n" 607 " delay();\n" 608 " } while (++k < count*100*1000);\n" 609 "}\n"; 610 611 // Check that the profile tree doesn't contain unexpected traces: 612 // - 'loop' can be called only by 'delay' 613 // - 'delay' may be called only by 'start' 614 // The profile will look like the following: 615 // 616 // [Top down]: 617 // 135 0 (root) [-1] #1 618 // 121 72 start [-1] #3 619 // 49 33 delay [-1] #4 620 // 16 16 loop [-1] #5 621 // 14 14 (program) [-1] #2 622 TEST(SampleWhenFrameIsNotSetup) { 623 LocalContext env; 624 v8::HandleScope scope(env->GetIsolate()); 625 626 v8::Script::Compile(v8::String::NewFromUtf8( 627 env->GetIsolate(), cpu_profiler_test_source2))->Run(); 628 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 629 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 630 631 int32_t repeat_count = 100; 632 #if defined(USE_SIMULATOR) 633 // Simulators are much slower. 634 repeat_count = 1; 635 #endif 636 v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) }; 637 const v8::CpuProfile* profile = 638 RunProfiler(env, function, args, ARRAY_SIZE(args), 100); 639 640 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 641 642 ScopedVector<v8::Handle<v8::String> > names(3); 643 names[0] = v8::String::NewFromUtf8( 644 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 645 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 646 ProfileGenerator::kProgramEntryName); 647 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 648 CheckChildrenNames(root, names); 649 650 const v8::CpuProfileNode* startNode = 651 FindChild(env->GetIsolate(), root, "start"); 652 // On slow machines there may be no meaningfull samples at all, skip the 653 // check there. 654 if (startNode && startNode->GetChildrenCount() > 0) { 655 CHECK_EQ(1, startNode->GetChildrenCount()); 656 const v8::CpuProfileNode* delayNode = 657 GetChild(env->GetIsolate(), startNode, "delay"); 658 if (delayNode->GetChildrenCount() > 0) { 659 CHECK_EQ(1, delayNode->GetChildrenCount()); 660 GetChild(env->GetIsolate(), delayNode, "loop"); 661 } 662 } 663 664 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 665 cpu_profiler->DeleteAllCpuProfiles(); 666 } 667 668 669 static const char* native_accessor_test_source = "function start(count) {\n" 670 " for (var i = 0; i < count; i++) {\n" 671 " var o = instance.foo;\n" 672 " instance.foo = o + 1;\n" 673 " }\n" 674 "}\n"; 675 676 677 class TestApiCallbacks { 678 public: 679 explicit TestApiCallbacks(int min_duration_ms) 680 : min_duration_ms_(min_duration_ms), 681 is_warming_up_(false) {} 682 683 static void Getter(v8::Local<v8::String> name, 684 const v8::PropertyCallbackInfo<v8::Value>& info) { 685 TestApiCallbacks* data = fromInfo(info); 686 data->Wait(); 687 } 688 689 static void Setter(v8::Local<v8::String> name, 690 v8::Local<v8::Value> value, 691 const v8::PropertyCallbackInfo<void>& info) { 692 TestApiCallbacks* data = fromInfo(info); 693 data->Wait(); 694 } 695 696 static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) { 697 TestApiCallbacks* data = fromInfo(info); 698 data->Wait(); 699 } 700 701 void set_warming_up(bool value) { is_warming_up_ = value; } 702 703 private: 704 void Wait() { 705 if (is_warming_up_) return; 706 double start = i::OS::TimeCurrentMillis(); 707 double duration = 0; 708 while (duration < min_duration_ms_) { 709 i::OS::Sleep(1); 710 duration = i::OS::TimeCurrentMillis() - start; 711 } 712 } 713 714 template<typename T> 715 static TestApiCallbacks* fromInfo(const T& info) { 716 void* data = v8::External::Cast(*info.Data())->Value(); 717 return reinterpret_cast<TestApiCallbacks*>(data); 718 } 719 720 int min_duration_ms_; 721 bool is_warming_up_; 722 }; 723 724 725 // Test that native accessors are properly reported in the CPU profile. 726 // This test checks the case when the long-running accessors are called 727 // only once and the optimizer doesn't have chance to change the invocation 728 // code. 729 TEST(NativeAccessorUninitializedIC) { 730 LocalContext env; 731 v8::HandleScope scope(env->GetIsolate()); 732 733 734 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 735 v8::Local<v8::ObjectTemplate> instance_template = 736 func_template->InstanceTemplate(); 737 738 TestApiCallbacks accessors(100); 739 v8::Local<v8::External> data = 740 v8::External::New(env->GetIsolate(), &accessors); 741 instance_template->SetAccessor( 742 v8::String::NewFromUtf8(env->GetIsolate(), "foo"), 743 &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data); 744 v8::Local<v8::Function> func = func_template->GetFunction(); 745 v8::Local<v8::Object> instance = func->NewInstance(); 746 env->Global()->Set(v8::String::NewFromUtf8(env->GetIsolate(), "instance"), 747 instance); 748 749 v8::Script::Compile( 750 v8::String::NewFromUtf8(env->GetIsolate(), native_accessor_test_source)) 751 ->Run(); 752 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 753 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 754 755 int32_t repeat_count = 1; 756 v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) }; 757 const v8::CpuProfile* profile = 758 RunProfiler(env, function, args, ARRAY_SIZE(args), 180); 759 760 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 761 const v8::CpuProfileNode* startNode = 762 GetChild(env->GetIsolate(), root, "start"); 763 GetChild(env->GetIsolate(), startNode, "get foo"); 764 GetChild(env->GetIsolate(), startNode, "set foo"); 765 766 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 767 cpu_profiler->DeleteAllCpuProfiles(); 768 } 769 770 771 // Test that native accessors are properly reported in the CPU profile. 772 // This test makes sure that the accessors are called enough times to become 773 // hot and to trigger optimizations. 774 TEST(NativeAccessorMonomorphicIC) { 775 LocalContext env; 776 v8::HandleScope scope(env->GetIsolate()); 777 778 779 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 780 v8::Local<v8::ObjectTemplate> instance_template = 781 func_template->InstanceTemplate(); 782 783 TestApiCallbacks accessors(1); 784 v8::Local<v8::External> data = 785 v8::External::New(env->GetIsolate(), &accessors); 786 instance_template->SetAccessor( 787 v8::String::NewFromUtf8(env->GetIsolate(), "foo"), 788 &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data); 789 v8::Local<v8::Function> func = func_template->GetFunction(); 790 v8::Local<v8::Object> instance = func->NewInstance(); 791 env->Global()->Set(v8::String::NewFromUtf8(env->GetIsolate(), "instance"), 792 instance); 793 794 v8::Script::Compile( 795 v8::String::NewFromUtf8(env->GetIsolate(), native_accessor_test_source)) 796 ->Run(); 797 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 798 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 799 800 { 801 // Make sure accessors ICs are in monomorphic state before starting 802 // profiling. 803 accessors.set_warming_up(true); 804 int32_t warm_up_iterations = 3; 805 v8::Handle<v8::Value> args[] = { v8::Integer::New(warm_up_iterations) }; 806 function->Call(env->Global(), ARRAY_SIZE(args), args); 807 accessors.set_warming_up(false); 808 } 809 810 int32_t repeat_count = 100; 811 v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) }; 812 const v8::CpuProfile* profile = 813 RunProfiler(env, function, args, ARRAY_SIZE(args), 200); 814 815 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 816 const v8::CpuProfileNode* startNode = 817 GetChild(env->GetIsolate(), root, "start"); 818 GetChild(env->GetIsolate(), startNode, "get foo"); 819 GetChild(env->GetIsolate(), startNode, "set foo"); 820 821 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 822 cpu_profiler->DeleteAllCpuProfiles(); 823 } 824 825 826 static const char* native_method_test_source = "function start(count) {\n" 827 " for (var i = 0; i < count; i++) {\n" 828 " instance.fooMethod();\n" 829 " }\n" 830 "}\n"; 831 832 833 TEST(NativeMethodUninitializedIC) { 834 LocalContext env; 835 v8::HandleScope scope(env->GetIsolate()); 836 837 TestApiCallbacks callbacks(100); 838 v8::Local<v8::External> data = 839 v8::External::New(env->GetIsolate(), &callbacks); 840 841 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 842 func_template->SetClassName( 843 v8::String::NewFromUtf8(env->GetIsolate(), "Test_InstanceCostructor")); 844 v8::Local<v8::ObjectTemplate> proto_template = 845 func_template->PrototypeTemplate(); 846 v8::Local<v8::Signature> signature = 847 v8::Signature::New(env->GetIsolate(), func_template); 848 proto_template->Set(v8::String::NewFromUtf8(env->GetIsolate(), "fooMethod"), 849 v8::FunctionTemplate::New(&TestApiCallbacks::Callback, 850 data, signature, 0)); 851 852 v8::Local<v8::Function> func = func_template->GetFunction(); 853 v8::Local<v8::Object> instance = func->NewInstance(); 854 env->Global()->Set(v8::String::NewFromUtf8(env->GetIsolate(), "instance"), 855 instance); 856 857 v8::Script::Compile(v8::String::NewFromUtf8( 858 env->GetIsolate(), native_method_test_source))->Run(); 859 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 860 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 861 862 int32_t repeat_count = 1; 863 v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) }; 864 const v8::CpuProfile* profile = 865 RunProfiler(env, function, args, ARRAY_SIZE(args), 100); 866 867 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 868 const v8::CpuProfileNode* startNode = 869 GetChild(env->GetIsolate(), root, "start"); 870 GetChild(env->GetIsolate(), startNode, "fooMethod"); 871 872 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 873 cpu_profiler->DeleteAllCpuProfiles(); 874 } 875 876 877 TEST(NativeMethodMonomorphicIC) { 878 LocalContext env; 879 v8::HandleScope scope(env->GetIsolate()); 880 881 TestApiCallbacks callbacks(1); 882 v8::Local<v8::External> data = 883 v8::External::New(env->GetIsolate(), &callbacks); 884 885 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 886 func_template->SetClassName( 887 v8::String::NewFromUtf8(env->GetIsolate(), "Test_InstanceCostructor")); 888 v8::Local<v8::ObjectTemplate> proto_template = 889 func_template->PrototypeTemplate(); 890 v8::Local<v8::Signature> signature = 891 v8::Signature::New(env->GetIsolate(), func_template); 892 proto_template->Set(v8::String::NewFromUtf8(env->GetIsolate(), "fooMethod"), 893 v8::FunctionTemplate::New(&TestApiCallbacks::Callback, 894 data, signature, 0)); 895 896 v8::Local<v8::Function> func = func_template->GetFunction(); 897 v8::Local<v8::Object> instance = func->NewInstance(); 898 env->Global()->Set(v8::String::NewFromUtf8(env->GetIsolate(), "instance"), 899 instance); 900 901 v8::Script::Compile(v8::String::NewFromUtf8( 902 env->GetIsolate(), native_method_test_source))->Run(); 903 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 904 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 905 { 906 // Make sure method ICs are in monomorphic state before starting 907 // profiling. 908 callbacks.set_warming_up(true); 909 int32_t warm_up_iterations = 3; 910 v8::Handle<v8::Value> args[] = { v8::Integer::New(warm_up_iterations) }; 911 function->Call(env->Global(), ARRAY_SIZE(args), args); 912 callbacks.set_warming_up(false); 913 } 914 915 int32_t repeat_count = 100; 916 v8::Handle<v8::Value> args[] = { v8::Integer::New(repeat_count) }; 917 const v8::CpuProfile* profile = 918 RunProfiler(env, function, args, ARRAY_SIZE(args), 100); 919 920 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 921 GetChild(env->GetIsolate(), root, "start"); 922 const v8::CpuProfileNode* startNode = 923 GetChild(env->GetIsolate(), root, "start"); 924 GetChild(env->GetIsolate(), startNode, "fooMethod"); 925 926 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 927 cpu_profiler->DeleteAllCpuProfiles(); 928 } 929 930 931 static const char* bound_function_test_source = "function foo(iterations) {\n" 932 " var r = 0;\n" 933 " for (var i = 0; i < iterations; i++) { r += i; }\n" 934 " return r;\n" 935 "}\n" 936 "function start(duration) {\n" 937 " var callback = foo.bind(this);\n" 938 " var start = Date.now();\n" 939 " while (Date.now() - start < duration) {\n" 940 " callback(10 * 1000);\n" 941 " }\n" 942 "}"; 943 944 945 TEST(BoundFunctionCall) { 946 LocalContext env; 947 v8::HandleScope scope(env->GetIsolate()); 948 949 v8::Script::Compile( 950 v8::String::NewFromUtf8(env->GetIsolate(), bound_function_test_source)) 951 ->Run(); 952 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 953 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 954 955 int32_t duration_ms = 100; 956 v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) }; 957 const v8::CpuProfile* profile = 958 RunProfiler(env, function, args, ARRAY_SIZE(args), 100); 959 960 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 961 ScopedVector<v8::Handle<v8::String> > names(3); 962 names[0] = v8::String::NewFromUtf8( 963 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 964 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 965 ProfileGenerator::kProgramEntryName); 966 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 967 // Don't allow |foo| node to be at the top level. 968 CheckChildrenNames(root, names); 969 970 const v8::CpuProfileNode* startNode = 971 GetChild(env->GetIsolate(), root, "start"); 972 GetChild(env->GetIsolate(), startNode, "foo"); 973 974 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 975 cpu_profiler->DeleteAllCpuProfiles(); 976 } 977 978 979 static const char* call_function_test_source = "function bar(iterations) {\n" 980 "}\n" 981 "function start(duration) {\n" 982 " var start = Date.now();\n" 983 " while (Date.now() - start < duration) {\n" 984 " try {\n" 985 " bar.call(this, 10 * 1000);\n" 986 " } catch(e) {}\n" 987 " }\n" 988 "}"; 989 990 991 // Test that if we sampled thread when it was inside FunctionCall buitin then 992 // its caller frame will be '(unresolved function)' as we have no reliable way 993 // to resolve it. 994 // 995 // [Top down]: 996 // 96 0 (root) [-1] #1 997 // 1 1 (garbage collector) [-1] #4 998 // 5 0 (unresolved function) [-1] #5 999 // 5 5 call [-1] #6 1000 // 71 70 start [-1] #3 1001 // 1 1 bar [-1] #7 1002 // 19 19 (program) [-1] #2 1003 TEST(FunctionCallSample) { 1004 LocalContext env; 1005 v8::HandleScope scope(env->GetIsolate()); 1006 1007 // Collect garbage that might have be generated while installing extensions. 1008 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags); 1009 1010 v8::Script::Compile(v8::String::NewFromUtf8( 1011 env->GetIsolate(), call_function_test_source))->Run(); 1012 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 1013 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 1014 1015 int32_t duration_ms = 100; 1016 v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) }; 1017 const v8::CpuProfile* profile = 1018 RunProfiler(env, function, args, ARRAY_SIZE(args), 100); 1019 1020 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 1021 { 1022 ScopedVector<v8::Handle<v8::String> > names(4); 1023 names[0] = v8::String::NewFromUtf8( 1024 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 1025 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 1026 ProfileGenerator::kProgramEntryName); 1027 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 1028 names[3] = v8::String::NewFromUtf8( 1029 env->GetIsolate(), i::ProfileGenerator::kUnresolvedFunctionName); 1030 // Don't allow |bar| and |call| nodes to be at the top level. 1031 CheckChildrenNames(root, names); 1032 } 1033 1034 // In case of GC stress tests all samples may be in GC phase and there 1035 // won't be |start| node in the profiles. 1036 bool is_gc_stress_testing = 1037 (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction; 1038 const v8::CpuProfileNode* startNode = 1039 FindChild(env->GetIsolate(), root, "start"); 1040 CHECK(is_gc_stress_testing || startNode); 1041 if (startNode) { 1042 ScopedVector<v8::Handle<v8::String> > names(2); 1043 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar"); 1044 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "call"); 1045 CheckChildrenNames(startNode, names); 1046 } 1047 1048 const v8::CpuProfileNode* unresolvedNode = FindChild( 1049 env->GetIsolate(), root, i::ProfileGenerator::kUnresolvedFunctionName); 1050 if (unresolvedNode) { 1051 ScopedVector<v8::Handle<v8::String> > names(1); 1052 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "call"); 1053 CheckChildrenNames(unresolvedNode, names); 1054 } 1055 1056 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 1057 cpu_profiler->DeleteAllCpuProfiles(); 1058 } 1059 1060 1061 static const char* function_apply_test_source = "function bar(iterations) {\n" 1062 "}\n" 1063 "function test() {\n" 1064 " bar.apply(this, [10 * 1000]);\n" 1065 "}\n" 1066 "function start(duration) {\n" 1067 " var start = Date.now();\n" 1068 " while (Date.now() - start < duration) {\n" 1069 " try {\n" 1070 " test();\n" 1071 " } catch(e) {}\n" 1072 " }\n" 1073 "}"; 1074 1075 1076 // [Top down]: 1077 // 94 0 (root) [-1] #0 1 1078 // 2 2 (garbage collector) [-1] #0 7 1079 // 82 49 start [-1] #16 3 1080 // 1 0 (unresolved function) [-1] #0 8 1081 // 1 1 apply [-1] #0 9 1082 // 32 21 test [-1] #16 4 1083 // 2 2 bar [-1] #16 6 1084 // 9 9 apply [-1] #0 5 1085 // 10 10 (program) [-1] #0 2 1086 TEST(FunctionApplySample) { 1087 LocalContext env; 1088 v8::HandleScope scope(env->GetIsolate()); 1089 1090 v8::Script::Compile( 1091 v8::String::NewFromUtf8(env->GetIsolate(), function_apply_test_source)) 1092 ->Run(); 1093 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 1094 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 1095 1096 int32_t duration_ms = 100; 1097 v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) }; 1098 1099 const v8::CpuProfile* profile = 1100 RunProfiler(env, function, args, ARRAY_SIZE(args), 100); 1101 1102 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 1103 { 1104 ScopedVector<v8::Handle<v8::String> > names(3); 1105 names[0] = v8::String::NewFromUtf8( 1106 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 1107 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 1108 ProfileGenerator::kProgramEntryName); 1109 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 1110 // Don't allow |test|, |bar| and |apply| nodes to be at the top level. 1111 CheckChildrenNames(root, names); 1112 } 1113 1114 const v8::CpuProfileNode* startNode = 1115 FindChild(env->GetIsolate(), root, "start"); 1116 if (startNode) { 1117 { 1118 ScopedVector<v8::Handle<v8::String> > names(2); 1119 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "test"); 1120 names[1] = v8::String::NewFromUtf8( 1121 env->GetIsolate(), ProfileGenerator::kUnresolvedFunctionName); 1122 CheckChildrenNames(startNode, names); 1123 } 1124 1125 const v8::CpuProfileNode* testNode = 1126 FindChild(env->GetIsolate(), startNode, "test"); 1127 if (testNode) { 1128 ScopedVector<v8::Handle<v8::String> > names(2); 1129 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar"); 1130 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "apply"); 1131 CheckChildrenNames(testNode, names); 1132 } 1133 1134 if (const v8::CpuProfileNode* unresolvedNode = 1135 FindChild(env->GetIsolate(), startNode, 1136 ProfileGenerator::kUnresolvedFunctionName)) { 1137 ScopedVector<v8::Handle<v8::String> > names(1); 1138 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "apply"); 1139 CheckChildrenNames(unresolvedNode, names); 1140 GetChild(env->GetIsolate(), unresolvedNode, "apply"); 1141 } 1142 } 1143 1144 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 1145 cpu_profiler->DeleteAllCpuProfiles(); 1146 } 1147 1148 1149 static const char* js_native_js_test_source = 1150 "var is_profiling = false;\n" 1151 "function foo(iterations) {\n" 1152 " if (!is_profiling) {\n" 1153 " is_profiling = true;\n" 1154 " startProfiling('my_profile');\n" 1155 " }\n" 1156 " var r = 0;\n" 1157 " for (var i = 0; i < iterations; i++) { r += i; }\n" 1158 " return r;\n" 1159 "}\n" 1160 "function bar(iterations) {\n" 1161 " try { foo(iterations); } catch(e) {}\n" 1162 "}\n" 1163 "function start(duration) {\n" 1164 " var start = Date.now();\n" 1165 " while (Date.now() - start < duration) {\n" 1166 " try {\n" 1167 " CallJsFunction(bar, 10 * 1000);\n" 1168 " } catch(e) {}\n" 1169 " }\n" 1170 "}"; 1171 1172 static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) { 1173 v8::Handle<v8::Function> function = info[0].As<v8::Function>(); 1174 v8::Handle<v8::Value> argv[] = { info[1] }; 1175 function->Call(info.This(), ARRAY_SIZE(argv), argv); 1176 } 1177 1178 1179 // [Top down]: 1180 // 58 0 (root) #0 1 1181 // 2 2 (program) #0 2 1182 // 56 1 start #16 3 1183 // 55 0 CallJsFunction #0 4 1184 // 55 1 bar #16 5 1185 // 54 54 foo #16 6 1186 TEST(JsNativeJsSample) { 1187 const char* extensions[] = { "v8/profiler" }; 1188 v8::ExtensionConfiguration config(1, extensions); 1189 LocalContext env(&config); 1190 v8::HandleScope scope(env->GetIsolate()); 1191 1192 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New( 1193 CallJsFunction); 1194 v8::Local<v8::Function> func = func_template->GetFunction(); 1195 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction")); 1196 env->Global()->Set( 1197 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func); 1198 1199 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(), 1200 js_native_js_test_source))->Run(); 1201 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 1202 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 1203 1204 int32_t duration_ms = 20; 1205 v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) }; 1206 const v8::CpuProfile* profile = 1207 RunProfiler(env, function, args, ARRAY_SIZE(args), 10); 1208 1209 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 1210 { 1211 ScopedVector<v8::Handle<v8::String> > names(3); 1212 names[0] = v8::String::NewFromUtf8( 1213 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 1214 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 1215 ProfileGenerator::kProgramEntryName); 1216 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 1217 CheckChildrenNames(root, names); 1218 } 1219 1220 const v8::CpuProfileNode* startNode = 1221 GetChild(env->GetIsolate(), root, "start"); 1222 CHECK_EQ(1, startNode->GetChildrenCount()); 1223 const v8::CpuProfileNode* nativeFunctionNode = 1224 GetChild(env->GetIsolate(), startNode, "CallJsFunction"); 1225 1226 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount()); 1227 const v8::CpuProfileNode* barNode = 1228 GetChild(env->GetIsolate(), nativeFunctionNode, "bar"); 1229 1230 CHECK_EQ(1, barNode->GetChildrenCount()); 1231 GetChild(env->GetIsolate(), barNode, "foo"); 1232 1233 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 1234 cpu_profiler->DeleteAllCpuProfiles(); 1235 } 1236 1237 1238 static const char* js_native_js_runtime_js_test_source = 1239 "var is_profiling = false;\n" 1240 "function foo(iterations) {\n" 1241 " if (!is_profiling) {\n" 1242 " is_profiling = true;\n" 1243 " startProfiling('my_profile');\n" 1244 " }\n" 1245 " var r = 0;\n" 1246 " for (var i = 0; i < iterations; i++) { r += i; }\n" 1247 " return r;\n" 1248 "}\n" 1249 "var bound = foo.bind(this);\n" 1250 "function bar(iterations) {\n" 1251 " try { bound(iterations); } catch(e) {}\n" 1252 "}\n" 1253 "function start(duration) {\n" 1254 " var start = Date.now();\n" 1255 " while (Date.now() - start < duration) {\n" 1256 " try {\n" 1257 " CallJsFunction(bar, 10 * 1000);\n" 1258 " } catch(e) {}\n" 1259 " }\n" 1260 "}"; 1261 1262 1263 // [Top down]: 1264 // 57 0 (root) #0 1 1265 // 55 1 start #16 3 1266 // 54 0 CallJsFunction #0 4 1267 // 54 3 bar #16 5 1268 // 51 51 foo #16 6 1269 // 2 2 (program) #0 2 1270 TEST(JsNativeJsRuntimeJsSample) { 1271 const char* extensions[] = { "v8/profiler" }; 1272 v8::ExtensionConfiguration config(1, extensions); 1273 LocalContext env(&config); 1274 v8::HandleScope scope(env->GetIsolate()); 1275 1276 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New( 1277 CallJsFunction); 1278 v8::Local<v8::Function> func = func_template->GetFunction(); 1279 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction")); 1280 env->Global()->Set( 1281 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func); 1282 1283 v8::Script::Compile( 1284 v8::String::NewFromUtf8(env->GetIsolate(), 1285 js_native_js_runtime_js_test_source))->Run(); 1286 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 1287 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 1288 1289 int32_t duration_ms = 20; 1290 v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) }; 1291 const v8::CpuProfile* profile = 1292 RunProfiler(env, function, args, ARRAY_SIZE(args), 10); 1293 1294 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 1295 ScopedVector<v8::Handle<v8::String> > names(3); 1296 names[0] = v8::String::NewFromUtf8( 1297 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 1298 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 1299 ProfileGenerator::kProgramEntryName); 1300 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 1301 CheckChildrenNames(root, names); 1302 1303 const v8::CpuProfileNode* startNode = 1304 GetChild(env->GetIsolate(), root, "start"); 1305 CHECK_EQ(1, startNode->GetChildrenCount()); 1306 const v8::CpuProfileNode* nativeFunctionNode = 1307 GetChild(env->GetIsolate(), startNode, "CallJsFunction"); 1308 1309 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount()); 1310 const v8::CpuProfileNode* barNode = 1311 GetChild(env->GetIsolate(), nativeFunctionNode, "bar"); 1312 1313 CHECK_EQ(1, barNode->GetChildrenCount()); 1314 GetChild(env->GetIsolate(), barNode, "foo"); 1315 1316 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 1317 cpu_profiler->DeleteAllCpuProfiles(); 1318 } 1319 1320 1321 static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) { 1322 CallJsFunction(info); 1323 } 1324 1325 1326 static const char* js_native1_js_native2_js_test_source = 1327 "var is_profiling = false;\n" 1328 "function foo(iterations) {\n" 1329 " if (!is_profiling) {\n" 1330 " is_profiling = true;\n" 1331 " startProfiling('my_profile');\n" 1332 " }\n" 1333 " var r = 0;\n" 1334 " for (var i = 0; i < iterations; i++) { r += i; }\n" 1335 " return r;\n" 1336 "}\n" 1337 "function bar(iterations) {\n" 1338 " CallJsFunction2(foo, iterations);\n" 1339 "}\n" 1340 "function start(duration) {\n" 1341 " var start = Date.now();\n" 1342 " while (Date.now() - start < duration) {\n" 1343 " try {\n" 1344 " CallJsFunction1(bar, 10 * 1000);\n" 1345 " } catch(e) {}\n" 1346 " }\n" 1347 "}"; 1348 1349 1350 // [Top down]: 1351 // 57 0 (root) #0 1 1352 // 55 1 start #16 3 1353 // 54 0 CallJsFunction1 #0 4 1354 // 54 0 bar #16 5 1355 // 54 0 CallJsFunction2 #0 6 1356 // 54 54 foo #16 7 1357 // 2 2 (program) #0 2 1358 TEST(JsNative1JsNative2JsSample) { 1359 const char* extensions[] = { "v8/profiler" }; 1360 v8::ExtensionConfiguration config(1, extensions); 1361 LocalContext env(&config); 1362 v8::HandleScope scope(env->GetIsolate()); 1363 1364 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New( 1365 CallJsFunction); 1366 v8::Local<v8::Function> func1 = func_template->GetFunction(); 1367 func1->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1")); 1368 env->Global()->Set( 1369 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"), func1); 1370 1371 v8::Local<v8::Function> func2 = v8::FunctionTemplate::New( 1372 CallJsFunction2)->GetFunction(); 1373 func2->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2")); 1374 env->Global()->Set( 1375 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"), func2); 1376 1377 v8::Script::Compile( 1378 v8::String::NewFromUtf8(env->GetIsolate(), 1379 js_native1_js_native2_js_test_source))->Run(); 1380 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( 1381 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start"))); 1382 1383 int32_t duration_ms = 20; 1384 v8::Handle<v8::Value> args[] = { v8::Integer::New(duration_ms) }; 1385 const v8::CpuProfile* profile = 1386 RunProfiler(env, function, args, ARRAY_SIZE(args), 10); 1387 1388 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 1389 ScopedVector<v8::Handle<v8::String> > names(3); 1390 names[0] = v8::String::NewFromUtf8( 1391 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 1392 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 1393 ProfileGenerator::kProgramEntryName); 1394 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start"); 1395 CheckChildrenNames(root, names); 1396 1397 const v8::CpuProfileNode* startNode = 1398 GetChild(env->GetIsolate(), root, "start"); 1399 CHECK_EQ(1, startNode->GetChildrenCount()); 1400 const v8::CpuProfileNode* nativeNode1 = 1401 GetChild(env->GetIsolate(), startNode, "CallJsFunction1"); 1402 1403 CHECK_EQ(1, nativeNode1->GetChildrenCount()); 1404 const v8::CpuProfileNode* barNode = 1405 GetChild(env->GetIsolate(), nativeNode1, "bar"); 1406 1407 CHECK_EQ(1, barNode->GetChildrenCount()); 1408 const v8::CpuProfileNode* nativeNode2 = 1409 GetChild(env->GetIsolate(), barNode, "CallJsFunction2"); 1410 1411 CHECK_EQ(1, nativeNode2->GetChildrenCount()); 1412 GetChild(env->GetIsolate(), nativeNode2, "foo"); 1413 1414 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 1415 cpu_profiler->DeleteAllCpuProfiles(); 1416 } 1417 1418 1419 // [Top down]: 1420 // 6 0 (root) #0 1 1421 // 3 3 (program) #0 2 1422 // 3 3 (idle) #0 3 1423 TEST(IdleTime) { 1424 LocalContext env; 1425 v8::HandleScope scope(env->GetIsolate()); 1426 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 1427 1428 v8::Local<v8::String> profile_name = 1429 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile"); 1430 cpu_profiler->StartCpuProfiling(profile_name); 1431 1432 i::Isolate* isolate = CcTest::i_isolate(); 1433 i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor(); 1434 processor->AddCurrentStack(isolate); 1435 1436 cpu_profiler->SetIdle(true); 1437 1438 for (int i = 0; i < 3; i++) { 1439 processor->AddCurrentStack(isolate); 1440 } 1441 1442 cpu_profiler->SetIdle(false); 1443 processor->AddCurrentStack(isolate); 1444 1445 1446 const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name); 1447 CHECK_NE(NULL, profile); 1448 // Dump collected profile to have a better diagnostic in case of failure. 1449 reinterpret_cast<i::CpuProfile*>( 1450 const_cast<v8::CpuProfile*>(profile))->Print(); 1451 1452 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 1453 ScopedVector<v8::Handle<v8::String> > names(3); 1454 names[0] = v8::String::NewFromUtf8( 1455 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName); 1456 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), 1457 ProfileGenerator::kProgramEntryName); 1458 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), 1459 ProfileGenerator::kIdleEntryName); 1460 CheckChildrenNames(root, names); 1461 1462 const v8::CpuProfileNode* programNode = 1463 GetChild(env->GetIsolate(), root, ProfileGenerator::kProgramEntryName); 1464 CHECK_EQ(0, programNode->GetChildrenCount()); 1465 CHECK_GE(programNode->GetHitCount(), 3); 1466 1467 const v8::CpuProfileNode* idleNode = 1468 GetChild(env->GetIsolate(), root, ProfileGenerator::kIdleEntryName); 1469 CHECK_EQ(0, idleNode->GetChildrenCount()); 1470 CHECK_GE(idleNode->GetHitCount(), 3); 1471 1472 cpu_profiler->DeleteAllCpuProfiles(); 1473 } 1474 1475 1476 static void CheckFunctionDetails(v8::Isolate* isolate, 1477 const v8::CpuProfileNode* node, 1478 const char* name, const char* script_name, 1479 int script_id, int line, int column) { 1480 CHECK_EQ(v8::String::NewFromUtf8(isolate, name), 1481 node->GetFunctionName()); 1482 CHECK_EQ(v8::String::NewFromUtf8(isolate, script_name), 1483 node->GetScriptResourceName()); 1484 CHECK_EQ(script_id, node->GetScriptId()); 1485 CHECK_EQ(line, node->GetLineNumber()); 1486 CHECK_EQ(column, node->GetColumnNumber()); 1487 } 1488 1489 1490 TEST(FunctionDetails) { 1491 const char* extensions[] = { "v8/profiler" }; 1492 v8::ExtensionConfiguration config(1, extensions); 1493 LocalContext env(&config); 1494 v8::HandleScope handleScope(env->GetIsolate()); 1495 1496 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler(); 1497 CHECK_EQ(0, profiler->GetProfileCount()); 1498 v8::Handle<v8::Script> script_a = v8::Script::Compile( 1499 v8::String::NewFromUtf8( 1500 env->GetIsolate(), 1501 " function foo\n() { try { bar(); } catch(e) {} }\n" 1502 " function bar() { startProfiling(); }\n"), 1503 v8::String::NewFromUtf8(env->GetIsolate(), "script_a")); 1504 script_a->Run(); 1505 v8::Handle<v8::Script> script_b = v8::Script::Compile( 1506 v8::String::NewFromUtf8( 1507 env->GetIsolate(), 1508 "\n\n function baz() { try { foo(); } catch(e) {} }\n" 1509 "\n\nbaz();\n" 1510 "stopProfiling();\n"), 1511 v8::String::NewFromUtf8(env->GetIsolate(), "script_b")); 1512 script_b->Run(); 1513 CHECK_EQ(1, profiler->GetProfileCount()); 1514 const v8::CpuProfile* profile = profiler->GetCpuProfile(0); 1515 const v8::CpuProfileNode* current = profile->GetTopDownRoot(); 1516 reinterpret_cast<ProfileNode*>( 1517 const_cast<v8::CpuProfileNode*>(current))->Print(0); 1518 // The tree should look like this: 1519 // 0 (root) 0 #1 1520 // 0 (anonymous function) 19 #2 no reason script_b:1 1521 // 0 baz 19 #3 TryCatchStatement script_b:3 1522 // 0 foo 18 #4 TryCatchStatement script_a:2 1523 // 1 bar 18 #5 no reason script_a:3 1524 const v8::CpuProfileNode* root = profile->GetTopDownRoot(); 1525 const v8::CpuProfileNode* script = GetChild(env->GetIsolate(), root, 1526 ProfileGenerator::kAnonymousFunctionName); 1527 CheckFunctionDetails(env->GetIsolate(), script, 1528 ProfileGenerator::kAnonymousFunctionName, "script_b", 1529 script_b->GetId(), 1, 1); 1530 const v8::CpuProfileNode* baz = GetChild(env->GetIsolate(), script, "baz"); 1531 CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b", 1532 script_b->GetId(), 3, 16); 1533 const v8::CpuProfileNode* foo = GetChild(env->GetIsolate(), baz, "foo"); 1534 CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a", 1535 script_a->GetId(), 2, 1); 1536 const v8::CpuProfileNode* bar = GetChild(env->GetIsolate(), foo, "bar"); 1537 CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a", 1538 script_a->GetId(), 3, 14); 1539 } 1540