• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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