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