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/profiler/cpu-profiler.h" 34 #include "src/profiler/profile-generator-inl.h" 35 #include "test/cctest/cctest.h" 36 #include "test/cctest/profiler-extension.h" 37 38 using i::CodeEntry; 39 using i::CodeMap; 40 using i::CpuProfile; 41 using i::CpuProfiler; 42 using i::CpuProfilesCollection; 43 using i::ProfileNode; 44 using i::ProfileTree; 45 using i::ProfileGenerator; 46 using i::TickSample; 47 using i::Vector; 48 49 50 TEST(ProfileNodeFindOrAddChild) { 51 CcTest::InitializeVM(); 52 ProfileTree tree(CcTest::i_isolate()); 53 ProfileNode* node = tree.root(); 54 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 55 ProfileNode* childNode1 = node->FindOrAddChild(&entry1); 56 CHECK(childNode1); 57 CHECK_EQ(childNode1, node->FindOrAddChild(&entry1)); 58 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 59 ProfileNode* childNode2 = node->FindOrAddChild(&entry2); 60 CHECK(childNode2); 61 CHECK_NE(childNode1, childNode2); 62 CHECK_EQ(childNode1, node->FindOrAddChild(&entry1)); 63 CHECK_EQ(childNode2, node->FindOrAddChild(&entry2)); 64 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 65 ProfileNode* childNode3 = node->FindOrAddChild(&entry3); 66 CHECK(childNode3); 67 CHECK_NE(childNode1, childNode3); 68 CHECK_NE(childNode2, childNode3); 69 CHECK_EQ(childNode1, node->FindOrAddChild(&entry1)); 70 CHECK_EQ(childNode2, node->FindOrAddChild(&entry2)); 71 CHECK_EQ(childNode3, node->FindOrAddChild(&entry3)); 72 } 73 74 75 TEST(ProfileNodeFindOrAddChildForSameFunction) { 76 CcTest::InitializeVM(); 77 const char* aaa = "aaa"; 78 ProfileTree tree(CcTest::i_isolate()); 79 ProfileNode* node = tree.root(); 80 CodeEntry entry1(i::Logger::FUNCTION_TAG, aaa); 81 ProfileNode* childNode1 = node->FindOrAddChild(&entry1); 82 CHECK(childNode1); 83 CHECK_EQ(childNode1, node->FindOrAddChild(&entry1)); 84 // The same function again. 85 CodeEntry entry2(i::Logger::FUNCTION_TAG, aaa); 86 CHECK_EQ(childNode1, node->FindOrAddChild(&entry2)); 87 // Now with a different security token. 88 CodeEntry entry3(i::Logger::FUNCTION_TAG, aaa); 89 CHECK_EQ(childNode1, node->FindOrAddChild(&entry3)); 90 } 91 92 93 namespace { 94 95 class ProfileTreeTestHelper { 96 public: 97 explicit ProfileTreeTestHelper(const ProfileTree* tree) 98 : tree_(tree) { } 99 100 ProfileNode* Walk(CodeEntry* entry1, 101 CodeEntry* entry2 = NULL, 102 CodeEntry* entry3 = NULL) { 103 ProfileNode* node = tree_->root(); 104 node = node->FindChild(entry1); 105 if (node == NULL) return NULL; 106 if (entry2 != NULL) { 107 node = node->FindChild(entry2); 108 if (node == NULL) return NULL; 109 } 110 if (entry3 != NULL) { 111 node = node->FindChild(entry3); 112 } 113 return node; 114 } 115 116 private: 117 const ProfileTree* tree_; 118 }; 119 120 } // namespace 121 122 123 TEST(ProfileTreeAddPathFromEnd) { 124 CcTest::InitializeVM(); 125 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 126 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 127 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 128 ProfileTree tree(CcTest::i_isolate()); 129 ProfileTreeTestHelper helper(&tree); 130 CHECK(!helper.Walk(&entry1)); 131 CHECK(!helper.Walk(&entry2)); 132 CHECK(!helper.Walk(&entry3)); 133 134 CodeEntry* path[] = {NULL, &entry3, NULL, &entry2, NULL, NULL, &entry1, NULL}; 135 Vector<CodeEntry*> path_vec(path, sizeof(path) / sizeof(path[0])); 136 tree.AddPathFromEnd(path_vec); 137 CHECK(!helper.Walk(&entry2)); 138 CHECK(!helper.Walk(&entry3)); 139 ProfileNode* node1 = helper.Walk(&entry1); 140 CHECK(node1); 141 CHECK_EQ(0u, node1->self_ticks()); 142 CHECK(!helper.Walk(&entry1, &entry1)); 143 CHECK(!helper.Walk(&entry1, &entry3)); 144 ProfileNode* node2 = helper.Walk(&entry1, &entry2); 145 CHECK(node2); 146 CHECK_NE(node1, node2); 147 CHECK_EQ(0u, node2->self_ticks()); 148 CHECK(!helper.Walk(&entry1, &entry2, &entry1)); 149 CHECK(!helper.Walk(&entry1, &entry2, &entry2)); 150 ProfileNode* node3 = helper.Walk(&entry1, &entry2, &entry3); 151 CHECK(node3); 152 CHECK_NE(node1, node3); 153 CHECK_NE(node2, node3); 154 CHECK_EQ(1u, node3->self_ticks()); 155 156 tree.AddPathFromEnd(path_vec); 157 CHECK_EQ(node1, helper.Walk(&entry1)); 158 CHECK_EQ(node2, helper.Walk(&entry1, &entry2)); 159 CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3)); 160 CHECK_EQ(0u, node1->self_ticks()); 161 CHECK_EQ(0u, node2->self_ticks()); 162 CHECK_EQ(2u, node3->self_ticks()); 163 164 CodeEntry* path2[] = {&entry2, &entry2, &entry1}; 165 Vector<CodeEntry*> path2_vec(path2, sizeof(path2) / sizeof(path2[0])); 166 tree.AddPathFromEnd(path2_vec); 167 CHECK(!helper.Walk(&entry2)); 168 CHECK(!helper.Walk(&entry3)); 169 CHECK_EQ(node1, helper.Walk(&entry1)); 170 CHECK(!helper.Walk(&entry1, &entry1)); 171 CHECK(!helper.Walk(&entry1, &entry3)); 172 CHECK_EQ(node2, helper.Walk(&entry1, &entry2)); 173 CHECK(!helper.Walk(&entry1, &entry2, &entry1)); 174 CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3)); 175 CHECK_EQ(2u, node3->self_ticks()); 176 ProfileNode* node4 = helper.Walk(&entry1, &entry2, &entry2); 177 CHECK(node4); 178 CHECK_NE(node3, node4); 179 CHECK_EQ(1u, node4->self_ticks()); 180 } 181 182 183 TEST(ProfileTreeCalculateTotalTicks) { 184 CcTest::InitializeVM(); 185 ProfileTree empty_tree(CcTest::i_isolate()); 186 CHECK_EQ(0u, empty_tree.root()->self_ticks()); 187 empty_tree.root()->IncrementSelfTicks(); 188 CHECK_EQ(1u, empty_tree.root()->self_ticks()); 189 190 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 191 CodeEntry* e1_path[] = {&entry1}; 192 Vector<CodeEntry*> e1_path_vec( 193 e1_path, sizeof(e1_path) / sizeof(e1_path[0])); 194 195 ProfileTree single_child_tree(CcTest::i_isolate()); 196 single_child_tree.AddPathFromEnd(e1_path_vec); 197 single_child_tree.root()->IncrementSelfTicks(); 198 CHECK_EQ(1u, single_child_tree.root()->self_ticks()); 199 ProfileTreeTestHelper single_child_helper(&single_child_tree); 200 ProfileNode* node1 = single_child_helper.Walk(&entry1); 201 CHECK(node1); 202 CHECK_EQ(1u, single_child_tree.root()->self_ticks()); 203 CHECK_EQ(1u, node1->self_ticks()); 204 205 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 206 CodeEntry* e2_e1_path[] = {&entry2, &entry1}; 207 Vector<CodeEntry*> e2_e1_path_vec(e2_e1_path, 208 sizeof(e2_e1_path) / sizeof(e2_e1_path[0])); 209 210 ProfileTree flat_tree(CcTest::i_isolate()); 211 ProfileTreeTestHelper flat_helper(&flat_tree); 212 flat_tree.AddPathFromEnd(e1_path_vec); 213 flat_tree.AddPathFromEnd(e1_path_vec); 214 flat_tree.AddPathFromEnd(e2_e1_path_vec); 215 flat_tree.AddPathFromEnd(e2_e1_path_vec); 216 flat_tree.AddPathFromEnd(e2_e1_path_vec); 217 // Results in {root,0,0} -> {entry1,0,2} -> {entry2,0,3} 218 CHECK_EQ(0u, flat_tree.root()->self_ticks()); 219 node1 = flat_helper.Walk(&entry1); 220 CHECK(node1); 221 CHECK_EQ(2u, node1->self_ticks()); 222 ProfileNode* node2 = flat_helper.Walk(&entry1, &entry2); 223 CHECK(node2); 224 CHECK_EQ(3u, node2->self_ticks()); 225 // Must calculate {root,5,0} -> {entry1,5,2} -> {entry2,3,3} 226 CHECK_EQ(0u, flat_tree.root()->self_ticks()); 227 CHECK_EQ(2u, node1->self_ticks()); 228 229 CodeEntry* e2_path[] = {&entry2}; 230 Vector<CodeEntry*> e2_path_vec( 231 e2_path, sizeof(e2_path) / sizeof(e2_path[0])); 232 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 233 CodeEntry* e3_path[] = {&entry3}; 234 Vector<CodeEntry*> e3_path_vec( 235 e3_path, sizeof(e3_path) / sizeof(e3_path[0])); 236 237 ProfileTree wide_tree(CcTest::i_isolate()); 238 ProfileTreeTestHelper wide_helper(&wide_tree); 239 wide_tree.AddPathFromEnd(e1_path_vec); 240 wide_tree.AddPathFromEnd(e1_path_vec); 241 wide_tree.AddPathFromEnd(e2_e1_path_vec); 242 wide_tree.AddPathFromEnd(e2_path_vec); 243 wide_tree.AddPathFromEnd(e2_path_vec); 244 wide_tree.AddPathFromEnd(e2_path_vec); 245 wide_tree.AddPathFromEnd(e3_path_vec); 246 wide_tree.AddPathFromEnd(e3_path_vec); 247 wide_tree.AddPathFromEnd(e3_path_vec); 248 wide_tree.AddPathFromEnd(e3_path_vec); 249 // Results in -> {entry1,0,2} -> {entry2,0,1} 250 // {root,0,0} -> {entry2,0,3} 251 // -> {entry3,0,4} 252 CHECK_EQ(0u, wide_tree.root()->self_ticks()); 253 node1 = wide_helper.Walk(&entry1); 254 CHECK(node1); 255 CHECK_EQ(2u, node1->self_ticks()); 256 ProfileNode* node1_2 = wide_helper.Walk(&entry1, &entry2); 257 CHECK(node1_2); 258 CHECK_EQ(1u, node1_2->self_ticks()); 259 node2 = wide_helper.Walk(&entry2); 260 CHECK(node2); 261 CHECK_EQ(3u, node2->self_ticks()); 262 ProfileNode* node3 = wide_helper.Walk(&entry3); 263 CHECK(node3); 264 CHECK_EQ(4u, node3->self_ticks()); 265 // Calculates -> {entry1,3,2} -> {entry2,1,1} 266 // {root,10,0} -> {entry2,3,3} 267 // -> {entry3,4,4} 268 CHECK_EQ(0u, wide_tree.root()->self_ticks()); 269 CHECK_EQ(2u, node1->self_ticks()); 270 CHECK_EQ(1u, node1_2->self_ticks()); 271 CHECK_EQ(3u, node2->self_ticks()); 272 CHECK_EQ(4u, node3->self_ticks()); 273 } 274 275 276 static inline i::Address ToAddress(int n) { 277 return reinterpret_cast<i::Address>(n); 278 } 279 280 281 TEST(CodeMapAddCode) { 282 CodeMap code_map; 283 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 284 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 285 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 286 CodeEntry entry4(i::Logger::FUNCTION_TAG, "ddd"); 287 code_map.AddCode(ToAddress(0x1500), &entry1, 0x200); 288 code_map.AddCode(ToAddress(0x1700), &entry2, 0x100); 289 code_map.AddCode(ToAddress(0x1900), &entry3, 0x50); 290 code_map.AddCode(ToAddress(0x1950), &entry4, 0x10); 291 CHECK(!code_map.FindEntry(0)); 292 CHECK(!code_map.FindEntry(ToAddress(0x1500 - 1))); 293 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500))); 294 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500 + 0x100))); 295 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500 + 0x200 - 1))); 296 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700))); 297 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700 + 0x50))); 298 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700 + 0x100 - 1))); 299 CHECK(!code_map.FindEntry(ToAddress(0x1700 + 0x100))); 300 CHECK(!code_map.FindEntry(ToAddress(0x1900 - 1))); 301 CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1900))); 302 CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1900 + 0x28))); 303 CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950))); 304 CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950 + 0x7))); 305 CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950 + 0x10 - 1))); 306 CHECK(!code_map.FindEntry(ToAddress(0x1950 + 0x10))); 307 CHECK(!code_map.FindEntry(ToAddress(0xFFFFFFFF))); 308 } 309 310 311 TEST(CodeMapMoveAndDeleteCode) { 312 CodeMap code_map; 313 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 314 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 315 code_map.AddCode(ToAddress(0x1500), &entry1, 0x200); 316 code_map.AddCode(ToAddress(0x1700), &entry2, 0x100); 317 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500))); 318 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700))); 319 code_map.MoveCode(ToAddress(0x1500), ToAddress(0x1700)); // Deprecate bbb. 320 CHECK(!code_map.FindEntry(ToAddress(0x1500))); 321 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1700))); 322 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 323 code_map.AddCode(ToAddress(0x1750), &entry3, 0x100); 324 CHECK(!code_map.FindEntry(ToAddress(0x1700))); 325 CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1750))); 326 } 327 328 329 namespace { 330 331 class TestSetup { 332 public: 333 TestSetup() 334 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) { 335 i::FLAG_prof_browser_mode = false; 336 } 337 338 ~TestSetup() { 339 i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_; 340 } 341 342 private: 343 bool old_flag_prof_browser_mode_; 344 }; 345 346 } // namespace 347 348 TEST(RecordTickSample) { 349 TestSetup test_setup; 350 CpuProfilesCollection profiles(CcTest::heap()); 351 profiles.StartProfiling("", false); 352 ProfileGenerator generator(&profiles); 353 CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); 354 CodeEntry* entry2 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb"); 355 CodeEntry* entry3 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "ccc"); 356 generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); 357 generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100); 358 generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50); 359 360 // We are building the following calls tree: 361 // -> aaa - sample1 362 // aaa -> bbb -> ccc - sample2 363 // -> ccc -> aaa - sample3 364 TickSample sample1; 365 sample1.pc = ToAddress(0x1600); 366 sample1.tos = ToAddress(0x1500); 367 sample1.stack[0] = ToAddress(0x1510); 368 sample1.frames_count = 1; 369 generator.RecordTickSample(sample1); 370 TickSample sample2; 371 sample2.pc = ToAddress(0x1925); 372 sample2.tos = ToAddress(0x1900); 373 sample2.stack[0] = ToAddress(0x1780); 374 sample2.stack[1] = ToAddress(0x10000); // non-existent. 375 sample2.stack[2] = ToAddress(0x1620); 376 sample2.frames_count = 3; 377 generator.RecordTickSample(sample2); 378 TickSample sample3; 379 sample3.pc = ToAddress(0x1510); 380 sample3.tos = ToAddress(0x1500); 381 sample3.stack[0] = ToAddress(0x1910); 382 sample3.stack[1] = ToAddress(0x1610); 383 sample3.frames_count = 2; 384 generator.RecordTickSample(sample3); 385 386 CpuProfile* profile = profiles.StopProfiling(""); 387 CHECK(profile); 388 ProfileTreeTestHelper top_down_test_helper(profile->top_down()); 389 CHECK(!top_down_test_helper.Walk(entry2)); 390 CHECK(!top_down_test_helper.Walk(entry3)); 391 ProfileNode* node1 = top_down_test_helper.Walk(entry1); 392 CHECK(node1); 393 CHECK_EQ(entry1, node1->entry()); 394 ProfileNode* node2 = top_down_test_helper.Walk(entry1, entry1); 395 CHECK(node2); 396 CHECK_EQ(entry1, node2->entry()); 397 ProfileNode* node3 = top_down_test_helper.Walk(entry1, entry2, entry3); 398 CHECK(node3); 399 CHECK_EQ(entry3, node3->entry()); 400 ProfileNode* node4 = top_down_test_helper.Walk(entry1, entry3, entry1); 401 CHECK(node4); 402 CHECK_EQ(entry1, node4->entry()); 403 } 404 405 406 static void CheckNodeIds(ProfileNode* node, unsigned* expectedId) { 407 CHECK_EQ((*expectedId)++, node->id()); 408 for (int i = 0; i < node->children()->length(); i++) { 409 CheckNodeIds(node->children()->at(i), expectedId); 410 } 411 } 412 413 414 TEST(SampleIds) { 415 TestSetup test_setup; 416 CpuProfilesCollection profiles(CcTest::heap()); 417 profiles.StartProfiling("", true); 418 ProfileGenerator generator(&profiles); 419 CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); 420 CodeEntry* entry2 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb"); 421 CodeEntry* entry3 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "ccc"); 422 generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); 423 generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100); 424 generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50); 425 426 // We are building the following calls tree: 427 // -> aaa #3 - sample1 428 // (root)#1 -> aaa #2 -> bbb #4 -> ccc #5 - sample2 429 // -> ccc #6 -> aaa #7 - sample3 430 TickSample sample1; 431 sample1.pc = ToAddress(0x1600); 432 sample1.stack[0] = ToAddress(0x1510); 433 sample1.frames_count = 1; 434 generator.RecordTickSample(sample1); 435 TickSample sample2; 436 sample2.pc = ToAddress(0x1925); 437 sample2.stack[0] = ToAddress(0x1780); 438 sample2.stack[1] = ToAddress(0x10000); // non-existent. 439 sample2.stack[2] = ToAddress(0x1620); 440 sample2.frames_count = 3; 441 generator.RecordTickSample(sample2); 442 TickSample sample3; 443 sample3.pc = ToAddress(0x1510); 444 sample3.stack[0] = ToAddress(0x1910); 445 sample3.stack[1] = ToAddress(0x1610); 446 sample3.frames_count = 2; 447 generator.RecordTickSample(sample3); 448 449 CpuProfile* profile = profiles.StopProfiling(""); 450 unsigned nodeId = 1; 451 CheckNodeIds(profile->top_down()->root(), &nodeId); 452 CHECK_EQ(7u, nodeId - 1); 453 454 CHECK_EQ(3, profile->samples_count()); 455 unsigned expected_id[] = {3, 5, 7}; 456 for (int i = 0; i < 3; i++) { 457 CHECK_EQ(expected_id[i], profile->sample(i)->id()); 458 } 459 } 460 461 462 TEST(NoSamples) { 463 TestSetup test_setup; 464 CpuProfilesCollection profiles(CcTest::heap()); 465 profiles.StartProfiling("", false); 466 ProfileGenerator generator(&profiles); 467 CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); 468 generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); 469 470 // We are building the following calls tree: 471 // (root)#1 -> aaa #2 -> aaa #3 - sample1 472 TickSample sample1; 473 sample1.pc = ToAddress(0x1600); 474 sample1.stack[0] = ToAddress(0x1510); 475 sample1.frames_count = 1; 476 generator.RecordTickSample(sample1); 477 478 CpuProfile* profile = profiles.StopProfiling(""); 479 unsigned nodeId = 1; 480 CheckNodeIds(profile->top_down()->root(), &nodeId); 481 CHECK_EQ(3u, nodeId - 1); 482 483 CHECK_EQ(0, profile->samples_count()); 484 } 485 486 487 static const ProfileNode* PickChild(const ProfileNode* parent, 488 const char* name) { 489 for (int i = 0; i < parent->children()->length(); ++i) { 490 const ProfileNode* child = parent->children()->at(i); 491 if (strcmp(child->entry()->name(), name) == 0) return child; 492 } 493 return NULL; 494 } 495 496 497 TEST(RecordStackTraceAtStartProfiling) { 498 // This test does not pass with inlining enabled since inlined functions 499 // don't appear in the stack trace. 500 i::FLAG_turbo_inlining = false; 501 i::FLAG_use_inlining = false; 502 503 v8::HandleScope scope(CcTest::isolate()); 504 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION); 505 v8::Context::Scope context_scope(env); 506 507 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler(); 508 CHECK_EQ(0, profiler->GetProfilesCount()); 509 CompileRun( 510 "function c() { startProfiling(); }\n" 511 "function b() { c(); }\n" 512 "function a() { b(); }\n" 513 "a();\n" 514 "stopProfiling();"); 515 CHECK_EQ(1, profiler->GetProfilesCount()); 516 CpuProfile* profile = profiler->GetProfile(0); 517 const ProfileTree* topDown = profile->top_down(); 518 const ProfileNode* current = topDown->root(); 519 const_cast<ProfileNode*>(current)->Print(0); 520 // The tree should look like this: 521 // (root) 522 // "" 523 // a 524 // b 525 // c 526 // There can also be: 527 // startProfiling 528 // if the sampler managed to get a tick. 529 current = PickChild(current, ""); 530 CHECK(const_cast<ProfileNode*>(current)); 531 current = PickChild(current, "a"); 532 CHECK(const_cast<ProfileNode*>(current)); 533 current = PickChild(current, "b"); 534 CHECK(const_cast<ProfileNode*>(current)); 535 current = PickChild(current, "c"); 536 CHECK(const_cast<ProfileNode*>(current)); 537 CHECK(current->children()->length() == 0 || 538 current->children()->length() == 1); 539 if (current->children()->length() == 1) { 540 current = PickChild(current, "startProfiling"); 541 CHECK_EQ(0, current->children()->length()); 542 } 543 } 544 545 546 TEST(Issue51919) { 547 CpuProfilesCollection collection(CcTest::heap()); 548 i::EmbeddedVector<char*, 549 CpuProfilesCollection::kMaxSimultaneousProfiles> titles; 550 for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) { 551 i::Vector<char> title = i::Vector<char>::New(16); 552 i::SNPrintF(title, "%d", i); 553 CHECK(collection.StartProfiling(title.start(), false)); 554 titles[i] = title.start(); 555 } 556 CHECK(!collection.StartProfiling("maximum", false)); 557 for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) 558 i::DeleteArray(titles[i]); 559 } 560 561 562 static const v8::CpuProfileNode* PickChild(const v8::CpuProfileNode* parent, 563 const char* name) { 564 for (int i = 0; i < parent->GetChildrenCount(); ++i) { 565 const v8::CpuProfileNode* child = parent->GetChild(i); 566 v8::String::Utf8Value function_name(child->GetFunctionName()); 567 if (strcmp(*function_name, name) == 0) return child; 568 } 569 return NULL; 570 } 571 572 573 TEST(ProfileNodeScriptId) { 574 // This test does not pass with inlining enabled since inlined functions 575 // don't appear in the stack trace. 576 i::FLAG_turbo_inlining = false; 577 i::FLAG_use_inlining = false; 578 579 v8::HandleScope scope(CcTest::isolate()); 580 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION); 581 v8::Context::Scope context_scope(env); 582 583 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler(); 584 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler); 585 CHECK_EQ(0, iprofiler->GetProfilesCount()); 586 v8::Local<v8::Script> script_a = 587 v8_compile(v8_str("function a() { startProfiling(); }\n")); 588 script_a->Run(v8::Isolate::GetCurrent()->GetCurrentContext()) 589 .ToLocalChecked(); 590 v8::Local<v8::Script> script_b = 591 v8_compile(v8_str("function b() { a(); }\n" 592 "b();\n" 593 "stopProfiling();\n")); 594 script_b->Run(v8::Isolate::GetCurrent()->GetCurrentContext()) 595 .ToLocalChecked(); 596 CHECK_EQ(1, iprofiler->GetProfilesCount()); 597 const v8::CpuProfile* profile = i::ProfilerExtension::last_profile; 598 const v8::CpuProfileNode* current = profile->GetTopDownRoot(); 599 reinterpret_cast<ProfileNode*>( 600 const_cast<v8::CpuProfileNode*>(current))->Print(0); 601 // The tree should look like this: 602 // (root) 603 // "" 604 // b 605 // a 606 // There can also be: 607 // startProfiling 608 // if the sampler managed to get a tick. 609 current = PickChild(current, ""); 610 CHECK(const_cast<v8::CpuProfileNode*>(current)); 611 612 current = PickChild(current, "b"); 613 CHECK(const_cast<v8::CpuProfileNode*>(current)); 614 CHECK_EQ(script_b->GetUnboundScript()->GetId(), current->GetScriptId()); 615 616 current = PickChild(current, "a"); 617 CHECK(const_cast<v8::CpuProfileNode*>(current)); 618 CHECK_EQ(script_a->GetUnboundScript()->GetId(), current->GetScriptId()); 619 } 620 621 622 623 624 static const char* line_number_test_source_existing_functions = 625 "function foo_at_the_first_line() {\n" 626 "}\n" 627 "foo_at_the_first_line();\n" 628 "function lazy_func_at_forth_line() {}\n"; 629 630 631 static const char* line_number_test_source_profile_time_functions = 632 "// Empty first line\n" 633 "function bar_at_the_second_line() {\n" 634 " foo_at_the_first_line();\n" 635 "}\n" 636 "bar_at_the_second_line();\n" 637 "function lazy_func_at_6th_line() {}"; 638 639 int GetFunctionLineNumber(LocalContext* env, const char* name) { 640 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler(); 641 CodeMap* code_map = profiler->generator()->code_map(); 642 i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast( 643 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( 644 (*(*env)) 645 ->Global() 646 ->Get(v8::Isolate::GetCurrent()->GetCurrentContext(), 647 v8_str(name)) 648 .ToLocalChecked()))); 649 CodeEntry* func_entry = code_map->FindEntry(func->code()->address()); 650 if (!func_entry) 651 FATAL(name); 652 return func_entry->line_number(); 653 } 654 655 656 TEST(LineNumber) { 657 i::FLAG_use_inlining = false; 658 659 CcTest::InitializeVM(); 660 LocalContext env; 661 i::Isolate* isolate = CcTest::i_isolate(); 662 TestSetup test_setup; 663 664 i::HandleScope scope(isolate); 665 666 CompileRun(line_number_test_source_existing_functions); 667 668 CpuProfiler* profiler = isolate->cpu_profiler(); 669 profiler->StartProfiling("LineNumber"); 670 671 CompileRun(line_number_test_source_profile_time_functions); 672 673 profiler->processor()->StopSynchronously(); 674 675 CHECK_EQ(1, GetFunctionLineNumber(&env, "foo_at_the_first_line")); 676 CHECK_EQ(0, GetFunctionLineNumber(&env, "lazy_func_at_forth_line")); 677 CHECK_EQ(2, GetFunctionLineNumber(&env, "bar_at_the_second_line")); 678 CHECK_EQ(0, GetFunctionLineNumber(&env, "lazy_func_at_6th_line")); 679 680 profiler->StopProfiling("LineNumber"); 681 } 682 683 684 685 TEST(BailoutReason) { 686 v8::HandleScope scope(CcTest::isolate()); 687 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION); 688 v8::Context::Scope context_scope(env); 689 690 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler(); 691 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler); 692 CHECK_EQ(0, iprofiler->GetProfilesCount()); 693 v8::Local<v8::Script> script = 694 v8_compile(v8_str("function Debugger() {\n" 695 " debugger;\n" 696 " startProfiling();\n" 697 "}\n" 698 "function TryFinally() {\n" 699 " try {\n" 700 " Debugger();\n" 701 " } finally { };\n" 702 "}\n" 703 "TryFinally();\n" 704 "stopProfiling();")); 705 script->Run(v8::Isolate::GetCurrent()->GetCurrentContext()).ToLocalChecked(); 706 CHECK_EQ(1, iprofiler->GetProfilesCount()); 707 const v8::CpuProfile* profile = i::ProfilerExtension::last_profile; 708 CHECK(profile); 709 const v8::CpuProfileNode* current = profile->GetTopDownRoot(); 710 reinterpret_cast<ProfileNode*>( 711 const_cast<v8::CpuProfileNode*>(current))->Print(0); 712 // The tree should look like this: 713 // (root) 714 // "" 715 // kTryFinallyStatement 716 // kDebuggerStatement 717 current = PickChild(current, ""); 718 CHECK(const_cast<v8::CpuProfileNode*>(current)); 719 720 current = PickChild(current, "TryFinally"); 721 CHECK(const_cast<v8::CpuProfileNode*>(current)); 722 CHECK(!strcmp("TryFinallyStatement", current->GetBailoutReason())); 723 724 current = PickChild(current, "Debugger"); 725 CHECK(const_cast<v8::CpuProfileNode*>(current)); 726 CHECK(!strcmp("DebuggerStatement", current->GetBailoutReason())); 727 } 728