Line data Source code
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 "include/v8-profiler.h"
31 : #include "src/api-inl.h"
32 : #include "src/log.h"
33 : #include "src/objects-inl.h"
34 : #include "src/profiler/cpu-profiler.h"
35 : #include "src/profiler/profile-generator-inl.h"
36 : #include "src/v8.h"
37 : #include "test/cctest/cctest.h"
38 : #include "test/cctest/profiler-extension.h"
39 :
40 : namespace v8 {
41 : namespace internal {
42 : namespace test_profile_generator {
43 :
44 26644 : TEST(ProfileNodeFindOrAddChild) {
45 5 : CcTest::InitializeVM();
46 10 : ProfileTree tree(CcTest::i_isolate());
47 : ProfileNode* node = tree.root();
48 10 : CodeEntry entry1(i::CodeEventListener::FUNCTION_TAG, "aaa");
49 5 : ProfileNode* childNode1 = node->FindOrAddChild(&entry1);
50 5 : CHECK(childNode1);
51 5 : CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
52 5 : CodeEntry entry2(i::CodeEventListener::FUNCTION_TAG, "bbb");
53 5 : ProfileNode* childNode2 = node->FindOrAddChild(&entry2);
54 5 : CHECK(childNode2);
55 5 : CHECK_NE(childNode1, childNode2);
56 5 : CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
57 5 : CHECK_EQ(childNode2, node->FindOrAddChild(&entry2));
58 5 : CodeEntry entry3(i::CodeEventListener::FUNCTION_TAG, "ccc");
59 5 : ProfileNode* childNode3 = node->FindOrAddChild(&entry3);
60 5 : CHECK(childNode3);
61 5 : CHECK_NE(childNode1, childNode3);
62 5 : CHECK_NE(childNode2, childNode3);
63 5 : CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
64 5 : CHECK_EQ(childNode2, node->FindOrAddChild(&entry2));
65 5 : CHECK_EQ(childNode3, node->FindOrAddChild(&entry3));
66 5 : }
67 :
68 26644 : TEST(ProfileNodeFindOrAddChildWithLineNumber) {
69 5 : CcTest::InitializeVM();
70 10 : ProfileTree tree(CcTest::i_isolate());
71 : ProfileNode* root = tree.root();
72 10 : CodeEntry a(i::CodeEventListener::FUNCTION_TAG, "a");
73 5 : ProfileNode* a_node = root->FindOrAddChild(&a, -1);
74 :
75 : // a --(22)--> child1
76 : // --(23)--> child1
77 :
78 5 : CodeEntry child1(i::CodeEventListener::FUNCTION_TAG, "child1");
79 5 : ProfileNode* child1_node = a_node->FindOrAddChild(&child1, 22);
80 5 : CHECK(child1_node);
81 5 : CHECK_EQ(child1_node, a_node->FindOrAddChild(&child1, 22));
82 :
83 5 : ProfileNode* child2_node = a_node->FindOrAddChild(&child1, 23);
84 5 : CHECK(child2_node);
85 5 : CHECK_NE(child1_node, child2_node);
86 5 : }
87 :
88 26644 : TEST(ProfileNodeFindOrAddChildForSameFunction) {
89 5 : CcTest::InitializeVM();
90 : const char* aaa = "aaa";
91 10 : ProfileTree tree(CcTest::i_isolate());
92 : ProfileNode* node = tree.root();
93 10 : CodeEntry entry1(i::CodeEventListener::FUNCTION_TAG, aaa);
94 5 : ProfileNode* childNode1 = node->FindOrAddChild(&entry1);
95 5 : CHECK(childNode1);
96 5 : CHECK_EQ(childNode1, node->FindOrAddChild(&entry1));
97 : // The same function again.
98 5 : CodeEntry entry2(i::CodeEventListener::FUNCTION_TAG, aaa);
99 5 : CHECK_EQ(childNode1, node->FindOrAddChild(&entry2));
100 : // Now with a different security token.
101 5 : CodeEntry entry3(i::CodeEventListener::FUNCTION_TAG, aaa);
102 5 : CHECK_EQ(childNode1, node->FindOrAddChild(&entry3));
103 5 : }
104 :
105 :
106 : namespace {
107 :
108 : class ProfileTreeTestHelper {
109 : public:
110 : explicit ProfileTreeTestHelper(const ProfileTree* tree)
111 20 : : tree_(tree) { }
112 :
113 95 : ProfileNode* Walk(CodeEntry* entry1, CodeEntry* entry2 = nullptr,
114 : CodeEntry* entry3 = nullptr) {
115 170 : ProfileNode* node = tree_->root();
116 185 : node = node->FindChild(entry1);
117 185 : if (node == nullptr) return nullptr;
118 95 : if (entry2 != nullptr) {
119 95 : node = node->FindChild(entry2);
120 95 : if (node == nullptr) return nullptr;
121 : }
122 75 : if (entry3 != nullptr) {
123 45 : node = node->FindChild(entry3);
124 : }
125 : return node;
126 : }
127 :
128 : private:
129 : const ProfileTree* tree_;
130 : };
131 :
132 : } // namespace
133 :
134 :
135 26644 : TEST(ProfileTreeAddPathFromEnd) {
136 5 : CcTest::InitializeVM();
137 10 : CodeEntry entry1(i::CodeEventListener::FUNCTION_TAG, "aaa");
138 5 : CodeEntry entry2(i::CodeEventListener::FUNCTION_TAG, "bbb");
139 5 : CodeEntry entry3(i::CodeEventListener::FUNCTION_TAG, "ccc");
140 10 : ProfileTree tree(CcTest::i_isolate());
141 : ProfileTreeTestHelper helper(&tree);
142 5 : CHECK(!helper.Walk(&entry1));
143 5 : CHECK(!helper.Walk(&entry2));
144 5 : CHECK(!helper.Walk(&entry3));
145 :
146 : CodeEntry* path[] = {nullptr, &entry3, nullptr, &entry2,
147 5 : nullptr, nullptr, &entry1, nullptr};
148 : std::vector<CodeEntry*> path_vec(path, path + arraysize(path));
149 5 : tree.AddPathFromEnd(path_vec);
150 5 : CHECK(!helper.Walk(&entry2));
151 5 : CHECK(!helper.Walk(&entry3));
152 : ProfileNode* node1 = helper.Walk(&entry1);
153 5 : CHECK(node1);
154 5 : CHECK_EQ(0u, node1->self_ticks());
155 5 : CHECK(!helper.Walk(&entry1, &entry1));
156 5 : CHECK(!helper.Walk(&entry1, &entry3));
157 5 : ProfileNode* node2 = helper.Walk(&entry1, &entry2);
158 5 : CHECK(node2);
159 5 : CHECK_NE(node1, node2);
160 5 : CHECK_EQ(0u, node2->self_ticks());
161 5 : CHECK(!helper.Walk(&entry1, &entry2, &entry1));
162 5 : CHECK(!helper.Walk(&entry1, &entry2, &entry2));
163 5 : ProfileNode* node3 = helper.Walk(&entry1, &entry2, &entry3);
164 5 : CHECK(node3);
165 5 : CHECK_NE(node1, node3);
166 5 : CHECK_NE(node2, node3);
167 5 : CHECK_EQ(1u, node3->self_ticks());
168 :
169 5 : tree.AddPathFromEnd(path_vec);
170 5 : CHECK_EQ(node1, helper.Walk(&entry1));
171 5 : CHECK_EQ(node2, helper.Walk(&entry1, &entry2));
172 5 : CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3));
173 5 : CHECK_EQ(0u, node1->self_ticks());
174 5 : CHECK_EQ(0u, node2->self_ticks());
175 5 : CHECK_EQ(2u, node3->self_ticks());
176 :
177 5 : CodeEntry* path2[] = {&entry2, &entry2, &entry1};
178 : std::vector<CodeEntry*> path2_vec(path2, path2 + arraysize(path2));
179 5 : tree.AddPathFromEnd(path2_vec);
180 5 : CHECK(!helper.Walk(&entry2));
181 5 : CHECK(!helper.Walk(&entry3));
182 5 : CHECK_EQ(node1, helper.Walk(&entry1));
183 5 : CHECK(!helper.Walk(&entry1, &entry1));
184 5 : CHECK(!helper.Walk(&entry1, &entry3));
185 5 : CHECK_EQ(node2, helper.Walk(&entry1, &entry2));
186 5 : CHECK(!helper.Walk(&entry1, &entry2, &entry1));
187 5 : CHECK_EQ(node3, helper.Walk(&entry1, &entry2, &entry3));
188 5 : CHECK_EQ(2u, node3->self_ticks());
189 5 : ProfileNode* node4 = helper.Walk(&entry1, &entry2, &entry2);
190 5 : CHECK(node4);
191 5 : CHECK_NE(node3, node4);
192 5 : CHECK_EQ(1u, node4->self_ticks());
193 5 : }
194 :
195 26644 : TEST(ProfileTreeAddPathFromEndWithLineNumbers) {
196 5 : CcTest::InitializeVM();
197 10 : CodeEntry a(i::CodeEventListener::FUNCTION_TAG, "a");
198 5 : CodeEntry b(i::CodeEventListener::FUNCTION_TAG, "b");
199 5 : CodeEntry c(i::CodeEventListener::FUNCTION_TAG, "c");
200 10 : ProfileTree tree(CcTest::i_isolate());
201 : ProfileTreeTestHelper helper(&tree);
202 :
203 10 : ProfileStackTrace path = {{&c, 5}, {&b, 3}, {&a, 1}};
204 : tree.AddPathFromEnd(path, v8::CpuProfileNode::kNoLineNumberInfo, true,
205 5 : v8::CpuProfilingMode::kCallerLineNumbers);
206 :
207 : ProfileNode* a_node =
208 5 : tree.root()->FindChild(&a, v8::CpuProfileNode::kNoLineNumberInfo);
209 : tree.Print();
210 5 : CHECK(a_node);
211 :
212 5 : ProfileNode* b_node = a_node->FindChild(&b, 1);
213 5 : CHECK(b_node);
214 :
215 5 : ProfileNode* c_node = b_node->FindChild(&c, 3);
216 5 : CHECK(c_node);
217 5 : }
218 :
219 26644 : TEST(ProfileTreeCalculateTotalTicks) {
220 5 : CcTest::InitializeVM();
221 10 : ProfileTree empty_tree(CcTest::i_isolate());
222 5 : CHECK_EQ(0u, empty_tree.root()->self_ticks());
223 : empty_tree.root()->IncrementSelfTicks();
224 5 : CHECK_EQ(1u, empty_tree.root()->self_ticks());
225 :
226 10 : CodeEntry entry1(i::CodeEventListener::FUNCTION_TAG, "aaa");
227 5 : CodeEntry* e1_path[] = {&entry1};
228 : std::vector<CodeEntry*> e1_path_vec(e1_path, e1_path + arraysize(e1_path));
229 :
230 10 : ProfileTree single_child_tree(CcTest::i_isolate());
231 5 : single_child_tree.AddPathFromEnd(e1_path_vec);
232 : single_child_tree.root()->IncrementSelfTicks();
233 5 : CHECK_EQ(1u, single_child_tree.root()->self_ticks());
234 : ProfileTreeTestHelper single_child_helper(&single_child_tree);
235 : ProfileNode* node1 = single_child_helper.Walk(&entry1);
236 5 : CHECK(node1);
237 5 : CHECK_EQ(1u, single_child_tree.root()->self_ticks());
238 5 : CHECK_EQ(1u, node1->self_ticks());
239 :
240 5 : CodeEntry entry2(i::CodeEventListener::FUNCTION_TAG, "bbb");
241 5 : CodeEntry* e2_e1_path[] = {&entry2, &entry1};
242 : std::vector<CodeEntry*> e2_e1_path_vec(e2_e1_path,
243 : e2_e1_path + arraysize(e2_e1_path));
244 :
245 10 : ProfileTree flat_tree(CcTest::i_isolate());
246 : ProfileTreeTestHelper flat_helper(&flat_tree);
247 5 : flat_tree.AddPathFromEnd(e1_path_vec);
248 5 : flat_tree.AddPathFromEnd(e1_path_vec);
249 5 : flat_tree.AddPathFromEnd(e2_e1_path_vec);
250 5 : flat_tree.AddPathFromEnd(e2_e1_path_vec);
251 5 : flat_tree.AddPathFromEnd(e2_e1_path_vec);
252 : // Results in {root,0,0} -> {entry1,0,2} -> {entry2,0,3}
253 5 : CHECK_EQ(0u, flat_tree.root()->self_ticks());
254 : node1 = flat_helper.Walk(&entry1);
255 5 : CHECK(node1);
256 5 : CHECK_EQ(2u, node1->self_ticks());
257 5 : ProfileNode* node2 = flat_helper.Walk(&entry1, &entry2);
258 5 : CHECK(node2);
259 5 : CHECK_EQ(3u, node2->self_ticks());
260 : // Must calculate {root,5,0} -> {entry1,5,2} -> {entry2,3,3}
261 5 : CHECK_EQ(0u, flat_tree.root()->self_ticks());
262 5 : CHECK_EQ(2u, node1->self_ticks());
263 :
264 5 : CodeEntry* e2_path[] = {&entry2};
265 : std::vector<CodeEntry*> e2_path_vec(e2_path, e2_path + arraysize(e2_path));
266 5 : CodeEntry entry3(i::CodeEventListener::FUNCTION_TAG, "ccc");
267 5 : CodeEntry* e3_path[] = {&entry3};
268 : std::vector<CodeEntry*> e3_path_vec(e3_path, e3_path + arraysize(e3_path));
269 :
270 10 : ProfileTree wide_tree(CcTest::i_isolate());
271 : ProfileTreeTestHelper wide_helper(&wide_tree);
272 5 : wide_tree.AddPathFromEnd(e1_path_vec);
273 5 : wide_tree.AddPathFromEnd(e1_path_vec);
274 5 : wide_tree.AddPathFromEnd(e2_e1_path_vec);
275 5 : wide_tree.AddPathFromEnd(e2_path_vec);
276 5 : wide_tree.AddPathFromEnd(e2_path_vec);
277 5 : wide_tree.AddPathFromEnd(e2_path_vec);
278 5 : wide_tree.AddPathFromEnd(e3_path_vec);
279 5 : wide_tree.AddPathFromEnd(e3_path_vec);
280 5 : wide_tree.AddPathFromEnd(e3_path_vec);
281 5 : wide_tree.AddPathFromEnd(e3_path_vec);
282 : // Results in -> {entry1,0,2} -> {entry2,0,1}
283 : // {root,0,0} -> {entry2,0,3}
284 : // -> {entry3,0,4}
285 5 : CHECK_EQ(0u, wide_tree.root()->self_ticks());
286 : node1 = wide_helper.Walk(&entry1);
287 5 : CHECK(node1);
288 5 : CHECK_EQ(2u, node1->self_ticks());
289 5 : ProfileNode* node1_2 = wide_helper.Walk(&entry1, &entry2);
290 5 : CHECK(node1_2);
291 5 : CHECK_EQ(1u, node1_2->self_ticks());
292 : node2 = wide_helper.Walk(&entry2);
293 5 : CHECK(node2);
294 5 : CHECK_EQ(3u, node2->self_ticks());
295 : ProfileNode* node3 = wide_helper.Walk(&entry3);
296 5 : CHECK(node3);
297 5 : CHECK_EQ(4u, node3->self_ticks());
298 : // Calculates -> {entry1,3,2} -> {entry2,1,1}
299 : // {root,10,0} -> {entry2,3,3}
300 : // -> {entry3,4,4}
301 5 : CHECK_EQ(0u, wide_tree.root()->self_ticks());
302 5 : CHECK_EQ(2u, node1->self_ticks());
303 5 : CHECK_EQ(1u, node1_2->self_ticks());
304 5 : CHECK_EQ(3u, node2->self_ticks());
305 5 : CHECK_EQ(4u, node3->self_ticks());
306 5 : }
307 :
308 : static inline i::Address ToAddress(int n) { return static_cast<i::Address>(n); }
309 :
310 : static inline void* ToPointer(int n) { return reinterpret_cast<void*>(n); }
311 :
312 26644 : TEST(CodeMapAddCode) {
313 10 : CodeMap code_map;
314 5 : CodeEntry* entry1 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
315 5 : CodeEntry* entry2 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "bbb");
316 5 : CodeEntry* entry3 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "ccc");
317 5 : CodeEntry* entry4 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "ddd");
318 5 : code_map.AddCode(ToAddress(0x1500), entry1, 0x200);
319 5 : code_map.AddCode(ToAddress(0x1700), entry2, 0x100);
320 5 : code_map.AddCode(ToAddress(0x1900), entry3, 0x50);
321 5 : code_map.AddCode(ToAddress(0x1950), entry4, 0x10);
322 5 : CHECK(!code_map.FindEntry(0));
323 5 : CHECK(!code_map.FindEntry(ToAddress(0x1500 - 1)));
324 5 : CHECK_EQ(entry1, code_map.FindEntry(ToAddress(0x1500)));
325 5 : CHECK_EQ(entry1, code_map.FindEntry(ToAddress(0x1500 + 0x100)));
326 5 : CHECK_EQ(entry1, code_map.FindEntry(ToAddress(0x1500 + 0x200 - 1)));
327 5 : CHECK_EQ(entry2, code_map.FindEntry(ToAddress(0x1700)));
328 5 : CHECK_EQ(entry2, code_map.FindEntry(ToAddress(0x1700 + 0x50)));
329 5 : CHECK_EQ(entry2, code_map.FindEntry(ToAddress(0x1700 + 0x100 - 1)));
330 5 : CHECK(!code_map.FindEntry(ToAddress(0x1700 + 0x100)));
331 5 : CHECK(!code_map.FindEntry(ToAddress(0x1900 - 1)));
332 5 : CHECK_EQ(entry3, code_map.FindEntry(ToAddress(0x1900)));
333 5 : CHECK_EQ(entry3, code_map.FindEntry(ToAddress(0x1900 + 0x28)));
334 5 : CHECK_EQ(entry4, code_map.FindEntry(ToAddress(0x1950)));
335 5 : CHECK_EQ(entry4, code_map.FindEntry(ToAddress(0x1950 + 0x7)));
336 5 : CHECK_EQ(entry4, code_map.FindEntry(ToAddress(0x1950 + 0x10 - 1)));
337 5 : CHECK(!code_map.FindEntry(ToAddress(0x1950 + 0x10)));
338 5 : CHECK(!code_map.FindEntry(ToAddress(0xFFFFFFFF)));
339 5 : }
340 :
341 26644 : TEST(CodeMapMoveAndDeleteCode) {
342 10 : CodeMap code_map;
343 5 : CodeEntry* entry1 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "aaa");
344 5 : CodeEntry* entry2 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "bbb");
345 5 : code_map.AddCode(ToAddress(0x1500), entry1, 0x200);
346 5 : code_map.AddCode(ToAddress(0x1700), entry2, 0x100);
347 5 : CHECK_EQ(entry1, code_map.FindEntry(ToAddress(0x1500)));
348 5 : CHECK_EQ(entry2, code_map.FindEntry(ToAddress(0x1700)));
349 5 : code_map.MoveCode(ToAddress(0x1500), ToAddress(0x1700)); // Deprecate bbb.
350 5 : CHECK(!code_map.FindEntry(ToAddress(0x1500)));
351 5 : CHECK_EQ(entry1, code_map.FindEntry(ToAddress(0x1700)));
352 5 : CodeEntry* entry3 = new CodeEntry(i::CodeEventListener::FUNCTION_TAG, "ccc");
353 5 : code_map.AddCode(ToAddress(0x1750), entry3, 0x100);
354 5 : CHECK(!code_map.FindEntry(ToAddress(0x1700)));
355 5 : CHECK_EQ(entry3, code_map.FindEntry(ToAddress(0x1750)));
356 5 : }
357 :
358 : namespace {
359 :
360 : class TestSetup {
361 : public:
362 : TestSetup()
363 20 : : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
364 20 : i::FLAG_prof_browser_mode = false;
365 : }
366 :
367 : ~TestSetup() {
368 20 : i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
369 : }
370 :
371 : private:
372 : bool old_flag_prof_browser_mode_;
373 : };
374 :
375 : } // namespace
376 :
377 26644 : TEST(RecordTickSample) {
378 : TestSetup test_setup;
379 : i::Isolate* isolate = CcTest::i_isolate();
380 10 : CpuProfilesCollection profiles(isolate);
381 10 : CpuProfiler profiler(isolate);
382 : profiles.set_cpu_profiler(&profiler);
383 5 : profiles.StartProfiling("", false);
384 5 : ProfileGenerator generator(&profiles);
385 5 : CodeEntry* entry1 = new CodeEntry(i::Logger::FUNCTION_TAG, "aaa");
386 5 : CodeEntry* entry2 = new CodeEntry(i::Logger::FUNCTION_TAG, "bbb");
387 5 : CodeEntry* entry3 = new CodeEntry(i::Logger::FUNCTION_TAG, "ccc");
388 5 : generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
389 5 : generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100);
390 5 : generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50);
391 :
392 : // We are building the following calls tree:
393 : // -> aaa - sample1
394 : // aaa -> bbb -> ccc - sample2
395 : // -> ccc -> aaa - sample3
396 : TickSample sample1;
397 5 : sample1.pc = ToPointer(0x1600);
398 5 : sample1.tos = ToPointer(0x1500);
399 5 : sample1.stack[0] = ToPointer(0x1510);
400 5 : sample1.frames_count = 1;
401 5 : generator.RecordTickSample(sample1);
402 : TickSample sample2;
403 5 : sample2.pc = ToPointer(0x1925);
404 5 : sample2.tos = ToPointer(0x1900);
405 5 : sample2.stack[0] = ToPointer(0x1780);
406 5 : sample2.stack[1] = ToPointer(0x10000); // non-existent.
407 5 : sample2.stack[2] = ToPointer(0x1620);
408 5 : sample2.frames_count = 3;
409 5 : generator.RecordTickSample(sample2);
410 : TickSample sample3;
411 5 : sample3.pc = ToPointer(0x1510);
412 5 : sample3.tos = ToPointer(0x1500);
413 5 : sample3.stack[0] = ToPointer(0x1910);
414 5 : sample3.stack[1] = ToPointer(0x1610);
415 5 : sample3.frames_count = 2;
416 5 : generator.RecordTickSample(sample3);
417 :
418 5 : CpuProfile* profile = profiles.StopProfiling("");
419 5 : CHECK(profile);
420 : ProfileTreeTestHelper top_down_test_helper(profile->top_down());
421 5 : CHECK(!top_down_test_helper.Walk(entry2));
422 5 : CHECK(!top_down_test_helper.Walk(entry3));
423 : ProfileNode* node1 = top_down_test_helper.Walk(entry1);
424 5 : CHECK(node1);
425 5 : CHECK_EQ(entry1, node1->entry());
426 5 : ProfileNode* node2 = top_down_test_helper.Walk(entry1, entry1);
427 5 : CHECK(node2);
428 5 : CHECK_EQ(entry1, node2->entry());
429 5 : ProfileNode* node3 = top_down_test_helper.Walk(entry1, entry2, entry3);
430 5 : CHECK(node3);
431 5 : CHECK_EQ(entry3, node3->entry());
432 5 : ProfileNode* node4 = top_down_test_helper.Walk(entry1, entry3, entry1);
433 5 : CHECK(node4);
434 5 : CHECK_EQ(entry1, node4->entry());
435 5 : }
436 :
437 50 : static void CheckNodeIds(const ProfileNode* node, unsigned* expectedId) {
438 50 : CHECK_EQ((*expectedId)++, node->id());
439 90 : for (const ProfileNode* child : *node->children()) {
440 40 : CheckNodeIds(child, expectedId);
441 : }
442 50 : }
443 :
444 :
445 26644 : TEST(SampleIds) {
446 : TestSetup test_setup;
447 : i::Isolate* isolate = CcTest::i_isolate();
448 10 : CpuProfilesCollection profiles(isolate);
449 10 : CpuProfiler profiler(isolate);
450 : profiles.set_cpu_profiler(&profiler);
451 5 : profiles.StartProfiling("", true);
452 5 : ProfileGenerator generator(&profiles);
453 5 : CodeEntry* entry1 = new CodeEntry(i::Logger::FUNCTION_TAG, "aaa");
454 5 : CodeEntry* entry2 = new CodeEntry(i::Logger::FUNCTION_TAG, "bbb");
455 5 : CodeEntry* entry3 = new CodeEntry(i::Logger::FUNCTION_TAG, "ccc");
456 5 : generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
457 5 : generator.code_map()->AddCode(ToAddress(0x1700), entry2, 0x100);
458 5 : generator.code_map()->AddCode(ToAddress(0x1900), entry3, 0x50);
459 :
460 : // We are building the following calls tree:
461 : // -> aaa #3 - sample1
462 : // (root)#1 -> aaa #2 -> bbb #4 -> ccc #5 - sample2
463 : // -> ccc #6 -> aaa #7 - sample3
464 : TickSample sample1;
465 5 : sample1.timestamp = v8::base::TimeTicks::HighResolutionNow();
466 5 : sample1.pc = ToPointer(0x1600);
467 5 : sample1.stack[0] = ToPointer(0x1510);
468 5 : sample1.frames_count = 1;
469 5 : generator.RecordTickSample(sample1);
470 : TickSample sample2;
471 5 : sample2.timestamp = v8::base::TimeTicks::HighResolutionNow();
472 5 : sample2.pc = ToPointer(0x1925);
473 5 : sample2.stack[0] = ToPointer(0x1780);
474 5 : sample2.stack[1] = ToPointer(0x10000); // non-existent.
475 5 : sample2.stack[2] = ToPointer(0x1620);
476 5 : sample2.frames_count = 3;
477 5 : generator.RecordTickSample(sample2);
478 : TickSample sample3;
479 5 : sample3.timestamp = v8::base::TimeTicks::HighResolutionNow();
480 5 : sample3.pc = ToPointer(0x1510);
481 5 : sample3.stack[0] = ToPointer(0x1910);
482 5 : sample3.stack[1] = ToPointer(0x1610);
483 5 : sample3.frames_count = 2;
484 5 : generator.RecordTickSample(sample3);
485 :
486 5 : CpuProfile* profile = profiles.StopProfiling("");
487 5 : unsigned nodeId = 1;
488 5 : CheckNodeIds(profile->top_down()->root(), &nodeId);
489 5 : CHECK_EQ(7u, nodeId - 1);
490 :
491 5 : CHECK_EQ(3, profile->samples_count());
492 5 : unsigned expected_id[] = {3, 5, 7};
493 35 : for (int i = 0; i < 3; i++) {
494 15 : CHECK_EQ(expected_id[i], profile->sample(i).node->id());
495 : }
496 5 : }
497 :
498 :
499 26644 : TEST(NoSamples) {
500 : TestSetup test_setup;
501 : i::Isolate* isolate = CcTest::i_isolate();
502 10 : CpuProfilesCollection profiles(isolate);
503 10 : CpuProfiler profiler(isolate);
504 : profiles.set_cpu_profiler(&profiler);
505 5 : profiles.StartProfiling("", false);
506 5 : ProfileGenerator generator(&profiles);
507 5 : CodeEntry* entry1 = new CodeEntry(i::Logger::FUNCTION_TAG, "aaa");
508 5 : generator.code_map()->AddCode(ToAddress(0x1500), entry1, 0x200);
509 :
510 : // We are building the following calls tree:
511 : // (root)#1 -> aaa #2 -> aaa #3 - sample1
512 : TickSample sample1;
513 5 : sample1.pc = ToPointer(0x1600);
514 5 : sample1.stack[0] = ToPointer(0x1510);
515 5 : sample1.frames_count = 1;
516 5 : generator.RecordTickSample(sample1);
517 :
518 5 : CpuProfile* profile = profiles.StopProfiling("");
519 5 : unsigned nodeId = 1;
520 5 : CheckNodeIds(profile->top_down()->root(), &nodeId);
521 5 : CHECK_EQ(3u, nodeId - 1);
522 :
523 5 : CHECK_EQ(0, profile->samples_count());
524 5 : }
525 :
526 :
527 45 : static const ProfileNode* PickChild(const ProfileNode* parent,
528 : const char* name) {
529 225 : for (const ProfileNode* child : *parent->children()) {
530 225 : if (strcmp(child->entry()->name(), name) == 0) return child;
531 : }
532 : return nullptr;
533 : }
534 :
535 :
536 26639 : TEST(RecordStackTraceAtStartProfiling) {
537 : // This test does not pass with inlining enabled since inlined functions
538 : // don't appear in the stack trace.
539 0 : i::FLAG_turbo_inlining = false;
540 :
541 0 : v8::HandleScope scope(CcTest::isolate());
542 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
543 : v8::Context::Scope context_scope(env);
544 : std::unique_ptr<i::CpuProfiler> iprofiler(
545 0 : new i::CpuProfiler(CcTest::i_isolate()));
546 : i::ProfilerExtension::set_profiler(iprofiler.get());
547 :
548 : CompileRun(
549 : "function c() { startProfiling(); }\n"
550 : "function b() { c(); }\n"
551 : "function a() { b(); }\n"
552 : "a();\n"
553 : "stopProfiling();");
554 0 : CHECK_EQ(1, iprofiler->GetProfilesCount());
555 0 : CpuProfile* profile = iprofiler->GetProfile(0);
556 : const ProfileTree* topDown = profile->top_down();
557 : const ProfileNode* current = topDown->root();
558 0 : const_cast<ProfileNode*>(current)->Print(0);
559 : // The tree should look like this:
560 : // (root)
561 : // ""
562 : // a
563 : // b
564 : // c
565 : // There can also be:
566 : // startProfiling
567 : // if the sampler managed to get a tick.
568 0 : current = PickChild(current, "");
569 0 : CHECK(const_cast<ProfileNode*>(current));
570 0 : current = PickChild(current, "a");
571 0 : CHECK(const_cast<ProfileNode*>(current));
572 0 : current = PickChild(current, "b");
573 0 : CHECK(const_cast<ProfileNode*>(current));
574 0 : current = PickChild(current, "c");
575 0 : CHECK(const_cast<ProfileNode*>(current));
576 0 : CHECK(current->children()->empty() || current->children()->size() == 1);
577 0 : if (current->children()->size() == 1) {
578 0 : current = PickChild(current, "startProfiling");
579 0 : CHECK(current->children()->empty());
580 : }
581 0 : }
582 :
583 :
584 26644 : TEST(Issue51919) {
585 10 : CpuProfilesCollection collection(CcTest::i_isolate());
586 10 : CpuProfiler profiler(CcTest::i_isolate());
587 : collection.set_cpu_profiler(&profiler);
588 : i::EmbeddedVector<char*,
589 : CpuProfilesCollection::kMaxSimultaneousProfiles> titles;
590 1005 : for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i) {
591 500 : i::Vector<char> title = i::Vector<char>::New(16);
592 500 : i::SNPrintF(title, "%d", i);
593 500 : CHECK(collection.StartProfiling(title.start(), false));
594 1000 : titles[i] = title.start();
595 : }
596 5 : CHECK(!collection.StartProfiling("maximum", false));
597 1005 : for (int i = 0; i < CpuProfilesCollection::kMaxSimultaneousProfiles; ++i)
598 1000 : i::DeleteArray(titles[i]);
599 5 : }
600 :
601 :
602 25 : static const v8::CpuProfileNode* PickChild(const v8::CpuProfileNode* parent,
603 : const char* name) {
604 25 : for (int i = 0; i < parent->GetChildrenCount(); ++i) {
605 25 : const v8::CpuProfileNode* child = parent->GetChild(i);
606 : v8::String::Utf8Value function_name(CcTest::isolate(),
607 50 : child->GetFunctionName());
608 25 : if (strcmp(*function_name, name) == 0) return child;
609 : }
610 : return nullptr;
611 : }
612 :
613 :
614 26644 : TEST(ProfileNodeScriptId) {
615 : // This test does not pass with inlining enabled since inlined functions
616 : // don't appear in the stack trace.
617 5 : i::FLAG_turbo_inlining = false;
618 :
619 10 : v8::HandleScope scope(CcTest::isolate());
620 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
621 : v8::Context::Scope context_scope(env);
622 10 : std::unique_ptr<CpuProfiler> iprofiler(new CpuProfiler(CcTest::i_isolate()));
623 : i::ProfilerExtension::set_profiler(iprofiler.get());
624 :
625 : v8::Local<v8::Script> script_a =
626 5 : v8_compile(v8_str("function a() { startProfiling(); }\n"));
627 5 : script_a->Run(v8::Isolate::GetCurrent()->GetCurrentContext())
628 : .ToLocalChecked();
629 : v8::Local<v8::Script> script_b =
630 : v8_compile(v8_str("function b() { a(); }\n"
631 : "b();\n"
632 5 : "stopProfiling();\n"));
633 5 : script_b->Run(v8::Isolate::GetCurrent()->GetCurrentContext())
634 : .ToLocalChecked();
635 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
636 5 : const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
637 5 : const v8::CpuProfileNode* current = profile->GetTopDownRoot();
638 : reinterpret_cast<ProfileNode*>(
639 5 : const_cast<v8::CpuProfileNode*>(current))->Print(0);
640 : // The tree should look like this:
641 : // (root)
642 : // ""
643 : // b
644 : // a
645 : // There can also be:
646 : // startProfiling
647 : // if the sampler managed to get a tick.
648 5 : current = PickChild(current, "");
649 5 : CHECK(const_cast<v8::CpuProfileNode*>(current));
650 :
651 5 : current = PickChild(current, "b");
652 5 : CHECK(const_cast<v8::CpuProfileNode*>(current));
653 10 : CHECK_EQ(script_b->GetUnboundScript()->GetId(), current->GetScriptId());
654 :
655 5 : current = PickChild(current, "a");
656 5 : CHECK(const_cast<v8::CpuProfileNode*>(current));
657 10 : CHECK_EQ(script_a->GetUnboundScript()->GetId(), current->GetScriptId());
658 5 : }
659 :
660 : static const char* line_number_test_source_existing_functions =
661 : "function foo_at_the_first_line() {\n"
662 : "}\n"
663 : "foo_at_the_first_line();\n"
664 : "function lazy_func_at_forth_line() {}\n";
665 :
666 : static const char* line_number_test_source_profile_time_functions =
667 : "// Empty first line\n"
668 : "function bar_at_the_second_line() {\n"
669 : " foo_at_the_first_line();\n"
670 : "}\n"
671 : "bar_at_the_second_line();\n"
672 : "function lazy_func_at_6th_line() {}";
673 :
674 20 : int GetFunctionLineNumber(CpuProfiler& profiler, LocalContext& env,
675 : const char* name) {
676 : CodeMap* code_map = profiler.generator()->code_map();
677 : i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(
678 : v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
679 80 : env->Global()->Get(env.local(), v8_str(name)).ToLocalChecked())));
680 : CodeEntry* func_entry =
681 20 : code_map->FindEntry(func->abstract_code()->InstructionStart());
682 20 : if (!func_entry) FATAL("%s", name);
683 20 : return func_entry->line_number();
684 : }
685 :
686 26644 : TEST(LineNumber) {
687 5 : CcTest::InitializeVM();
688 5 : LocalContext env;
689 : i::Isolate* isolate = CcTest::i_isolate();
690 : TestSetup test_setup;
691 :
692 : i::HandleScope scope(isolate);
693 :
694 5 : CompileRun(line_number_test_source_existing_functions);
695 :
696 10 : CpuProfiler profiler(isolate);
697 5 : profiler.StartProfiling("LineNumber");
698 :
699 5 : CompileRun(line_number_test_source_profile_time_functions);
700 :
701 5 : profiler.processor()->StopSynchronously();
702 :
703 5 : bool is_lazy = i::FLAG_lazy;
704 5 : CHECK_EQ(1, GetFunctionLineNumber(profiler, env, "foo_at_the_first_line"));
705 5 : CHECK_EQ(is_lazy ? 0 : 4,
706 : GetFunctionLineNumber(profiler, env, "lazy_func_at_forth_line"));
707 5 : CHECK_EQ(2, GetFunctionLineNumber(profiler, env, "bar_at_the_second_line"));
708 5 : CHECK_EQ(is_lazy ? 0 : 6,
709 : GetFunctionLineNumber(profiler, env, "lazy_func_at_6th_line"));
710 :
711 5 : profiler.StopProfiling("LineNumber");
712 5 : }
713 :
714 26644 : TEST(BailoutReason) {
715 : #ifndef V8_LITE_MODE
716 5 : i::FLAG_allow_natives_syntax = true;
717 5 : i::FLAG_always_opt = false;
718 5 : i::FLAG_opt = true;
719 10 : v8::HandleScope scope(CcTest::isolate());
720 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
721 : v8::Context::Scope context_scope(env);
722 10 : std::unique_ptr<CpuProfiler> iprofiler(new CpuProfiler(CcTest::i_isolate()));
723 : i::ProfilerExtension::set_profiler(iprofiler.get());
724 :
725 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
726 : v8::Local<v8::Function> function = CompileRun(
727 : "function Debugger() {\n"
728 : " startProfiling();\n"
729 : "}"
730 : "Debugger")
731 : .As<v8::Function>();
732 : i::Handle<i::JSFunction> i_function =
733 : i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function));
734 : USE(i_function);
735 :
736 : CompileRun(
737 : "%OptimizeFunctionOnNextCall(Debugger);"
738 : "%NeverOptimizeFunction(Debugger);"
739 : "Debugger();"
740 : "stopProfiling()");
741 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
742 5 : const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
743 5 : CHECK(profile);
744 5 : const v8::CpuProfileNode* current = profile->GetTopDownRoot();
745 : reinterpret_cast<ProfileNode*>(
746 5 : const_cast<v8::CpuProfileNode*>(current))->Print(0);
747 : // The tree should look like this:
748 : // (root)
749 : // ""
750 : // kOptimizationDisabledForTest
751 5 : current = PickChild(current, "");
752 5 : CHECK(const_cast<v8::CpuProfileNode*>(current));
753 :
754 5 : current = PickChild(current, "Debugger");
755 5 : CHECK(const_cast<v8::CpuProfileNode*>(current));
756 5 : CHECK(
757 : !strcmp("Optimization is always disabled", current->GetBailoutReason()));
758 : #endif // V8_LITE_MODE
759 5 : }
760 :
761 26644 : TEST(NodeSourceTypes) {
762 10 : ProfileTree tree(CcTest::i_isolate());
763 10 : CodeEntry function_entry(CodeEventListener::FUNCTION_TAG, "function");
764 15 : tree.AddPathFromEnd({&function_entry});
765 5 : CodeEntry builtin_entry(CodeEventListener::BUILTIN_TAG, "builtin");
766 15 : tree.AddPathFromEnd({&builtin_entry});
767 5 : CodeEntry callback_entry(CodeEventListener::CALLBACK_TAG, "callback");
768 15 : tree.AddPathFromEnd({&callback_entry});
769 5 : CodeEntry regex_entry(CodeEventListener::REG_EXP_TAG, "regex");
770 15 : tree.AddPathFromEnd({®ex_entry});
771 5 : CodeEntry stub_entry(CodeEventListener::STUB_TAG, "stub");
772 15 : tree.AddPathFromEnd({&stub_entry});
773 :
774 15 : tree.AddPathFromEnd({CodeEntry::gc_entry()});
775 15 : tree.AddPathFromEnd({CodeEntry::idle_entry()});
776 15 : tree.AddPathFromEnd({CodeEntry::program_entry()});
777 15 : tree.AddPathFromEnd({CodeEntry::unresolved_entry()});
778 :
779 : auto* root = tree.root();
780 5 : CHECK(root);
781 5 : CHECK_EQ(root->source_type(), v8::CpuProfileNode::kInternal);
782 :
783 5 : auto* function_node = PickChild(root, "function");
784 5 : CHECK(function_node);
785 5 : CHECK_EQ(function_node->source_type(), v8::CpuProfileNode::kScript);
786 :
787 5 : auto* builtin_node = PickChild(root, "builtin");
788 5 : CHECK(builtin_node);
789 5 : CHECK_EQ(builtin_node->source_type(), v8::CpuProfileNode::kBuiltin);
790 :
791 5 : auto* callback_node = PickChild(root, "callback");
792 5 : CHECK(callback_node);
793 5 : CHECK_EQ(callback_node->source_type(), v8::CpuProfileNode::kCallback);
794 :
795 5 : auto* regex_node = PickChild(root, "regex");
796 5 : CHECK(regex_node);
797 5 : CHECK_EQ(regex_node->source_type(), v8::CpuProfileNode::kInternal);
798 :
799 5 : auto* stub_node = PickChild(root, "stub");
800 5 : CHECK(stub_node);
801 5 : CHECK_EQ(stub_node->source_type(), v8::CpuProfileNode::kInternal);
802 :
803 5 : auto* gc_node = PickChild(root, "(garbage collector)");
804 5 : CHECK(gc_node);
805 5 : CHECK_EQ(gc_node->source_type(), v8::CpuProfileNode::kInternal);
806 :
807 5 : auto* idle_node = PickChild(root, "(idle)");
808 5 : CHECK(idle_node);
809 5 : CHECK_EQ(idle_node->source_type(), v8::CpuProfileNode::kInternal);
810 :
811 5 : auto* program_node = PickChild(root, "(program)");
812 5 : CHECK(program_node);
813 5 : CHECK_EQ(program_node->source_type(), v8::CpuProfileNode::kInternal);
814 :
815 5 : auto* unresolved_node = PickChild(root, "(unresolved function)");
816 5 : CHECK(unresolved_node);
817 5 : CHECK_EQ(unresolved_node->source_type(), v8::CpuProfileNode::kUnresolved);
818 5 : }
819 :
820 : } // namespace test_profile_generator
821 : } // namespace internal
822 79917 : } // namespace v8
|