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