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