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