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