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 <limits>
31 : #include <memory>
32 :
33 : #include "src/v8.h"
34 :
35 : #include "include/v8-profiler.h"
36 : #include "src/api-inl.h"
37 : #include "src/base/platform/platform.h"
38 : #include "src/deoptimizer.h"
39 : #include "src/libplatform/default-platform.h"
40 : #include "src/log.h"
41 : #include "src/objects-inl.h"
42 : #include "src/profiler/cpu-profiler-inl.h"
43 : #include "src/profiler/profiler-listener.h"
44 : #include "src/profiler/tracing-cpu-profiler.h"
45 : #include "src/source-position-table.h"
46 : #include "src/utils.h"
47 : #include "test/cctest/cctest.h"
48 : #include "test/cctest/profiler-extension.h"
49 :
50 : #include "include/libplatform/v8-tracing.h"
51 : #include "src/tracing/trace-event.h"
52 :
53 : namespace v8 {
54 : namespace internal {
55 : namespace test_cpu_profiler {
56 :
57 : // Helper methods
58 129 : static v8::Local<v8::Function> GetFunction(v8::Local<v8::Context> env,
59 : const char* name) {
60 : return v8::Local<v8::Function>::Cast(
61 516 : env->Global()->Get(env, v8_str(name)).ToLocalChecked());
62 : }
63 :
64 9 : static size_t offset(const char* src, const char* substring) {
65 : const char* it = strstr(src, substring);
66 9 : CHECK(it);
67 9 : return static_cast<size_t>(it - src);
68 : }
69 :
70 : template <typename A, typename B>
71 : static int dist(A a, B b) {
72 3 : return abs(static_cast<int>(a) - static_cast<int>(b));
73 : }
74 :
75 : static const char* reason(const i::DeoptimizeReason reason) {
76 3 : return i::DeoptimizeReasonToString(reason);
77 : }
78 :
79 26644 : TEST(StartStop) {
80 : i::Isolate* isolate = CcTest::i_isolate();
81 10 : CpuProfilesCollection profiles(isolate);
82 5 : ProfileGenerator generator(&profiles);
83 : std::unique_ptr<ProfilerEventsProcessor> processor(
84 : new SamplingEventsProcessor(isolate, &generator,
85 10 : v8::base::TimeDelta::FromMicroseconds(100),
86 5 : true));
87 5 : processor->Start();
88 5 : processor->StopSynchronously();
89 5 : }
90 :
91 29 : static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
92 : i::Address frame1,
93 : i::Address frame2 = kNullAddress,
94 : i::Address frame3 = kNullAddress) {
95 : v8::internal::TickSample sample;
96 29 : sample.pc = reinterpret_cast<void*>(frame1);
97 29 : sample.tos = reinterpret_cast<void*>(frame1);
98 : sample.frames_count = 0;
99 29 : if (frame2 != kNullAddress) {
100 10 : sample.stack[0] = reinterpret_cast<void*>(frame2);
101 10 : sample.frames_count = 1;
102 : }
103 29 : if (frame3 != kNullAddress) {
104 5 : sample.stack[1] = reinterpret_cast<void*>(frame3);
105 5 : sample.frames_count = 2;
106 : }
107 29 : sample.timestamp = base::TimeTicks::HighResolutionNow();
108 29 : proc->AddSample(sample);
109 29 : }
110 :
111 : namespace {
112 :
113 : class TestSetup {
114 : public:
115 : TestSetup()
116 25 : : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
117 25 : i::FLAG_prof_browser_mode = false;
118 : }
119 :
120 : ~TestSetup() {
121 25 : i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
122 : }
123 :
124 : private:
125 : bool old_flag_prof_browser_mode_;
126 : };
127 :
128 : } // namespace
129 :
130 45 : i::AbstractCode CreateCode(LocalContext* env) {
131 : static int counter = 0;
132 : i::EmbeddedVector<char, 256> script;
133 : i::EmbeddedVector<char, 32> name;
134 :
135 45 : i::SNPrintF(name, "function_%d", ++counter);
136 : const char* name_start = name.start();
137 : i::SNPrintF(script,
138 : "function %s() {\n"
139 : "var counter = 0;\n"
140 : "for (var i = 0; i < %d; ++i) counter += i;\n"
141 : "return '%s_' + counter;\n"
142 : "}\n"
143 45 : "%s();\n", name_start, counter, name_start, name_start);
144 : CompileRun(script.start());
145 :
146 : i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(
147 90 : v8::Utils::OpenHandle(*GetFunction(env->local(), name_start)));
148 45 : return fun->abstract_code();
149 : }
150 :
151 26644 : TEST(CodeEvents) {
152 5 : CcTest::InitializeVM();
153 5 : LocalContext env;
154 : i::Isolate* isolate = CcTest::i_isolate();
155 : i::Factory* factory = isolate->factory();
156 : TestSetup test_setup;
157 :
158 : i::HandleScope scope(isolate);
159 :
160 5 : i::AbstractCode aaa_code = CreateCode(&env);
161 5 : i::AbstractCode comment_code = CreateCode(&env);
162 5 : i::AbstractCode comment2_code = CreateCode(&env);
163 5 : i::AbstractCode moved_code = CreateCode(&env);
164 :
165 5 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
166 5 : ProfileGenerator* generator = new ProfileGenerator(profiles);
167 : ProfilerEventsProcessor* processor = new SamplingEventsProcessor(
168 5 : isolate, generator, v8::base::TimeDelta::FromMicroseconds(100), true);
169 5 : processor->Start();
170 10 : ProfilerListener profiler_listener(isolate, processor);
171 5 : isolate->logger()->AddCodeEventListener(&profiler_listener);
172 :
173 : // Enqueue code creation events.
174 : const char* aaa_str = "aaa";
175 5 : i::Handle<i::String> aaa_name = factory->NewStringFromAsciiChecked(aaa_str);
176 10 : profiler_listener.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code,
177 5 : *aaa_name);
178 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code,
179 5 : "comment");
180 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code,
181 5 : "comment2");
182 5 : profiler_listener.CodeMoveEvent(comment2_code, moved_code);
183 :
184 : // Enqueue a tick event to enable code events processing.
185 5 : EnqueueTickSampleEvent(processor, aaa_code->InstructionStart());
186 :
187 5 : isolate->logger()->RemoveCodeEventListener(&profiler_listener);
188 5 : processor->StopSynchronously();
189 :
190 : // Check the state of profile generator.
191 : CodeEntry* aaa =
192 10 : generator->code_map()->FindEntry(aaa_code->InstructionStart());
193 5 : CHECK(aaa);
194 5 : CHECK_EQ(0, strcmp(aaa_str, aaa->name()));
195 :
196 : CodeEntry* comment =
197 5 : generator->code_map()->FindEntry(comment_code->InstructionStart());
198 5 : CHECK(comment);
199 5 : CHECK_EQ(0, strcmp("comment", comment->name()));
200 :
201 5 : CHECK(!generator->code_map()->FindEntry(comment2_code->InstructionStart()));
202 :
203 : CodeEntry* comment2 =
204 5 : generator->code_map()->FindEntry(moved_code->InstructionStart());
205 5 : CHECK(comment2);
206 5 : CHECK_EQ(0, strcmp("comment2", comment2->name()));
207 5 : }
208 :
209 : template<typename T>
210 : static int CompareProfileNodes(const T* p1, const T* p2) {
211 : return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
212 : }
213 :
214 26644 : TEST(TickEvents) {
215 : TestSetup test_setup;
216 5 : LocalContext env;
217 : i::Isolate* isolate = CcTest::i_isolate();
218 : i::HandleScope scope(isolate);
219 :
220 5 : i::AbstractCode frame1_code = CreateCode(&env);
221 5 : i::AbstractCode frame2_code = CreateCode(&env);
222 5 : i::AbstractCode frame3_code = CreateCode(&env);
223 :
224 5 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
225 5 : ProfileGenerator* generator = new ProfileGenerator(profiles);
226 : ProfilerEventsProcessor* processor = new SamplingEventsProcessor(
227 : CcTest::i_isolate(), generator,
228 5 : v8::base::TimeDelta::FromMicroseconds(100), true);
229 10 : CpuProfiler profiler(isolate, profiles, generator, processor);
230 5 : profiles->StartProfiling("", false);
231 5 : processor->Start();
232 10 : ProfilerListener profiler_listener(isolate, processor);
233 5 : isolate->logger()->AddCodeEventListener(&profiler_listener);
234 :
235 5 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
236 5 : profiler_listener.CodeCreateEvent(i::Logger::STUB_TAG, frame2_code, "ccc");
237 5 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame3_code, "ddd");
238 :
239 5 : EnqueueTickSampleEvent(processor, frame1_code->raw_instruction_start());
240 10 : EnqueueTickSampleEvent(
241 : processor,
242 5 : frame2_code->raw_instruction_start() + frame2_code->ExecutableSize() / 2,
243 10 : frame1_code->raw_instruction_start() + frame1_code->ExecutableSize() / 2);
244 10 : EnqueueTickSampleEvent(processor, frame3_code->raw_instruction_end() - 1,
245 5 : frame2_code->raw_instruction_end() - 1,
246 10 : frame1_code->raw_instruction_end() - 1);
247 :
248 5 : isolate->logger()->RemoveCodeEventListener(&profiler_listener);
249 5 : processor->StopSynchronously();
250 5 : CpuProfile* profile = profiles->StopProfiling("");
251 5 : CHECK(profile);
252 :
253 : // Check call trees.
254 : const std::vector<ProfileNode*>* top_down_root_children =
255 : profile->top_down()->root()->children();
256 5 : CHECK_EQ(1, top_down_root_children->size());
257 5 : CHECK_EQ(0, strcmp("bbb", top_down_root_children->back()->entry()->name()));
258 : const std::vector<ProfileNode*>* top_down_bbb_children =
259 : top_down_root_children->back()->children();
260 5 : CHECK_EQ(1, top_down_bbb_children->size());
261 5 : CHECK_EQ(0, strcmp("ccc", top_down_bbb_children->back()->entry()->name()));
262 : const std::vector<ProfileNode*>* top_down_stub_children =
263 : top_down_bbb_children->back()->children();
264 5 : CHECK_EQ(1, top_down_stub_children->size());
265 5 : CHECK_EQ(0, strcmp("ddd", top_down_stub_children->back()->entry()->name()));
266 : const std::vector<ProfileNode*>* top_down_ddd_children =
267 : top_down_stub_children->back()->children();
268 5 : CHECK(top_down_ddd_children->empty());
269 5 : }
270 :
271 : // http://crbug/51594
272 : // This test must not crash.
273 26644 : TEST(CrashIfStoppingLastNonExistentProfile) {
274 5 : CcTest::InitializeVM();
275 : TestSetup test_setup;
276 10 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(CcTest::i_isolate()));
277 5 : profiler->StartProfiling("1");
278 5 : profiler->StopProfiling("2");
279 5 : profiler->StartProfiling("1");
280 5 : profiler->StopProfiling("");
281 5 : }
282 :
283 : // http://code.google.com/p/v8/issues/detail?id=1398
284 : // Long stacks (exceeding max frames limit) must not be erased.
285 26644 : TEST(Issue1398) {
286 : TestSetup test_setup;
287 5 : LocalContext env;
288 : i::Isolate* isolate = CcTest::i_isolate();
289 : i::HandleScope scope(isolate);
290 :
291 5 : i::AbstractCode code = CreateCode(&env);
292 :
293 5 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
294 5 : ProfileGenerator* generator = new ProfileGenerator(profiles);
295 : ProfilerEventsProcessor* processor = new SamplingEventsProcessor(
296 : CcTest::i_isolate(), generator,
297 5 : v8::base::TimeDelta::FromMicroseconds(100), true);
298 10 : CpuProfiler profiler(isolate, profiles, generator, processor);
299 5 : profiles->StartProfiling("", false);
300 5 : processor->Start();
301 10 : ProfilerListener profiler_listener(isolate, processor);
302 :
303 5 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
304 :
305 : v8::internal::TickSample sample;
306 5 : sample.pc = reinterpret_cast<void*>(code->InstructionStart());
307 : sample.tos = nullptr;
308 : sample.frames_count = v8::TickSample::kMaxFramesCount;
309 2555 : for (unsigned i = 0; i < sample.frames_count; ++i) {
310 1275 : sample.stack[i] = reinterpret_cast<void*>(code->InstructionStart());
311 : }
312 5 : sample.timestamp = base::TimeTicks::HighResolutionNow();
313 5 : processor->AddSample(sample);
314 :
315 5 : processor->StopSynchronously();
316 5 : CpuProfile* profile = profiles->StopProfiling("");
317 5 : CHECK(profile);
318 :
319 : unsigned actual_depth = 0;
320 : const ProfileNode* node = profile->top_down()->root();
321 2565 : while (!node->children()->empty()) {
322 1280 : node = node->children()->back();
323 1280 : ++actual_depth;
324 : }
325 :
326 5 : CHECK_EQ(1 + v8::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
327 5 : }
328 :
329 26644 : TEST(DeleteAllCpuProfiles) {
330 5 : CcTest::InitializeVM();
331 : TestSetup test_setup;
332 10 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(CcTest::i_isolate()));
333 5 : CHECK_EQ(0, profiler->GetProfilesCount());
334 5 : profiler->DeleteAllProfiles();
335 5 : CHECK_EQ(0, profiler->GetProfilesCount());
336 :
337 5 : profiler->StartProfiling("1");
338 5 : profiler->StopProfiling("1");
339 5 : CHECK_EQ(1, profiler->GetProfilesCount());
340 5 : profiler->DeleteAllProfiles();
341 5 : CHECK_EQ(0, profiler->GetProfilesCount());
342 5 : profiler->StartProfiling("1");
343 5 : profiler->StartProfiling("2");
344 5 : profiler->StopProfiling("2");
345 5 : profiler->StopProfiling("1");
346 5 : CHECK_EQ(2, profiler->GetProfilesCount());
347 5 : profiler->DeleteAllProfiles();
348 5 : CHECK_EQ(0, profiler->GetProfilesCount());
349 :
350 : // Test profiling cancellation by the 'delete' command.
351 5 : profiler->StartProfiling("1");
352 5 : profiler->StartProfiling("2");
353 5 : CHECK_EQ(0, profiler->GetProfilesCount());
354 5 : profiler->DeleteAllProfiles();
355 5 : CHECK_EQ(0, profiler->GetProfilesCount());
356 5 : }
357 :
358 :
359 30 : static bool FindCpuProfile(v8::CpuProfiler* v8profiler,
360 : const v8::CpuProfile* v8profile) {
361 : i::CpuProfiler* profiler = reinterpret_cast<i::CpuProfiler*>(v8profiler);
362 : const i::CpuProfile* profile =
363 : reinterpret_cast<const i::CpuProfile*>(v8profile);
364 30 : int length = profiler->GetProfilesCount();
365 50 : for (int i = 0; i < length; i++) {
366 35 : if (profile == profiler->GetProfile(i))
367 : return true;
368 : }
369 : return false;
370 : }
371 :
372 :
373 26644 : TEST(DeleteCpuProfile) {
374 5 : LocalContext env;
375 10 : v8::HandleScope scope(env->GetIsolate());
376 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
377 : i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(cpu_profiler);
378 :
379 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
380 5 : v8::Local<v8::String> name1 = v8_str("1");
381 5 : cpu_profiler->StartProfiling(name1);
382 5 : v8::CpuProfile* p1 = cpu_profiler->StopProfiling(name1);
383 5 : CHECK(p1);
384 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
385 5 : CHECK(FindCpuProfile(cpu_profiler, p1));
386 5 : p1->Delete();
387 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
388 :
389 5 : v8::Local<v8::String> name2 = v8_str("2");
390 5 : cpu_profiler->StartProfiling(name2);
391 5 : v8::CpuProfile* p2 = cpu_profiler->StopProfiling(name2);
392 5 : CHECK(p2);
393 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
394 5 : CHECK(FindCpuProfile(cpu_profiler, p2));
395 5 : v8::Local<v8::String> name3 = v8_str("3");
396 5 : cpu_profiler->StartProfiling(name3);
397 5 : v8::CpuProfile* p3 = cpu_profiler->StopProfiling(name3);
398 5 : CHECK(p3);
399 5 : CHECK_EQ(2, iprofiler->GetProfilesCount());
400 5 : CHECK_NE(p2, p3);
401 5 : CHECK(FindCpuProfile(cpu_profiler, p3));
402 5 : CHECK(FindCpuProfile(cpu_profiler, p2));
403 5 : p2->Delete();
404 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
405 5 : CHECK(!FindCpuProfile(cpu_profiler, p2));
406 5 : CHECK(FindCpuProfile(cpu_profiler, p3));
407 5 : p3->Delete();
408 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
409 5 : cpu_profiler->Dispose();
410 5 : }
411 :
412 :
413 26644 : TEST(ProfileStartEndTime) {
414 5 : LocalContext env;
415 10 : v8::HandleScope scope(env->GetIsolate());
416 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
417 :
418 5 : v8::Local<v8::String> profile_name = v8_str("test");
419 5 : cpu_profiler->StartProfiling(profile_name);
420 5 : const v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
421 5 : CHECK(profile->GetStartTime() <= profile->GetEndTime());
422 5 : cpu_profiler->Dispose();
423 5 : }
424 :
425 : class ProfilerHelper {
426 : public:
427 84 : explicit ProfilerHelper(const v8::Local<v8::Context>& context)
428 : : context_(context),
429 168 : profiler_(v8::CpuProfiler::New(context->GetIsolate())) {
430 : i::ProfilerExtension::set_profiler(profiler_);
431 84 : }
432 84 : ~ProfilerHelper() {
433 : i::ProfilerExtension::set_profiler(static_cast<CpuProfiler*>(nullptr));
434 84 : profiler_->Dispose();
435 : }
436 :
437 : typedef v8::CpuProfilingMode ProfilingMode;
438 :
439 : v8::CpuProfile* Run(v8::Local<v8::Function> function,
440 : v8::Local<v8::Value> argv[], int argc,
441 : unsigned min_js_samples = 0,
442 : unsigned min_external_samples = 0,
443 : bool collect_samples = false,
444 : ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
445 :
446 : v8::CpuProfiler* profiler() { return profiler_; }
447 :
448 : private:
449 : v8::Local<v8::Context> context_;
450 : v8::CpuProfiler* profiler_;
451 : };
452 :
453 560 : v8::CpuProfile* ProfilerHelper::Run(v8::Local<v8::Function> function,
454 : v8::Local<v8::Value> argv[], int argc,
455 : unsigned min_js_samples,
456 : unsigned min_external_samples,
457 : bool collect_samples, ProfilingMode mode) {
458 560 : v8::Local<v8::String> profile_name = v8_str("my_profile");
459 :
460 560 : profiler_->SetSamplingInterval(100);
461 560 : profiler_->StartProfiling(profile_name, mode, collect_samples);
462 :
463 : v8::internal::CpuProfiler* iprofiler =
464 560 : reinterpret_cast<v8::internal::CpuProfiler*>(profiler_);
465 : v8::sampler::Sampler* sampler =
466 : reinterpret_cast<i::SamplingEventsProcessor*>(iprofiler->processor())
467 : ->sampler();
468 : sampler->StartCountingSamples();
469 3270 : do {
470 9810 : function->Call(context_, context_->Global(), argc, argv).ToLocalChecked();
471 3270 : } while (sampler->js_sample_count() < min_js_samples ||
472 : sampler->external_sample_count() < min_external_samples);
473 :
474 560 : v8::CpuProfile* profile = profiler_->StopProfiling(profile_name);
475 :
476 560 : CHECK(profile);
477 : // Dump collected profile to have a better diagnostic in case of failure.
478 560 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
479 :
480 560 : return profile;
481 : }
482 :
483 0 : static unsigned TotalHitCount(const v8::CpuProfileNode* node) {
484 0 : unsigned hit_count = node->GetHitCount();
485 0 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i)
486 0 : hit_count += TotalHitCount(node->GetChild(i));
487 0 : return hit_count;
488 : }
489 :
490 1455 : static const v8::CpuProfileNode* FindChild(v8::Local<v8::Context> context,
491 : const v8::CpuProfileNode* node,
492 : const char* name) {
493 1455 : int count = node->GetChildrenCount();
494 1455 : v8::Local<v8::String> name_handle = v8_str(name);
495 1665 : for (int i = 0; i < count; i++) {
496 1544 : const v8::CpuProfileNode* child = node->GetChild(i);
497 4632 : if (name_handle->Equals(context, child->GetFunctionName()).FromJust()) {
498 : return child;
499 : }
500 : }
501 : return nullptr;
502 : }
503 :
504 0 : static const v8::CpuProfileNode* FindChild(const v8::CpuProfileNode* node,
505 : const char* name) {
506 0 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
507 0 : const v8::CpuProfileNode* child = node->GetChild(i);
508 0 : if (strcmp(child->GetFunctionNameStr(), name) == 0) {
509 : return child;
510 : }
511 : }
512 : return nullptr;
513 : }
514 :
515 1435 : static const v8::CpuProfileNode* GetChild(v8::Local<v8::Context> context,
516 : const v8::CpuProfileNode* node,
517 : const char* name) {
518 1435 : const v8::CpuProfileNode* result = FindChild(context, node, name);
519 1435 : if (!result) FATAL("Failed to GetChild: %s", name);
520 1435 : return result;
521 : }
522 :
523 : static void CheckSimpleBranch(v8::Local<v8::Context> context,
524 : const v8::CpuProfileNode* node,
525 : const char* names[], int length) {
526 0 : for (int i = 0; i < length; i++) {
527 0 : const char* name = names[i];
528 0 : node = GetChild(context, node, name);
529 : }
530 : }
531 :
532 6 : static const ProfileNode* GetSimpleBranch(v8::Local<v8::Context> context,
533 : v8::CpuProfile* profile,
534 : const char* names[], int length) {
535 6 : const v8::CpuProfileNode* node = profile->GetTopDownRoot();
536 30 : for (int i = 0; i < length; i++) {
537 12 : node = GetChild(context, node, names[i]);
538 : }
539 6 : return reinterpret_cast<const ProfileNode*>(node);
540 : }
541 :
542 : struct NameLinePair {
543 : const char* name;
544 : int line_number;
545 : };
546 :
547 170 : static const v8::CpuProfileNode* FindChild(const v8::CpuProfileNode* node,
548 : NameLinePair pair) {
549 233 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
550 233 : const v8::CpuProfileNode* child = node->GetChild(i);
551 : // The name and line number must match, or if the requested line number was
552 : // -1, then match any function of the same name.
553 661 : if (strcmp(child->GetFunctionNameStr(), pair.name) == 0 &&
554 220 : (child->GetLineNumber() == pair.line_number ||
555 : pair.line_number == -1)) {
556 : return child;
557 : }
558 : }
559 : return nullptr;
560 : }
561 :
562 170 : static const v8::CpuProfileNode* GetChild(const v8::CpuProfileNode* node,
563 : NameLinePair pair) {
564 170 : const v8::CpuProfileNode* result = FindChild(node, pair);
565 170 : if (!result) FATAL("Failed to GetChild: %s:%d", pair.name, pair.line_number);
566 170 : return result;
567 : }
568 :
569 45 : static void CheckBranch(const v8::CpuProfileNode* node, NameLinePair path[],
570 : int length) {
571 365 : for (int i = 0; i < length; i++) {
572 160 : NameLinePair pair = path[i];
573 160 : node = GetChild(node, pair);
574 : }
575 45 : }
576 :
577 : static const char* cpu_profiler_test_source =
578 : "%NeverOptimizeFunction(loop);\n"
579 : "%NeverOptimizeFunction(delay);\n"
580 : "%NeverOptimizeFunction(bar);\n"
581 : "%NeverOptimizeFunction(baz);\n"
582 : "%NeverOptimizeFunction(foo);\n"
583 : "%NeverOptimizeFunction(start);\n"
584 : "function loop(timeout) {\n"
585 : " this.mmm = 0;\n"
586 : " var start = Date.now();\n"
587 : " do {\n"
588 : " var n = 1000;\n"
589 : " while(n > 1) {\n"
590 : " n--;\n"
591 : " this.mmm += n * n * n;\n"
592 : " }\n"
593 : " } while (Date.now() - start < timeout);\n"
594 : "}\n"
595 : "function delay() { loop(10); }\n"
596 : "function bar() { delay(); }\n"
597 : "function baz() { delay(); }\n"
598 : "function foo() {\n"
599 : " delay();\n"
600 : " bar();\n"
601 : " delay();\n"
602 : " baz();\n"
603 : "}\n"
604 : "function start(duration) {\n"
605 : " var start = Date.now();\n"
606 : " do {\n"
607 : " foo();\n"
608 : " } while (Date.now() - start < duration);\n"
609 : "}\n";
610 :
611 : // Check that the profile tree for the script above will look like the
612 : // following:
613 : //
614 : // [Top down]:
615 : // 1062 0 (root) [-1]
616 : // 1054 0 start [-1]
617 : // 1054 1 foo [-1]
618 : // 265 0 baz [-1]
619 : // 265 1 delay [-1]
620 : // 264 264 loop [-1]
621 : // 525 3 delay [-1]
622 : // 522 522 loop [-1]
623 : // 263 0 bar [-1]
624 : // 263 1 delay [-1]
625 : // 262 262 loop [-1]
626 : // 2 2 (program) [-1]
627 : // 6 6 (garbage collector) [-1]
628 26639 : TEST(CollectCpuProfile) {
629 0 : i::FLAG_allow_natives_syntax = true;
630 0 : LocalContext env;
631 0 : v8::HandleScope scope(env->GetIsolate());
632 :
633 0 : CompileRun(cpu_profiler_test_source);
634 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
635 :
636 : int32_t profiling_interval_ms = 200;
637 : v8::Local<v8::Value> args[] = {
638 0 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
639 0 : ProfilerHelper helper(env.local());
640 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
641 :
642 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
643 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
644 0 : const v8::CpuProfileNode* foo_node = GetChild(env.local(), start_node, "foo");
645 :
646 0 : const char* bar_branch[] = {"bar", "delay", "loop"};
647 : CheckSimpleBranch(env.local(), foo_node, bar_branch, arraysize(bar_branch));
648 0 : const char* baz_branch[] = {"baz", "delay", "loop"};
649 : CheckSimpleBranch(env.local(), foo_node, baz_branch, arraysize(baz_branch));
650 0 : const char* delay_branch[] = {"delay", "loop"};
651 : CheckSimpleBranch(env.local(), foo_node, delay_branch,
652 : arraysize(delay_branch));
653 :
654 0 : profile->Delete();
655 0 : }
656 :
657 26644 : TEST(CollectCpuProfileCallerLineNumbers) {
658 5 : i::FLAG_allow_natives_syntax = true;
659 5 : LocalContext env;
660 10 : v8::HandleScope scope(env->GetIsolate());
661 :
662 5 : CompileRun(cpu_profiler_test_source);
663 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
664 :
665 : int32_t profiling_interval_ms = 200;
666 : v8::Local<v8::Value> args[] = {
667 5 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
668 5 : ProfilerHelper helper(env.local());
669 : helper.Run(function, args, arraysize(args), 1000, 0, false,
670 5 : v8::CpuProfilingMode::kCallerLineNumbers);
671 : v8::CpuProfile* profile =
672 : helper.Run(function, args, arraysize(args), 1000, 0, false,
673 5 : v8::CpuProfilingMode::kCallerLineNumbers);
674 :
675 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
676 5 : const v8::CpuProfileNode* start_node = GetChild(root, {"start", 27});
677 5 : const v8::CpuProfileNode* foo_node = GetChild(start_node, {"foo", 30});
678 :
679 5 : NameLinePair bar_branch[] = {{"bar", 23}, {"delay", 19}, {"loop", 18}};
680 5 : CheckBranch(foo_node, bar_branch, arraysize(bar_branch));
681 5 : NameLinePair baz_branch[] = {{"baz", 25}, {"delay", 20}, {"loop", 18}};
682 5 : CheckBranch(foo_node, baz_branch, arraysize(baz_branch));
683 5 : NameLinePair delay_at22_branch[] = {{"delay", 22}, {"loop", 18}};
684 5 : CheckBranch(foo_node, delay_at22_branch, arraysize(delay_at22_branch));
685 5 : NameLinePair delay_at24_branch[] = {{"delay", 24}, {"loop", 18}};
686 5 : CheckBranch(foo_node, delay_at24_branch, arraysize(delay_at24_branch));
687 :
688 5 : profile->Delete();
689 5 : }
690 :
691 : static const char* hot_deopt_no_frame_entry_test_source =
692 : "%NeverOptimizeFunction(foo);\n"
693 : "%NeverOptimizeFunction(start);\n"
694 : "function foo(a, b) {\n"
695 : " return a + b;\n"
696 : "}\n"
697 : "function start(timeout) {\n"
698 : " var start = Date.now();\n"
699 : " do {\n"
700 : " for (var i = 1; i < 1000; ++i) foo(1, i);\n"
701 : " var duration = Date.now() - start;\n"
702 : " } while (duration < timeout);\n"
703 : " return duration;\n"
704 : "}\n";
705 :
706 : // Check that the profile tree for the script above will look like the
707 : // following:
708 : //
709 : // [Top down]:
710 : // 1062 0 (root) [-1]
711 : // 1054 0 start [-1]
712 : // 1054 1 foo [-1]
713 : // 2 2 (program) [-1]
714 : // 6 6 (garbage collector) [-1]
715 : //
716 : // The test checks no FP ranges are present in a deoptimized function.
717 : // If 'foo' has no ranges the samples falling into the prologue will miss the
718 : // 'start' function on the stack, so 'foo' will be attached to the (root).
719 26639 : TEST(HotDeoptNoFrameEntry) {
720 0 : i::FLAG_allow_natives_syntax = true;
721 0 : LocalContext env;
722 0 : v8::HandleScope scope(env->GetIsolate());
723 :
724 0 : CompileRun(hot_deopt_no_frame_entry_test_source);
725 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
726 :
727 : int32_t profiling_interval_ms = 200;
728 : v8::Local<v8::Value> args[] = {
729 0 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
730 0 : ProfilerHelper helper(env.local());
731 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
732 0 : function->Call(env.local(), env->Global(), arraysize(args), args)
733 : .ToLocalChecked();
734 :
735 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
736 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
737 0 : GetChild(env.local(), start_node, "foo");
738 :
739 0 : profile->Delete();
740 0 : }
741 :
742 26644 : TEST(CollectCpuProfileSamples) {
743 5 : i::FLAG_allow_natives_syntax = true;
744 5 : LocalContext env;
745 10 : v8::HandleScope scope(env->GetIsolate());
746 :
747 5 : CompileRun(cpu_profiler_test_source);
748 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
749 :
750 : int32_t profiling_interval_ms = 200;
751 : v8::Local<v8::Value> args[] = {
752 5 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
753 5 : ProfilerHelper helper(env.local());
754 : v8::CpuProfile* profile =
755 5 : helper.Run(function, args, arraysize(args), 1000, 0, true);
756 :
757 5 : CHECK_LE(200, profile->GetSamplesCount());
758 5 : uint64_t end_time = profile->GetEndTime();
759 5 : uint64_t current_time = profile->GetStartTime();
760 5 : CHECK_LE(current_time, end_time);
761 18861 : for (int i = 0; i < profile->GetSamplesCount(); i++) {
762 9428 : CHECK(profile->GetSample(i));
763 9428 : uint64_t timestamp = profile->GetSampleTimestamp(i);
764 9428 : CHECK_LE(current_time, timestamp);
765 9428 : CHECK_LE(timestamp, end_time);
766 : current_time = timestamp;
767 : }
768 :
769 5 : profile->Delete();
770 5 : }
771 :
772 : static const char* cpu_profiler_test_source2 =
773 : "%NeverOptimizeFunction(loop);\n"
774 : "%NeverOptimizeFunction(delay);\n"
775 : "%NeverOptimizeFunction(start);\n"
776 : "function loop() {}\n"
777 : "function delay() { loop(); }\n"
778 : "function start(duration) {\n"
779 : " var start = Date.now();\n"
780 : " do {\n"
781 : " for (var i = 0; i < 10000; ++i) delay();\n"
782 : " } while (Date.now() - start < duration);\n"
783 : "}";
784 :
785 : // Check that the profile tree doesn't contain unexpected traces:
786 : // - 'loop' can be called only by 'delay'
787 : // - 'delay' may be called only by 'start'
788 : // The profile will look like the following:
789 : //
790 : // [Top down]:
791 : // 135 0 (root) [-1] #1
792 : // 121 72 start [-1] #3
793 : // 49 33 delay [-1] #4
794 : // 16 16 loop [-1] #5
795 : // 14 14 (program) [-1] #2
796 26639 : TEST(SampleWhenFrameIsNotSetup) {
797 0 : i::FLAG_allow_natives_syntax = true;
798 0 : LocalContext env;
799 0 : v8::HandleScope scope(env->GetIsolate());
800 :
801 0 : CompileRun(cpu_profiler_test_source2);
802 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
803 :
804 : int32_t duration_ms = 100;
805 : v8::Local<v8::Value> args[] = {
806 0 : v8::Integer::New(env->GetIsolate(), duration_ms)};
807 0 : ProfilerHelper helper(env.local());
808 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
809 :
810 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
811 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
812 : const v8::CpuProfileNode* delay_node =
813 0 : GetChild(env.local(), start_node, "delay");
814 0 : GetChild(env.local(), delay_node, "loop");
815 :
816 0 : profile->Delete();
817 0 : }
818 :
819 : static const char* native_accessor_test_source = "function start(count) {\n"
820 : " for (var i = 0; i < count; i++) {\n"
821 : " var o = instance.foo;\n"
822 : " instance.foo = o + 1;\n"
823 : " }\n"
824 : "}\n";
825 :
826 : class TestApiCallbacks {
827 : public:
828 : explicit TestApiCallbacks(int min_duration_ms)
829 : : min_duration_ms_(min_duration_ms),
830 20 : is_warming_up_(false) {}
831 :
832 520 : static void Getter(v8::Local<v8::String> name,
833 : const v8::PropertyCallbackInfo<v8::Value>& info) {
834 : TestApiCallbacks* data = FromInfo(info);
835 520 : data->Wait();
836 520 : }
837 :
838 520 : static void Setter(v8::Local<v8::String> name,
839 : v8::Local<v8::Value> value,
840 : const v8::PropertyCallbackInfo<void>& info) {
841 : TestApiCallbacks* data = FromInfo(info);
842 520 : data->Wait();
843 520 : }
844 :
845 520 : static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
846 : TestApiCallbacks* data = FromInfo(info);
847 520 : data->Wait();
848 520 : }
849 :
850 20 : void set_warming_up(bool value) { is_warming_up_ = value; }
851 :
852 : private:
853 1560 : void Wait() {
854 1560 : if (is_warming_up_) return;
855 1515 : v8::Platform* platform = v8::internal::V8::GetCurrentPlatform();
856 1515 : double start = platform->CurrentClockTimeMillis();
857 : double duration = 0;
858 35807 : while (duration < min_duration_ms_) {
859 17146 : v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(1));
860 17146 : duration = platform->CurrentClockTimeMillis() - start;
861 : }
862 : }
863 :
864 : template <typename T>
865 : static TestApiCallbacks* FromInfo(const T& info) {
866 1560 : void* data = v8::External::Cast(*info.Data())->Value();
867 : return reinterpret_cast<TestApiCallbacks*>(data);
868 : }
869 :
870 : int min_duration_ms_;
871 : bool is_warming_up_;
872 : };
873 :
874 :
875 : // Test that native accessors are properly reported in the CPU profile.
876 : // This test checks the case when the long-running accessors are called
877 : // only once and the optimizer doesn't have chance to change the invocation
878 : // code.
879 26644 : TEST(NativeAccessorUninitializedIC) {
880 5 : LocalContext env;
881 5 : v8::Isolate* isolate = env->GetIsolate();
882 10 : v8::HandleScope scope(isolate);
883 :
884 : v8::Local<v8::FunctionTemplate> func_template =
885 5 : v8::FunctionTemplate::New(isolate);
886 : v8::Local<v8::ObjectTemplate> instance_template =
887 5 : func_template->InstanceTemplate();
888 :
889 : TestApiCallbacks accessors(100);
890 5 : v8::Local<v8::External> data = v8::External::New(isolate, &accessors);
891 10 : instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter,
892 5 : &TestApiCallbacks::Setter, data);
893 : v8::Local<v8::Function> func =
894 5 : func_template->GetFunction(env.local()).ToLocalChecked();
895 : v8::Local<v8::Object> instance =
896 : func->NewInstance(env.local()).ToLocalChecked();
897 20 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
898 :
899 5 : CompileRun(native_accessor_test_source);
900 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
901 :
902 5 : ProfilerHelper helper(env.local());
903 : int32_t repeat_count = 1;
904 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
905 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 100);
906 :
907 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
908 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
909 5 : GetChild(env.local(), start_node, "get foo");
910 5 : GetChild(env.local(), start_node, "set foo");
911 :
912 5 : profile->Delete();
913 5 : }
914 :
915 :
916 : // Test that native accessors are properly reported in the CPU profile.
917 : // This test makes sure that the accessors are called enough times to become
918 : // hot and to trigger optimizations.
919 26644 : TEST(NativeAccessorMonomorphicIC) {
920 5 : LocalContext env;
921 5 : v8::Isolate* isolate = env->GetIsolate();
922 10 : v8::HandleScope scope(isolate);
923 :
924 : v8::Local<v8::FunctionTemplate> func_template =
925 5 : v8::FunctionTemplate::New(isolate);
926 : v8::Local<v8::ObjectTemplate> instance_template =
927 5 : func_template->InstanceTemplate();
928 :
929 : TestApiCallbacks accessors(1);
930 : v8::Local<v8::External> data =
931 5 : v8::External::New(isolate, &accessors);
932 10 : instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter,
933 5 : &TestApiCallbacks::Setter, data);
934 : v8::Local<v8::Function> func =
935 5 : func_template->GetFunction(env.local()).ToLocalChecked();
936 : v8::Local<v8::Object> instance =
937 : func->NewInstance(env.local()).ToLocalChecked();
938 20 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
939 :
940 5 : CompileRun(native_accessor_test_source);
941 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
942 :
943 : {
944 : // Make sure accessors ICs are in monomorphic state before starting
945 : // profiling.
946 : accessors.set_warming_up(true);
947 : int32_t warm_up_iterations = 3;
948 : v8::Local<v8::Value> args[] = {
949 5 : v8::Integer::New(isolate, warm_up_iterations)};
950 15 : function->Call(env.local(), env->Global(), arraysize(args), args)
951 : .ToLocalChecked();
952 : accessors.set_warming_up(false);
953 : }
954 :
955 : int32_t repeat_count = 100;
956 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
957 5 : ProfilerHelper helper(env.local());
958 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 100);
959 :
960 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
961 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
962 5 : GetChild(env.local(), start_node, "get foo");
963 5 : GetChild(env.local(), start_node, "set foo");
964 :
965 5 : profile->Delete();
966 5 : }
967 :
968 :
969 : static const char* native_method_test_source = "function start(count) {\n"
970 : " for (var i = 0; i < count; i++) {\n"
971 : " instance.fooMethod();\n"
972 : " }\n"
973 : "}\n";
974 :
975 :
976 26644 : TEST(NativeMethodUninitializedIC) {
977 5 : LocalContext env;
978 5 : v8::Isolate* isolate = env->GetIsolate();
979 10 : v8::HandleScope scope(isolate);
980 :
981 : TestApiCallbacks callbacks(100);
982 5 : v8::Local<v8::External> data = v8::External::New(isolate, &callbacks);
983 :
984 : v8::Local<v8::FunctionTemplate> func_template =
985 5 : v8::FunctionTemplate::New(isolate);
986 5 : func_template->SetClassName(v8_str("Test_InstanceConstructor"));
987 : v8::Local<v8::ObjectTemplate> proto_template =
988 5 : func_template->PrototypeTemplate();
989 : v8::Local<v8::Signature> signature =
990 5 : v8::Signature::New(isolate, func_template);
991 15 : proto_template->Set(
992 : v8_str("fooMethod"),
993 : v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
994 5 : signature, 0));
995 :
996 : v8::Local<v8::Function> func =
997 5 : func_template->GetFunction(env.local()).ToLocalChecked();
998 : v8::Local<v8::Object> instance =
999 : func->NewInstance(env.local()).ToLocalChecked();
1000 20 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
1001 :
1002 5 : CompileRun(native_method_test_source);
1003 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
1004 :
1005 5 : ProfilerHelper helper(env.local());
1006 : int32_t repeat_count = 1;
1007 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
1008 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 100);
1009 :
1010 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1011 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
1012 5 : GetChild(env.local(), start_node, "fooMethod");
1013 :
1014 5 : profile->Delete();
1015 5 : }
1016 :
1017 :
1018 26644 : TEST(NativeMethodMonomorphicIC) {
1019 5 : LocalContext env;
1020 5 : v8::Isolate* isolate = env->GetIsolate();
1021 10 : v8::HandleScope scope(isolate);
1022 :
1023 : TestApiCallbacks callbacks(1);
1024 : v8::Local<v8::External> data =
1025 5 : v8::External::New(isolate, &callbacks);
1026 :
1027 : v8::Local<v8::FunctionTemplate> func_template =
1028 5 : v8::FunctionTemplate::New(isolate);
1029 5 : func_template->SetClassName(v8_str("Test_InstanceCostructor"));
1030 : v8::Local<v8::ObjectTemplate> proto_template =
1031 5 : func_template->PrototypeTemplate();
1032 : v8::Local<v8::Signature> signature =
1033 5 : v8::Signature::New(isolate, func_template);
1034 15 : proto_template->Set(
1035 : v8_str("fooMethod"),
1036 : v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
1037 5 : signature, 0));
1038 :
1039 : v8::Local<v8::Function> func =
1040 5 : func_template->GetFunction(env.local()).ToLocalChecked();
1041 : v8::Local<v8::Object> instance =
1042 : func->NewInstance(env.local()).ToLocalChecked();
1043 20 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
1044 :
1045 5 : CompileRun(native_method_test_source);
1046 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
1047 : {
1048 : // Make sure method ICs are in monomorphic state before starting
1049 : // profiling.
1050 : callbacks.set_warming_up(true);
1051 : int32_t warm_up_iterations = 3;
1052 : v8::Local<v8::Value> args[] = {
1053 5 : v8::Integer::New(isolate, warm_up_iterations)};
1054 15 : function->Call(env.local(), env->Global(), arraysize(args), args)
1055 : .ToLocalChecked();
1056 : callbacks.set_warming_up(false);
1057 : }
1058 :
1059 5 : ProfilerHelper helper(env.local());
1060 : int32_t repeat_count = 100;
1061 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
1062 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 200);
1063 :
1064 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1065 5 : GetChild(env.local(), root, "start");
1066 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
1067 5 : GetChild(env.local(), start_node, "fooMethod");
1068 :
1069 5 : profile->Delete();
1070 5 : }
1071 :
1072 :
1073 : static const char* bound_function_test_source =
1074 : "function foo() {\n"
1075 : " startProfiling('my_profile');\n"
1076 : "}\n"
1077 : "function start() {\n"
1078 : " var callback = foo.bind(this);\n"
1079 : " callback();\n"
1080 : "}";
1081 :
1082 :
1083 26644 : TEST(BoundFunctionCall) {
1084 10 : v8::HandleScope scope(CcTest::isolate());
1085 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1086 : v8::Context::Scope context_scope(env);
1087 :
1088 5 : CompileRun(bound_function_test_source);
1089 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1090 :
1091 5 : ProfilerHelper helper(env);
1092 5 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0);
1093 :
1094 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1095 :
1096 5 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1097 5 : GetChild(env, start_node, "foo");
1098 :
1099 5 : profile->Delete();
1100 5 : }
1101 :
1102 : // This tests checks distribution of the samples through the source lines.
1103 9 : static void TickLines(bool optimize) {
1104 : #ifndef V8_LITE_MODE
1105 9 : FLAG_opt = optimize;
1106 : #endif // V8_LITE_MODE
1107 9 : CcTest::InitializeVM();
1108 9 : LocalContext env;
1109 9 : i::FLAG_allow_natives_syntax = true;
1110 : i::Isolate* isolate = CcTest::i_isolate();
1111 : i::Factory* factory = isolate->factory();
1112 : i::HandleScope scope(isolate);
1113 :
1114 : i::EmbeddedVector<char, 512> script;
1115 : i::EmbeddedVector<char, 64> optimize_call;
1116 :
1117 : const char* func_name = "func";
1118 9 : if (optimize) {
1119 : i::SNPrintF(optimize_call, "%%OptimizeFunctionOnNextCall(%s);\n",
1120 4 : func_name);
1121 : } else {
1122 5 : optimize_call[0] = '\0';
1123 : }
1124 : i::SNPrintF(script,
1125 : "function %s() {\n"
1126 : " var n = 0;\n"
1127 : " var m = 100*100;\n"
1128 : " while (m > 1) {\n"
1129 : " m--;\n"
1130 : " n += m * m * m;\n"
1131 : " }\n"
1132 : "}\n"
1133 : "%s();\n"
1134 : "%s"
1135 : "%s();\n",
1136 9 : func_name, func_name, optimize_call.start(), func_name);
1137 :
1138 : CompileRun(script.start());
1139 :
1140 : i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(
1141 18 : v8::Utils::OpenHandle(*GetFunction(env.local(), func_name)));
1142 9 : CHECK(!func->shared().is_null());
1143 18 : CHECK(!func->shared()->abstract_code().is_null());
1144 13 : CHECK(!optimize || func->IsOptimized() ||
1145 : !CcTest::i_isolate()->use_optimizer());
1146 9 : i::AbstractCode code = func->abstract_code();
1147 9 : CHECK(!code.is_null());
1148 9 : i::Address code_address = code->raw_instruction_start();
1149 9 : CHECK_NE(code_address, kNullAddress);
1150 :
1151 9 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
1152 9 : ProfileGenerator* generator = new ProfileGenerator(profiles);
1153 : ProfilerEventsProcessor* processor = new SamplingEventsProcessor(
1154 : CcTest::i_isolate(), generator,
1155 9 : v8::base::TimeDelta::FromMicroseconds(100), true);
1156 18 : CpuProfiler profiler(isolate, profiles, generator, processor);
1157 9 : profiles->StartProfiling("", false);
1158 : // TODO(delphick): Stop using the CpuProfiler internals here: This forces
1159 : // LogCompiledFunctions so that source positions are collected everywhere.
1160 : // This would normally happen automatically with CpuProfiler::StartProfiling
1161 : // but doesn't because it's constructed with a generator and a processor.
1162 9 : isolate->logger()->LogCompiledFunctions();
1163 9 : processor->Start();
1164 18 : ProfilerListener profiler_listener(isolate, processor);
1165 :
1166 : // Enqueue code creation events.
1167 9 : i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name);
1168 : int line = 1;
1169 : int column = 1;
1170 18 : profiler_listener.CodeCreateEvent(i::Logger::FUNCTION_TAG, code,
1171 9 : func->shared(), *str, line, column);
1172 :
1173 : // Enqueue a tick event to enable code events processing.
1174 9 : EnqueueTickSampleEvent(processor, code_address);
1175 :
1176 9 : processor->StopSynchronously();
1177 :
1178 9 : CpuProfile* profile = profiles->StopProfiling("");
1179 9 : CHECK(profile);
1180 :
1181 : // Check the state of profile generator.
1182 9 : CodeEntry* func_entry = generator->code_map()->FindEntry(code_address);
1183 9 : CHECK(func_entry);
1184 9 : CHECK_EQ(0, strcmp(func_name, func_entry->name()));
1185 : const i::SourcePositionTable* line_info = func_entry->line_info();
1186 9 : CHECK(line_info);
1187 9 : CHECK_NE(v8::CpuProfileNode::kNoLineNumberInfo,
1188 : line_info->GetSourceLineNumber(100));
1189 :
1190 : // Check the hit source lines using V8 Public APIs.
1191 : const i::ProfileTree* tree = profile->top_down();
1192 : ProfileNode* root = tree->root();
1193 9 : CHECK(root);
1194 9 : ProfileNode* func_node = root->FindChild(func_entry);
1195 9 : CHECK(func_node);
1196 :
1197 : // Add 10 faked ticks to source line #5.
1198 : int hit_line = 5;
1199 : int hit_count = 10;
1200 99 : for (int i = 0; i < hit_count; i++) func_node->IncrementLineTicks(hit_line);
1201 :
1202 : unsigned int line_count = func_node->GetHitLineCount();
1203 9 : CHECK_EQ(2u, line_count); // Expect two hit source lines - #1 and #5.
1204 9 : ScopedVector<v8::CpuProfileNode::LineTick> entries(line_count);
1205 9 : CHECK(func_node->GetLineTicks(&entries[0], line_count));
1206 : int value = 0;
1207 9 : for (int i = 0; i < entries.length(); i++)
1208 18 : if (entries[i].line == hit_line) {
1209 9 : value = entries[i].hit_count;
1210 9 : break;
1211 : }
1212 9 : CHECK_EQ(hit_count, value);
1213 9 : }
1214 :
1215 26644 : TEST(TickLinesBaseline) { TickLines(false); }
1216 :
1217 26643 : TEST(TickLinesOptimized) { TickLines(true); }
1218 :
1219 : static const char* call_function_test_source =
1220 : "%NeverOptimizeFunction(bar);\n"
1221 : "%NeverOptimizeFunction(start);\n"
1222 : "function bar(n) {\n"
1223 : " var s = 0;\n"
1224 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1225 : " return s;\n"
1226 : "}\n"
1227 : "function start(duration) {\n"
1228 : " var start = Date.now();\n"
1229 : " do {\n"
1230 : " for (var i = 0; i < 100; ++i)\n"
1231 : " bar.call(this, 1000);\n"
1232 : " } while (Date.now() - start < duration);\n"
1233 : "}";
1234 :
1235 : // Test that if we sampled thread when it was inside FunctionCall buitin then
1236 : // its caller frame will be '(unresolved function)' as we have no reliable way
1237 : // to resolve it.
1238 : //
1239 : // [Top down]:
1240 : // 96 0 (root) [-1] #1
1241 : // 1 1 (garbage collector) [-1] #4
1242 : // 5 0 (unresolved function) [-1] #5
1243 : // 5 5 call [-1] #6
1244 : // 71 70 start [-1] #3
1245 : // 1 1 bar [-1] #7
1246 : // 19 19 (program) [-1] #2
1247 26644 : TEST(FunctionCallSample) {
1248 5 : i::FLAG_allow_natives_syntax = true;
1249 5 : LocalContext env;
1250 10 : v8::HandleScope scope(env->GetIsolate());
1251 :
1252 : // Collect garbage that might have be generated while installing
1253 : // extensions.
1254 5 : CcTest::CollectAllGarbage();
1255 :
1256 5 : CompileRun(call_function_test_source);
1257 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
1258 :
1259 5 : ProfilerHelper helper(env.local());
1260 : int32_t duration_ms = 100;
1261 : v8::Local<v8::Value> args[] = {
1262 5 : v8::Integer::New(env->GetIsolate(), duration_ms)};
1263 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
1264 :
1265 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1266 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
1267 5 : GetChild(env.local(), start_node, "bar");
1268 :
1269 : const v8::CpuProfileNode* unresolved_node =
1270 10 : FindChild(env.local(), root, i::CodeEntry::kUnresolvedFunctionName);
1271 5 : CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "call"));
1272 :
1273 5 : profile->Delete();
1274 5 : }
1275 :
1276 : static const char* function_apply_test_source =
1277 : "%NeverOptimizeFunction(bar);\n"
1278 : "%NeverOptimizeFunction(test);\n"
1279 : "%NeverOptimizeFunction(start);\n"
1280 : "function bar(n) {\n"
1281 : " var s = 0;\n"
1282 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1283 : " return s;\n"
1284 : "}\n"
1285 : "function test() {\n"
1286 : " bar.apply(this, [1000]);\n"
1287 : "}\n"
1288 : "function start(duration) {\n"
1289 : " var start = Date.now();\n"
1290 : " do {\n"
1291 : " for (var i = 0; i < 100; ++i) test();\n"
1292 : " } while (Date.now() - start < duration);\n"
1293 : "}";
1294 :
1295 : // [Top down]:
1296 : // 94 0 (root) [-1] #0 1
1297 : // 2 2 (garbage collector) [-1] #0 7
1298 : // 82 49 start [-1] #16 3
1299 : // 1 0 (unresolved function) [-1] #0 8
1300 : // 1 1 apply [-1] #0 9
1301 : // 32 21 test [-1] #16 4
1302 : // 2 2 bar [-1] #16 6
1303 : // 10 10 (program) [-1] #0 2
1304 26644 : TEST(FunctionApplySample) {
1305 5 : i::FLAG_allow_natives_syntax = true;
1306 5 : LocalContext env;
1307 10 : v8::HandleScope scope(env->GetIsolate());
1308 :
1309 5 : CompileRun(function_apply_test_source);
1310 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
1311 :
1312 5 : ProfilerHelper helper(env.local());
1313 : int32_t duration_ms = 100;
1314 : v8::Local<v8::Value> args[] = {
1315 5 : v8::Integer::New(env->GetIsolate(), duration_ms)};
1316 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
1317 :
1318 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1319 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
1320 : const v8::CpuProfileNode* test_node =
1321 5 : GetChild(env.local(), start_node, "test");
1322 5 : GetChild(env.local(), test_node, "bar");
1323 :
1324 : const v8::CpuProfileNode* unresolved_node =
1325 10 : FindChild(env.local(), start_node, CodeEntry::kUnresolvedFunctionName);
1326 5 : CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "apply"));
1327 :
1328 5 : profile->Delete();
1329 5 : }
1330 :
1331 : static const char* cpu_profiler_deep_stack_test_source =
1332 : "function foo(n) {\n"
1333 : " if (n)\n"
1334 : " foo(n - 1);\n"
1335 : " else\n"
1336 : " collectSample();\n"
1337 : "}\n"
1338 : "function start() {\n"
1339 : " startProfiling('my_profile');\n"
1340 : " foo(250);\n"
1341 : "}\n";
1342 :
1343 : // Check a deep stack
1344 : //
1345 : // [Top down]:
1346 : // 0 (root) 0 #1
1347 : // 2 (program) 0 #2
1348 : // 0 start 21 #3 no reason
1349 : // 0 foo 21 #4 no reason
1350 : // 0 foo 21 #5 no reason
1351 : // ....
1352 : // 0 foo 21 #254 no reason
1353 26644 : TEST(CpuProfileDeepStack) {
1354 10 : v8::HandleScope scope(CcTest::isolate());
1355 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1356 : v8::Context::Scope context_scope(env);
1357 5 : ProfilerHelper helper(env);
1358 :
1359 5 : CompileRun(cpu_profiler_deep_stack_test_source);
1360 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1361 :
1362 5 : v8::Local<v8::String> profile_name = v8_str("my_profile");
1363 15 : function->Call(env, env->Global(), 0, nullptr).ToLocalChecked();
1364 5 : v8::CpuProfile* profile = helper.profiler()->StopProfiling(profile_name);
1365 5 : CHECK(profile);
1366 : // Dump collected profile to have a better diagnostic in case of failure.
1367 5 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1368 :
1369 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1370 5 : const v8::CpuProfileNode* node = GetChild(env, root, "start");
1371 2515 : for (int i = 0; i <= 250; ++i) {
1372 1255 : node = GetChild(env, node, "foo");
1373 : }
1374 5 : CHECK(!FindChild(env, node, "foo"));
1375 :
1376 5 : profile->Delete();
1377 5 : }
1378 :
1379 : static const char* js_native_js_test_source =
1380 : "%NeverOptimizeFunction(foo);\n"
1381 : "%NeverOptimizeFunction(bar);\n"
1382 : "%NeverOptimizeFunction(start);\n"
1383 : "function foo(n) {\n"
1384 : " var s = 0;\n"
1385 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1386 : " return s;\n"
1387 : "}\n"
1388 : "function bar() {\n"
1389 : " foo(1000);\n"
1390 : "}\n"
1391 : "function start() {\n"
1392 : " CallJsFunction(bar);\n"
1393 : "}";
1394 :
1395 0 : static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1396 : v8::Local<v8::Function> function = info[0].As<v8::Function>();
1397 0 : v8::Local<v8::Value> argv[] = {info[1]};
1398 0 : function->Call(info.GetIsolate()->GetCurrentContext(), info.This(),
1399 0 : arraysize(argv), argv)
1400 : .ToLocalChecked();
1401 0 : }
1402 :
1403 : // [Top down]:
1404 : // 58 0 (root) #0 1
1405 : // 2 2 (program) #0 2
1406 : // 56 1 start #16 3
1407 : // 55 0 CallJsFunction #0 4
1408 : // 55 1 bar #16 5
1409 : // 54 54 foo #16 6
1410 26639 : TEST(JsNativeJsSample) {
1411 0 : i::FLAG_allow_natives_syntax = true;
1412 0 : v8::HandleScope scope(CcTest::isolate());
1413 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1414 : v8::Context::Scope context_scope(env);
1415 :
1416 : v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1417 0 : env->GetIsolate(), CallJsFunction);
1418 : v8::Local<v8::Function> func =
1419 0 : func_template->GetFunction(env).ToLocalChecked();
1420 0 : func->SetName(v8_str("CallJsFunction"));
1421 0 : env->Global()->Set(env, v8_str("CallJsFunction"), func).FromJust();
1422 :
1423 0 : CompileRun(js_native_js_test_source);
1424 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1425 :
1426 0 : ProfilerHelper helper(env);
1427 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 1000);
1428 :
1429 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1430 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1431 : const v8::CpuProfileNode* native_node =
1432 0 : GetChild(env, start_node, "CallJsFunction");
1433 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node, "bar");
1434 0 : GetChild(env, bar_node, "foo");
1435 :
1436 0 : profile->Delete();
1437 0 : }
1438 :
1439 : static const char* js_native_js_runtime_js_test_source =
1440 : "%NeverOptimizeFunction(foo);\n"
1441 : "%NeverOptimizeFunction(bar);\n"
1442 : "%NeverOptimizeFunction(start);\n"
1443 : "function foo(n) {\n"
1444 : " var s = 0;\n"
1445 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1446 : " return s;\n"
1447 : "}\n"
1448 : "var bound = foo.bind(this);\n"
1449 : "function bar() {\n"
1450 : " bound(1000);\n"
1451 : "}\n"
1452 : "function start() {\n"
1453 : " CallJsFunction(bar);\n"
1454 : "}";
1455 :
1456 : // [Top down]:
1457 : // 57 0 (root) #0 1
1458 : // 55 1 start #16 3
1459 : // 54 0 CallJsFunction #0 4
1460 : // 54 3 bar #16 5
1461 : // 51 51 foo #16 6
1462 : // 2 2 (program) #0 2
1463 26639 : TEST(JsNativeJsRuntimeJsSample) {
1464 0 : i::FLAG_allow_natives_syntax = true;
1465 0 : v8::HandleScope scope(CcTest::isolate());
1466 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1467 : v8::Context::Scope context_scope(env);
1468 :
1469 : v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1470 0 : env->GetIsolate(), CallJsFunction);
1471 : v8::Local<v8::Function> func =
1472 0 : func_template->GetFunction(env).ToLocalChecked();
1473 0 : func->SetName(v8_str("CallJsFunction"));
1474 0 : env->Global()->Set(env, v8_str("CallJsFunction"), func).FromJust();
1475 :
1476 0 : CompileRun(js_native_js_runtime_js_test_source);
1477 0 : ProfilerHelper helper(env);
1478 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1479 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 1000);
1480 :
1481 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1482 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1483 : const v8::CpuProfileNode* native_node =
1484 0 : GetChild(env, start_node, "CallJsFunction");
1485 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node, "bar");
1486 0 : GetChild(env, bar_node, "foo");
1487 :
1488 0 : profile->Delete();
1489 0 : }
1490 :
1491 0 : static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1492 0 : v8::base::OS::Print("In CallJsFunction2\n");
1493 0 : CallJsFunction(info);
1494 0 : }
1495 :
1496 : static const char* js_native1_js_native2_js_test_source =
1497 : "%NeverOptimizeFunction(foo);\n"
1498 : "%NeverOptimizeFunction(bar);\n"
1499 : "%NeverOptimizeFunction(start);\n"
1500 : "function foo() {\n"
1501 : " var s = 0;\n"
1502 : " for (var i = 0; i < 1000; i++) s += i * i * i;\n"
1503 : " return s;\n"
1504 : "}\n"
1505 : "function bar() {\n"
1506 : " CallJsFunction2(foo);\n"
1507 : "}\n"
1508 : "function start() {\n"
1509 : " CallJsFunction1(bar);\n"
1510 : "}";
1511 :
1512 : // [Top down]:
1513 : // 57 0 (root) #0 1
1514 : // 55 1 start #16 3
1515 : // 54 0 CallJsFunction1 #0 4
1516 : // 54 0 bar #16 5
1517 : // 54 0 CallJsFunction2 #0 6
1518 : // 54 54 foo #16 7
1519 : // 2 2 (program) #0 2
1520 26639 : TEST(JsNative1JsNative2JsSample) {
1521 0 : i::FLAG_allow_natives_syntax = true;
1522 0 : v8::HandleScope scope(CcTest::isolate());
1523 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1524 : v8::Context::Scope context_scope(env);
1525 :
1526 : v8::Local<v8::Function> func1 =
1527 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallJsFunction)
1528 0 : ->GetFunction(env)
1529 : .ToLocalChecked();
1530 0 : func1->SetName(v8_str("CallJsFunction1"));
1531 0 : env->Global()->Set(env, v8_str("CallJsFunction1"), func1).FromJust();
1532 :
1533 : v8::Local<v8::Function> func2 =
1534 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallJsFunction2)
1535 0 : ->GetFunction(env)
1536 : .ToLocalChecked();
1537 0 : func2->SetName(v8_str("CallJsFunction2"));
1538 0 : env->Global()->Set(env, v8_str("CallJsFunction2"), func2).FromJust();
1539 :
1540 0 : CompileRun(js_native1_js_native2_js_test_source);
1541 :
1542 0 : ProfilerHelper helper(env);
1543 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1544 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 1000);
1545 :
1546 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1547 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1548 : const v8::CpuProfileNode* native_node1 =
1549 0 : GetChild(env, start_node, "CallJsFunction1");
1550 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node1, "bar");
1551 : const v8::CpuProfileNode* native_node2 =
1552 0 : GetChild(env, bar_node, "CallJsFunction2");
1553 0 : GetChild(env, native_node2, "foo");
1554 :
1555 0 : profile->Delete();
1556 0 : }
1557 :
1558 : static const char* js_force_collect_sample_source =
1559 : "function start() {\n"
1560 : " CallCollectSample();\n"
1561 : "}";
1562 :
1563 5 : static void CallCollectSample(const v8::FunctionCallbackInfo<v8::Value>& info) {
1564 5 : v8::CpuProfiler::CollectSample(info.GetIsolate());
1565 5 : }
1566 :
1567 26644 : TEST(CollectSampleAPI) {
1568 10 : v8::HandleScope scope(CcTest::isolate());
1569 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1570 : v8::Context::Scope context_scope(env);
1571 :
1572 : v8::Local<v8::FunctionTemplate> func_template =
1573 5 : v8::FunctionTemplate::New(env->GetIsolate(), CallCollectSample);
1574 : v8::Local<v8::Function> func =
1575 5 : func_template->GetFunction(env).ToLocalChecked();
1576 5 : func->SetName(v8_str("CallCollectSample"));
1577 20 : env->Global()->Set(env, v8_str("CallCollectSample"), func).FromJust();
1578 :
1579 5 : CompileRun(js_force_collect_sample_source);
1580 5 : ProfilerHelper helper(env);
1581 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1582 5 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 0);
1583 :
1584 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1585 5 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1586 5 : CHECK_LE(1, start_node->GetChildrenCount());
1587 5 : GetChild(env, start_node, "CallCollectSample");
1588 :
1589 5 : profile->Delete();
1590 5 : }
1591 :
1592 : static const char* js_native_js_runtime_multiple_test_source =
1593 : "%NeverOptimizeFunction(foo);\n"
1594 : "%NeverOptimizeFunction(bar);\n"
1595 : "%NeverOptimizeFunction(start);\n"
1596 : "function foo() {\n"
1597 : " return Math.sin(Math.random());\n"
1598 : "}\n"
1599 : "var bound = foo.bind(this);\n"
1600 : "function bar() {\n"
1601 : " return bound();\n"
1602 : "}\n"
1603 : "function start() {\n"
1604 : " startProfiling('my_profile');\n"
1605 : " var startTime = Date.now();\n"
1606 : " do {\n"
1607 : " CallJsFunction(bar);\n"
1608 : " } while (Date.now() - startTime < 200);\n"
1609 : "}";
1610 :
1611 : // The test check multiple entrances/exits between JS and native code.
1612 : //
1613 : // [Top down]:
1614 : // (root) #0 1
1615 : // start #16 3
1616 : // CallJsFunction #0 4
1617 : // bar #16 5
1618 : // foo #16 6
1619 : // (program) #0 2
1620 26639 : TEST(JsNativeJsRuntimeJsSampleMultiple) {
1621 0 : i::FLAG_allow_natives_syntax = true;
1622 0 : v8::HandleScope scope(CcTest::isolate());
1623 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1624 : v8::Context::Scope context_scope(env);
1625 :
1626 : v8::Local<v8::FunctionTemplate> func_template =
1627 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallJsFunction);
1628 : v8::Local<v8::Function> func =
1629 0 : func_template->GetFunction(env).ToLocalChecked();
1630 0 : func->SetName(v8_str("CallJsFunction"));
1631 0 : env->Global()->Set(env, v8_str("CallJsFunction"), func).FromJust();
1632 :
1633 0 : CompileRun(js_native_js_runtime_multiple_test_source);
1634 :
1635 0 : ProfilerHelper helper(env);
1636 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1637 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 500, 500);
1638 :
1639 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1640 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1641 : const v8::CpuProfileNode* native_node =
1642 0 : GetChild(env, start_node, "CallJsFunction");
1643 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node, "bar");
1644 0 : GetChild(env, bar_node, "foo");
1645 :
1646 0 : profile->Delete();
1647 0 : }
1648 :
1649 : static const char* inlining_test_source =
1650 : "%NeverOptimizeFunction(action);\n"
1651 : "%NeverOptimizeFunction(start);\n"
1652 : "level1();\n"
1653 : "%OptimizeFunctionOnNextCall(level1);\n"
1654 : "%OptimizeFunctionOnNextCall(level2);\n"
1655 : "%OptimizeFunctionOnNextCall(level3);\n"
1656 : "var finish = false;\n"
1657 : "function action(n) {\n"
1658 : " var s = 0;\n"
1659 : " for (var i = 0; i < n; ++i) s += i*i*i;\n"
1660 : " if (finish)\n"
1661 : " startProfiling('my_profile');\n"
1662 : " return s;\n"
1663 : "}\n"
1664 : "function level3() { return action(100); }\n"
1665 : "function level2() { return level3() * 2; }\n"
1666 : "function level1() { return level2(); }\n"
1667 : "function start() {\n"
1668 : " var n = 100;\n"
1669 : " while (--n)\n"
1670 : " level1();\n"
1671 : " finish = true;\n"
1672 : " level1();\n"
1673 : "}";
1674 :
1675 : // The test check multiple entrances/exits between JS and native code.
1676 : //
1677 : // [Top down]:
1678 : // (root) #0 1
1679 : // start #16 3
1680 : // level1 #0 4
1681 : // level2 #16 5
1682 : // level3 #16 6
1683 : // action #16 7
1684 : // (program) #0 2
1685 26644 : TEST(Inlining) {
1686 5 : i::FLAG_allow_natives_syntax = true;
1687 10 : v8::HandleScope scope(CcTest::isolate());
1688 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1689 : v8::Context::Scope context_scope(env);
1690 5 : ProfilerHelper helper(env);
1691 :
1692 5 : CompileRun(inlining_test_source);
1693 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1694 :
1695 5 : v8::Local<v8::String> profile_name = v8_str("my_profile");
1696 15 : function->Call(env, env->Global(), 0, nullptr).ToLocalChecked();
1697 5 : v8::CpuProfile* profile = helper.profiler()->StopProfiling(profile_name);
1698 5 : CHECK(profile);
1699 : // Dump collected profile to have a better diagnostic in case of failure.
1700 5 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1701 :
1702 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1703 5 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1704 5 : const v8::CpuProfileNode* level1_node = GetChild(env, start_node, "level1");
1705 5 : const v8::CpuProfileNode* level2_node = GetChild(env, level1_node, "level2");
1706 5 : const v8::CpuProfileNode* level3_node = GetChild(env, level2_node, "level3");
1707 5 : GetChild(env, level3_node, "action");
1708 :
1709 5 : profile->Delete();
1710 5 : }
1711 :
1712 : static const char* inlining_test_source2 = R"(
1713 : %NeverOptimizeFunction(action);
1714 : %NeverOptimizeFunction(start);
1715 : level1();
1716 : level1();
1717 : %OptimizeFunctionOnNextCall(level1);
1718 : %OptimizeFunctionOnNextCall(level2);
1719 : %OptimizeFunctionOnNextCall(level3);
1720 : %OptimizeFunctionOnNextCall(level4);
1721 : level1();
1722 : function action(n) {
1723 : var s = 0;
1724 : for (var i = 0; i < n; ++i) s += i*i*i;
1725 : return s;
1726 : }
1727 : function level4() {
1728 : action(100);
1729 : return action(100);
1730 : }
1731 : function level3() {
1732 : const a = level4();
1733 : const b = level4();
1734 : return a + b * 1.1;
1735 : }
1736 : function level2() {
1737 : return level3() * 2;
1738 : }
1739 : function level1() {
1740 : action(1);
1741 : action(200);
1742 : action(1);
1743 : return level2();
1744 : }
1745 : function start(n) {
1746 : while (--n)
1747 : level1();
1748 : };
1749 : )";
1750 :
1751 : // The simulator builds are extremely slow. We run them with fewer iterations.
1752 : #ifdef USE_SIMULATOR
1753 : const double load_factor = 0.01;
1754 : #else
1755 : const double load_factor = 1.0;
1756 : #endif
1757 :
1758 : // [Top down]:
1759 : // 0 (root):0 0 #1
1760 : // 13 start:34 6 #3
1761 : // bailed out due to 'Optimization is always disabled'
1762 : // 19 level1:36 6 #4
1763 : // 16 action:29 6 #14
1764 : // bailed out due to 'Optimization is always disabled'
1765 : // 2748 action:30 6 #10
1766 : // bailed out due to 'Optimization is always disabled'
1767 : // 18 action:31 6 #15
1768 : // bailed out due to 'Optimization is always disabled'
1769 : // 0 level2:32 6 #5
1770 : // 0 level3:26 6 #6
1771 : // 12 level4:22 6 #11
1772 : // 1315 action:17 6 #13
1773 : // bailed out due to 'Optimization is always disabled'
1774 : // 1324 action:18 6 #12
1775 : // bailed out due to 'Optimization is always disabled'
1776 : // 16 level4:21 6 #7
1777 : // 1268 action:17 6 #9
1778 : // bailed out due to 'Optimization is always disabled'
1779 : // 1322 action:18 6 #8
1780 : // bailed out due to 'Optimization is always disabled'
1781 : // 2 (program):0 0 #2
1782 26644 : TEST(Inlining2) {
1783 5 : FLAG_allow_natives_syntax = true;
1784 10 : v8::HandleScope scope(CcTest::isolate());
1785 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1786 : v8::Context::Scope context_scope(env);
1787 :
1788 5 : CompileRun(inlining_test_source2);
1789 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1790 :
1791 5 : v8::CpuProfiler* profiler = v8::CpuProfiler::New(CcTest::isolate());
1792 5 : v8::Local<v8::String> profile_name = v8_str("inlining");
1793 : profiler->StartProfiling(profile_name,
1794 5 : v8::CpuProfilingMode::kCallerLineNumbers);
1795 :
1796 : v8::Local<v8::Value> args[] = {
1797 5 : v8::Integer::New(env->GetIsolate(), 50000 * load_factor)};
1798 15 : function->Call(env, env->Global(), arraysize(args), args).ToLocalChecked();
1799 5 : v8::CpuProfile* profile = profiler->StopProfiling(profile_name);
1800 5 : CHECK(profile);
1801 :
1802 : // Dump collected profile to have a better diagnostic in case of failure.
1803 5 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1804 :
1805 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1806 5 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1807 :
1808 : NameLinePair l421_a17[] = {{"level1", 36},
1809 : {"level2", 32},
1810 : {"level3", 26},
1811 : {"level4", 21},
1812 5 : {"action", 17}};
1813 5 : CheckBranch(start_node, l421_a17, arraysize(l421_a17));
1814 : NameLinePair l422_a17[] = {{"level1", 36},
1815 : {"level2", 32},
1816 : {"level3", 26},
1817 : {"level4", 22},
1818 5 : {"action", 17}};
1819 5 : CheckBranch(start_node, l422_a17, arraysize(l422_a17));
1820 :
1821 : NameLinePair l421_a18[] = {{"level1", 36},
1822 : {"level2", 32},
1823 : {"level3", 26},
1824 : {"level4", 21},
1825 5 : {"action", 18}};
1826 5 : CheckBranch(start_node, l421_a18, arraysize(l421_a18));
1827 : NameLinePair l422_a18[] = {{"level1", 36},
1828 : {"level2", 32},
1829 : {"level3", 26},
1830 : {"level4", 22},
1831 5 : {"action", 18}};
1832 5 : CheckBranch(start_node, l422_a18, arraysize(l422_a18));
1833 :
1834 5 : NameLinePair action_direct[] = {{"level1", 36}, {"action", 30}};
1835 5 : CheckBranch(start_node, action_direct, arraysize(action_direct));
1836 :
1837 5 : profile->Delete();
1838 5 : profiler->Dispose();
1839 5 : }
1840 :
1841 : // [Top down]:
1842 : // 0 (root) #0 1
1843 : // 2 (program) #0 2
1844 : // 3 (idle) #0 3
1845 26644 : TEST(IdleTime) {
1846 5 : LocalContext env;
1847 10 : v8::HandleScope scope(env->GetIsolate());
1848 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
1849 :
1850 5 : v8::Local<v8::String> profile_name = v8_str("my_profile");
1851 5 : cpu_profiler->StartProfiling(profile_name);
1852 :
1853 : i::Isolate* isolate = CcTest::i_isolate();
1854 : i::ProfilerEventsProcessor* processor =
1855 : reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->processor();
1856 :
1857 5 : processor->AddCurrentStack(true);
1858 5 : isolate->SetIdle(true);
1859 35 : for (int i = 0; i < 3; i++) {
1860 15 : processor->AddCurrentStack(true);
1861 : }
1862 5 : isolate->SetIdle(false);
1863 5 : processor->AddCurrentStack(true);
1864 :
1865 5 : v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1866 5 : CHECK(profile);
1867 : // Dump collected profile to have a better diagnostic in case of failure.
1868 5 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1869 :
1870 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1871 : const v8::CpuProfileNode* program_node =
1872 10 : GetChild(env.local(), root, CodeEntry::kProgramEntryName);
1873 5 : CHECK_EQ(0, program_node->GetChildrenCount());
1874 5 : CHECK_GE(program_node->GetHitCount(), 2u);
1875 :
1876 : const v8::CpuProfileNode* idle_node =
1877 10 : GetChild(env.local(), root, CodeEntry::kIdleEntryName);
1878 5 : CHECK_EQ(0, idle_node->GetChildrenCount());
1879 5 : CHECK_GE(idle_node->GetHitCount(), 3u);
1880 :
1881 5 : profile->Delete();
1882 5 : cpu_profiler->Dispose();
1883 5 : }
1884 :
1885 27 : static void CheckFunctionDetails(v8::Isolate* isolate,
1886 : const v8::CpuProfileNode* node,
1887 : const char* name, const char* script_name,
1888 : bool is_shared_cross_origin, int script_id,
1889 : int line, int column,
1890 : const v8::CpuProfileNode* parent) {
1891 27 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
1892 108 : CHECK(v8_str(name)->Equals(context, node->GetFunctionName()).FromJust());
1893 27 : CHECK_EQ(0, strcmp(name, node->GetFunctionNameStr()));
1894 108 : CHECK(v8_str(script_name)
1895 : ->Equals(context, node->GetScriptResourceName())
1896 : .FromJust());
1897 27 : CHECK_EQ(0, strcmp(script_name, node->GetScriptResourceNameStr()));
1898 27 : CHECK_EQ(script_id, node->GetScriptId());
1899 27 : CHECK_EQ(line, node->GetLineNumber());
1900 27 : CHECK_EQ(column, node->GetColumnNumber());
1901 27 : CHECK_EQ(parent, node->GetParent());
1902 27 : CHECK_EQ(v8::CpuProfileNode::kScript, node->GetSourceType());
1903 27 : }
1904 :
1905 26644 : TEST(FunctionDetails) {
1906 5 : i::FLAG_allow_natives_syntax = true;
1907 10 : v8::HandleScope scope(CcTest::isolate());
1908 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1909 : v8::Context::Scope context_scope(env);
1910 5 : ProfilerHelper helper(env);
1911 :
1912 : v8::Local<v8::Script> script_a = CompileWithOrigin(
1913 : "%NeverOptimizeFunction(foo);\n"
1914 : "%NeverOptimizeFunction(bar);\n"
1915 : " function foo\n() { bar(); }\n"
1916 : " function bar() { startProfiling(); }\n",
1917 5 : "script_a", false);
1918 5 : script_a->Run(env).ToLocalChecked();
1919 : v8::Local<v8::Script> script_b = CompileWithOrigin(
1920 : "%NeverOptimizeFunction(baz);"
1921 : "\n\n function baz() { foo(); }\n"
1922 : "\n\nbaz();\n"
1923 : "stopProfiling();\n",
1924 5 : "script_b", true);
1925 5 : script_b->Run(env).ToLocalChecked();
1926 5 : const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1927 5 : const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1928 : reinterpret_cast<ProfileNode*>(
1929 5 : const_cast<v8::CpuProfileNode*>(current))->Print(0);
1930 : // The tree should look like this:
1931 : // 0 (root) 0 #1
1932 : // 0 "" 19 #2 no reason script_b:1
1933 : // 0 baz 19 #3 TryCatchStatement script_b:3
1934 : // 0 foo 18 #4 TryCatchStatement script_a:2
1935 : // 1 bar 18 #5 no reason script_a:3
1936 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1937 5 : CHECK_EQ(root->GetParent(), nullptr);
1938 5 : const v8::CpuProfileNode* script = GetChild(env, root, "");
1939 10 : CheckFunctionDetails(env->GetIsolate(), script, "", "script_b", true,
1940 10 : script_b->GetUnboundScript()->GetId(), 1, 1, root);
1941 5 : const v8::CpuProfileNode* baz = GetChild(env, script, "baz");
1942 10 : CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b", true,
1943 10 : script_b->GetUnboundScript()->GetId(), 3, 16, script);
1944 5 : const v8::CpuProfileNode* foo = GetChild(env, baz, "foo");
1945 10 : CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a", false,
1946 10 : script_a->GetUnboundScript()->GetId(), 4, 1, baz);
1947 5 : const v8::CpuProfileNode* bar = GetChild(env, foo, "bar");
1948 10 : CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a", false,
1949 10 : script_a->GetUnboundScript()->GetId(), 5, 14, foo);
1950 5 : }
1951 :
1952 26644 : TEST(FunctionDetailsInlining) {
1953 8 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
1954 3 : i::FLAG_allow_natives_syntax = true;
1955 5 : v8::HandleScope scope(CcTest::isolate());
1956 6 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1957 : v8::Context::Scope context_scope(env);
1958 3 : ProfilerHelper helper(env);
1959 :
1960 : // alpha is in a_script, beta in b_script. beta is
1961 : // inlined in alpha, but it should be attributed to b_script.
1962 :
1963 : v8::Local<v8::Script> script_b = CompileWithOrigin(
1964 : "function beta(k) {\n"
1965 : " let sum = 2;\n"
1966 : " for(let i = 0; i < k; i ++) {\n"
1967 : " sum += i;\n"
1968 : " sum = sum + 'a';\n"
1969 : " }\n"
1970 : " return sum;\n"
1971 : "}\n"
1972 : "\n",
1973 3 : "script_b", true);
1974 :
1975 : v8::Local<v8::Script> script_a = CompileWithOrigin(
1976 : "function alpha(p) {\n"
1977 : " let res = beta(p);\n"
1978 : " res = res + res;\n"
1979 : " return res;\n"
1980 : "}\n"
1981 : "let p = 2;\n"
1982 : "\n"
1983 : "\n"
1984 : "// Warm up before profiling or the inlining doesn't happen.\n"
1985 : "p = alpha(p);\n"
1986 : "p = alpha(p);\n"
1987 : "%OptimizeFunctionOnNextCall(alpha);\n"
1988 : "p = alpha(p);\n"
1989 : "\n"
1990 : "\n"
1991 : "startProfiling();\n"
1992 : "for(let i = 0; i < 10000; i++) {\n"
1993 : " p = alpha(p);\n"
1994 : "}\n"
1995 : "stopProfiling();\n"
1996 : "\n"
1997 : "\n",
1998 3 : "script_a", false);
1999 :
2000 3 : script_b->Run(env).ToLocalChecked();
2001 3 : script_a->Run(env).ToLocalChecked();
2002 :
2003 3 : const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
2004 3 : const v8::CpuProfileNode* current = profile->GetTopDownRoot();
2005 : reinterpret_cast<ProfileNode*>(const_cast<v8::CpuProfileNode*>(current))
2006 3 : ->Print(0);
2007 : // The tree should look like this:
2008 : // 0 (root) 0 #1
2009 : // 5 (program) 0 #6
2010 : // 2 14 #2 script_a:1
2011 : // ;;; deopted at script_id: 14 position: 299 with reason 'Insufficient
2012 : // type feedback for call'.
2013 : // 1 alpha 14 #4 script_a:1
2014 : // 9 beta 13 #5 script_b:0
2015 : // 0 startProfiling 0 #3
2016 :
2017 3 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
2018 3 : CHECK_EQ(root->GetParent(), nullptr);
2019 3 : const v8::CpuProfileNode* script = GetChild(env, root, "");
2020 6 : CheckFunctionDetails(env->GetIsolate(), script, "", "script_a", false,
2021 6 : script_a->GetUnboundScript()->GetId(), 1, 1, root);
2022 3 : const v8::CpuProfileNode* alpha = FindChild(env, script, "alpha");
2023 : // Return early if profiling didn't sample alpha.
2024 3 : if (!alpha) return;
2025 4 : CheckFunctionDetails(env->GetIsolate(), alpha, "alpha", "script_a", false,
2026 4 : script_a->GetUnboundScript()->GetId(), 1, 15, script);
2027 2 : const v8::CpuProfileNode* beta = FindChild(env, alpha, "beta");
2028 2 : if (!beta) return;
2029 4 : CheckFunctionDetails(env->GetIsolate(), beta, "beta", "script_b", true,
2030 4 : script_b->GetUnboundScript()->GetId(), 1, 14, alpha);
2031 : }
2032 :
2033 26644 : TEST(DontStopOnFinishedProfileDelete) {
2034 10 : v8::HandleScope scope(CcTest::isolate());
2035 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2036 : v8::Context::Scope context_scope(env);
2037 :
2038 5 : v8::CpuProfiler* profiler = v8::CpuProfiler::New(env->GetIsolate());
2039 : i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
2040 :
2041 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2042 5 : v8::Local<v8::String> outer = v8_str("outer");
2043 5 : profiler->StartProfiling(outer);
2044 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2045 :
2046 5 : v8::Local<v8::String> inner = v8_str("inner");
2047 5 : profiler->StartProfiling(inner);
2048 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2049 :
2050 5 : v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
2051 5 : CHECK(inner_profile);
2052 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
2053 5 : inner_profile->Delete();
2054 : inner_profile = nullptr;
2055 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2056 :
2057 5 : v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
2058 5 : CHECK(outer_profile);
2059 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
2060 5 : outer_profile->Delete();
2061 : outer_profile = nullptr;
2062 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2063 5 : profiler->Dispose();
2064 5 : }
2065 :
2066 :
2067 0 : const char* GetBranchDeoptReason(v8::Local<v8::Context> context,
2068 : i::CpuProfile* iprofile, const char* branch[],
2069 : int length) {
2070 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2071 : const ProfileNode* iopt_function = nullptr;
2072 0 : iopt_function = GetSimpleBranch(context, profile, branch, length);
2073 0 : CHECK_EQ(1U, iopt_function->deopt_infos().size());
2074 0 : return iopt_function->deopt_infos()[0].deopt_reason;
2075 : }
2076 :
2077 :
2078 : // deopt at top function
2079 26639 : TEST(CollectDeoptEvents) {
2080 0 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2081 0 : i::FLAG_allow_natives_syntax = true;
2082 0 : v8::HandleScope scope(CcTest::isolate());
2083 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2084 : v8::Context::Scope context_scope(env);
2085 0 : ProfilerHelper helper(env);
2086 : i::CpuProfiler* iprofiler =
2087 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2088 :
2089 : const char opt_source[] =
2090 : "function opt_function%d(value, depth) {\n"
2091 : " if (depth) return opt_function%d(value, depth - 1);\n"
2092 : "\n"
2093 : " return 10 / value;\n"
2094 : "}\n"
2095 0 : "\n";
2096 :
2097 0 : for (int i = 0; i < 3; ++i) {
2098 : i::EmbeddedVector<char, sizeof(opt_source) + 100> buffer;
2099 0 : i::SNPrintF(buffer, opt_source, i, i);
2100 0 : v8::Script::Compile(env, v8_str(buffer.start()))
2101 : .ToLocalChecked()
2102 0 : ->Run(env)
2103 : .ToLocalChecked();
2104 : }
2105 :
2106 : const char* source =
2107 : "startProfiling();\n"
2108 : "\n"
2109 : "opt_function0(1, 1);\n"
2110 : "\n"
2111 : "%OptimizeFunctionOnNextCall(opt_function0)\n"
2112 : "\n"
2113 : "opt_function0(1, 1);\n"
2114 : "\n"
2115 : "opt_function0(undefined, 1);\n"
2116 : "\n"
2117 : "opt_function1(1, 1);\n"
2118 : "\n"
2119 : "%OptimizeFunctionOnNextCall(opt_function1)\n"
2120 : "\n"
2121 : "opt_function1(1, 1);\n"
2122 : "\n"
2123 : "opt_function1(NaN, 1);\n"
2124 : "\n"
2125 : "opt_function2(1, 1);\n"
2126 : "\n"
2127 : "%OptimizeFunctionOnNextCall(opt_function2)\n"
2128 : "\n"
2129 : "opt_function2(1, 1);\n"
2130 : "\n"
2131 : "opt_function2(0, 1);\n"
2132 : "\n"
2133 : "stopProfiling();\n"
2134 : "\n";
2135 :
2136 0 : v8::Script::Compile(env, v8_str(source))
2137 : .ToLocalChecked()
2138 0 : ->Run(env)
2139 : .ToLocalChecked();
2140 0 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2141 0 : iprofile->Print();
2142 : /* The expected profile
2143 : [Top down]:
2144 : 0 (root) 0 #1
2145 : 23 32 #2
2146 : 1 opt_function2 31 #7
2147 : 1 opt_function2 31 #8
2148 : ;;; deopted at script_id: 31 position: 106 with reason
2149 : 'division by zero'.
2150 : 2 opt_function0 29 #3
2151 : 4 opt_function0 29 #4
2152 : ;;; deopted at script_id: 29 position: 108 with reason 'not a
2153 : heap number'.
2154 : 0 opt_function1 30 #5
2155 : 1 opt_function1 30 #6
2156 : ;;; deopted at script_id: 30 position: 108 with reason 'lost
2157 : precision or NaN'.
2158 : */
2159 :
2160 : {
2161 0 : const char* branch[] = {"", "opt_function0", "opt_function0"};
2162 : const char* deopt_reason =
2163 0 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch));
2164 0 : if (deopt_reason != reason(i::DeoptimizeReason::kNotAHeapNumber) &&
2165 : deopt_reason != reason(i::DeoptimizeReason::kNotASmi)) {
2166 0 : FATAL("%s", deopt_reason);
2167 : }
2168 : }
2169 : {
2170 0 : const char* branch[] = {"", "opt_function1", "opt_function1"};
2171 : const char* deopt_reason =
2172 0 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch));
2173 0 : if (deopt_reason != reason(i::DeoptimizeReason::kNaN) &&
2174 0 : deopt_reason != reason(i::DeoptimizeReason::kLostPrecisionOrNaN) &&
2175 : deopt_reason != reason(i::DeoptimizeReason::kNotASmi)) {
2176 0 : FATAL("%s", deopt_reason);
2177 : }
2178 : }
2179 : {
2180 0 : const char* branch[] = {"", "opt_function2", "opt_function2"};
2181 0 : CHECK_EQ(reason(i::DeoptimizeReason::kDivisionByZero),
2182 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch)));
2183 : }
2184 0 : iprofiler->DeleteProfile(iprofile);
2185 : }
2186 :
2187 :
2188 26644 : TEST(SourceLocation) {
2189 5 : i::FLAG_always_opt = true;
2190 5 : LocalContext env;
2191 10 : v8::HandleScope scope(CcTest::isolate());
2192 :
2193 : const char* source =
2194 : "function CompareStatementWithThis() {\n"
2195 : " if (this === 1) {}\n"
2196 : "}\n"
2197 : "CompareStatementWithThis();\n";
2198 :
2199 10 : v8::Script::Compile(env.local(), v8_str(source))
2200 : .ToLocalChecked()
2201 5 : ->Run(env.local())
2202 : .ToLocalChecked();
2203 5 : }
2204 :
2205 : static const char* inlined_source =
2206 : "function opt_function(left, right) { var k = left*right; return k + 1; "
2207 : "}\n";
2208 : // 0.........1.........2.........3.........4....*....5.........6......*..7
2209 :
2210 :
2211 : // deopt at the first level inlined function
2212 26639 : TEST(DeoptAtFirstLevelInlinedSource) {
2213 0 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2214 0 : i::FLAG_allow_natives_syntax = true;
2215 0 : v8::HandleScope scope(CcTest::isolate());
2216 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2217 : v8::Context::Scope context_scope(env);
2218 0 : ProfilerHelper helper(env);
2219 : i::CpuProfiler* iprofiler =
2220 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2221 :
2222 : // 0.........1.........2.........3.........4.........5.........6.........7
2223 : const char* source =
2224 : "function test(left, right) { return opt_function(left, right); }\n"
2225 : "\n"
2226 : "startProfiling();\n"
2227 : "\n"
2228 : "test(10, 10);\n"
2229 : "\n"
2230 : "%OptimizeFunctionOnNextCall(test)\n"
2231 : "\n"
2232 : "test(10, 10);\n"
2233 : "\n"
2234 : "test(undefined, 1e9);\n"
2235 : "\n"
2236 : "stopProfiling();\n"
2237 : "\n";
2238 :
2239 0 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2240 0 : inlined_script->Run(env).ToLocalChecked();
2241 0 : int inlined_script_id = inlined_script->GetUnboundScript()->GetId();
2242 :
2243 : v8::Local<v8::Script> script = v8_compile(source);
2244 0 : script->Run(env).ToLocalChecked();
2245 0 : int script_id = script->GetUnboundScript()->GetId();
2246 :
2247 0 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2248 0 : iprofile->Print();
2249 : /* The expected profile output
2250 : [Top down]:
2251 : 0 (root) 0 #1
2252 : 10 30 #2
2253 : 1 test 30 #3
2254 : ;;; deopted at script_id: 29 position: 45 with reason 'not a
2255 : heap number'.
2256 : ;;; Inline point: script_id 30 position: 36.
2257 : 4 opt_function 29 #4
2258 : */
2259 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2260 :
2261 0 : const char* branch[] = {"", "test"};
2262 : const ProfileNode* itest_node =
2263 0 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2264 : const std::vector<v8::CpuProfileDeoptInfo>& deopt_infos =
2265 : itest_node->deopt_infos();
2266 0 : CHECK_EQ(1U, deopt_infos.size());
2267 :
2268 : const v8::CpuProfileDeoptInfo& info = deopt_infos[0];
2269 0 : CHECK(reason(i::DeoptimizeReason::kNotASmi) == info.deopt_reason ||
2270 : reason(i::DeoptimizeReason::kNotAHeapNumber) == info.deopt_reason);
2271 0 : CHECK_EQ(2U, info.stack.size());
2272 0 : CHECK_EQ(inlined_script_id, info.stack[0].script_id);
2273 0 : CHECK_LE(dist(offset(inlined_source, "*right"), info.stack[0].position), 1);
2274 0 : CHECK_EQ(script_id, info.stack[1].script_id);
2275 0 : CHECK_EQ(offset(source, "opt_function(left,"), info.stack[1].position);
2276 :
2277 0 : iprofiler->DeleteProfile(iprofile);
2278 : }
2279 :
2280 :
2281 : // deopt at the second level inlined function
2282 26644 : TEST(DeoptAtSecondLevelInlinedSource) {
2283 7 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2284 3 : i::FLAG_allow_natives_syntax = true;
2285 6 : v8::HandleScope scope(CcTest::isolate());
2286 6 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2287 : v8::Context::Scope context_scope(env);
2288 3 : ProfilerHelper helper(env);
2289 : i::CpuProfiler* iprofiler =
2290 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2291 :
2292 : // 0.........1.........2.........3.........4.........5.........6.........7
2293 : const char* source =
2294 : "function test2(left, right) { return opt_function(left, right); }\n"
2295 : "function test1(left, right) { return test2(left, right); } \n"
2296 : "\n"
2297 : "startProfiling();\n"
2298 : "\n"
2299 : "test1(10, 10);\n"
2300 : "\n"
2301 : "%OptimizeFunctionOnNextCall(test1)\n"
2302 : "\n"
2303 : "test1(10, 10);\n"
2304 : "\n"
2305 : "test1(undefined, 1e9);\n"
2306 : "\n"
2307 : "stopProfiling();\n"
2308 : "\n";
2309 :
2310 3 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2311 3 : inlined_script->Run(env).ToLocalChecked();
2312 6 : int inlined_script_id = inlined_script->GetUnboundScript()->GetId();
2313 :
2314 : v8::Local<v8::Script> script = v8_compile(source);
2315 3 : script->Run(env).ToLocalChecked();
2316 6 : int script_id = script->GetUnboundScript()->GetId();
2317 :
2318 3 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2319 3 : iprofile->Print();
2320 : /* The expected profile output
2321 : [Top down]:
2322 : 0 (root) 0 #1
2323 : 11 30 #2
2324 : 1 test1 30 #3
2325 : ;;; deopted at script_id: 29 position: 45 with reason 'not a
2326 : heap number'.
2327 : ;;; Inline point: script_id 30 position: 37.
2328 : ;;; Inline point: script_id 30 position: 103.
2329 : 1 test2 30 #4
2330 : 3 opt_function 29 #5
2331 : */
2332 :
2333 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2334 :
2335 3 : const char* branch[] = {"", "test1"};
2336 : const ProfileNode* itest_node =
2337 3 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2338 : const std::vector<v8::CpuProfileDeoptInfo>& deopt_infos =
2339 : itest_node->deopt_infos();
2340 3 : CHECK_EQ(1U, deopt_infos.size());
2341 :
2342 : const v8::CpuProfileDeoptInfo info = deopt_infos[0];
2343 3 : CHECK(reason(i::DeoptimizeReason::kNotASmi) == info.deopt_reason ||
2344 : reason(i::DeoptimizeReason::kNotAHeapNumber) == info.deopt_reason);
2345 3 : CHECK_EQ(3U, info.stack.size());
2346 3 : CHECK_EQ(inlined_script_id, info.stack[0].script_id);
2347 6 : CHECK_LE(dist(offset(inlined_source, "*right"), info.stack[0].position), 1);
2348 3 : CHECK_EQ(script_id, info.stack[1].script_id);
2349 3 : CHECK_EQ(offset(source, "opt_function(left,"), info.stack[1].position);
2350 3 : CHECK_EQ(offset(source, "test2(left, right);"), info.stack[2].position);
2351 :
2352 3 : iprofiler->DeleteProfile(iprofile);
2353 : }
2354 :
2355 :
2356 : // deopt in untracked function
2357 26644 : TEST(DeoptUntrackedFunction) {
2358 7 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2359 3 : i::FLAG_allow_natives_syntax = true;
2360 6 : v8::HandleScope scope(CcTest::isolate());
2361 6 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2362 : v8::Context::Scope context_scope(env);
2363 3 : ProfilerHelper helper(env);
2364 : i::CpuProfiler* iprofiler =
2365 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2366 :
2367 : // 0.........1.........2.........3.........4.........5.........6.........7
2368 : const char* source =
2369 : "function test(left, right) { return opt_function(left, right); }\n"
2370 : "\n"
2371 : "test(10, 10);\n"
2372 : "\n"
2373 : "%OptimizeFunctionOnNextCall(test)\n"
2374 : "\n"
2375 : "test(10, 10);\n"
2376 : "\n"
2377 : "startProfiling();\n" // profiler started after compilation.
2378 : "\n"
2379 : "test(undefined, 10);\n"
2380 : "\n"
2381 : "stopProfiling();\n"
2382 : "\n";
2383 :
2384 3 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2385 3 : inlined_script->Run(env).ToLocalChecked();
2386 :
2387 : v8::Local<v8::Script> script = v8_compile(source);
2388 3 : script->Run(env).ToLocalChecked();
2389 :
2390 3 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2391 3 : iprofile->Print();
2392 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2393 :
2394 3 : const char* branch[] = {"", "test"};
2395 : const ProfileNode* itest_node =
2396 3 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2397 3 : CHECK_EQ(0U, itest_node->deopt_infos().size());
2398 :
2399 3 : iprofiler->DeleteProfile(iprofile);
2400 : }
2401 :
2402 : using v8::platform::tracing::TraceBuffer;
2403 : using v8::platform::tracing::TraceConfig;
2404 : using v8::platform::tracing::TraceObject;
2405 :
2406 : namespace {
2407 :
2408 15 : class CpuProfileEventChecker : public v8::platform::tracing::TraceWriter {
2409 : public:
2410 13 : void AppendTraceEvent(TraceObject* trace_event) override {
2411 47 : if (trace_event->name() != std::string("Profile") &&
2412 29 : trace_event->name() != std::string("ProfileChunk"))
2413 : return;
2414 13 : CHECK(!profile_id_ || trace_event->id() == profile_id_);
2415 13 : CHECK_EQ(1, trace_event->num_args());
2416 13 : CHECK_EQ(TRACE_VALUE_TYPE_CONVERTABLE, trace_event->arg_types()[0]);
2417 13 : profile_id_ = trace_event->id();
2418 : v8::ConvertableToTraceFormat* arg =
2419 : trace_event->arg_convertables()[0].get();
2420 13 : result_json_ += result_json_.empty() ? "[" : ",\n";
2421 13 : arg->AppendAsTraceFormat(&result_json_);
2422 : }
2423 10 : void Flush() override { result_json_ += "]"; }
2424 :
2425 : const std::string& result_json() const { return result_json_; }
2426 : void Reset() {
2427 : result_json_.clear();
2428 5 : profile_id_ = 0;
2429 : }
2430 :
2431 : private:
2432 : std::string result_json_;
2433 : uint64_t profile_id_ = 0;
2434 : };
2435 :
2436 : } // namespace
2437 :
2438 26644 : TEST(TracingCpuProfiler) {
2439 10 : v8::HandleScope scope(CcTest::isolate());
2440 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2441 : v8::Context::Scope context_scope(env);
2442 :
2443 5 : CpuProfileEventChecker* event_checker = new CpuProfileEventChecker();
2444 : TraceBuffer* ring_buffer =
2445 5 : TraceBuffer::CreateTraceBufferRingBuffer(1, event_checker);
2446 : auto* tracing_controller =
2447 : static_cast<v8::platform::tracing::TracingController*>(
2448 5 : i::V8::GetCurrentPlatform()->GetTracingController());
2449 5 : tracing_controller->Initialize(ring_buffer);
2450 :
2451 : bool result = false;
2452 15 : for (int run_duration = 50; !result; run_duration += 50) {
2453 5 : TraceConfig* trace_config = new TraceConfig();
2454 : trace_config->AddIncludedCategory(
2455 5 : TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"));
2456 : trace_config->AddIncludedCategory(
2457 5 : TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler.hires"));
2458 :
2459 : std::string test_code = R"(
2460 : function foo() {
2461 : let s = 0;
2462 10 : const endTime = Date.now() + )" +
2463 10 : std::to_string(run_duration) + R"(
2464 : while (Date.now() < endTime) s += Math.cos(s);
2465 : return s;
2466 : }
2467 : foo();)";
2468 :
2469 5 : tracing_controller->StartTracing(trace_config);
2470 : CompileRun(test_code.c_str());
2471 5 : tracing_controller->StopTracing();
2472 :
2473 : std::string profile_json = event_checker->result_json();
2474 : event_checker->Reset();
2475 5 : CHECK_LT(0u, profile_json.length());
2476 : printf("Profile JSON: %s\n", profile_json.c_str());
2477 :
2478 : std::string profile_checker_code = R"(
2479 : function checkProfile(json) {
2480 : const profile_header = json[0];
2481 : if (typeof profile_header['startTime'] !== 'number')
2482 : return false;
2483 : return json.some(event => (event.lines || []).some(line => line));
2484 : }
2485 10 : checkProfile()" + profile_json +
2486 5 : ")";
2487 10 : result = CompileRunChecked(CcTest::isolate(), profile_checker_code.c_str())
2488 5 : ->IsTrue();
2489 : }
2490 :
2491 : static_cast<v8::platform::tracing::TracingController*>(
2492 5 : i::V8::GetCurrentPlatform()->GetTracingController())
2493 5 : ->Initialize(nullptr);
2494 5 : }
2495 :
2496 26644 : TEST(Issue763073) {
2497 : class AllowNativesSyntax {
2498 : public:
2499 : AllowNativesSyntax()
2500 : : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
2501 5 : trace_deopt_(i::FLAG_trace_deopt) {
2502 5 : i::FLAG_allow_natives_syntax = true;
2503 5 : i::FLAG_trace_deopt = true;
2504 : }
2505 :
2506 : ~AllowNativesSyntax() {
2507 5 : i::FLAG_allow_natives_syntax = allow_natives_syntax_;
2508 5 : i::FLAG_trace_deopt = trace_deopt_;
2509 : }
2510 :
2511 : private:
2512 : bool allow_natives_syntax_;
2513 : bool trace_deopt_;
2514 : };
2515 :
2516 : AllowNativesSyntax allow_natives_syntax_scope;
2517 5 : LocalContext env;
2518 10 : v8::HandleScope scope(env->GetIsolate());
2519 :
2520 : CompileRun(
2521 : "function f() { return function g(x) { }; }"
2522 : // Create first closure, optimize it, and deoptimize it.
2523 : "var g = f();"
2524 : "g(1);"
2525 : "%OptimizeFunctionOnNextCall(g);"
2526 : "g(1);"
2527 : "%DeoptimizeFunction(g);"
2528 : // Create second closure, and optimize it. This will create another
2529 : // optimized code object and put in the (shared) type feedback vector.
2530 : "var h = f();"
2531 : "h(1);"
2532 : "%OptimizeFunctionOnNextCall(h);"
2533 : "h(1);");
2534 :
2535 : // Start profiling.
2536 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
2537 5 : v8::Local<v8::String> profile_name = v8_str("test");
2538 :
2539 : // Here we test that the heap iteration upon profiling start is not
2540 : // confused by having a deoptimized code object for a closure while
2541 : // having a different optimized code object in the type feedback vector.
2542 5 : cpu_profiler->StartProfiling(profile_name);
2543 5 : v8::CpuProfile* p = cpu_profiler->StopProfiling(profile_name);
2544 5 : p->Delete();
2545 5 : cpu_profiler->Dispose();
2546 5 : }
2547 :
2548 : static const char* js_collect_sample_api_source =
2549 : "%NeverOptimizeFunction(start);\n"
2550 : "function start() {\n"
2551 : " CallStaticCollectSample();\n"
2552 : "}";
2553 :
2554 0 : static void CallStaticCollectSample(
2555 : const v8::FunctionCallbackInfo<v8::Value>& info) {
2556 0 : v8::CpuProfiler::CollectSample(info.GetIsolate());
2557 0 : }
2558 :
2559 26639 : TEST(StaticCollectSampleAPI) {
2560 0 : i::FLAG_allow_natives_syntax = true;
2561 0 : LocalContext env;
2562 0 : v8::HandleScope scope(env->GetIsolate());
2563 :
2564 : v8::Local<v8::FunctionTemplate> func_template =
2565 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallStaticCollectSample);
2566 : v8::Local<v8::Function> func =
2567 0 : func_template->GetFunction(env.local()).ToLocalChecked();
2568 0 : func->SetName(v8_str("CallStaticCollectSample"));
2569 0 : env->Global()
2570 0 : ->Set(env.local(), v8_str("CallStaticCollectSample"), func)
2571 : .FromJust();
2572 :
2573 0 : CompileRun(js_collect_sample_api_source);
2574 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
2575 :
2576 0 : ProfilerHelper helper(env.local());
2577 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 100);
2578 :
2579 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
2580 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
2581 0 : GetChild(env.local(), start_node, "CallStaticCollectSample");
2582 :
2583 0 : profile->Delete();
2584 0 : }
2585 :
2586 26644 : TEST(CodeEntriesMemoryLeak) {
2587 10 : v8::HandleScope scope(CcTest::isolate());
2588 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2589 : v8::Context::Scope context_scope(env);
2590 :
2591 5 : std::string source = "function start() {}\n";
2592 10005 : for (int i = 0; i < 1000; ++i) {
2593 25000 : source += "function foo" + std::to_string(i) + "() { return " +
2594 15000 : std::to_string(i) +
2595 : "; }\n"
2596 10000 : "foo" +
2597 15000 : std::to_string(i) + "();\n";
2598 : }
2599 : CompileRun(source.c_str());
2600 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
2601 :
2602 5 : ProfilerHelper helper(env);
2603 :
2604 1005 : for (int j = 0; j < 100; ++j) {
2605 500 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0);
2606 500 : profile->Delete();
2607 : }
2608 :
2609 : i::CpuProfiler* profiler =
2610 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2611 5 : CHECK(!profiler->profiler_listener_for_test());
2612 5 : }
2613 :
2614 26639 : TEST(NativeFrameStackTrace) {
2615 : // A test for issue https://crbug.com/768540
2616 : // When a sample lands in a native function which has not EXIT frame
2617 : // stack frame iterator used to bail out and produce an empty stack trace.
2618 : // The source code below makes v8 call the
2619 : // v8::internal::StringTable::LookupStringIfExists_NoAllocate native function
2620 : // without producing an EXIT frame.
2621 0 : v8::HandleScope scope(CcTest::isolate());
2622 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2623 : v8::Context::Scope context_scope(env);
2624 :
2625 : const char* source = R"(
2626 : function jsFunction() {
2627 : var s = {};
2628 : for (var i = 0; i < 1e4; ++i) {
2629 : for (var j = 0; j < 100; j++) {
2630 : s['item' + j] = 'alph';
2631 : }
2632 : }
2633 : })";
2634 :
2635 : CompileRun(source);
2636 0 : v8::Local<v8::Function> function = GetFunction(env, "jsFunction");
2637 :
2638 0 : ProfilerHelper helper(env);
2639 :
2640 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 100, 0, true);
2641 :
2642 : // Count the fraction of samples landing in 'jsFunction' (valid stack)
2643 : // vs '(program)' (no stack captured).
2644 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
2645 0 : const v8::CpuProfileNode* js_function = FindChild(root, "jsFunction");
2646 0 : const v8::CpuProfileNode* program = FindChild(root, "(program)");
2647 0 : if (program) {
2648 0 : unsigned js_function_samples = TotalHitCount(js_function);
2649 0 : unsigned program_samples = TotalHitCount(program);
2650 : double valid_samples_ratio =
2651 0 : 1. * js_function_samples / (js_function_samples + program_samples);
2652 0 : i::PrintF("Ratio: %f\n", valid_samples_ratio);
2653 : // TODO(alph): Investigate other causes of dropped frames. The ratio
2654 : // should be close to 99%.
2655 0 : CHECK_GE(valid_samples_ratio, 0.3);
2656 : }
2657 :
2658 0 : profile->Delete();
2659 0 : }
2660 :
2661 26644 : TEST(SourcePositionTable) {
2662 : i::SourcePositionTable info;
2663 :
2664 : // Newly created tables should return NoLineNumberInfo for any lookup.
2665 : int no_info = v8::CpuProfileNode::kNoLineNumberInfo;
2666 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(std::numeric_limits<int>::min()));
2667 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(0));
2668 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(0));
2669 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(1));
2670 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(9));
2671 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(10));
2672 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(11));
2673 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(19));
2674 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(20));
2675 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(21));
2676 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(100));
2677 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(100));
2678 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(std::numeric_limits<int>::max()));
2679 :
2680 5 : info.SetPosition(10, 1, SourcePosition::kNotInlined);
2681 5 : info.SetPosition(20, 2, SourcePosition::kNotInlined);
2682 :
2683 : // The only valid return values are 1 or 2 - every pc maps to a line
2684 : // number.
2685 5 : CHECK_EQ(1, info.GetSourceLineNumber(std::numeric_limits<int>::min()));
2686 5 : CHECK_EQ(1, info.GetSourceLineNumber(0));
2687 5 : CHECK_EQ(1, info.GetSourceLineNumber(1));
2688 5 : CHECK_EQ(1, info.GetSourceLineNumber(9));
2689 5 : CHECK_EQ(1, info.GetSourceLineNumber(10));
2690 5 : CHECK_EQ(1, info.GetSourceLineNumber(11));
2691 5 : CHECK_EQ(1, info.GetSourceLineNumber(19));
2692 5 : CHECK_EQ(1, info.GetSourceLineNumber(20));
2693 5 : CHECK_EQ(2, info.GetSourceLineNumber(21));
2694 5 : CHECK_EQ(2, info.GetSourceLineNumber(100));
2695 5 : CHECK_EQ(2, info.GetSourceLineNumber(std::numeric_limits<int>::max()));
2696 :
2697 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(0));
2698 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(100));
2699 :
2700 : // Test SetPosition behavior.
2701 5 : info.SetPosition(25, 3, 0);
2702 5 : CHECK_EQ(2, info.GetSourceLineNumber(21));
2703 5 : CHECK_EQ(3, info.GetSourceLineNumber(100));
2704 5 : CHECK_EQ(3, info.GetSourceLineNumber(std::numeric_limits<int>::max()));
2705 :
2706 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(21));
2707 5 : CHECK_EQ(0, info.GetInliningId(100));
2708 5 : }
2709 :
2710 26644 : TEST(MultipleProfilers) {
2711 10 : std::unique_ptr<CpuProfiler> profiler1(new CpuProfiler(CcTest::i_isolate()));
2712 10 : std::unique_ptr<CpuProfiler> profiler2(new CpuProfiler(CcTest::i_isolate()));
2713 5 : profiler1->StartProfiling("1");
2714 5 : profiler2->StartProfiling("2");
2715 5 : profiler1->StopProfiling("1");
2716 5 : profiler2->StopProfiling("2");
2717 5 : }
2718 :
2719 : // Tests that logged CodeCreateEvent calls do not crash a reused CpuProfiler.
2720 : // crbug.com/929928
2721 26644 : TEST(CrashReusedProfiler) {
2722 5 : LocalContext env;
2723 : i::Isolate* isolate = CcTest::i_isolate();
2724 : i::HandleScope scope(isolate);
2725 :
2726 10 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(isolate));
2727 5 : profiler->StartProfiling("1");
2728 5 : profiler->StopProfiling("1");
2729 :
2730 5 : profiler->StartProfiling("2");
2731 5 : CreateCode(&env);
2732 5 : profiler->StopProfiling("2");
2733 5 : }
2734 :
2735 : // Tests that samples from different profilers on the same isolate do not leak
2736 : // samples to each other. See crbug.com/v8/8835.
2737 26644 : TEST(MultipleProfilersSampleIndependently) {
2738 5 : LocalContext env;
2739 : i::Isolate* isolate = CcTest::i_isolate();
2740 : i::HandleScope scope(isolate);
2741 :
2742 : // Create two profilers- one slow ticking one, and one fast ticking one.
2743 : // Ensure that the slow ticking profiler does not receive samples from the
2744 : // fast ticking one.
2745 : std::unique_ptr<CpuProfiler> slow_profiler(
2746 10 : new CpuProfiler(CcTest::i_isolate()));
2747 5 : slow_profiler->set_sampling_interval(base::TimeDelta::FromSeconds(1));
2748 5 : slow_profiler->StartProfiling("1", true);
2749 :
2750 : CompileRun(R"(
2751 : function start() {
2752 : let val = 1;
2753 : for (let i = 0; i < 10e3; i++) {
2754 : val = (val * 2) % 3;
2755 : }
2756 : return val;
2757 : }
2758 : )");
2759 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
2760 5 : ProfilerHelper helper(env.local());
2761 5 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 100, 0, true);
2762 :
2763 5 : auto slow_profile = slow_profiler->StopProfiling("1");
2764 5 : CHECK_GT(profile->GetSamplesCount(), slow_profile->samples_count());
2765 5 : }
2766 :
2767 10 : void ProfileSomeCode(v8::Isolate* isolate) {
2768 : v8::Isolate::Scope isolate_scope(isolate);
2769 20 : v8::HandleScope scope(isolate);
2770 10 : LocalContext context(isolate);
2771 :
2772 10 : v8::CpuProfiler* profiler = v8::CpuProfiler::New(isolate);
2773 :
2774 10 : v8::Local<v8::String> profile_name = v8_str("1");
2775 10 : profiler->StartProfiling(profile_name);
2776 : const char* source = R"(
2777 : function foo() {
2778 : var x = 0;
2779 : for (var i = 0; i < 1e3; i++) {
2780 : for (var j = 0; j < 1e3; j++) {
2781 : x = i * j;
2782 : }
2783 : }
2784 : return x;
2785 : }
2786 : foo();
2787 : )";
2788 :
2789 : CompileRun(source);
2790 10 : profiler->StopProfiling(profile_name);
2791 10 : profiler->Dispose();
2792 10 : }
2793 :
2794 5 : class IsolateThread : public v8::base::Thread {
2795 : public:
2796 10 : IsolateThread() : Thread(Options("IsolateThread")) {}
2797 :
2798 10 : void Run() override {
2799 : v8::Isolate::CreateParams create_params;
2800 10 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
2801 10 : v8::Isolate* isolate = v8::Isolate::New(create_params);
2802 10 : ProfileSomeCode(isolate);
2803 10 : isolate->Dispose();
2804 10 : }
2805 : };
2806 :
2807 : // Checking for crashes and TSAN issues with multiple isolates profiling.
2808 26644 : TEST(MultipleIsolates) {
2809 : IsolateThread thread1;
2810 : IsolateThread thread2;
2811 :
2812 5 : thread1.Start();
2813 5 : thread2.Start();
2814 :
2815 5 : thread1.Join();
2816 5 : thread2.Join();
2817 5 : }
2818 :
2819 : // Tests that StopProfiling doesn't wait for the next sample tick in order to
2820 : // stop, but rather exits early before a given wait threshold.
2821 26644 : TEST(FastStopProfiling) {
2822 : static const base::TimeDelta kLongInterval = base::TimeDelta::FromSeconds(10);
2823 : static const base::TimeDelta kWaitThreshold = base::TimeDelta::FromSeconds(5);
2824 :
2825 10 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(CcTest::i_isolate()));
2826 5 : profiler->set_sampling_interval(kLongInterval);
2827 5 : profiler->StartProfiling("", true);
2828 :
2829 5 : v8::Platform* platform = v8::internal::V8::GetCurrentPlatform();
2830 5 : double start = platform->CurrentClockTimeMillis();
2831 5 : profiler->StopProfiling("");
2832 5 : double duration = platform->CurrentClockTimeMillis() - start;
2833 :
2834 10 : CHECK_LT(duration, kWaitThreshold.InMillisecondsF());
2835 5 : }
2836 :
2837 26644 : TEST(LowPrecisionSamplingStartStopInternal) {
2838 : i::Isolate* isolate = CcTest::i_isolate();
2839 10 : CpuProfilesCollection profiles(isolate);
2840 5 : ProfileGenerator generator(&profiles);
2841 : std::unique_ptr<ProfilerEventsProcessor> processor(
2842 : new SamplingEventsProcessor(isolate, &generator,
2843 10 : v8::base::TimeDelta::FromMicroseconds(100),
2844 5 : false));
2845 5 : processor->Start();
2846 5 : processor->StopSynchronously();
2847 5 : }
2848 :
2849 26644 : TEST(LowPrecisionSamplingStartStopPublic) {
2850 5 : LocalContext env;
2851 10 : v8::HandleScope scope(env->GetIsolate());
2852 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
2853 5 : cpu_profiler->SetUsePreciseSampling(false);
2854 5 : v8::Local<v8::String> profile_name = v8_str("");
2855 5 : cpu_profiler->StartProfiling(profile_name, true);
2856 5 : cpu_profiler->StopProfiling(profile_name);
2857 5 : cpu_profiler->Dispose();
2858 5 : }
2859 :
2860 : enum class EntryCountMode { kAll, kOnlyInlined };
2861 :
2862 : // Count the number of unique source positions.
2863 30 : int GetSourcePositionEntryCount(i::Isolate* isolate, const char* source,
2864 : EntryCountMode mode = EntryCountMode::kAll) {
2865 : std::unordered_set<int64_t> raw_position_set;
2866 : i::Handle<i::JSFunction> function = i::Handle<i::JSFunction>::cast(
2867 : v8::Utils::OpenHandle(*CompileRun(source)));
2868 30 : if (function->IsInterpreted()) return -1;
2869 : i::Handle<i::Code> code(function->code(), isolate);
2870 : i::SourcePositionTableIterator iterator(
2871 24 : ByteArray::cast(code->source_position_table()));
2872 :
2873 412 : while (!iterator.done()) {
2874 262 : if (mode == EntryCountMode::kAll ||
2875 : iterator.source_position().isInlined()) {
2876 332 : raw_position_set.insert(iterator.source_position().raw());
2877 : }
2878 194 : iterator.Advance();
2879 : }
2880 24 : return static_cast<int>(raw_position_set.size());
2881 : }
2882 :
2883 26644 : UNINITIALIZED_TEST(DetailedSourcePositionAPI) {
2884 5 : i::FLAG_detailed_line_info = false;
2885 5 : i::FLAG_allow_natives_syntax = true;
2886 : v8::Isolate::CreateParams create_params;
2887 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
2888 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
2889 :
2890 : const char* source =
2891 : "function fib(i) {"
2892 : " if (i <= 1) return 1; "
2893 : " return fib(i - 1) +"
2894 : " fib(i - 2);"
2895 : "}"
2896 : "fib(5);"
2897 : "%OptimizeFunctionOnNextCall(fib);"
2898 : "fib(5);"
2899 : "fib";
2900 : {
2901 : v8::Isolate::Scope isolate_scope(isolate);
2902 10 : v8::HandleScope handle_scope(isolate);
2903 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
2904 : v8::Context::Scope context_scope(context);
2905 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2906 :
2907 5 : CHECK(!i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2908 :
2909 5 : int non_detailed_positions = GetSourcePositionEntryCount(i_isolate, source);
2910 :
2911 5 : v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
2912 5 : CHECK(i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2913 :
2914 5 : int detailed_positions = GetSourcePositionEntryCount(i_isolate, source);
2915 :
2916 5 : CHECK((non_detailed_positions == -1 && detailed_positions == -1) ||
2917 : non_detailed_positions < detailed_positions);
2918 : }
2919 :
2920 5 : isolate->Dispose();
2921 5 : }
2922 :
2923 26644 : UNINITIALIZED_TEST(DetailedSourcePositionAPI_Inlining) {
2924 5 : i::FLAG_detailed_line_info = false;
2925 5 : i::FLAG_turbo_inlining = true;
2926 5 : i::FLAG_stress_inline = true;
2927 5 : i::FLAG_always_opt = false;
2928 5 : i::FLAG_allow_natives_syntax = true;
2929 : v8::Isolate::CreateParams create_params;
2930 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
2931 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
2932 :
2933 : const char* source = R"(
2934 : function foo(x) {
2935 : return bar(x) + 1;
2936 : }
2937 :
2938 : function bar(x) {
2939 : var y = 1;
2940 : for (var i = 0; i < x; ++i) {
2941 : y = y * x;
2942 : }
2943 : return x;
2944 : }
2945 :
2946 : foo(5);
2947 : %OptimizeFunctionOnNextCall(foo);
2948 : foo(5);
2949 : foo;
2950 : )";
2951 :
2952 : {
2953 : v8::Isolate::Scope isolate_scope(isolate);
2954 10 : v8::HandleScope handle_scope(isolate);
2955 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
2956 : v8::Context::Scope context_scope(context);
2957 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2958 :
2959 5 : CHECK(!i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2960 :
2961 : int non_detailed_positions =
2962 5 : GetSourcePositionEntryCount(i_isolate, source, EntryCountMode::kAll);
2963 : int non_detailed_inlined_positions = GetSourcePositionEntryCount(
2964 5 : i_isolate, source, EntryCountMode::kOnlyInlined);
2965 :
2966 5 : v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
2967 5 : CHECK(i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2968 :
2969 : int detailed_positions =
2970 5 : GetSourcePositionEntryCount(i_isolate, source, EntryCountMode::kAll);
2971 : int detailed_inlined_positions = GetSourcePositionEntryCount(
2972 5 : i_isolate, source, EntryCountMode::kOnlyInlined);
2973 :
2974 5 : if (non_detailed_positions == -1) {
2975 1 : CHECK_EQ(non_detailed_positions, detailed_positions);
2976 : } else {
2977 4 : CHECK_LT(non_detailed_positions, detailed_positions);
2978 4 : CHECK_LT(non_detailed_inlined_positions, detailed_inlined_positions);
2979 : }
2980 : }
2981 :
2982 5 : isolate->Dispose();
2983 5 : }
2984 :
2985 : } // namespace test_cpu_profiler
2986 : } // namespace internal
2987 79917 : } // namespace v8
|