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