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
TEST(ProfileNodeFindOrAddChild)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
TEST(ProfileNodeFindOrAddChildForSameFunction)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:
ProfileTreeTestHelper(const ProfileTree * tree)95 explicit ProfileTreeTestHelper(const ProfileTree* tree)
96 : tree_(tree) { }
97
Walk(CodeEntry * entry1,CodeEntry * entry2=NULL,CodeEntry * entry3=NULL)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
TEST(ProfileTreeAddPathFromStart)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
TEST(ProfileTreeAddPathFromEnd)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
TEST(ProfileTreeCalculateTotalTicks)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
ToAddress(int n)330 static inline i::Address ToAddress(int n) {
331 return reinterpret_cast<i::Address>(n);
332 }
333
334
TEST(CodeMapAddCode)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
TEST(CodeMapMoveAndDeleteCode)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:
TestSetup()387 TestSetup()
388 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
389 i::FLAG_prof_browser_mode = false;
390 }
391
~TestSetup()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
TEST(RecordTickSample)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
CheckNodeIds(ProfileNode * node,int * expectedId)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
TEST(SampleIds)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
TEST(NoSamples)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
PickChild(const ProfileNode * parent,const char * name)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
TEST(RecordStackTraceAtStartProfiling)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 // ""
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, "");
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
TEST(Issue51919)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
PickChild(const v8::CpuProfileNode * parent,const char * name)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
TEST(ProfileNodeScriptId)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 // ""
655 // b
656 // a
657 // There can also be:
658 // startProfiling
659 // if the sampler managed to get a tick.
660 current = PickChild(current, "");
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
GetFunctionLineNumber(LocalContext * env,const char * name)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
TEST(LineNumber)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
TEST(BailoutReason)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 // ""
764 // kTryFinally
765 // kTryCatch
766 current = PickChild(current, "");
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