1 // Copyright 2010 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 // 28 // Tests of profiles generator and utilities. 29 30 #include "v8.h" 31 #include "profile-generator-inl.h" 32 #include "cctest.h" 33 #include "cpu-profiler.h" 34 #include "../include/v8-profiler.h" 35 36 using i::CodeEntry; 37 using i::CodeMap; 38 using i::CpuProfile; 39 using i::CpuProfiler; 40 using i::CpuProfilesCollection; 41 using i::ProfileNode; 42 using i::ProfileTree; 43 using i::ProfileGenerator; 44 using i::TickSample; 45 using i::Vector; 46 47 48 TEST(ProfileNodeFindOrAddChild) { 49 ProfileTree tree; 50 ProfileNode node(&tree, NULL); 51 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 52 ProfileNode* childNode1 = node.FindOrAddChild(&entry1); 53 CHECK_NE(NULL, childNode1); 54 CHECK_EQ(childNode1, node.FindOrAddChild(&entry1)); 55 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 56 ProfileNode* childNode2 = node.FindOrAddChild(&entry2); 57 CHECK_NE(NULL, childNode2); 58 CHECK_NE(childNode1, childNode2); 59 CHECK_EQ(childNode1, node.FindOrAddChild(&entry1)); 60 CHECK_EQ(childNode2, node.FindOrAddChild(&entry2)); 61 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 62 ProfileNode* childNode3 = node.FindOrAddChild(&entry3); 63 CHECK_NE(NULL, childNode3); 64 CHECK_NE(childNode1, childNode3); 65 CHECK_NE(childNode2, childNode3); 66 CHECK_EQ(childNode1, node.FindOrAddChild(&entry1)); 67 CHECK_EQ(childNode2, node.FindOrAddChild(&entry2)); 68 CHECK_EQ(childNode3, node.FindOrAddChild(&entry3)); 69 } 70 71 72 TEST(ProfileNodeFindOrAddChildForSameFunction) { 73 const char* aaa = "aaa"; 74 ProfileTree tree; 75 ProfileNode node(&tree, NULL); 76 CodeEntry entry1(i::Logger::FUNCTION_TAG, aaa); 77 ProfileNode* childNode1 = node.FindOrAddChild(&entry1); 78 CHECK_NE(NULL, childNode1); 79 CHECK_EQ(childNode1, node.FindOrAddChild(&entry1)); 80 // The same function again. 81 CodeEntry entry2(i::Logger::FUNCTION_TAG, aaa); 82 CHECK_EQ(childNode1, node.FindOrAddChild(&entry2)); 83 // Now with a different security token. 84 CodeEntry entry3(i::Logger::FUNCTION_TAG, aaa); 85 CHECK_EQ(childNode1, node.FindOrAddChild(&entry3)); 86 } 87 88 89 namespace { 90 91 class ProfileTreeTestHelper { 92 public: 93 explicit ProfileTreeTestHelper(const ProfileTree* tree) 94 : tree_(tree) { } 95 96 ProfileNode* Walk(CodeEntry* entry1, 97 CodeEntry* entry2 = NULL, 98 CodeEntry* entry3 = NULL) { 99 ProfileNode* node = tree_->root(); 100 node = node->FindChild(entry1); 101 if (node == NULL) return NULL; 102 if (entry2 != NULL) { 103 node = node->FindChild(entry2); 104 if (node == NULL) return NULL; 105 } 106 if (entry3 != NULL) { 107 node = node->FindChild(entry3); 108 } 109 return node; 110 } 111 112 private: 113 const ProfileTree* tree_; 114 }; 115 116 } // namespace 117 118 TEST(ProfileTreeAddPathFromStart) { 119 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 120 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 121 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 122 ProfileTree tree; 123 ProfileTreeTestHelper helper(&tree); 124 CHECK_EQ(NULL, helper.Walk(&entry1)); 125 CHECK_EQ(NULL, helper.Walk(&entry2)); 126 CHECK_EQ(NULL, helper.Walk(&entry3)); 127 128 CodeEntry* path[] = {NULL, &entry1, NULL, &entry2, NULL, NULL, &entry3, NULL}; 129 Vector<CodeEntry*> path_vec(path, sizeof(path) / sizeof(path[0])); 130 tree.AddPathFromStart(path_vec); 131 CHECK_EQ(NULL, helper.Walk(&entry2)); 132 CHECK_EQ(NULL, helper.Walk(&entry3)); 133 ProfileNode* node1 = helper.Walk(&entry1); 134 CHECK_NE(NULL, node1); 135 CHECK_EQ(0, node1->self_ticks()); 136 CHECK_EQ(NULL, helper.Walk(&entry1, &entry1)); 137 CHECK_EQ(NULL, helper.Walk(&entry1, &entry3)); 138 ProfileNode* node2 = helper.Walk(&entry1, &entry2); 139 CHECK_NE(NULL, node2); 140 CHECK_NE(node1, node2); 141 CHECK_EQ(0, node2->self_ticks()); 142 CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1)); 143 CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry2)); 144 ProfileNode* node3 = helper.Walk(&entry1, &entry2, &entry3); 145 CHECK_NE(NULL, node3); 146 CHECK_NE(node1, node3); 147 CHECK_NE(node2, node3); 148 CHECK_EQ(1, node3->self_ticks()); 149 150 tree.AddPathFromStart(path_vec); 151 CHECK_EQ(node1, helper.Walk(&entry1)); 152 CHECK_EQ(node2, helper.Walk(&entry1, &entry2)); 153 CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3)); 154 CHECK_EQ(0, node1->self_ticks()); 155 CHECK_EQ(0, node2->self_ticks()); 156 CHECK_EQ(2, node3->self_ticks()); 157 158 CodeEntry* path2[] = {&entry1, &entry2, &entry2}; 159 Vector<CodeEntry*> path2_vec(path2, sizeof(path2) / sizeof(path2[0])); 160 tree.AddPathFromStart(path2_vec); 161 CHECK_EQ(NULL, helper.Walk(&entry2)); 162 CHECK_EQ(NULL, helper.Walk(&entry3)); 163 CHECK_EQ(node1, helper.Walk(&entry1)); 164 CHECK_EQ(NULL, helper.Walk(&entry1, &entry1)); 165 CHECK_EQ(NULL, helper.Walk(&entry1, &entry3)); 166 CHECK_EQ(node2, helper.Walk(&entry1, &entry2)); 167 CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1)); 168 CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3)); 169 CHECK_EQ(2, node3->self_ticks()); 170 ProfileNode* node4 = helper.Walk(&entry1, &entry2, &entry2); 171 CHECK_NE(NULL, node4); 172 CHECK_NE(node3, node4); 173 CHECK_EQ(1, node4->self_ticks()); 174 } 175 176 177 TEST(ProfileTreeAddPathFromEnd) { 178 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 179 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 180 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 181 ProfileTree tree; 182 ProfileTreeTestHelper helper(&tree); 183 CHECK_EQ(NULL, helper.Walk(&entry1)); 184 CHECK_EQ(NULL, helper.Walk(&entry2)); 185 CHECK_EQ(NULL, helper.Walk(&entry3)); 186 187 CodeEntry* path[] = {NULL, &entry3, NULL, &entry2, NULL, NULL, &entry1, NULL}; 188 Vector<CodeEntry*> path_vec(path, sizeof(path) / sizeof(path[0])); 189 tree.AddPathFromEnd(path_vec); 190 CHECK_EQ(NULL, helper.Walk(&entry2)); 191 CHECK_EQ(NULL, helper.Walk(&entry3)); 192 ProfileNode* node1 = helper.Walk(&entry1); 193 CHECK_NE(NULL, node1); 194 CHECK_EQ(0, node1->self_ticks()); 195 CHECK_EQ(NULL, helper.Walk(&entry1, &entry1)); 196 CHECK_EQ(NULL, helper.Walk(&entry1, &entry3)); 197 ProfileNode* node2 = helper.Walk(&entry1, &entry2); 198 CHECK_NE(NULL, node2); 199 CHECK_NE(node1, node2); 200 CHECK_EQ(0, node2->self_ticks()); 201 CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1)); 202 CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry2)); 203 ProfileNode* node3 = helper.Walk(&entry1, &entry2, &entry3); 204 CHECK_NE(NULL, node3); 205 CHECK_NE(node1, node3); 206 CHECK_NE(node2, node3); 207 CHECK_EQ(1, node3->self_ticks()); 208 209 tree.AddPathFromEnd(path_vec); 210 CHECK_EQ(node1, helper.Walk(&entry1)); 211 CHECK_EQ(node2, helper.Walk(&entry1, &entry2)); 212 CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3)); 213 CHECK_EQ(0, node1->self_ticks()); 214 CHECK_EQ(0, node2->self_ticks()); 215 CHECK_EQ(2, node3->self_ticks()); 216 217 CodeEntry* path2[] = {&entry2, &entry2, &entry1}; 218 Vector<CodeEntry*> path2_vec(path2, sizeof(path2) / sizeof(path2[0])); 219 tree.AddPathFromEnd(path2_vec); 220 CHECK_EQ(NULL, helper.Walk(&entry2)); 221 CHECK_EQ(NULL, helper.Walk(&entry3)); 222 CHECK_EQ(node1, helper.Walk(&entry1)); 223 CHECK_EQ(NULL, helper.Walk(&entry1, &entry1)); 224 CHECK_EQ(NULL, helper.Walk(&entry1, &entry3)); 225 CHECK_EQ(node2, helper.Walk(&entry1, &entry2)); 226 CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1)); 227 CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3)); 228 CHECK_EQ(2, node3->self_ticks()); 229 ProfileNode* node4 = helper.Walk(&entry1, &entry2, &entry2); 230 CHECK_NE(NULL, node4); 231 CHECK_NE(node3, node4); 232 CHECK_EQ(1, node4->self_ticks()); 233 } 234 235 236 TEST(ProfileTreeCalculateTotalTicks) { 237 ProfileTree empty_tree; 238 CHECK_EQ(0, empty_tree.root()->self_ticks()); 239 empty_tree.root()->IncrementSelfTicks(); 240 CHECK_EQ(1, empty_tree.root()->self_ticks()); 241 242 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 243 CodeEntry* e1_path[] = {&entry1}; 244 Vector<CodeEntry*> e1_path_vec( 245 e1_path, sizeof(e1_path) / sizeof(e1_path[0])); 246 247 ProfileTree single_child_tree; 248 single_child_tree.AddPathFromStart(e1_path_vec); 249 single_child_tree.root()->IncrementSelfTicks(); 250 CHECK_EQ(1, single_child_tree.root()->self_ticks()); 251 ProfileTreeTestHelper single_child_helper(&single_child_tree); 252 ProfileNode* node1 = single_child_helper.Walk(&entry1); 253 CHECK_NE(NULL, node1); 254 CHECK_EQ(1, single_child_tree.root()->self_ticks()); 255 CHECK_EQ(1, node1->self_ticks()); 256 257 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 258 CodeEntry* e1_e2_path[] = {&entry1, &entry2}; 259 Vector<CodeEntry*> e1_e2_path_vec( 260 e1_e2_path, sizeof(e1_e2_path) / sizeof(e1_e2_path[0])); 261 262 ProfileTree flat_tree; 263 ProfileTreeTestHelper flat_helper(&flat_tree); 264 flat_tree.AddPathFromStart(e1_path_vec); 265 flat_tree.AddPathFromStart(e1_path_vec); 266 flat_tree.AddPathFromStart(e1_e2_path_vec); 267 flat_tree.AddPathFromStart(e1_e2_path_vec); 268 flat_tree.AddPathFromStart(e1_e2_path_vec); 269 // Results in {root,0,0} -> {entry1,0,2} -> {entry2,0,3} 270 CHECK_EQ(0, flat_tree.root()->self_ticks()); 271 node1 = flat_helper.Walk(&entry1); 272 CHECK_NE(NULL, node1); 273 CHECK_EQ(2, node1->self_ticks()); 274 ProfileNode* node2 = flat_helper.Walk(&entry1, &entry2); 275 CHECK_NE(NULL, node2); 276 CHECK_EQ(3, node2->self_ticks()); 277 // Must calculate {root,5,0} -> {entry1,5,2} -> {entry2,3,3} 278 CHECK_EQ(0, flat_tree.root()->self_ticks()); 279 CHECK_EQ(2, node1->self_ticks()); 280 281 CodeEntry* e2_path[] = {&entry2}; 282 Vector<CodeEntry*> e2_path_vec( 283 e2_path, sizeof(e2_path) / sizeof(e2_path[0])); 284 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 285 CodeEntry* e3_path[] = {&entry3}; 286 Vector<CodeEntry*> e3_path_vec( 287 e3_path, sizeof(e3_path) / sizeof(e3_path[0])); 288 289 ProfileTree wide_tree; 290 ProfileTreeTestHelper wide_helper(&wide_tree); 291 wide_tree.AddPathFromStart(e1_path_vec); 292 wide_tree.AddPathFromStart(e1_path_vec); 293 wide_tree.AddPathFromStart(e1_e2_path_vec); 294 wide_tree.AddPathFromStart(e2_path_vec); 295 wide_tree.AddPathFromStart(e2_path_vec); 296 wide_tree.AddPathFromStart(e2_path_vec); 297 wide_tree.AddPathFromStart(e3_path_vec); 298 wide_tree.AddPathFromStart(e3_path_vec); 299 wide_tree.AddPathFromStart(e3_path_vec); 300 wide_tree.AddPathFromStart(e3_path_vec); 301 // Results in -> {entry1,0,2} -> {entry2,0,1} 302 // {root,0,0} -> {entry2,0,3} 303 // -> {entry3,0,4} 304 CHECK_EQ(0, wide_tree.root()->self_ticks()); 305 node1 = wide_helper.Walk(&entry1); 306 CHECK_NE(NULL, node1); 307 CHECK_EQ(2, node1->self_ticks()); 308 ProfileNode* node1_2 = wide_helper.Walk(&entry1, &entry2); 309 CHECK_NE(NULL, node1_2); 310 CHECK_EQ(1, node1_2->self_ticks()); 311 node2 = wide_helper.Walk(&entry2); 312 CHECK_NE(NULL, node2); 313 CHECK_EQ(3, node2->self_ticks()); 314 ProfileNode* node3 = wide_helper.Walk(&entry3); 315 CHECK_NE(NULL, node3); 316 CHECK_EQ(4, node3->self_ticks()); 317 // Calculates -> {entry1,3,2} -> {entry2,1,1} 318 // {root,10,0} -> {entry2,3,3} 319 // -> {entry3,4,4} 320 CHECK_EQ(0, wide_tree.root()->self_ticks()); 321 CHECK_EQ(2, node1->self_ticks()); 322 CHECK_EQ(1, node1_2->self_ticks()); 323 CHECK_EQ(3, node2->self_ticks()); 324 CHECK_EQ(4, node3->self_ticks()); 325 } 326 327 328 static inline i::Address ToAddress(int n) { 329 return reinterpret_cast<i::Address>(n); 330 } 331 332 333 TEST(CodeMapAddCode) { 334 CodeMap code_map; 335 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 336 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 337 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 338 CodeEntry entry4(i::Logger::FUNCTION_TAG, "ddd"); 339 code_map.AddCode(ToAddress(0x1500), &entry1, 0x200); 340 code_map.AddCode(ToAddress(0x1700), &entry2, 0x100); 341 code_map.AddCode(ToAddress(0x1900), &entry3, 0x50); 342 code_map.AddCode(ToAddress(0x1950), &entry4, 0x10); 343 CHECK_EQ(NULL, code_map.FindEntry(0)); 344 CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1500 - 1))); 345 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500))); 346 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500 + 0x100))); 347 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500 + 0x200 - 1))); 348 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700))); 349 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700 + 0x50))); 350 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700 + 0x100 - 1))); 351 CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1700 + 0x100))); 352 CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1900 - 1))); 353 CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1900))); 354 CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1900 + 0x28))); 355 CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950))); 356 CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950 + 0x7))); 357 CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950 + 0x10 - 1))); 358 CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1950 + 0x10))); 359 CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0xFFFFFFFF))); 360 } 361 362 363 TEST(CodeMapMoveAndDeleteCode) { 364 CodeMap code_map; 365 CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa"); 366 CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb"); 367 code_map.AddCode(ToAddress(0x1500), &entry1, 0x200); 368 code_map.AddCode(ToAddress(0x1700), &entry2, 0x100); 369 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500))); 370 CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700))); 371 code_map.MoveCode(ToAddress(0x1500), ToAddress(0x1700)); // Deprecate bbb. 372 CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1500))); 373 CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1700))); 374 CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc"); 375 code_map.AddCode(ToAddress(0x1750), &entry3, 0x100); 376 CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1700))); 377 CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1750))); 378 } 379 380 381 namespace { 382 383 class TestSetup { 384 public: 385 TestSetup() 386 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) { 387 i::FLAG_prof_browser_mode = false; 388 } 389 390 ~TestSetup() { 391 i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_; 392 } 393 394 private: 395 bool old_flag_prof_browser_mode_; 396 }; 397 398 } // namespace 399 400 TEST(RecordTickSample) { 401 TestSetup test_setup; 402 CpuProfilesCollection profiles(CcTest::heap()); 403 profiles.StartProfiling("", 1, false); 404 ProfileGenerator generator(&profiles); 405 CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); 406 CodeEntry* entry2 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb"); 407 CodeEntry* entry3 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "ccc"); 408 generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); 409 generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100); 410 generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50); 411 412 // We are building the following calls tree: 413 // -> aaa - sample1 414 // aaa -> bbb -> ccc - sample2 415 // -> ccc -> aaa - sample3 416 TickSample sample1; 417 sample1.pc = ToAddress(0x1600); 418 sample1.tos = ToAddress(0x1500); 419 sample1.stack[0] = ToAddress(0x1510); 420 sample1.frames_count = 1; 421 generator.RecordTickSample(sample1); 422 TickSample sample2; 423 sample2.pc = ToAddress(0x1925); 424 sample2.tos = ToAddress(0x1900); 425 sample2.stack[0] = ToAddress(0x1780); 426 sample2.stack[1] = ToAddress(0x10000); // non-existent. 427 sample2.stack[2] = ToAddress(0x1620); 428 sample2.frames_count = 3; 429 generator.RecordTickSample(sample2); 430 TickSample sample3; 431 sample3.pc = ToAddress(0x1510); 432 sample3.tos = ToAddress(0x1500); 433 sample3.stack[0] = ToAddress(0x1910); 434 sample3.stack[1] = ToAddress(0x1610); 435 sample3.frames_count = 2; 436 generator.RecordTickSample(sample3); 437 438 CpuProfile* profile = profiles.StopProfiling(""); 439 CHECK_NE(NULL, profile); 440 ProfileTreeTestHelper top_down_test_helper(profile->top_down()); 441 CHECK_EQ(NULL, top_down_test_helper.Walk(entry2)); 442 CHECK_EQ(NULL, top_down_test_helper.Walk(entry3)); 443 ProfileNode* node1 = top_down_test_helper.Walk(entry1); 444 CHECK_NE(NULL, node1); 445 CHECK_EQ(entry1, node1->entry()); 446 ProfileNode* node2 = top_down_test_helper.Walk(entry1, entry1); 447 CHECK_NE(NULL, node2); 448 CHECK_EQ(entry1, node2->entry()); 449 ProfileNode* node3 = top_down_test_helper.Walk(entry1, entry2, entry3); 450 CHECK_NE(NULL, node3); 451 CHECK_EQ(entry3, node3->entry()); 452 ProfileNode* node4 = top_down_test_helper.Walk(entry1, entry3, entry1); 453 CHECK_NE(NULL, node4); 454 CHECK_EQ(entry1, node4->entry()); 455 } 456 457 458 static void CheckNodeIds(ProfileNode* node, int* expectedId) { 459 CHECK_EQ((*expectedId)++, node->id()); 460 for (int i = 0; i < node->children()->length(); i++) { 461 CheckNodeIds(node->children()->at(i), expectedId); 462 } 463 } 464 465 466 TEST(SampleIds) { 467 TestSetup test_setup; 468 CpuProfilesCollection profiles(CcTest::heap()); 469 profiles.StartProfiling("", 1, true); 470 ProfileGenerator generator(&profiles); 471 CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); 472 CodeEntry* entry2 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb"); 473 CodeEntry* entry3 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "ccc"); 474 generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); 475 generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100); 476 generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50); 477 478 // We are building the following calls tree: 479 // -> aaa #3 - sample1 480 // (root)#1 -> aaa #2 -> bbb #4 -> ccc #5 - sample2 481 // -> ccc #6 -> aaa #7 - sample3 482 TickSample sample1; 483 sample1.pc = ToAddress(0x1600); 484 sample1.stack[0] = ToAddress(0x1510); 485 sample1.frames_count = 1; 486 generator.RecordTickSample(sample1); 487 TickSample sample2; 488 sample2.pc = ToAddress(0x1925); 489 sample2.stack[0] = ToAddress(0x1780); 490 sample2.stack[1] = ToAddress(0x10000); // non-existent. 491 sample2.stack[2] = ToAddress(0x1620); 492 sample2.frames_count = 3; 493 generator.RecordTickSample(sample2); 494 TickSample sample3; 495 sample3.pc = ToAddress(0x1510); 496 sample3.stack[0] = ToAddress(0x1910); 497 sample3.stack[1] = ToAddress(0x1610); 498 sample3.frames_count = 2; 499 generator.RecordTickSample(sample3); 500 501 CpuProfile* profile = profiles.StopProfiling(""); 502 int nodeId = 1; 503 CheckNodeIds(profile->top_down()->root(), &nodeId); 504 CHECK_EQ(7, nodeId - 1); 505 506 CHECK_EQ(3, profile->samples_count()); 507 int expected_id[] = {3, 5, 7}; 508 for (int i = 0; i < 3; i++) { 509 CHECK_EQ(expected_id[i], profile->sample(i)->id()); 510 } 511 } 512 513 514 TEST(NoSamples) { 515 TestSetup test_setup; 516 CpuProfilesCollection profiles(CcTest::heap()); 517 profiles.StartProfiling("", 1, false); 518 ProfileGenerator generator(&profiles); 519 CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa"); 520 generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200); 521 522 // We are building the following calls tree: 523 // (root)#1 -> aaa #2 -> aaa #3 - sample1 524 TickSample sample1; 525 sample1.pc = ToAddress(0x1600); 526 sample1.stack[0] = ToAddress(0x1510); 527 sample1.frames_count = 1; 528 generator.RecordTickSample(sample1); 529 530 CpuProfile* profile = profiles.StopProfiling(""); 531 int nodeId = 1; 532 CheckNodeIds(profile->top_down()->root(), &nodeId); 533 CHECK_EQ(3, nodeId - 1); 534 535 CHECK_EQ(0, profile->samples_count()); 536 } 537 538 539 // --- P r o f i l e r E x t e n s i o n --- 540 541 class ProfilerExtension : public v8::Extension { 542 public: 543 ProfilerExtension() : v8::Extension("v8/profiler", kSource) { } 544 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( 545 v8::Isolate* isolate, 546 v8::Handle<v8::String> name); 547 static void StartProfiling(const v8::FunctionCallbackInfo<v8::Value>& args); 548 static void StopProfiling(const v8::FunctionCallbackInfo<v8::Value>& args); 549 private: 550 static const char* kSource; 551 }; 552 553 554 const char* ProfilerExtension::kSource = 555 "native function startProfiling();" 556 "native function stopProfiling();"; 557 558 v8::Handle<v8::FunctionTemplate> ProfilerExtension::GetNativeFunctionTemplate( 559 v8::Isolate* isolate, v8::Handle<v8::String> name) { 560 if (name->Equals(v8::String::NewFromUtf8(isolate, "startProfiling"))) { 561 return v8::FunctionTemplate::New(ProfilerExtension::StartProfiling); 562 } else if (name->Equals(v8::String::NewFromUtf8(isolate, "stopProfiling"))) { 563 return v8::FunctionTemplate::New(ProfilerExtension::StopProfiling); 564 } else { 565 CHECK(false); 566 return v8::Handle<v8::FunctionTemplate>(); 567 } 568 } 569 570 571 void ProfilerExtension::StartProfiling( 572 const v8::FunctionCallbackInfo<v8::Value>& args) { 573 v8::CpuProfiler* cpu_profiler = args.GetIsolate()->GetCpuProfiler(); 574 if (args.Length() > 0) 575 cpu_profiler->StartCpuProfiling(args[0].As<v8::String>()); 576 else 577 cpu_profiler->StartCpuProfiling( 578 v8::String::NewFromUtf8(args.GetIsolate(), "")); 579 } 580 581 582 void ProfilerExtension::StopProfiling( 583 const v8::FunctionCallbackInfo<v8::Value>& args) { 584 v8::CpuProfiler* cpu_profiler = args.GetIsolate()->GetCpuProfiler(); 585 if (args.Length() > 0) 586 cpu_profiler->StopCpuProfiling(args[0].As<v8::String>()); 587 else 588 cpu_profiler->StopCpuProfiling( 589 v8::String::NewFromUtf8(args.GetIsolate(), "")); 590 } 591 592 593 static ProfilerExtension kProfilerExtension; 594 v8::DeclareExtension kProfilerExtensionDeclaration(&kProfilerExtension); 595 596 static const ProfileNode* PickChild(const ProfileNode* parent, 597 const char* name) { 598 for (int i = 0; i < parent->children()->length(); ++i) { 599 const ProfileNode* child = parent->children()->at(i); 600 if (strcmp(child->entry()->name(), name) == 0) return child; 601 } 602 return NULL; 603 } 604 605 606 TEST(RecordStackTraceAtStartProfiling) { 607 // This test does not pass with inlining enabled since inlined functions 608 // don't appear in the stack trace. 609 i::FLAG_use_inlining = false; 610 611 v8::Isolate* isolate = CcTest::isolate(); 612 v8::HandleScope scope(isolate); 613 const char* extensions[] = { "v8/profiler" }; 614 v8::ExtensionConfiguration config(1, extensions); 615 v8::Local<v8::Context> context = v8::Context::New(isolate, &config); 616 context->Enter(); 617 618 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler(); 619 CHECK_EQ(0, profiler->GetProfilesCount()); 620 CompileRun( 621 "function c() { startProfiling(); }\n" 622 "function b() { c(); }\n" 623 "function a() { b(); }\n" 624 "a();\n" 625 "stopProfiling();"); 626 CHECK_EQ(1, profiler->GetProfilesCount()); 627 CpuProfile* profile = profiler->GetProfile(0); 628 const ProfileTree* topDown = profile->top_down(); 629 const ProfileNode* current = topDown->root(); 630 const_cast<ProfileNode*>(current)->Print(0); 631 // The tree should look like this: 632 // (root) 633 // (anonymous function) 634 // a 635 // b 636 // c 637 // There can also be: 638 // startProfiling 639 // if the sampler managed to get a tick. 640 current = PickChild(current, "(anonymous function)"); 641 CHECK_NE(NULL, const_cast<ProfileNode*>(current)); 642 current = PickChild(current, "a"); 643 CHECK_NE(NULL, const_cast<ProfileNode*>(current)); 644 current = PickChild(current, "b"); 645 CHECK_NE(NULL, const_cast<ProfileNode*>(current)); 646 current = PickChild(current, "c"); 647 CHECK_NE(NULL, const_cast<ProfileNode*>(current)); 648 CHECK(current->children()->length() == 0 || 649 current->children()->length() == 1); 650 if (current->children()->length() == 1) { 651 current = PickChild(current, "startProfiling"); 652 CHECK_EQ(0, current->children()->length()); 653 } 654 } 655 656 657 TEST(Issue51919) { 658 CpuProfilesCollection collection(CcTest::heap()); 659 i::EmbeddedVector<char*, 660 CpuProfilesCollection::kMaxSimultaneousProfiles> titles; 661 for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) { 662 i::Vector<char> title = i::Vector<char>::New(16); 663 i::OS::SNPrintF(title, "%d", i); 664 // UID must be > 0. 665 CHECK(collection.StartProfiling(title.start(), i + 1, false)); 666 titles[i] = title.start(); 667 } 668 CHECK(!collection.StartProfiling( 669 "maximum", CpuProfilesCollection::kMaxSimultaneousProfiles + 1, false)); 670 for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) 671 i::DeleteArray(titles[i]); 672 } 673 674 675 static const v8::CpuProfileNode* PickChild(const v8::CpuProfileNode* parent, 676 const char* name) { 677 for (int i = 0; i < parent->GetChildrenCount(); ++i) { 678 const v8::CpuProfileNode* child = parent->GetChild(i); 679 v8::String::Utf8Value function_name(child->GetFunctionName()); 680 if (strcmp(*function_name, name) == 0) return child; 681 } 682 return NULL; 683 } 684 685 686 TEST(ProfileNodeScriptId) { 687 // This test does not pass with inlining enabled since inlined functions 688 // don't appear in the stack trace. 689 i::FLAG_use_inlining = false; 690 691 const char* extensions[] = { "v8/profiler" }; 692 v8::ExtensionConfiguration config(1, extensions); 693 LocalContext env(&config); 694 v8::HandleScope hs(env->GetIsolate()); 695 696 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler(); 697 CHECK_EQ(0, profiler->GetProfileCount()); 698 v8::Handle<v8::Script> script_a = v8::Script::Compile(v8::String::NewFromUtf8( 699 env->GetIsolate(), "function a() { startProfiling(); }\n")); 700 script_a->Run(); 701 v8::Handle<v8::Script> script_b = 702 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(), 703 "function b() { a(); }\n" 704 "b();\n" 705 "stopProfiling();\n")); 706 script_b->Run(); 707 CHECK_EQ(1, profiler->GetProfileCount()); 708 const v8::CpuProfile* profile = profiler->GetCpuProfile(0); 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 // (anonymous function) 715 // b 716 // a 717 // There can also be: 718 // startProfiling 719 // if the sampler managed to get a tick. 720 current = PickChild(current, i::ProfileGenerator::kAnonymousFunctionName); 721 CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current)); 722 723 current = PickChild(current, "b"); 724 CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current)); 725 CHECK_EQ(script_b->GetId(), current->GetScriptId()); 726 727 current = PickChild(current, "a"); 728 CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current)); 729 CHECK_EQ(script_a->GetId(), current->GetScriptId()); 730 } 731 732 733 734 735 static const char* line_number_test_source_existing_functions = 736 "function foo_at_the_first_line() {\n" 737 "}\n" 738 "foo_at_the_first_line();\n" 739 "function lazy_func_at_forth_line() {}\n"; 740 741 742 static const char* line_number_test_source_profile_time_functions = 743 "// Empty first line\n" 744 "function bar_at_the_second_line() {\n" 745 " foo_at_the_first_line();\n" 746 "}\n" 747 "bar_at_the_second_line();\n" 748 "function lazy_func_at_6th_line() {}"; 749 750 int GetFunctionLineNumber(LocalContext* env, const char* name) { 751 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler(); 752 CodeMap* code_map = profiler->generator()->code_map(); 753 i::Handle<i::JSFunction> func = v8::Utils::OpenHandle( 754 *v8::Local<v8::Function>::Cast( 755 (*(*env))->Global()->Get(v8_str(name)))); 756 CodeEntry* func_entry = code_map->FindEntry(func->code()->address()); 757 if (!func_entry) 758 FATAL(name); 759 return func_entry->line_number(); 760 } 761 762 763 TEST(LineNumber) { 764 i::FLAG_use_inlining = false; 765 766 CcTest::InitializeVM(); 767 LocalContext env; 768 i::Isolate* isolate = CcTest::i_isolate(); 769 TestSetup test_setup; 770 771 i::HandleScope scope(isolate); 772 773 CompileRun(line_number_test_source_existing_functions); 774 775 CpuProfiler* profiler = isolate->cpu_profiler(); 776 profiler->StartProfiling("LineNumber"); 777 778 CompileRun(line_number_test_source_profile_time_functions); 779 780 profiler->processor()->StopSynchronously(); 781 782 CHECK_EQ(1, GetFunctionLineNumber(&env, "foo_at_the_first_line")); 783 CHECK_EQ(0, GetFunctionLineNumber(&env, "lazy_func_at_forth_line")); 784 CHECK_EQ(2, GetFunctionLineNumber(&env, "bar_at_the_second_line")); 785 CHECK_EQ(0, GetFunctionLineNumber(&env, "lazy_func_at_6th_line")); 786 787 profiler->StopProfiling("LineNumber"); 788 } 789 790 791 792 TEST(BailoutReason) { 793 const char* extensions[] = { "v8/profiler" }; 794 v8::ExtensionConfiguration config(1, extensions); 795 LocalContext env(&config); 796 v8::HandleScope hs(env->GetIsolate()); 797 798 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler(); 799 CHECK_EQ(0, profiler->GetProfileCount()); 800 v8::Handle<v8::Script> script = 801 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(), 802 "function TryCatch() {\n" 803 " try {\n" 804 " startProfiling();\n" 805 " } catch (e) { };\n" 806 "}\n" 807 "function TryFinally() {\n" 808 " try {\n" 809 " TryCatch();\n" 810 " } finally { };\n" 811 "}\n" 812 "TryFinally();\n" 813 "stopProfiling();")); 814 script->Run(); 815 CHECK_EQ(1, profiler->GetProfileCount()); 816 const v8::CpuProfile* profile = profiler->GetCpuProfile(0); 817 const v8::CpuProfileNode* current = profile->GetTopDownRoot(); 818 reinterpret_cast<ProfileNode*>( 819 const_cast<v8::CpuProfileNode*>(current))->Print(0); 820 // The tree should look like this: 821 // (root) 822 // (anonymous function) 823 // kTryFinally 824 // kTryCatch 825 current = PickChild(current, i::ProfileGenerator::kAnonymousFunctionName); 826 CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current)); 827 828 current = PickChild(current, "TryFinally"); 829 CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current)); 830 CHECK(!strcmp("TryFinallyStatement", current->GetBailoutReason())); 831 832 current = PickChild(current, "TryCatch"); 833 CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current)); 834 CHECK(!strcmp("TryCatchStatement", current->GetBailoutReason())); 835 } 836