v8  3.25.30(node0.11.13)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
test-profile-generator.cc
Go to the documentation of this file.
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 "profiler-extension.h"
33 #include "cctest.h"
34 #include "cpu-profiler.h"
35 #include "../include/v8-profiler.h"
36 
37 using i::CodeEntry;
38 using i::CodeMap;
39 using i::CpuProfile;
40 using i::CpuProfiler;
42 using i::ProfileNode;
43 using i::ProfileTree;
45 using i::TickSample;
46 using i::Vector;
47 
48 
49 TEST(ProfileNodeFindOrAddChild) {
50  ProfileTree tree;
51  ProfileNode* node = tree.root();
52  CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
53  ProfileNode* childNode1 = node->FindOrAddChild(&entry1);
54  CHECK_NE(NULL, childNode1);
55  CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
56  CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
57  ProfileNode* childNode2 = node->FindOrAddChild(&entry2);
58  CHECK_NE(NULL, childNode2);
59  CHECK_NE(childNode1, childNode2);
60  CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
61  CHECK_EQ(childNode2, node->FindOrAddChild(&entry2));
62  CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
63  ProfileNode* childNode3 = node->FindOrAddChild(&entry3);
64  CHECK_NE(NULL, childNode3);
65  CHECK_NE(childNode1, childNode3);
66  CHECK_NE(childNode2, childNode3);
67  CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
68  CHECK_EQ(childNode2, node->FindOrAddChild(&entry2));
69  CHECK_EQ(childNode3, node->FindOrAddChild(&entry3));
70 }
71 
72 
73 TEST(ProfileNodeFindOrAddChildForSameFunction) {
74  const char* aaa = "aaa";
75  ProfileTree tree;
76  ProfileNode* node = tree.root();
77  CodeEntry entry1(i::Logger::FUNCTION_TAG, aaa);
78  ProfileNode* childNode1 = node->FindOrAddChild(&entry1);
79  CHECK_NE(NULL, childNode1);
80  CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
81  // The same function again.
82  CodeEntry entry2(i::Logger::FUNCTION_TAG, aaa);
83  CHECK_EQ(childNode1, node->FindOrAddChild(&entry2));
84  // Now with a different security token.
85  CodeEntry entry3(i::Logger::FUNCTION_TAG, aaa);
86  CHECK_EQ(childNode1, node->FindOrAddChild(&entry3));
87 }
88 
89 
90 namespace {
91 
92 class ProfileTreeTestHelper {
93  public:
94  explicit ProfileTreeTestHelper(const ProfileTree* tree)
95  : tree_(tree) { }
96 
97  ProfileNode* Walk(CodeEntry* entry1,
98  CodeEntry* entry2 = NULL,
99  CodeEntry* entry3 = NULL) {
100  ProfileNode* node = tree_->root();
101  node = node->FindChild(entry1);
102  if (node == NULL) return NULL;
103  if (entry2 != NULL) {
104  node = node->FindChild(entry2);
105  if (node == NULL) return NULL;
106  }
107  if (entry3 != NULL) {
108  node = node->FindChild(entry3);
109  }
110  return node;
111  }
112 
113  private:
114  const ProfileTree* tree_;
115 };
116 
117 } // namespace
118 
119 TEST(ProfileTreeAddPathFromStart) {
120  CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
121  CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
122  CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
123  ProfileTree tree;
124  ProfileTreeTestHelper helper(&tree);
125  CHECK_EQ(NULL, helper.Walk(&entry1));
126  CHECK_EQ(NULL, helper.Walk(&entry2));
127  CHECK_EQ(NULL, helper.Walk(&entry3));
128 
129  CodeEntry* path[] = {NULL, &entry1, NULL, &entry2, NULL, NULL, &entry3, NULL};
130  Vector<CodeEntry*> path_vec(path, sizeof(path) / sizeof(path[0]));
131  tree.AddPathFromStart(path_vec);
132  CHECK_EQ(NULL, helper.Walk(&entry2));
133  CHECK_EQ(NULL, helper.Walk(&entry3));
134  ProfileNode* node1 = helper.Walk(&entry1);
135  CHECK_NE(NULL, node1);
136  CHECK_EQ(0, node1->self_ticks());
137  CHECK_EQ(NULL, helper.Walk(&entry1, &entry1));
138  CHECK_EQ(NULL, helper.Walk(&entry1, &entry3));
139  ProfileNode* node2 = helper.Walk(&entry1, &entry2);
140  CHECK_NE(NULL, node2);
141  CHECK_NE(node1, node2);
142  CHECK_EQ(0, node2->self_ticks());
143  CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1));
144  CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry2));
145  ProfileNode* node3 = helper.Walk(&entry1, &entry2, &entry3);
146  CHECK_NE(NULL, node3);
147  CHECK_NE(node1, node3);
148  CHECK_NE(node2, node3);
149  CHECK_EQ(1, node3->self_ticks());
150 
151  tree.AddPathFromStart(path_vec);
152  CHECK_EQ(node1, helper.Walk(&entry1));
153  CHECK_EQ(node2, helper.Walk(&entry1, &entry2));
154  CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3));
155  CHECK_EQ(0, node1->self_ticks());
156  CHECK_EQ(0, node2->self_ticks());
157  CHECK_EQ(2, node3->self_ticks());
158 
159  CodeEntry* path2[] = {&entry1, &entry2, &entry2};
160  Vector<CodeEntry*> path2_vec(path2, sizeof(path2) / sizeof(path2[0]));
161  tree.AddPathFromStart(path2_vec);
162  CHECK_EQ(NULL, helper.Walk(&entry2));
163  CHECK_EQ(NULL, helper.Walk(&entry3));
164  CHECK_EQ(node1, helper.Walk(&entry1));
165  CHECK_EQ(NULL, helper.Walk(&entry1, &entry1));
166  CHECK_EQ(NULL, helper.Walk(&entry1, &entry3));
167  CHECK_EQ(node2, helper.Walk(&entry1, &entry2));
168  CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1));
169  CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3));
170  CHECK_EQ(2, node3->self_ticks());
171  ProfileNode* node4 = helper.Walk(&entry1, &entry2, &entry2);
172  CHECK_NE(NULL, node4);
173  CHECK_NE(node3, node4);
174  CHECK_EQ(1, node4->self_ticks());
175 }
176 
177 
178 TEST(ProfileTreeAddPathFromEnd) {
179  CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
180  CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
181  CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
182  ProfileTree tree;
183  ProfileTreeTestHelper helper(&tree);
184  CHECK_EQ(NULL, helper.Walk(&entry1));
185  CHECK_EQ(NULL, helper.Walk(&entry2));
186  CHECK_EQ(NULL, helper.Walk(&entry3));
187 
188  CodeEntry* path[] = {NULL, &entry3, NULL, &entry2, NULL, NULL, &entry1, NULL};
189  Vector<CodeEntry*> path_vec(path, sizeof(path) / sizeof(path[0]));
190  tree.AddPathFromEnd(path_vec);
191  CHECK_EQ(NULL, helper.Walk(&entry2));
192  CHECK_EQ(NULL, helper.Walk(&entry3));
193  ProfileNode* node1 = helper.Walk(&entry1);
194  CHECK_NE(NULL, node1);
195  CHECK_EQ(0, node1->self_ticks());
196  CHECK_EQ(NULL, helper.Walk(&entry1, &entry1));
197  CHECK_EQ(NULL, helper.Walk(&entry1, &entry3));
198  ProfileNode* node2 = helper.Walk(&entry1, &entry2);
199  CHECK_NE(NULL, node2);
200  CHECK_NE(node1, node2);
201  CHECK_EQ(0, node2->self_ticks());
202  CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1));
203  CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry2));
204  ProfileNode* node3 = helper.Walk(&entry1, &entry2, &entry3);
205  CHECK_NE(NULL, node3);
206  CHECK_NE(node1, node3);
207  CHECK_NE(node2, node3);
208  CHECK_EQ(1, node3->self_ticks());
209 
210  tree.AddPathFromEnd(path_vec);
211  CHECK_EQ(node1, helper.Walk(&entry1));
212  CHECK_EQ(node2, helper.Walk(&entry1, &entry2));
213  CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3));
214  CHECK_EQ(0, node1->self_ticks());
215  CHECK_EQ(0, node2->self_ticks());
216  CHECK_EQ(2, node3->self_ticks());
217 
218  CodeEntry* path2[] = {&entry2, &entry2, &entry1};
219  Vector<CodeEntry*> path2_vec(path2, sizeof(path2) / sizeof(path2[0]));
220  tree.AddPathFromEnd(path2_vec);
221  CHECK_EQ(NULL, helper.Walk(&entry2));
222  CHECK_EQ(NULL, helper.Walk(&entry3));
223  CHECK_EQ(node1, helper.Walk(&entry1));
224  CHECK_EQ(NULL, helper.Walk(&entry1, &entry1));
225  CHECK_EQ(NULL, helper.Walk(&entry1, &entry3));
226  CHECK_EQ(node2, helper.Walk(&entry1, &entry2));
227  CHECK_EQ(NULL, helper.Walk(&entry1, &entry2, &entry1));
228  CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3));
229  CHECK_EQ(2, node3->self_ticks());
230  ProfileNode* node4 = helper.Walk(&entry1, &entry2, &entry2);
231  CHECK_NE(NULL, node4);
232  CHECK_NE(node3, node4);
233  CHECK_EQ(1, node4->self_ticks());
234 }
235 
236 
237 TEST(ProfileTreeCalculateTotalTicks) {
238  ProfileTree empty_tree;
239  CHECK_EQ(0, empty_tree.root()->self_ticks());
240  empty_tree.root()->IncrementSelfTicks();
241  CHECK_EQ(1, empty_tree.root()->self_ticks());
242 
243  CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
244  CodeEntry* e1_path[] = {&entry1};
245  Vector<CodeEntry*> e1_path_vec(
246  e1_path, sizeof(e1_path) / sizeof(e1_path[0]));
247 
248  ProfileTree single_child_tree;
249  single_child_tree.AddPathFromStart(e1_path_vec);
250  single_child_tree.root()->IncrementSelfTicks();
251  CHECK_EQ(1, single_child_tree.root()->self_ticks());
252  ProfileTreeTestHelper single_child_helper(&single_child_tree);
253  ProfileNode* node1 = single_child_helper.Walk(&entry1);
254  CHECK_NE(NULL, node1);
255  CHECK_EQ(1, single_child_tree.root()->self_ticks());
256  CHECK_EQ(1, node1->self_ticks());
257 
258  CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
259  CodeEntry* e1_e2_path[] = {&entry1, &entry2};
260  Vector<CodeEntry*> e1_e2_path_vec(
261  e1_e2_path, sizeof(e1_e2_path) / sizeof(e1_e2_path[0]));
262 
263  ProfileTree flat_tree;
264  ProfileTreeTestHelper flat_helper(&flat_tree);
265  flat_tree.AddPathFromStart(e1_path_vec);
266  flat_tree.AddPathFromStart(e1_path_vec);
267  flat_tree.AddPathFromStart(e1_e2_path_vec);
268  flat_tree.AddPathFromStart(e1_e2_path_vec);
269  flat_tree.AddPathFromStart(e1_e2_path_vec);
270  // Results in {root,0,0} -> {entry1,0,2} -> {entry2,0,3}
271  CHECK_EQ(0, flat_tree.root()->self_ticks());
272  node1 = flat_helper.Walk(&entry1);
273  CHECK_NE(NULL, node1);
274  CHECK_EQ(2, node1->self_ticks());
275  ProfileNode* node2 = flat_helper.Walk(&entry1, &entry2);
276  CHECK_NE(NULL, node2);
277  CHECK_EQ(3, node2->self_ticks());
278  // Must calculate {root,5,0} -> {entry1,5,2} -> {entry2,3,3}
279  CHECK_EQ(0, flat_tree.root()->self_ticks());
280  CHECK_EQ(2, node1->self_ticks());
281 
282  CodeEntry* e2_path[] = {&entry2};
283  Vector<CodeEntry*> e2_path_vec(
284  e2_path, sizeof(e2_path) / sizeof(e2_path[0]));
285  CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
286  CodeEntry* e3_path[] = {&entry3};
287  Vector<CodeEntry*> e3_path_vec(
288  e3_path, sizeof(e3_path) / sizeof(e3_path[0]));
289 
290  ProfileTree wide_tree;
291  ProfileTreeTestHelper wide_helper(&wide_tree);
292  wide_tree.AddPathFromStart(e1_path_vec);
293  wide_tree.AddPathFromStart(e1_path_vec);
294  wide_tree.AddPathFromStart(e1_e2_path_vec);
295  wide_tree.AddPathFromStart(e2_path_vec);
296  wide_tree.AddPathFromStart(e2_path_vec);
297  wide_tree.AddPathFromStart(e2_path_vec);
298  wide_tree.AddPathFromStart(e3_path_vec);
299  wide_tree.AddPathFromStart(e3_path_vec);
300  wide_tree.AddPathFromStart(e3_path_vec);
301  wide_tree.AddPathFromStart(e3_path_vec);
302  // Results in -> {entry1,0,2} -> {entry2,0,1}
303  // {root,0,0} -> {entry2,0,3}
304  // -> {entry3,0,4}
305  CHECK_EQ(0, wide_tree.root()->self_ticks());
306  node1 = wide_helper.Walk(&entry1);
307  CHECK_NE(NULL, node1);
308  CHECK_EQ(2, node1->self_ticks());
309  ProfileNode* node1_2 = wide_helper.Walk(&entry1, &entry2);
310  CHECK_NE(NULL, node1_2);
311  CHECK_EQ(1, node1_2->self_ticks());
312  node2 = wide_helper.Walk(&entry2);
313  CHECK_NE(NULL, node2);
314  CHECK_EQ(3, node2->self_ticks());
315  ProfileNode* node3 = wide_helper.Walk(&entry3);
316  CHECK_NE(NULL, node3);
317  CHECK_EQ(4, node3->self_ticks());
318  // Calculates -> {entry1,3,2} -> {entry2,1,1}
319  // {root,10,0} -> {entry2,3,3}
320  // -> {entry3,4,4}
321  CHECK_EQ(0, wide_tree.root()->self_ticks());
322  CHECK_EQ(2, node1->self_ticks());
323  CHECK_EQ(1, node1_2->self_ticks());
324  CHECK_EQ(3, node2->self_ticks());
325  CHECK_EQ(4, node3->self_ticks());
326 }
327 
328 
329 static inline i::Address ToAddress(int n) {
330  return reinterpret_cast<i::Address>(n);
331 }
332 
333 
334 TEST(CodeMapAddCode) {
335  CodeMap code_map;
336  CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
337  CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
338  CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
339  CodeEntry entry4(i::Logger::FUNCTION_TAG, "ddd");
340  code_map.AddCode(ToAddress(0x1500), &entry1, 0x200);
341  code_map.AddCode(ToAddress(0x1700), &entry2, 0x100);
342  code_map.AddCode(ToAddress(0x1900), &entry3, 0x50);
343  code_map.AddCode(ToAddress(0x1950), &entry4, 0x10);
344  CHECK_EQ(NULL, code_map.FindEntry(0));
345  CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1500 - 1)));
346  CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500)));
347  CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500 + 0x100)));
348  CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500 + 0x200 - 1)));
349  CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700)));
350  CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700 + 0x50)));
351  CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700 + 0x100 - 1)));
352  CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1700 + 0x100)));
353  CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1900 - 1)));
354  CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1900)));
355  CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1900 + 0x28)));
356  CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950)));
357  CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950 + 0x7)));
358  CHECK_EQ(&entry4, code_map.FindEntry(ToAddress(0x1950 + 0x10 - 1)));
359  CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1950 + 0x10)));
360  CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0xFFFFFFFF)));
361 }
362 
363 
364 TEST(CodeMapMoveAndDeleteCode) {
365  CodeMap code_map;
366  CodeEntry entry1(i::Logger::FUNCTION_TAG, "aaa");
367  CodeEntry entry2(i::Logger::FUNCTION_TAG, "bbb");
368  code_map.AddCode(ToAddress(0x1500), &entry1, 0x200);
369  code_map.AddCode(ToAddress(0x1700), &entry2, 0x100);
370  CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500)));
371  CHECK_EQ(&entry2, code_map.FindEntry(ToAddress(0x1700)));
372  code_map.MoveCode(ToAddress(0x1500), ToAddress(0x1700)); // Deprecate bbb.
373  CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1500)));
374  CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1700)));
375  CodeEntry entry3(i::Logger::FUNCTION_TAG, "ccc");
376  code_map.AddCode(ToAddress(0x1750), &entry3, 0x100);
377  CHECK_EQ(NULL, code_map.FindEntry(ToAddress(0x1700)));
378  CHECK_EQ(&entry3, code_map.FindEntry(ToAddress(0x1750)));
379 }
380 
381 
382 namespace {
383 
384 class TestSetup {
385  public:
386  TestSetup()
387  : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
388  i::FLAG_prof_browser_mode = false;
389  }
390 
391  ~TestSetup() {
392  i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
393  }
394 
395  private:
396  bool old_flag_prof_browser_mode_;
397 };
398 
399 } // namespace
400 
401 TEST(RecordTickSample) {
402  TestSetup test_setup;
403  CpuProfilesCollection profiles(CcTest::heap());
404  profiles.StartProfiling("", false);
405  ProfileGenerator generator(&profiles);
406  CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa");
407  CodeEntry* entry2 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb");
408  CodeEntry* entry3 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "ccc");
409  generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
410  generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100);
411  generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50);
412 
413  // We are building the following calls tree:
414  // -> aaa - sample1
415  // aaa -> bbb -> ccc - sample2
416  // -> ccc -> aaa - sample3
417  TickSample sample1;
418  sample1.pc = ToAddress(0x1600);
419  sample1.tos = ToAddress(0x1500);
420  sample1.stack[0] = ToAddress(0x1510);
421  sample1.frames_count = 1;
422  generator.RecordTickSample(sample1);
423  TickSample sample2;
424  sample2.pc = ToAddress(0x1925);
425  sample2.tos = ToAddress(0x1900);
426  sample2.stack[0] = ToAddress(0x1780);
427  sample2.stack[1] = ToAddress(0x10000); // non-existent.
428  sample2.stack[2] = ToAddress(0x1620);
429  sample2.frames_count = 3;
430  generator.RecordTickSample(sample2);
431  TickSample sample3;
432  sample3.pc = ToAddress(0x1510);
433  sample3.tos = ToAddress(0x1500);
434  sample3.stack[0] = ToAddress(0x1910);
435  sample3.stack[1] = ToAddress(0x1610);
436  sample3.frames_count = 2;
437  generator.RecordTickSample(sample3);
438 
439  CpuProfile* profile = profiles.StopProfiling("");
440  CHECK_NE(NULL, profile);
441  ProfileTreeTestHelper top_down_test_helper(profile->top_down());
442  CHECK_EQ(NULL, top_down_test_helper.Walk(entry2));
443  CHECK_EQ(NULL, top_down_test_helper.Walk(entry3));
444  ProfileNode* node1 = top_down_test_helper.Walk(entry1);
445  CHECK_NE(NULL, node1);
446  CHECK_EQ(entry1, node1->entry());
447  ProfileNode* node2 = top_down_test_helper.Walk(entry1, entry1);
448  CHECK_NE(NULL, node2);
449  CHECK_EQ(entry1, node2->entry());
450  ProfileNode* node3 = top_down_test_helper.Walk(entry1, entry2, entry3);
451  CHECK_NE(NULL, node3);
452  CHECK_EQ(entry3, node3->entry());
453  ProfileNode* node4 = top_down_test_helper.Walk(entry1, entry3, entry1);
454  CHECK_NE(NULL, node4);
455  CHECK_EQ(entry1, node4->entry());
456 }
457 
458 
459 static void CheckNodeIds(ProfileNode* node, int* expectedId) {
460  CHECK_EQ((*expectedId)++, node->id());
461  for (int i = 0; i < node->children()->length(); i++) {
462  CheckNodeIds(node->children()->at(i), expectedId);
463  }
464 }
465 
466 
467 TEST(SampleIds) {
468  TestSetup test_setup;
469  CpuProfilesCollection profiles(CcTest::heap());
470  profiles.StartProfiling("", true);
471  ProfileGenerator generator(&profiles);
472  CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa");
473  CodeEntry* entry2 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "bbb");
474  CodeEntry* entry3 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "ccc");
475  generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
476  generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100);
477  generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50);
478 
479  // We are building the following calls tree:
480  // -> aaa #3 - sample1
481  // (root)#1 -> aaa #2 -> bbb #4 -> ccc #5 - sample2
482  // -> ccc #6 -> aaa #7 - sample3
483  TickSample sample1;
484  sample1.pc = ToAddress(0x1600);
485  sample1.stack[0] = ToAddress(0x1510);
486  sample1.frames_count = 1;
487  generator.RecordTickSample(sample1);
488  TickSample sample2;
489  sample2.pc = ToAddress(0x1925);
490  sample2.stack[0] = ToAddress(0x1780);
491  sample2.stack[1] = ToAddress(0x10000); // non-existent.
492  sample2.stack[2] = ToAddress(0x1620);
493  sample2.frames_count = 3;
494  generator.RecordTickSample(sample2);
495  TickSample sample3;
496  sample3.pc = ToAddress(0x1510);
497  sample3.stack[0] = ToAddress(0x1910);
498  sample3.stack[1] = ToAddress(0x1610);
499  sample3.frames_count = 2;
500  generator.RecordTickSample(sample3);
501 
502  CpuProfile* profile = profiles.StopProfiling("");
503  int nodeId = 1;
504  CheckNodeIds(profile->top_down()->root(), &nodeId);
505  CHECK_EQ(7, nodeId - 1);
506 
507  CHECK_EQ(3, profile->samples_count());
508  int expected_id[] = {3, 5, 7};
509  for (int i = 0; i < 3; i++) {
510  CHECK_EQ(expected_id[i], profile->sample(i)->id());
511  }
512 }
513 
514 
515 TEST(NoSamples) {
516  TestSetup test_setup;
517  CpuProfilesCollection profiles(CcTest::heap());
518  profiles.StartProfiling("", false);
519  ProfileGenerator generator(&profiles);
520  CodeEntry* entry1 = profiles.NewCodeEntry(i::Logger::FUNCTION_TAG, "aaa");
521  generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
522 
523  // We are building the following calls tree:
524  // (root)#1 -> aaa #2 -> aaa #3 - sample1
525  TickSample sample1;
526  sample1.pc = ToAddress(0x1600);
527  sample1.stack[0] = ToAddress(0x1510);
528  sample1.frames_count = 1;
529  generator.RecordTickSample(sample1);
530 
531  CpuProfile* profile = profiles.StopProfiling("");
532  int nodeId = 1;
533  CheckNodeIds(profile->top_down()->root(), &nodeId);
534  CHECK_EQ(3, nodeId - 1);
535 
536  CHECK_EQ(0, profile->samples_count());
537 }
538 
539 
540 static const ProfileNode* PickChild(const ProfileNode* parent,
541  const char* name) {
542  for (int i = 0; i < parent->children()->length(); ++i) {
543  const ProfileNode* child = parent->children()->at(i);
544  if (strcmp(child->entry()->name(), name) == 0) return child;
545  }
546  return NULL;
547 }
548 
549 
550 TEST(RecordStackTraceAtStartProfiling) {
551  // This test does not pass with inlining enabled since inlined functions
552  // don't appear in the stack trace.
553  i::FLAG_use_inlining = false;
554 
556  v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
557  v8::Context::Scope context_scope(env);
558 
559  CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
560  CHECK_EQ(0, profiler->GetProfilesCount());
561  CompileRun(
562  "function c() { startProfiling(); }\n"
563  "function b() { c(); }\n"
564  "function a() { b(); }\n"
565  "a();\n"
566  "stopProfiling();");
567  CHECK_EQ(1, profiler->GetProfilesCount());
568  CpuProfile* profile = profiler->GetProfile(0);
569  const ProfileTree* topDown = profile->top_down();
570  const ProfileNode* current = topDown->root();
571  const_cast<ProfileNode*>(current)->Print(0);
572  // The tree should look like this:
573  // (root)
574  // (anonymous function)
575  // a
576  // b
577  // c
578  // There can also be:
579  // startProfiling
580  // if the sampler managed to get a tick.
581  current = PickChild(current, "(anonymous function)");
582  CHECK_NE(NULL, const_cast<ProfileNode*>(current));
583  current = PickChild(current, "a");
584  CHECK_NE(NULL, const_cast<ProfileNode*>(current));
585  current = PickChild(current, "b");
586  CHECK_NE(NULL, const_cast<ProfileNode*>(current));
587  current = PickChild(current, "c");
588  CHECK_NE(NULL, const_cast<ProfileNode*>(current));
589  CHECK(current->children()->length() == 0 ||
590  current->children()->length() == 1);
591  if (current->children()->length() == 1) {
592  current = PickChild(current, "startProfiling");
593  CHECK_EQ(0, current->children()->length());
594  }
595 }
596 
597 
598 TEST(Issue51919) {
599  CpuProfilesCollection collection(CcTest::heap());
600  i::EmbeddedVector<char*,
601  CpuProfilesCollection::kMaxSimultaneousProfiles> titles;
602  for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) {
604  i::OS::SNPrintF(title, "%d", i);
605  CHECK(collection.StartProfiling(title.start(), false));
606  titles[i] = title.start();
607  }
608  CHECK(!collection.StartProfiling("maximum", false));
609  for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i)
610  i::DeleteArray(titles[i]);
611 }
612 
613 
614 static const v8::CpuProfileNode* PickChild(const v8::CpuProfileNode* parent,
615  const char* name) {
616  for (int i = 0; i < parent->GetChildrenCount(); ++i) {
617  const v8::CpuProfileNode* child = parent->GetChild(i);
618  v8::String::Utf8Value function_name(child->GetFunctionName());
619  if (strcmp(*function_name, name) == 0) return child;
620  }
621  return NULL;
622 }
623 
624 
625 TEST(ProfileNodeScriptId) {
626  // This test does not pass with inlining enabled since inlined functions
627  // don't appear in the stack trace.
628  i::FLAG_use_inlining = false;
629 
631  v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
632  v8::Context::Scope context_scope(env);
633 
635  i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
636  CHECK_EQ(0, iprofiler->GetProfilesCount());
638  env->GetIsolate(), "function a() { startProfiling(); }\n"));
639  script_a->Run();
640  v8::Handle<v8::Script> script_b =
642  "function b() { a(); }\n"
643  "b();\n"
644  "stopProfiling();\n"));
645  script_b->Run();
646  CHECK_EQ(1, iprofiler->GetProfilesCount());
648  const v8::CpuProfileNode* current = profile->GetTopDownRoot();
649  reinterpret_cast<ProfileNode*>(
650  const_cast<v8::CpuProfileNode*>(current))->Print(0);
651  // The tree should look like this:
652  // (root)
653  // (anonymous function)
654  // b
655  // a
656  // There can also be:
657  // startProfiling
658  // if the sampler managed to get a tick.
659  current = PickChild(current, i::ProfileGenerator::kAnonymousFunctionName);
660  CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current));
661 
662  current = PickChild(current, "b");
663  CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current));
664  CHECK_EQ(script_b->GetId(), current->GetScriptId());
665 
666  current = PickChild(current, "a");
667  CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current));
668  CHECK_EQ(script_a->GetId(), current->GetScriptId());
669 }
670 
671 
672 
673 
674 static const char* line_number_test_source_existing_functions =
675 "function foo_at_the_first_line() {\n"
676 "}\n"
677 "foo_at_the_first_line();\n"
678 "function lazy_func_at_forth_line() {}\n";
679 
680 
681 static const char* line_number_test_source_profile_time_functions =
682 "// Empty first line\n"
683 "function bar_at_the_second_line() {\n"
684 " foo_at_the_first_line();\n"
685 "}\n"
686 "bar_at_the_second_line();\n"
687 "function lazy_func_at_6th_line() {}";
688 
689 int GetFunctionLineNumber(LocalContext* env, const char* name) {
690  CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
691  CodeMap* code_map = profiler->generator()->code_map();
694  (*(*env))->Global()->Get(v8_str(name))));
695  CodeEntry* func_entry = code_map->FindEntry(func->code()->address());
696  if (!func_entry)
697  FATAL(name);
698  return func_entry->line_number();
699 }
700 
701 
702 TEST(LineNumber) {
703  i::FLAG_use_inlining = false;
704 
706  LocalContext env;
707  i::Isolate* isolate = CcTest::i_isolate();
708  TestSetup test_setup;
709 
710  i::HandleScope scope(isolate);
711 
712  CompileRun(line_number_test_source_existing_functions);
713 
714  CpuProfiler* profiler = isolate->cpu_profiler();
715  profiler->StartProfiling("LineNumber");
716 
717  CompileRun(line_number_test_source_profile_time_functions);
718 
719  profiler->processor()->StopSynchronously();
720 
721  CHECK_EQ(1, GetFunctionLineNumber(&env, "foo_at_the_first_line"));
722  CHECK_EQ(0, GetFunctionLineNumber(&env, "lazy_func_at_forth_line"));
723  CHECK_EQ(2, GetFunctionLineNumber(&env, "bar_at_the_second_line"));
724  CHECK_EQ(0, GetFunctionLineNumber(&env, "lazy_func_at_6th_line"));
725 
726  profiler->StopProfiling("LineNumber");
727 }
728 
729 
730 
733  v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
734  v8::Context::Scope context_scope(env);
735 
737  i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
738  CHECK_EQ(0, iprofiler->GetProfilesCount());
739  v8::Handle<v8::Script> script =
741  "function TryCatch() {\n"
742  " try {\n"
743  " startProfiling();\n"
744  " } catch (e) { };\n"
745  "}\n"
746  "function TryFinally() {\n"
747  " try {\n"
748  " TryCatch();\n"
749  " } finally { };\n"
750  "}\n"
751  "TryFinally();\n"
752  "stopProfiling();"));
753  script->Run();
754  CHECK_EQ(1, iprofiler->GetProfilesCount());
756  CHECK(profile);
757  const v8::CpuProfileNode* current = profile->GetTopDownRoot();
758  reinterpret_cast<ProfileNode*>(
759  const_cast<v8::CpuProfileNode*>(current))->Print(0);
760  // The tree should look like this:
761  // (root)
762  // (anonymous function)
763  // kTryFinally
764  // kTryCatch
765  current = PickChild(current, i::ProfileGenerator::kAnonymousFunctionName);
766  CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current));
767 
768  current = PickChild(current, "TryFinally");
769  CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current));
770  CHECK(!strcmp("TryFinallyStatement", current->GetBailoutReason()));
771 
772  current = PickChild(current, "TryCatch");
773  CHECK_NE(NULL, const_cast<v8::CpuProfileNode*>(current));
774  CHECK(!strcmp("TryCatchStatement", current->GetBailoutReason()));
775 }
void StartProfiling(const char *title, bool record_samples=false)
byte * Address
Definition: globals.h:186
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter NULL
#define CHECK_EQ(expected, value)
Definition: checks.h:252
CpuProfiler * GetCpuProfiler()
Definition: api.cc:6346
#define FATAL(msg)
Definition: checks.h:48
TEST(ProfileNodeFindOrAddChild)
const char * GetBailoutReason() const
Definition: api.cc:7055
const CpuProfileNode * GetChild(int index) const
Definition: api.cc:7081
static const v8::CpuProfile * last_profile
static i::Heap * heap()
Definition: cctest.h:106
ProfileGenerator * generator() const
Definition: cpu-profiler.h:262
#define CHECK(condition)
Definition: checks.h:75
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes number of stack frames inspected by the profiler percentage of ICs that must have type info to allow optimization extra verbose compilation tracing generate extra emit comments in code disassembly enable use of SSE3 instructions if available enable use of CMOV instruction if available enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long expose natives in global object expose freeBuffer extension expose gc extension under the specified name expose externalize string extension number of stack frames to capture disable builtin natives files print name of functions for which code is generated use random jit cookie to mask large constants trace lazy optimization use adaptive optimizations always try to OSR functions trace optimize function deoptimization minimum length for automatic enable preparsing maximum number of optimization attempts before giving up cache prototype transitions trace debugging JSON request response trace out of bounds accesses to external arrays trace_js_array_abuse automatically set the debug break flag when debugger commands are in the queue abort by crashing maximum length of function source code printed in a stack trace max size of the new max size of the old max size of executable always perform global GCs print one trace line following each garbage collection do not print trace line after scavenger collection print statistics of the maximum memory committed for the heap in name
Handle< String > GetFunctionName() const
Definition: api.cc:7011
static v8::Local< v8::Context > NewContext(CcTestExtensionFlags extensions, v8::Isolate *isolate=CcTest::isolate())
Definition: cctest.cc:90
int GetId()
Definition: v8.h:1067
v8::Isolate * GetIsolate()
Definition: api.cc:5233
T * start() const
Definition: utils.h:426
static Local< Script > Compile(Handle< String > source, ScriptOrigin *origin=NULL, ScriptData *script_data=NULL)
Definition: api.cc:1832
int GetFunctionLineNumber(LocalContext *env, const char *name)
static i::Isolate * i_isolate()
Definition: cctest.h:102
static Vector< T > New(int length)
Definition: utils.h:406
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes number of stack frames inspected by the profiler percentage of ICs that must have type info to allow optimization extra verbose compilation tracing generate extra emit comments in code disassembly enable use of SSE3 instructions if available enable use of CMOV instruction if available enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long expose natives in global object expose freeBuffer extension expose gc extension under the specified name expose externalize string extension number of stack frames to capture disable builtin natives files print name of functions for which code is generated use random jit cookie to mask large constants trace lazy optimization use adaptive optimizations always try to OSR functions trace optimize function deoptimization minimum length for automatic enable preparsing maximum number of optimization attempts before giving up cache prototype transitions trace debugging JSON request response trace out of bounds accesses to external arrays trace_js_array_abuse automatically set the debug break flag when debugger commands are in the queue abort by crashing maximum length of function source code printed in a stack trace max size of the new max size of the old max size of executable always perform global GCs print one trace line following each garbage collection do not print trace line after scavenger collection print statistics of the maximum memory committed for the heap in only print modified registers Don t break for ASM_UNIMPLEMENTED_BREAK macros print stack trace when an illegal exception is thrown randomize hashes to avoid predictable hash Fixed seed to use to hash property Print the time it takes to deserialize the snapshot testing_bool_flag testing_int_flag string flag tmp file in which to serialize heap Print the time it takes to lazily compile hydrogen code stubs concurrent_recompilation concurrent_sweeping Print usage including on console Map counters to a file Enable debugger compile events enable GDBJIT enable GDBJIT interface for all code objects dump only objects containing this substring stress the GC compactor to flush out pretty print source code print source AST function name where to insert a breakpoint print scopes for builtins trace contexts operations print stuff during garbage collection report code statistics after GC report handles after GC trace cache state transitions print interface inference details prints when objects are turned into dictionaries report heap spill statistics along with trace isolate state changes trace regexp bytecode execution Minimal Log all events to the log file Log API events to the log file Log heap samples on garbage collection for the hp2ps tool log positions Log suspect operations Used with turns on browser compatible mode for profiling v8 Specify the name of the log file Enable low level linux profiler Enable perf linux profiler(experimental annotate support).") DEFINE_string(gc_fake_mmap
static v8::internal::Handle< To > OpenHandle(v8::Local< From > handle)
Definition: api.h:308
#define CHECK_NE(unexpected, value)
Definition: checks.h:256
int GetScriptId() const
Definition: api.cc:7029
static int SNPrintF(Vector< char > str, const char *format,...)
static void InitializeVM()
Definition: cctest.h:116
static const char *const kAnonymousFunctionName
CpuProfiler * cpu_profiler() const
Definition: isolate.h:984
int GetChildrenCount() const
Definition: api.cc:7076
void Print(const v8::FunctionCallbackInfo< v8::Value > &args)
Local< Value > Run()
Definition: api.cc:1686
void DeleteArray(T *array)
Definition: allocation.h:91
Definition: v8.h:124
static v8::Isolate * isolate()
Definition: cctest.h:96
static Local< String > NewFromUtf8(Isolate *isolate, const char *data, NewStringType type=kNormalString, int length=-1)
Definition: api.cc:5417