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