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