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
TEST(ProfileNodeFindOrAddChild)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
TEST(ProfileNodeFindOrAddChildForSameFunction)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:
ProfileTreeTestHelper(const ProfileTree * tree)93 explicit ProfileTreeTestHelper(const ProfileTree* tree)
94 : tree_(tree) { }
95
Walk(CodeEntry * entry1,CodeEntry * entry2=NULL,CodeEntry * entry3=NULL)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
TEST(ProfileTreeAddPathFromStart)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
TEST(ProfileTreeAddPathFromEnd)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
TEST(ProfileTreeCalculateTotalTicks)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
ToAddress(int n)328 static inline i::Address ToAddress(int n) {
329 return reinterpret_cast<i::Address>(n);
330 }
331
332
TEST(CodeMapAddCode)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
TEST(CodeMapMoveAndDeleteCode)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:
TestSetup()385 TestSetup()
386 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
387 i::FLAG_prof_browser_mode = false;
388 }
389
~TestSetup()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
TEST(RecordTickSample)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
CheckNodeIds(ProfileNode * node,int * expectedId)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
TEST(SampleIds)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
TEST(NoSamples)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:
ProfilerExtension()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
GetNativeFunctionTemplate(v8::Isolate * isolate,v8::Handle<v8::String> name)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
StartProfiling(const v8::FunctionCallbackInfo<v8::Value> & args)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
StopProfiling(const v8::FunctionCallbackInfo<v8::Value> & args)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
PickChild(const ProfileNode * parent,const char * name)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
TEST(RecordStackTraceAtStartProfiling)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
TEST(Issue51919)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
PickChild(const v8::CpuProfileNode * parent,const char * name)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
TEST(ProfileNodeScriptId)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
GetFunctionLineNumber(LocalContext * env,const char * name)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
TEST(LineNumber)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
TEST(BailoutReason)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