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 104 : static v8::Local<v8::Function> GetFunction(v8::Local<v8::Context> env,
59 : const char* name) {
60 : return v8::Local<v8::Function>::Cast(
61 416 : env->Global()->Get(env, v8_str(name)).ToLocalChecked());
62 : }
63 :
64 0 : static size_t offset(const char* src, const char* substring) {
65 : const char* it = strstr(src, substring);
66 0 : CHECK(it);
67 0 : return static_cast<size_t>(it - src);
68 : }
69 :
70 : template <typename A, typename B>
71 : static int dist(A a, B b) {
72 0 : return abs(static_cast<int>(a) - static_cast<int>(b));
73 : }
74 :
75 : static const char* reason(const i::DeoptimizeReason reason) {
76 0 : return i::DeoptimizeReasonToString(reason);
77 : }
78 :
79 25880 : TEST(StartStop) {
80 : i::Isolate* isolate = CcTest::i_isolate();
81 5 : 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 10 : 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 45 : 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 45 : 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 25880 : TEST(CodeEvents) {
151 5 : CcTest::InitializeVM();
152 5 : LocalContext env;
153 10 : 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 : 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 5 : CodeEntry* aaa =
191 5 : generator->code_map()->FindEntry(aaa_code->InstructionStart());
192 5 : CHECK(aaa);
193 5 : CHECK_EQ(0, strcmp(aaa_str, aaa->name()));
194 :
195 5 : 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 5 : CodeEntry* comment2 =
203 5 : generator->code_map()->FindEntry(moved_code->InstructionStart());
204 5 : CHECK(comment2);
205 10 : 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 25880 : TEST(TickEvents) {
214 : TestSetup test_setup;
215 10 : LocalContext env;
216 10 : 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 : 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 5 : EnqueueTickSampleEvent(processor, frame3_code->raw_instruction_end() - 1,
244 5 : frame2_code->raw_instruction_end() - 1,
245 15 : 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 5 : const std::vector<ProfileNode*>* top_down_root_children =
254 5 : 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 5 : 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 5 : 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 25880 : TEST(CrashIfStoppingLastNonExistentProfile) {
273 5 : CcTest::InitializeVM();
274 : TestSetup test_setup;
275 5 : 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 25880 : TEST(Issue1398) {
285 : TestSetup test_setup;
286 10 : 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 1280 : 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 5 : const ProfileNode* node = profile->top_down()->root();
320 1290 : 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 25880 : TEST(DeleteAllCpuProfiles) {
329 5 : CcTest::InitializeVM();
330 : TestSetup test_setup;
331 5 : 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 40 : 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 25880 : 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 10 : cpu_profiler->Dispose();
409 5 : }
410 :
411 :
412 25880 : 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 10 : cpu_profiler->Dispose();
422 5 : }
423 :
424 : class ProfilerHelper {
425 : public:
426 53 : explicit ProfilerHelper(const v8::Local<v8::Context>& context)
427 : : context_(context),
428 106 : profiler_(v8::CpuProfiler::New(context->GetIsolate())) {
429 : i::ProfilerExtension::set_profiler(profiler_);
430 53 : }
431 : ~ProfilerHelper() {
432 : i::ProfilerExtension::set_profiler(static_cast<CpuProfiler*>(nullptr));
433 53 : 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 535 : 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 535 : v8::Local<v8::String> profile_name = v8_str("my_profile");
458 :
459 535 : profiler_->SetSamplingInterval(100);
460 535 : profiler_->StartProfiling(profile_name, mode, collect_samples);
461 :
462 : v8::internal::CpuProfiler* iprofiler =
463 535 : reinterpret_cast<v8::internal::CpuProfiler*>(profiler_);
464 3769 : v8::sampler::Sampler* sampler =
465 : reinterpret_cast<i::SamplingEventsProcessor*>(iprofiler->processor())
466 : ->sampler();
467 : sampler->StartCountingSamples();
468 3234 : do {
469 9702 : function->Call(context_, context_->Global(), argc, argv).ToLocalChecked();
470 3769 : } while (sampler->js_sample_count() < min_js_samples ||
471 : sampler->external_sample_count() < min_external_samples);
472 :
473 535 : v8::CpuProfile* profile = profiler_->StopProfiling(profile_name);
474 :
475 535 : CHECK(profile);
476 : // Dump collected profile to have a better diagnostic in case of failure.
477 535 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
478 :
479 535 : 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 144 : static const v8::CpuProfileNode* FindChild(v8::Local<v8::Context> context,
490 : const v8::CpuProfileNode* node,
491 : const char* name) {
492 144 : int count = node->GetChildrenCount();
493 144 : v8::Local<v8::String> name_handle = v8_str(name);
494 214 : for (int i = 0; i < count; i++) {
495 209 : const v8::CpuProfileNode* child = node->GetChild(i);
496 627 : 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 133 : static const v8::CpuProfileNode* GetChild(v8::Local<v8::Context> context,
515 : const v8::CpuProfileNode* node,
516 : const char* name) {
517 133 : const v8::CpuProfileNode* result = FindChild(context, node, name);
518 133 : if (!result) FATAL("Failed to GetChild: %s", name);
519 133 : return result;
520 : }
521 :
522 0 : 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 0 : }
530 :
531 0 : static const ProfileNode* GetSimpleBranch(v8::Local<v8::Context> context,
532 : v8::CpuProfile* profile,
533 : const char* names[], int length) {
534 0 : const v8::CpuProfileNode* node = profile->GetTopDownRoot();
535 0 : for (int i = 0; i < length; i++) {
536 0 : node = GetChild(context, node, names[i]);
537 : }
538 0 : return reinterpret_cast<const ProfileNode*>(node);
539 : }
540 :
541 : struct NameLinePair {
542 : const char* name;
543 : int line_number;
544 : };
545 :
546 110 : static const v8::CpuProfileNode* FindChild(const v8::CpuProfileNode* node,
547 : NameLinePair pair) {
548 138 : for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
549 138 : 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 406 : if (strcmp(child->GetFunctionNameStr(), pair.name) == 0 &&
553 150 : (child->GetLineNumber() == pair.line_number ||
554 : pair.line_number == -1)) {
555 : return child;
556 : }
557 : }
558 : return nullptr;
559 : }
560 :
561 110 : static const v8::CpuProfileNode* GetChild(const v8::CpuProfileNode* node,
562 : NameLinePair pair) {
563 110 : const v8::CpuProfileNode* result = FindChild(node, pair);
564 110 : if (!result) FATAL("Failed to GetChild: %s:%d", pair.name, pair.line_number);
565 110 : return result;
566 : }
567 :
568 25 : static void CheckBranch(const v8::CpuProfileNode* node, NameLinePair path[],
569 : int length) {
570 135 : for (int i = 0; i < length; i++) {
571 110 : NameLinePair pair = path[i];
572 110 : node = GetChild(node, pair);
573 : }
574 25 : }
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 25875 : 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 0 : CheckSimpleBranch(env.local(), foo_node, bar_branch, arraysize(bar_branch));
647 0 : const char* baz_branch[] = {"baz", "delay", "loop"};
648 0 : 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 0 : arraysize(delay_branch));
652 :
653 0 : profile->Delete();
654 0 : }
655 :
656 25875 : TEST(CollectCpuProfileCallerLineNumbers) {
657 0 : i::FLAG_allow_natives_syntax = true;
658 0 : LocalContext env;
659 0 : v8::HandleScope scope(env->GetIsolate());
660 :
661 0 : CompileRun(cpu_profiler_test_source);
662 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
663 :
664 : int32_t profiling_interval_ms = 200;
665 : v8::Local<v8::Value> args[] = {
666 0 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
667 0 : ProfilerHelper helper(env.local());
668 : helper.Run(function, args, arraysize(args), 1000, 0, false,
669 0 : v8::CpuProfilingMode::kCallerLineNumbers);
670 : v8::CpuProfile* profile =
671 : helper.Run(function, args, arraysize(args), 1000, 0, false,
672 0 : v8::CpuProfilingMode::kCallerLineNumbers);
673 :
674 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
675 0 : const v8::CpuProfileNode* start_node = GetChild(root, {"start", 27});
676 0 : const v8::CpuProfileNode* foo_node = GetChild(start_node, {"foo", 30});
677 :
678 0 : NameLinePair bar_branch[] = {{"bar", 23}, {"delay", 19}, {"loop", 18}};
679 0 : CheckBranch(foo_node, bar_branch, arraysize(bar_branch));
680 0 : NameLinePair baz_branch[] = {{"baz", 25}, {"delay", 20}, {"loop", 18}};
681 0 : CheckBranch(foo_node, baz_branch, arraysize(baz_branch));
682 0 : NameLinePair delay_at22_branch[] = {{"delay", 22}, {"loop", 18}};
683 0 : CheckBranch(foo_node, delay_at22_branch, arraysize(delay_at22_branch));
684 0 : NameLinePair delay_at24_branch[] = {{"delay", 24}, {"loop", 18}};
685 0 : CheckBranch(foo_node, delay_at24_branch, arraysize(delay_at24_branch));
686 :
687 0 : profile->Delete();
688 0 : }
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 25875 : 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 0 : .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 25875 : TEST(CollectCpuProfileSamples) {
742 0 : i::FLAG_allow_natives_syntax = true;
743 0 : LocalContext env;
744 0 : v8::HandleScope scope(env->GetIsolate());
745 :
746 0 : CompileRun(cpu_profiler_test_source);
747 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
748 :
749 : int32_t profiling_interval_ms = 200;
750 : v8::Local<v8::Value> args[] = {
751 0 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
752 0 : ProfilerHelper helper(env.local());
753 : v8::CpuProfile* profile =
754 0 : helper.Run(function, args, arraysize(args), 1000, 0, true);
755 :
756 0 : CHECK_LE(200, profile->GetSamplesCount());
757 0 : uint64_t end_time = profile->GetEndTime();
758 0 : uint64_t current_time = profile->GetStartTime();
759 0 : CHECK_LE(current_time, end_time);
760 0 : for (int i = 0; i < profile->GetSamplesCount(); i++) {
761 0 : CHECK(profile->GetSample(i));
762 0 : uint64_t timestamp = profile->GetSampleTimestamp(i);
763 0 : CHECK_LE(current_time, timestamp);
764 0 : CHECK_LE(timestamp, end_time);
765 : current_time = timestamp;
766 : }
767 :
768 0 : profile->Delete();
769 0 : }
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 25875 : 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 15 : is_warming_up_(false) {}
830 :
831 515 : static void Getter(v8::Local<v8::String> name,
832 : const v8::PropertyCallbackInfo<v8::Value>& info) {
833 : TestApiCallbacks* data = FromInfo(info);
834 515 : data->Wait();
835 515 : }
836 :
837 515 : 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 515 : data->Wait();
842 515 : }
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 1550 : void Wait() {
853 3100 : if (is_warming_up_) return;
854 1505 : v8::Platform* platform = v8::internal::V8::GetCurrentPlatform();
855 1505 : double start = platform->CurrentClockTimeMillis();
856 : double duration = 0;
857 15818 : while (duration < min_duration_ms_) {
858 12808 : v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(1));
859 12808 : duration = platform->CurrentClockTimeMillis() - start;
860 : }
861 : }
862 :
863 : template <typename T>
864 520 : static TestApiCallbacks* FromInfo(const T& info) {
865 1550 : 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 25875 : TEST(NativeAccessorUninitializedIC) {
879 0 : LocalContext env;
880 0 : v8::Isolate* isolate = env->GetIsolate();
881 0 : v8::HandleScope scope(isolate);
882 :
883 : v8::Local<v8::FunctionTemplate> func_template =
884 0 : v8::FunctionTemplate::New(isolate);
885 : v8::Local<v8::ObjectTemplate> instance_template =
886 0 : func_template->InstanceTemplate();
887 :
888 : TestApiCallbacks accessors(100);
889 0 : v8::Local<v8::External> data = v8::External::New(isolate, &accessors);
890 : instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter,
891 0 : &TestApiCallbacks::Setter, data);
892 : v8::Local<v8::Function> func =
893 0 : func_template->GetFunction(env.local()).ToLocalChecked();
894 : v8::Local<v8::Object> instance =
895 0 : func->NewInstance(env.local()).ToLocalChecked();
896 0 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
897 :
898 0 : CompileRun(native_accessor_test_source);
899 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
900 :
901 0 : ProfilerHelper helper(env.local());
902 : int32_t repeat_count = 1;
903 0 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
904 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 100);
905 :
906 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
907 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
908 0 : GetChild(env.local(), start_node, "get foo");
909 0 : GetChild(env.local(), start_node, "set foo");
910 :
911 0 : profile->Delete();
912 0 : }
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 25880 : 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 : instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter,
932 10 : &TestApiCallbacks::Setter, data);
933 : v8::Local<v8::Function> func =
934 5 : func_template->GetFunction(env.local()).ToLocalChecked();
935 : v8::Local<v8::Object> instance =
936 5 : func->NewInstance(env.local()).ToLocalChecked();
937 25 : 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 5 : .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 10 : 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 25880 : 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 : proto_template->Set(
991 : v8_str("fooMethod"),
992 : v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
993 15 : signature, 0));
994 :
995 : v8::Local<v8::Function> func =
996 5 : func_template->GetFunction(env.local()).ToLocalChecked();
997 : v8::Local<v8::Object> instance =
998 5 : func->NewInstance(env.local()).ToLocalChecked();
999 25 : 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 10 : profile->Delete();
1014 5 : }
1015 :
1016 :
1017 25880 : 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 : proto_template->Set(
1034 : v8_str("fooMethod"),
1035 : v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
1036 15 : signature, 0));
1037 :
1038 : v8::Local<v8::Function> func =
1039 5 : func_template->GetFunction(env.local()).ToLocalChecked();
1040 : v8::Local<v8::Object> instance =
1041 5 : func->NewInstance(env.local()).ToLocalChecked();
1042 25 : 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 5 : .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 10 : 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 25880 : TEST(BoundFunctionCall) {
1083 5 : 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 10 : 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 9 : 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 18 : 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 : profiler_listener.CodeCreateEvent(i::Logger::FUNCTION_TAG, code,
1165 18 : 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 18 : 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 9 : 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 90 : 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 0 : 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 18 : CHECK_EQ(hit_count, value);
1207 9 : }
1208 :
1209 25880 : TEST(TickLinesBaseline) { TickLines(false); }
1210 :
1211 25879 : 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 25880 : 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 5 : FindChild(env.local(), root, i::CodeEntry::kUnresolvedFunctionName);
1265 5 : CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "call"));
1266 :
1267 10 : 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 25875 : TEST(FunctionApplySample) {
1299 0 : i::FLAG_allow_natives_syntax = true;
1300 0 : LocalContext env;
1301 0 : v8::HandleScope scope(env->GetIsolate());
1302 :
1303 0 : CompileRun(function_apply_test_source);
1304 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
1305 :
1306 0 : ProfilerHelper helper(env.local());
1307 : int32_t duration_ms = 100;
1308 : v8::Local<v8::Value> args[] = {
1309 0 : v8::Integer::New(env->GetIsolate(), duration_ms)};
1310 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
1311 :
1312 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1313 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
1314 : const v8::CpuProfileNode* test_node =
1315 0 : GetChild(env.local(), start_node, "test");
1316 0 : GetChild(env.local(), test_node, "bar");
1317 :
1318 : const v8::CpuProfileNode* unresolved_node =
1319 0 : FindChild(env.local(), start_node, CodeEntry::kUnresolvedFunctionName);
1320 0 : CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "apply"));
1321 :
1322 0 : profile->Delete();
1323 0 : }
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 25875 : TEST(CpuProfileDeepStack) {
1348 0 : v8::HandleScope scope(CcTest::isolate());
1349 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1350 : v8::Context::Scope context_scope(env);
1351 0 : ProfilerHelper helper(env);
1352 :
1353 0 : CompileRun(cpu_profiler_deep_stack_test_source);
1354 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1355 :
1356 0 : v8::Local<v8::String> profile_name = v8_str("my_profile");
1357 0 : function->Call(env, env->Global(), 0, nullptr).ToLocalChecked();
1358 0 : v8::CpuProfile* profile = helper.profiler()->StopProfiling(profile_name);
1359 0 : CHECK(profile);
1360 : // Dump collected profile to have a better diagnostic in case of failure.
1361 0 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1362 :
1363 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1364 0 : const v8::CpuProfileNode* node = GetChild(env, root, "start");
1365 0 : for (int i = 0; i <= 250; ++i) {
1366 0 : node = GetChild(env, node, "foo");
1367 : }
1368 0 : CHECK(!FindChild(env, node, "foo"));
1369 :
1370 0 : profile->Delete();
1371 0 : }
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 : function->Call(info.GetIsolate()->GetCurrentContext(), info.This(),
1393 0 : arraysize(argv), argv)
1394 0 : .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 25875 : 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 25875 : 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 25875 : 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 0 : .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 0 : .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 25880 : TEST(CollectSampleAPI) {
1562 5 : 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 10 : 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 25875 : 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 25880 : TEST(Inlining) {
1680 5 : i::FLAG_allow_natives_syntax = true;
1681 5 : 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 10 : 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 25880 : TEST(Inlining2) {
1777 5 : FLAG_allow_natives_syntax = true;
1778 5 : 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 10 : profiler->Dispose();
1833 5 : }
1834 :
1835 : // [Top down]:
1836 : // 0 (root) #0 1
1837 : // 2 (program) #0 2
1838 : // 3 (idle) #0 3
1839 25880 : 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 20 : 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 5 : 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 5 : 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 10 : cpu_profiler->Dispose();
1877 5 : }
1878 :
1879 29 : static void CheckFunctionDetails(v8::Isolate* isolate,
1880 : const v8::CpuProfileNode* node,
1881 : const char* name, const char* script_name,
1882 : int script_id, int line, int column) {
1883 29 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
1884 116 : CHECK(v8_str(name)->Equals(context, node->GetFunctionName()).FromJust());
1885 29 : CHECK_EQ(0, strcmp(name, node->GetFunctionNameStr()));
1886 116 : CHECK(v8_str(script_name)
1887 : ->Equals(context, node->GetScriptResourceName())
1888 : .FromJust());
1889 29 : CHECK_EQ(0, strcmp(script_name, node->GetScriptResourceNameStr()));
1890 29 : CHECK_EQ(script_id, node->GetScriptId());
1891 29 : CHECK_EQ(line, node->GetLineNumber());
1892 29 : CHECK_EQ(column, node->GetColumnNumber());
1893 29 : }
1894 :
1895 :
1896 25880 : TEST(FunctionDetails) {
1897 5 : i::FLAG_allow_natives_syntax = true;
1898 5 : v8::HandleScope scope(CcTest::isolate());
1899 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1900 : v8::Context::Scope context_scope(env);
1901 5 : ProfilerHelper helper(env);
1902 :
1903 : v8::Local<v8::Script> script_a = CompileWithOrigin(
1904 : "%NeverOptimizeFunction(foo);\n"
1905 : "%NeverOptimizeFunction(bar);\n"
1906 : " function foo\n() { bar(); }\n"
1907 : " function bar() { startProfiling(); }\n",
1908 5 : "script_a");
1909 5 : script_a->Run(env).ToLocalChecked();
1910 : v8::Local<v8::Script> script_b = CompileWithOrigin(
1911 : "%NeverOptimizeFunction(baz);"
1912 : "\n\n function baz() { foo(); }\n"
1913 : "\n\nbaz();\n"
1914 : "stopProfiling();\n",
1915 5 : "script_b");
1916 5 : script_b->Run(env).ToLocalChecked();
1917 5 : const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1918 5 : const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1919 : reinterpret_cast<ProfileNode*>(
1920 5 : const_cast<v8::CpuProfileNode*>(current))->Print(0);
1921 : // The tree should look like this:
1922 : // 0 (root) 0 #1
1923 : // 0 "" 19 #2 no reason script_b:1
1924 : // 0 baz 19 #3 TryCatchStatement script_b:3
1925 : // 0 foo 18 #4 TryCatchStatement script_a:2
1926 : // 1 bar 18 #5 no reason script_a:3
1927 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1928 5 : const v8::CpuProfileNode* script = GetChild(env, root, "");
1929 : CheckFunctionDetails(env->GetIsolate(), script, "", "script_b",
1930 15 : script_b->GetUnboundScript()->GetId(), 1, 1);
1931 5 : const v8::CpuProfileNode* baz = GetChild(env, script, "baz");
1932 : CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b",
1933 15 : script_b->GetUnboundScript()->GetId(), 3, 16);
1934 5 : const v8::CpuProfileNode* foo = GetChild(env, baz, "foo");
1935 : CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a",
1936 15 : script_a->GetUnboundScript()->GetId(), 4, 1);
1937 5 : const v8::CpuProfileNode* bar = GetChild(env, foo, "bar");
1938 : CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a",
1939 20 : script_a->GetUnboundScript()->GetId(), 5, 14);
1940 5 : }
1941 :
1942 25880 : TEST(FunctionDetailsInlining) {
1943 7 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
1944 3 : i::FLAG_allow_natives_syntax = true;
1945 3 : v8::HandleScope scope(CcTest::isolate());
1946 6 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
1947 : v8::Context::Scope context_scope(env);
1948 3 : ProfilerHelper helper(env);
1949 :
1950 : // alpha is in a_script, beta in b_script. beta is
1951 : // inlined in alpha, but it should be attributed to b_script.
1952 :
1953 : v8::Local<v8::Script> script_b = CompileWithOrigin(
1954 : "function beta(k) {\n"
1955 : " let sum = 2;\n"
1956 : " for(let i = 0; i < k; i ++) {\n"
1957 : " sum += i;\n"
1958 : " sum = sum + 'a';\n"
1959 : " }\n"
1960 : " return sum;\n"
1961 : "}\n"
1962 : "\n",
1963 3 : "script_b");
1964 :
1965 : v8::Local<v8::Script> script_a = CompileWithOrigin(
1966 : "function alpha(p) {\n"
1967 : " let res = beta(p);\n"
1968 : " res = res + res;\n"
1969 : " return res;\n"
1970 : "}\n"
1971 : "let p = 2;\n"
1972 : "\n"
1973 : "\n"
1974 : "// Warm up before profiling or the inlining doesn't happen.\n"
1975 : "p = alpha(p);\n"
1976 : "p = alpha(p);\n"
1977 : "%OptimizeFunctionOnNextCall(alpha);\n"
1978 : "p = alpha(p);\n"
1979 : "\n"
1980 : "\n"
1981 : "startProfiling();\n"
1982 : "for(let i = 0; i < 10000; i++) {\n"
1983 : " p = alpha(p);\n"
1984 : "}\n"
1985 : "stopProfiling();\n"
1986 : "\n"
1987 : "\n",
1988 3 : "script_a");
1989 :
1990 3 : script_b->Run(env).ToLocalChecked();
1991 3 : script_a->Run(env).ToLocalChecked();
1992 :
1993 3 : const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1994 3 : const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1995 : reinterpret_cast<ProfileNode*>(const_cast<v8::CpuProfileNode*>(current))
1996 3 : ->Print(0);
1997 : // The tree should look like this:
1998 : // 0 (root) 0 #1
1999 : // 5 (program) 0 #6
2000 : // 2 14 #2 script_a:1
2001 : // ;;; deopted at script_id: 14 position: 299 with reason 'Insufficient
2002 : // type feedback for call'.
2003 : // 1 alpha 14 #4 script_a:1
2004 : // 9 beta 13 #5 script_b:0
2005 : // 0 startProfiling 0 #3
2006 :
2007 3 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
2008 3 : const v8::CpuProfileNode* script = GetChild(env, root, "");
2009 : CheckFunctionDetails(env->GetIsolate(), script, "", "script_a",
2010 9 : script_a->GetUnboundScript()->GetId(), 1, 1);
2011 3 : const v8::CpuProfileNode* alpha = FindChild(env, script, "alpha");
2012 : // Return early if profiling didn't sample alpha.
2013 3 : if (!alpha) return;
2014 : CheckFunctionDetails(env->GetIsolate(), alpha, "alpha", "script_a",
2015 9 : script_a->GetUnboundScript()->GetId(), 1, 15);
2016 3 : const v8::CpuProfileNode* beta = FindChild(env, alpha, "beta");
2017 3 : if (!beta) return;
2018 : CheckFunctionDetails(env->GetIsolate(), beta, "beta", "script_b",
2019 12 : script_b->GetUnboundScript()->GetId(), 1, 14);
2020 : }
2021 :
2022 25880 : TEST(DontStopOnFinishedProfileDelete) {
2023 5 : v8::HandleScope scope(CcTest::isolate());
2024 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2025 : v8::Context::Scope context_scope(env);
2026 :
2027 5 : v8::CpuProfiler* profiler = v8::CpuProfiler::New(env->GetIsolate());
2028 : i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
2029 :
2030 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2031 5 : v8::Local<v8::String> outer = v8_str("outer");
2032 5 : profiler->StartProfiling(outer);
2033 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2034 :
2035 5 : v8::Local<v8::String> inner = v8_str("inner");
2036 5 : profiler->StartProfiling(inner);
2037 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2038 :
2039 5 : v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
2040 5 : CHECK(inner_profile);
2041 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
2042 5 : inner_profile->Delete();
2043 : inner_profile = nullptr;
2044 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2045 :
2046 5 : v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
2047 5 : CHECK(outer_profile);
2048 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
2049 5 : outer_profile->Delete();
2050 : outer_profile = nullptr;
2051 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
2052 10 : profiler->Dispose();
2053 5 : }
2054 :
2055 :
2056 0 : const char* GetBranchDeoptReason(v8::Local<v8::Context> context,
2057 : i::CpuProfile* iprofile, const char* branch[],
2058 : int length) {
2059 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2060 : const ProfileNode* iopt_function = nullptr;
2061 0 : iopt_function = GetSimpleBranch(context, profile, branch, length);
2062 0 : CHECK_EQ(1U, iopt_function->deopt_infos().size());
2063 0 : return iopt_function->deopt_infos()[0].deopt_reason;
2064 : }
2065 :
2066 :
2067 : // deopt at top function
2068 25875 : TEST(CollectDeoptEvents) {
2069 0 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2070 0 : i::FLAG_allow_natives_syntax = true;
2071 0 : v8::HandleScope scope(CcTest::isolate());
2072 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2073 : v8::Context::Scope context_scope(env);
2074 0 : ProfilerHelper helper(env);
2075 : i::CpuProfiler* iprofiler =
2076 0 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2077 :
2078 : const char opt_source[] =
2079 : "function opt_function%d(value, depth) {\n"
2080 : " if (depth) return opt_function%d(value, depth - 1);\n"
2081 : "\n"
2082 : " return 10 / value;\n"
2083 : "}\n"
2084 0 : "\n";
2085 :
2086 0 : for (int i = 0; i < 3; ++i) {
2087 : i::EmbeddedVector<char, sizeof(opt_source) + 100> buffer;
2088 0 : i::SNPrintF(buffer, opt_source, i, i);
2089 0 : v8::Script::Compile(env, v8_str(buffer.start()))
2090 0 : .ToLocalChecked()
2091 : ->Run(env)
2092 0 : .ToLocalChecked();
2093 : }
2094 :
2095 : const char* source =
2096 : "startProfiling();\n"
2097 : "\n"
2098 : "opt_function0(1, 1);\n"
2099 : "\n"
2100 : "%OptimizeFunctionOnNextCall(opt_function0)\n"
2101 : "\n"
2102 : "opt_function0(1, 1);\n"
2103 : "\n"
2104 : "opt_function0(undefined, 1);\n"
2105 : "\n"
2106 : "opt_function1(1, 1);\n"
2107 : "\n"
2108 : "%OptimizeFunctionOnNextCall(opt_function1)\n"
2109 : "\n"
2110 : "opt_function1(1, 1);\n"
2111 : "\n"
2112 : "opt_function1(NaN, 1);\n"
2113 : "\n"
2114 : "opt_function2(1, 1);\n"
2115 : "\n"
2116 : "%OptimizeFunctionOnNextCall(opt_function2)\n"
2117 : "\n"
2118 : "opt_function2(1, 1);\n"
2119 : "\n"
2120 : "opt_function2(0, 1);\n"
2121 : "\n"
2122 : "stopProfiling();\n"
2123 : "\n";
2124 :
2125 0 : v8::Script::Compile(env, v8_str(source))
2126 0 : .ToLocalChecked()
2127 : ->Run(env)
2128 0 : .ToLocalChecked();
2129 0 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2130 0 : iprofile->Print();
2131 : /* The expected profile
2132 : [Top down]:
2133 : 0 (root) 0 #1
2134 : 23 32 #2
2135 : 1 opt_function2 31 #7
2136 : 1 opt_function2 31 #8
2137 : ;;; deopted at script_id: 31 position: 106 with reason
2138 : 'division by zero'.
2139 : 2 opt_function0 29 #3
2140 : 4 opt_function0 29 #4
2141 : ;;; deopted at script_id: 29 position: 108 with reason 'not a
2142 : heap number'.
2143 : 0 opt_function1 30 #5
2144 : 1 opt_function1 30 #6
2145 : ;;; deopted at script_id: 30 position: 108 with reason 'lost
2146 : precision or NaN'.
2147 : */
2148 :
2149 : {
2150 0 : const char* branch[] = {"", "opt_function0", "opt_function0"};
2151 : const char* deopt_reason =
2152 0 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch));
2153 0 : if (deopt_reason != reason(i::DeoptimizeReason::kNotAHeapNumber) &&
2154 : deopt_reason != reason(i::DeoptimizeReason::kNotASmi)) {
2155 0 : FATAL("%s", deopt_reason);
2156 : }
2157 : }
2158 : {
2159 0 : const char* branch[] = {"", "opt_function1", "opt_function1"};
2160 : const char* deopt_reason =
2161 0 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch));
2162 0 : if (deopt_reason != reason(i::DeoptimizeReason::kNaN) &&
2163 0 : deopt_reason != reason(i::DeoptimizeReason::kLostPrecisionOrNaN) &&
2164 : deopt_reason != reason(i::DeoptimizeReason::kNotASmi)) {
2165 0 : FATAL("%s", deopt_reason);
2166 : }
2167 : }
2168 : {
2169 0 : const char* branch[] = {"", "opt_function2", "opt_function2"};
2170 0 : CHECK_EQ(reason(i::DeoptimizeReason::kDivisionByZero),
2171 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch)));
2172 : }
2173 0 : iprofiler->DeleteProfile(iprofile);
2174 : }
2175 :
2176 :
2177 25880 : TEST(SourceLocation) {
2178 5 : i::FLAG_always_opt = true;
2179 5 : LocalContext env;
2180 10 : v8::HandleScope scope(CcTest::isolate());
2181 :
2182 : const char* source =
2183 : "function CompareStatementWithThis() {\n"
2184 : " if (this === 1) {}\n"
2185 : "}\n"
2186 : "CompareStatementWithThis();\n";
2187 :
2188 5 : v8::Script::Compile(env.local(), v8_str(source))
2189 5 : .ToLocalChecked()
2190 5 : ->Run(env.local())
2191 10 : .ToLocalChecked();
2192 5 : }
2193 :
2194 : static const char* inlined_source =
2195 : "function opt_function(left, right) { var k = left*right; return k + 1; "
2196 : "}\n";
2197 : // 0.........1.........2.........3.........4....*....5.........6......*..7
2198 :
2199 :
2200 : // deopt at the first level inlined function
2201 25875 : TEST(DeoptAtFirstLevelInlinedSource) {
2202 0 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2203 0 : i::FLAG_allow_natives_syntax = true;
2204 0 : v8::HandleScope scope(CcTest::isolate());
2205 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2206 : v8::Context::Scope context_scope(env);
2207 0 : ProfilerHelper helper(env);
2208 : i::CpuProfiler* iprofiler =
2209 0 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2210 :
2211 : // 0.........1.........2.........3.........4.........5.........6.........7
2212 : const char* source =
2213 : "function test(left, right) { return opt_function(left, right); }\n"
2214 : "\n"
2215 : "startProfiling();\n"
2216 : "\n"
2217 : "test(10, 10);\n"
2218 : "\n"
2219 : "%OptimizeFunctionOnNextCall(test)\n"
2220 : "\n"
2221 : "test(10, 10);\n"
2222 : "\n"
2223 : "test(undefined, 1e9);\n"
2224 : "\n"
2225 : "stopProfiling();\n"
2226 : "\n";
2227 :
2228 0 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2229 0 : inlined_script->Run(env).ToLocalChecked();
2230 0 : int inlined_script_id = inlined_script->GetUnboundScript()->GetId();
2231 :
2232 : v8::Local<v8::Script> script = v8_compile(source);
2233 0 : script->Run(env).ToLocalChecked();
2234 0 : int script_id = script->GetUnboundScript()->GetId();
2235 :
2236 0 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2237 0 : iprofile->Print();
2238 : /* The expected profile output
2239 : [Top down]:
2240 : 0 (root) 0 #1
2241 : 10 30 #2
2242 : 1 test 30 #3
2243 : ;;; deopted at script_id: 29 position: 45 with reason 'not a
2244 : heap number'.
2245 : ;;; Inline point: script_id 30 position: 36.
2246 : 4 opt_function 29 #4
2247 : */
2248 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2249 :
2250 0 : const char* branch[] = {"", "test"};
2251 : const ProfileNode* itest_node =
2252 0 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2253 0 : const std::vector<v8::CpuProfileDeoptInfo>& deopt_infos =
2254 : itest_node->deopt_infos();
2255 0 : CHECK_EQ(1U, deopt_infos.size());
2256 :
2257 : const v8::CpuProfileDeoptInfo& info = deopt_infos[0];
2258 0 : CHECK(reason(i::DeoptimizeReason::kNotASmi) == info.deopt_reason ||
2259 : reason(i::DeoptimizeReason::kNotAHeapNumber) == info.deopt_reason);
2260 0 : CHECK_EQ(2U, info.stack.size());
2261 0 : CHECK_EQ(inlined_script_id, info.stack[0].script_id);
2262 0 : CHECK_LE(dist(offset(inlined_source, "*right"), info.stack[0].position), 1);
2263 0 : CHECK_EQ(script_id, info.stack[1].script_id);
2264 0 : CHECK_EQ(offset(source, "opt_function(left,"), info.stack[1].position);
2265 :
2266 0 : iprofiler->DeleteProfile(iprofile);
2267 : }
2268 :
2269 :
2270 : // deopt at the second level inlined function
2271 25875 : TEST(DeoptAtSecondLevelInlinedSource) {
2272 0 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2273 0 : i::FLAG_allow_natives_syntax = true;
2274 0 : v8::HandleScope scope(CcTest::isolate());
2275 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2276 : v8::Context::Scope context_scope(env);
2277 0 : ProfilerHelper helper(env);
2278 : i::CpuProfiler* iprofiler =
2279 0 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2280 :
2281 : // 0.........1.........2.........3.........4.........5.........6.........7
2282 : const char* source =
2283 : "function test2(left, right) { return opt_function(left, right); }\n"
2284 : "function test1(left, right) { return test2(left, right); } \n"
2285 : "\n"
2286 : "startProfiling();\n"
2287 : "\n"
2288 : "test1(10, 10);\n"
2289 : "\n"
2290 : "%OptimizeFunctionOnNextCall(test1)\n"
2291 : "\n"
2292 : "test1(10, 10);\n"
2293 : "\n"
2294 : "test1(undefined, 1e9);\n"
2295 : "\n"
2296 : "stopProfiling();\n"
2297 : "\n";
2298 :
2299 0 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2300 0 : inlined_script->Run(env).ToLocalChecked();
2301 0 : int inlined_script_id = inlined_script->GetUnboundScript()->GetId();
2302 :
2303 : v8::Local<v8::Script> script = v8_compile(source);
2304 0 : script->Run(env).ToLocalChecked();
2305 0 : int script_id = script->GetUnboundScript()->GetId();
2306 :
2307 0 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2308 0 : iprofile->Print();
2309 : /* The expected profile output
2310 : [Top down]:
2311 : 0 (root) 0 #1
2312 : 11 30 #2
2313 : 1 test1 30 #3
2314 : ;;; deopted at script_id: 29 position: 45 with reason 'not a
2315 : heap number'.
2316 : ;;; Inline point: script_id 30 position: 37.
2317 : ;;; Inline point: script_id 30 position: 103.
2318 : 1 test2 30 #4
2319 : 3 opt_function 29 #5
2320 : */
2321 :
2322 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2323 :
2324 0 : const char* branch[] = {"", "test1"};
2325 : const ProfileNode* itest_node =
2326 0 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2327 0 : const std::vector<v8::CpuProfileDeoptInfo>& deopt_infos =
2328 : itest_node->deopt_infos();
2329 0 : CHECK_EQ(1U, deopt_infos.size());
2330 :
2331 : const v8::CpuProfileDeoptInfo info = deopt_infos[0];
2332 0 : CHECK(reason(i::DeoptimizeReason::kNotASmi) == info.deopt_reason ||
2333 : reason(i::DeoptimizeReason::kNotAHeapNumber) == info.deopt_reason);
2334 0 : CHECK_EQ(3U, info.stack.size());
2335 0 : CHECK_EQ(inlined_script_id, info.stack[0].script_id);
2336 0 : CHECK_LE(dist(offset(inlined_source, "*right"), info.stack[0].position), 1);
2337 0 : CHECK_EQ(script_id, info.stack[1].script_id);
2338 0 : CHECK_EQ(offset(source, "opt_function(left,"), info.stack[1].position);
2339 0 : CHECK_EQ(offset(source, "test2(left, right);"), info.stack[2].position);
2340 :
2341 0 : iprofiler->DeleteProfile(iprofile);
2342 : }
2343 :
2344 :
2345 : // deopt in untracked function
2346 25875 : TEST(DeoptUntrackedFunction) {
2347 0 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2348 0 : i::FLAG_allow_natives_syntax = true;
2349 0 : v8::HandleScope scope(CcTest::isolate());
2350 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2351 : v8::Context::Scope context_scope(env);
2352 0 : ProfilerHelper helper(env);
2353 : i::CpuProfiler* iprofiler =
2354 0 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2355 :
2356 : // 0.........1.........2.........3.........4.........5.........6.........7
2357 : const char* source =
2358 : "function test(left, right) { return opt_function(left, right); }\n"
2359 : "\n"
2360 : "test(10, 10);\n"
2361 : "\n"
2362 : "%OptimizeFunctionOnNextCall(test)\n"
2363 : "\n"
2364 : "test(10, 10);\n"
2365 : "\n"
2366 : "startProfiling();\n" // profiler started after compilation.
2367 : "\n"
2368 : "test(undefined, 10);\n"
2369 : "\n"
2370 : "stopProfiling();\n"
2371 : "\n";
2372 :
2373 0 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2374 0 : inlined_script->Run(env).ToLocalChecked();
2375 :
2376 : v8::Local<v8::Script> script = v8_compile(source);
2377 0 : script->Run(env).ToLocalChecked();
2378 :
2379 0 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2380 0 : iprofile->Print();
2381 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2382 :
2383 0 : const char* branch[] = {"", "test"};
2384 : const ProfileNode* itest_node =
2385 0 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2386 0 : CHECK_EQ(0U, itest_node->deopt_infos().size());
2387 :
2388 0 : iprofiler->DeleteProfile(iprofile);
2389 : }
2390 :
2391 : using v8::platform::tracing::TraceBuffer;
2392 : using v8::platform::tracing::TraceConfig;
2393 : using v8::platform::tracing::TraceObject;
2394 :
2395 : namespace {
2396 :
2397 0 : class CpuProfileEventChecker : public v8::platform::tracing::TraceWriter {
2398 : public:
2399 0 : void AppendTraceEvent(TraceObject* trace_event) override {
2400 0 : if (trace_event->name() != std::string("Profile") &&
2401 0 : trace_event->name() != std::string("ProfileChunk"))
2402 0 : return;
2403 0 : CHECK(!profile_id_ || trace_event->id() == profile_id_);
2404 0 : CHECK_EQ(1, trace_event->num_args());
2405 0 : CHECK_EQ(TRACE_VALUE_TYPE_CONVERTABLE, trace_event->arg_types()[0]);
2406 0 : profile_id_ = trace_event->id();
2407 : v8::ConvertableToTraceFormat* arg =
2408 : trace_event->arg_convertables()[0].get();
2409 0 : result_json_ += result_json_.empty() ? "[" : ",\n";
2410 0 : arg->AppendAsTraceFormat(&result_json_);
2411 : }
2412 0 : void Flush() override { result_json_ += "]"; }
2413 :
2414 : const std::string& result_json() const { return result_json_; }
2415 : void Reset() {
2416 : result_json_.clear();
2417 0 : profile_id_ = 0;
2418 : }
2419 :
2420 : private:
2421 : std::string result_json_;
2422 : uint64_t profile_id_ = 0;
2423 : };
2424 :
2425 : } // namespace
2426 :
2427 25875 : TEST(TracingCpuProfiler) {
2428 0 : v8::HandleScope scope(CcTest::isolate());
2429 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2430 : v8::Context::Scope context_scope(env);
2431 :
2432 0 : CpuProfileEventChecker* event_checker = new CpuProfileEventChecker();
2433 : TraceBuffer* ring_buffer =
2434 0 : TraceBuffer::CreateTraceBufferRingBuffer(1, event_checker);
2435 : auto* tracing_controller =
2436 : static_cast<v8::platform::tracing::TracingController*>(
2437 0 : i::V8::GetCurrentPlatform()->GetTracingController());
2438 0 : tracing_controller->Initialize(ring_buffer);
2439 :
2440 : bool result = false;
2441 0 : for (int run_duration = 50; !result; run_duration += 50) {
2442 0 : TraceConfig* trace_config = new TraceConfig();
2443 : trace_config->AddIncludedCategory(
2444 0 : TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"));
2445 : trace_config->AddIncludedCategory(
2446 0 : TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler.hires"));
2447 :
2448 : std::string test_code = R"(
2449 : function foo() {
2450 : let s = 0;
2451 0 : const endTime = Date.now() + )" +
2452 : std::to_string(run_duration) + R"(
2453 : while (Date.now() < endTime) s += Math.cos(s);
2454 : return s;
2455 : }
2456 : foo();)";
2457 :
2458 0 : tracing_controller->StartTracing(trace_config);
2459 : CompileRun(test_code.c_str());
2460 0 : tracing_controller->StopTracing();
2461 :
2462 0 : std::string profile_json = event_checker->result_json();
2463 : event_checker->Reset();
2464 0 : CHECK_LT(0u, profile_json.length());
2465 : printf("Profile JSON: %s\n", profile_json.c_str());
2466 :
2467 : std::string profile_checker_code = R"(
2468 : function checkProfile(json) {
2469 : const profile_header = json[0];
2470 : if (typeof profile_header['startTime'] !== 'number')
2471 : return false;
2472 : return json.some(event => (event.lines || []).some(line => line));
2473 : }
2474 0 : checkProfile()" + profile_json +
2475 : ")";
2476 0 : result = CompileRunChecked(CcTest::isolate(), profile_checker_code.c_str())
2477 0 : ->IsTrue();
2478 : }
2479 :
2480 : static_cast<v8::platform::tracing::TracingController*>(
2481 0 : i::V8::GetCurrentPlatform()->GetTracingController())
2482 0 : ->Initialize(nullptr);
2483 0 : }
2484 :
2485 25880 : TEST(Issue763073) {
2486 : class AllowNativesSyntax {
2487 : public:
2488 : AllowNativesSyntax()
2489 : : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
2490 5 : trace_deopt_(i::FLAG_trace_deopt) {
2491 5 : i::FLAG_allow_natives_syntax = true;
2492 5 : i::FLAG_trace_deopt = true;
2493 : }
2494 :
2495 : ~AllowNativesSyntax() {
2496 5 : i::FLAG_allow_natives_syntax = allow_natives_syntax_;
2497 5 : i::FLAG_trace_deopt = trace_deopt_;
2498 : }
2499 :
2500 : private:
2501 : bool allow_natives_syntax_;
2502 : bool trace_deopt_;
2503 : };
2504 :
2505 : AllowNativesSyntax allow_natives_syntax_scope;
2506 10 : LocalContext env;
2507 10 : v8::HandleScope scope(env->GetIsolate());
2508 :
2509 : CompileRun(
2510 : "function f() { return function g(x) { }; }"
2511 : // Create first closure, optimize it, and deoptimize it.
2512 : "var g = f();"
2513 : "g(1);"
2514 : "%OptimizeFunctionOnNextCall(g);"
2515 : "g(1);"
2516 : "%DeoptimizeFunction(g);"
2517 : // Create second closure, and optimize it. This will create another
2518 : // optimized code object and put in the (shared) type feedback vector.
2519 : "var h = f();"
2520 : "h(1);"
2521 : "%OptimizeFunctionOnNextCall(h);"
2522 : "h(1);");
2523 :
2524 : // Start profiling.
2525 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
2526 5 : v8::Local<v8::String> profile_name = v8_str("test");
2527 :
2528 : // Here we test that the heap iteration upon profiling start is not
2529 : // confused by having a deoptimized code object for a closure while
2530 : // having a different optimized code object in the type feedback vector.
2531 5 : cpu_profiler->StartProfiling(profile_name);
2532 5 : v8::CpuProfile* p = cpu_profiler->StopProfiling(profile_name);
2533 5 : p->Delete();
2534 5 : cpu_profiler->Dispose();
2535 5 : }
2536 :
2537 : static const char* js_collect_sample_api_source =
2538 : "%NeverOptimizeFunction(start);\n"
2539 : "function start() {\n"
2540 : " CallStaticCollectSample();\n"
2541 : "}";
2542 :
2543 0 : static void CallStaticCollectSample(
2544 0 : const v8::FunctionCallbackInfo<v8::Value>& info) {
2545 0 : v8::CpuProfiler::CollectSample(info.GetIsolate());
2546 0 : }
2547 :
2548 25875 : TEST(StaticCollectSampleAPI) {
2549 0 : i::FLAG_allow_natives_syntax = true;
2550 0 : LocalContext env;
2551 0 : v8::HandleScope scope(env->GetIsolate());
2552 :
2553 : v8::Local<v8::FunctionTemplate> func_template =
2554 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallStaticCollectSample);
2555 : v8::Local<v8::Function> func =
2556 0 : func_template->GetFunction(env.local()).ToLocalChecked();
2557 0 : func->SetName(v8_str("CallStaticCollectSample"));
2558 : env->Global()
2559 0 : ->Set(env.local(), v8_str("CallStaticCollectSample"), func)
2560 0 : .FromJust();
2561 :
2562 0 : CompileRun(js_collect_sample_api_source);
2563 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
2564 :
2565 0 : ProfilerHelper helper(env.local());
2566 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 100);
2567 :
2568 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
2569 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
2570 0 : GetChild(env.local(), start_node, "CallStaticCollectSample");
2571 :
2572 0 : profile->Delete();
2573 0 : }
2574 :
2575 25880 : TEST(CodeEntriesMemoryLeak) {
2576 5 : v8::HandleScope scope(CcTest::isolate());
2577 10 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2578 : v8::Context::Scope context_scope(env);
2579 :
2580 5 : std::string source = "function start() {}\n";
2581 5005 : for (int i = 0; i < 1000; ++i) {
2582 30000 : source += "function foo" + std::to_string(i) + "() { return " +
2583 5000 : std::to_string(i) +
2584 : "; }\n"
2585 15000 : "foo" +
2586 5000 : std::to_string(i) + "();\n";
2587 : }
2588 : CompileRun(source.c_str());
2589 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
2590 :
2591 5 : ProfilerHelper helper(env);
2592 :
2593 505 : for (int j = 0; j < 100; ++j) {
2594 500 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0);
2595 500 : profile->Delete();
2596 : }
2597 :
2598 : i::CpuProfiler* profiler =
2599 5 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2600 10 : CHECK(!profiler->profiler_listener_for_test());
2601 5 : }
2602 :
2603 25875 : TEST(NativeFrameStackTrace) {
2604 : // A test for issue https://crbug.com/768540
2605 : // When a sample lands in a native function which has not EXIT frame
2606 : // stack frame iterator used to bail out and produce an empty stack trace.
2607 : // The source code below makes v8 call the
2608 : // v8::internal::StringTable::LookupStringIfExists_NoAllocate native function
2609 : // without producing an EXIT frame.
2610 0 : v8::HandleScope scope(CcTest::isolate());
2611 0 : v8::Local<v8::Context> env = CcTest::NewContext({PROFILER_EXTENSION_ID});
2612 : v8::Context::Scope context_scope(env);
2613 :
2614 : const char* source = R"(
2615 : function jsFunction() {
2616 : var s = {};
2617 : for (var i = 0; i < 1e4; ++i) {
2618 : for (var j = 0; j < 100; j++) {
2619 : s['item' + j] = 'alph';
2620 : }
2621 : }
2622 : })";
2623 :
2624 : CompileRun(source);
2625 0 : v8::Local<v8::Function> function = GetFunction(env, "jsFunction");
2626 :
2627 0 : ProfilerHelper helper(env);
2628 :
2629 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 100, 0, true);
2630 :
2631 : // Count the fraction of samples landing in 'jsFunction' (valid stack)
2632 : // vs '(program)' (no stack captured).
2633 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
2634 0 : const v8::CpuProfileNode* js_function = FindChild(root, "jsFunction");
2635 0 : const v8::CpuProfileNode* program = FindChild(root, "(program)");
2636 0 : if (program) {
2637 0 : unsigned js_function_samples = TotalHitCount(js_function);
2638 0 : unsigned program_samples = TotalHitCount(program);
2639 : double valid_samples_ratio =
2640 0 : 1. * js_function_samples / (js_function_samples + program_samples);
2641 0 : i::PrintF("Ratio: %f\n", valid_samples_ratio);
2642 : // TODO(alph): Investigate other causes of dropped frames. The ratio
2643 : // should be close to 99%.
2644 0 : CHECK_GE(valid_samples_ratio, 0.3);
2645 : }
2646 :
2647 0 : profile->Delete();
2648 0 : }
2649 :
2650 25880 : TEST(SourcePositionTable) {
2651 : i::SourcePositionTable info;
2652 :
2653 : // Newly created tables should return NoLineNumberInfo for any lookup.
2654 : int no_info = v8::CpuProfileNode::kNoLineNumberInfo;
2655 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(std::numeric_limits<int>::min()));
2656 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(0));
2657 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(0));
2658 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(1));
2659 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(9));
2660 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(10));
2661 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(11));
2662 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(19));
2663 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(20));
2664 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(21));
2665 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(100));
2666 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(100));
2667 5 : CHECK_EQ(no_info, info.GetSourceLineNumber(std::numeric_limits<int>::max()));
2668 :
2669 5 : info.SetPosition(10, 1, SourcePosition::kNotInlined);
2670 5 : info.SetPosition(20, 2, SourcePosition::kNotInlined);
2671 :
2672 : // The only valid return values are 1 or 2 - every pc maps to a line
2673 : // number.
2674 5 : CHECK_EQ(1, info.GetSourceLineNumber(std::numeric_limits<int>::min()));
2675 5 : CHECK_EQ(1, info.GetSourceLineNumber(0));
2676 5 : CHECK_EQ(1, info.GetSourceLineNumber(1));
2677 5 : CHECK_EQ(1, info.GetSourceLineNumber(9));
2678 5 : CHECK_EQ(1, info.GetSourceLineNumber(10));
2679 5 : CHECK_EQ(1, info.GetSourceLineNumber(11));
2680 5 : CHECK_EQ(1, info.GetSourceLineNumber(19));
2681 5 : CHECK_EQ(1, info.GetSourceLineNumber(20));
2682 5 : CHECK_EQ(2, info.GetSourceLineNumber(21));
2683 5 : CHECK_EQ(2, info.GetSourceLineNumber(100));
2684 5 : CHECK_EQ(2, info.GetSourceLineNumber(std::numeric_limits<int>::max()));
2685 :
2686 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(0));
2687 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(100));
2688 :
2689 : // Test SetPosition behavior.
2690 5 : info.SetPosition(25, 3, 0);
2691 5 : CHECK_EQ(2, info.GetSourceLineNumber(21));
2692 5 : CHECK_EQ(3, info.GetSourceLineNumber(100));
2693 5 : CHECK_EQ(3, info.GetSourceLineNumber(std::numeric_limits<int>::max()));
2694 :
2695 5 : CHECK_EQ(SourcePosition::kNotInlined, info.GetInliningId(21));
2696 5 : CHECK_EQ(0, info.GetInliningId(100));
2697 5 : }
2698 :
2699 25880 : TEST(MultipleProfilers) {
2700 5 : std::unique_ptr<CpuProfiler> profiler1(new CpuProfiler(CcTest::i_isolate()));
2701 5 : std::unique_ptr<CpuProfiler> profiler2(new CpuProfiler(CcTest::i_isolate()));
2702 5 : profiler1->StartProfiling("1");
2703 5 : profiler2->StartProfiling("2");
2704 5 : profiler1->StopProfiling("1");
2705 5 : profiler2->StopProfiling("2");
2706 5 : }
2707 :
2708 : // Tests that logged CodeCreateEvent calls do not crash a reused CpuProfiler.
2709 : // crbug.com/929928
2710 25880 : TEST(CrashReusedProfiler) {
2711 5 : LocalContext env;
2712 : i::Isolate* isolate = CcTest::i_isolate();
2713 : i::HandleScope scope(isolate);
2714 :
2715 5 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(isolate));
2716 5 : profiler->StartProfiling("1");
2717 5 : profiler->StopProfiling("1");
2718 :
2719 5 : profiler->StartProfiling("2");
2720 5 : CreateCode(&env);
2721 10 : profiler->StopProfiling("2");
2722 5 : }
2723 :
2724 : // Tests that samples from different profilers on the same isolate do not leak
2725 : // samples to each other. See crbug.com/v8/8835.
2726 25880 : TEST(MultipleProfilersSampleIndependently) {
2727 5 : LocalContext env;
2728 : i::Isolate* isolate = CcTest::i_isolate();
2729 : i::HandleScope scope(isolate);
2730 :
2731 : // Create two profilers- one slow ticking one, and one fast ticking one.
2732 : // Ensure that the slow ticking profiler does not receive samples from the
2733 : // fast ticking one.
2734 : std::unique_ptr<CpuProfiler> slow_profiler(
2735 5 : new CpuProfiler(CcTest::i_isolate()));
2736 5 : slow_profiler->set_sampling_interval(base::TimeDelta::FromSeconds(1));
2737 5 : slow_profiler->StartProfiling("1", true);
2738 :
2739 : CompileRun(R"(
2740 : function start() {
2741 : let val = 1;
2742 : for (let i = 0; i < 10e3; i++) {
2743 : val = (val * 2) % 3;
2744 : }
2745 : return val;
2746 : }
2747 : )");
2748 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
2749 5 : ProfilerHelper helper(env.local());
2750 5 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 100, 0, true);
2751 :
2752 5 : auto slow_profile = slow_profiler->StopProfiling("1");
2753 10 : CHECK_GT(profile->GetSamplesCount(), slow_profile->samples_count());
2754 5 : }
2755 :
2756 10 : void ProfileSomeCode(v8::Isolate* isolate) {
2757 : v8::Isolate::Scope isolate_scope(isolate);
2758 20 : v8::HandleScope scope(isolate);
2759 10 : LocalContext context(isolate);
2760 :
2761 10 : v8::CpuProfiler* profiler = v8::CpuProfiler::New(isolate);
2762 :
2763 10 : v8::Local<v8::String> profile_name = v8_str("1");
2764 10 : profiler->StartProfiling(profile_name);
2765 : const char* source = R"(
2766 : function foo() {
2767 : var x = 0;
2768 : for (var i = 0; i < 1e3; i++) {
2769 : for (var j = 0; j < 1e3; j++) {
2770 : x = i * j;
2771 : }
2772 : }
2773 : return x;
2774 : }
2775 : foo();
2776 : )";
2777 :
2778 : CompileRun(source);
2779 10 : profiler->StopProfiling(profile_name);
2780 10 : profiler->Dispose();
2781 10 : }
2782 :
2783 5 : class IsolateThread : public v8::base::Thread {
2784 : public:
2785 10 : IsolateThread() : Thread(Options("IsolateThread")) {}
2786 :
2787 10 : void Run() override {
2788 : v8::Isolate::CreateParams create_params;
2789 10 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
2790 10 : v8::Isolate* isolate = v8::Isolate::New(create_params);
2791 10 : ProfileSomeCode(isolate);
2792 10 : isolate->Dispose();
2793 10 : }
2794 : };
2795 :
2796 : // Checking for crashes and TSAN issues with multiple isolates profiling.
2797 25880 : TEST(MultipleIsolates) {
2798 : IsolateThread thread1;
2799 : IsolateThread thread2;
2800 :
2801 5 : thread1.Start();
2802 5 : thread2.Start();
2803 :
2804 5 : thread1.Join();
2805 5 : thread2.Join();
2806 5 : }
2807 :
2808 : // Tests that StopProfiling doesn't wait for the next sample tick in order to
2809 : // stop, but rather exits early before a given wait threshold.
2810 25880 : TEST(FastStopProfiling) {
2811 : static const base::TimeDelta kLongInterval = base::TimeDelta::FromSeconds(10);
2812 : static const base::TimeDelta kWaitThreshold = base::TimeDelta::FromSeconds(5);
2813 :
2814 5 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(CcTest::i_isolate()));
2815 5 : profiler->set_sampling_interval(kLongInterval);
2816 5 : profiler->StartProfiling("", true);
2817 :
2818 5 : v8::Platform* platform = v8::internal::V8::GetCurrentPlatform();
2819 5 : double start = platform->CurrentClockTimeMillis();
2820 5 : profiler->StopProfiling("");
2821 5 : double duration = platform->CurrentClockTimeMillis() - start;
2822 :
2823 10 : CHECK_LT(duration, kWaitThreshold.InMillisecondsF());
2824 5 : }
2825 :
2826 : enum class EntryCountMode { kAll, kOnlyInlined };
2827 :
2828 : // Count the number of unique source positions.
2829 30 : int GetSourcePositionEntryCount(i::Isolate* isolate, const char* source,
2830 : EntryCountMode mode = EntryCountMode::kAll) {
2831 30 : std::unordered_set<int64_t> raw_position_set;
2832 : i::Handle<i::JSFunction> function = i::Handle<i::JSFunction>::cast(
2833 30 : v8::Utils::OpenHandle(*CompileRun(source)));
2834 30 : if (function->IsInterpreted()) return -1;
2835 48 : i::Handle<i::Code> code(function->code(), isolate);
2836 : i::SourcePositionTableIterator iterator(
2837 24 : ByteArray::cast(code->source_position_table()));
2838 :
2839 242 : while (!iterator.done()) {
2840 262 : if (mode == EntryCountMode::kAll ||
2841 68 : iterator.source_position().isInlined()) {
2842 332 : raw_position_set.insert(iterator.source_position().raw());
2843 : }
2844 194 : iterator.Advance();
2845 : }
2846 24 : return static_cast<int>(raw_position_set.size());
2847 : }
2848 :
2849 25880 : UNINITIALIZED_TEST(DetailedSourcePositionAPI) {
2850 5 : i::FLAG_detailed_line_info = false;
2851 5 : i::FLAG_allow_natives_syntax = true;
2852 : v8::Isolate::CreateParams create_params;
2853 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
2854 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
2855 :
2856 : const char* source =
2857 : "function fib(i) {"
2858 : " if (i <= 1) return 1; "
2859 : " return fib(i - 1) +"
2860 : " fib(i - 2);"
2861 : "}"
2862 : "fib(5);"
2863 : "%OptimizeFunctionOnNextCall(fib);"
2864 : "fib(5);"
2865 : "fib";
2866 : {
2867 : v8::Isolate::Scope isolate_scope(isolate);
2868 10 : v8::HandleScope handle_scope(isolate);
2869 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
2870 : v8::Context::Scope context_scope(context);
2871 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2872 :
2873 5 : CHECK(!i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2874 :
2875 5 : int non_detailed_positions = GetSourcePositionEntryCount(i_isolate, source);
2876 :
2877 5 : v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
2878 5 : CHECK(i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2879 :
2880 5 : int detailed_positions = GetSourcePositionEntryCount(i_isolate, source);
2881 :
2882 5 : CHECK((non_detailed_positions == -1 && detailed_positions == -1) ||
2883 : non_detailed_positions < detailed_positions);
2884 : }
2885 :
2886 5 : isolate->Dispose();
2887 5 : }
2888 :
2889 25880 : UNINITIALIZED_TEST(DetailedSourcePositionAPI_Inlining) {
2890 5 : i::FLAG_detailed_line_info = false;
2891 5 : i::FLAG_stress_inline = true;
2892 5 : i::FLAG_always_opt = false;
2893 5 : i::FLAG_allow_natives_syntax = true;
2894 : v8::Isolate::CreateParams create_params;
2895 5 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
2896 5 : v8::Isolate* isolate = v8::Isolate::New(create_params);
2897 :
2898 : const char* source = R"(
2899 : function foo(x) {
2900 : return bar(x) + 1;
2901 : }
2902 :
2903 : function bar(x) {
2904 : var y = 1;
2905 : for (var i = 0; i < x; ++i) {
2906 : y = y * x;
2907 : }
2908 : return x;
2909 : }
2910 :
2911 : foo(5);
2912 : %OptimizeFunctionOnNextCall(foo);
2913 : foo(5);
2914 : foo;
2915 : )";
2916 :
2917 : {
2918 : v8::Isolate::Scope isolate_scope(isolate);
2919 10 : v8::HandleScope handle_scope(isolate);
2920 5 : v8::Local<v8::Context> context = v8::Context::New(isolate);
2921 : v8::Context::Scope context_scope(context);
2922 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2923 :
2924 5 : CHECK(!i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2925 :
2926 : int non_detailed_positions =
2927 5 : GetSourcePositionEntryCount(i_isolate, source, EntryCountMode::kAll);
2928 : int non_detailed_inlined_positions = GetSourcePositionEntryCount(
2929 5 : i_isolate, source, EntryCountMode::kOnlyInlined);
2930 :
2931 5 : v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
2932 5 : CHECK(i_isolate->NeedsDetailedOptimizedCodeLineInfo());
2933 :
2934 : int detailed_positions =
2935 5 : GetSourcePositionEntryCount(i_isolate, source, EntryCountMode::kAll);
2936 : int detailed_inlined_positions = GetSourcePositionEntryCount(
2937 5 : i_isolate, source, EntryCountMode::kOnlyInlined);
2938 :
2939 5 : if (non_detailed_positions == -1) {
2940 1 : CHECK_EQ(non_detailed_positions, detailed_positions);
2941 : } else {
2942 4 : CHECK_LT(non_detailed_positions, detailed_positions);
2943 4 : CHECK_LT(non_detailed_inlined_positions, detailed_inlined_positions);
2944 : }
2945 : }
2946 :
2947 5 : isolate->Dispose();
2948 5 : }
2949 :
2950 : } // namespace test_cpu_profiler
2951 : } // namespace internal
2952 77625 : } // namespace v8
|