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 "src/v8.h"
31 :
32 : #include "include/v8-profiler.h"
33 : #include "src/api.h"
34 : #include "src/base/platform/platform.h"
35 : #include "src/deoptimizer.h"
36 : #include "src/libplatform/default-platform.h"
37 : #include "src/objects-inl.h"
38 : #include "src/profiler/cpu-profiler-inl.h"
39 : #include "src/profiler/profiler-listener.h"
40 : #include "src/utils.h"
41 : #include "test/cctest/cctest.h"
42 : #include "test/cctest/profiler-extension.h"
43 :
44 : #include "include/libplatform/v8-tracing.h"
45 : #include "src/tracing/trace-event.h"
46 :
47 : namespace v8 {
48 : namespace internal {
49 : namespace test_cpu_profiler {
50 :
51 : // Helper methods
52 95 : static v8::Local<v8::Function> GetFunction(v8::Local<v8::Context> env,
53 : const char* name) {
54 : return v8::Local<v8::Function>::Cast(
55 380 : env->Global()->Get(env, v8_str(name)).ToLocalChecked());
56 : }
57 :
58 15 : static size_t offset(const char* src, const char* substring) {
59 : const char* it = strstr(src, substring);
60 15 : CHECK(it);
61 15 : return static_cast<size_t>(it - src);
62 : }
63 :
64 : template <typename A, typename B>
65 : static int dist(A a, B b) {
66 6 : return abs(static_cast<int>(a) - static_cast<int>(b));
67 : }
68 :
69 : static const char* reason(const i::DeoptimizeReason reason) {
70 6 : return i::DeoptimizeReasonToString(reason);
71 : }
72 :
73 23723 : TEST(StartStop) {
74 : i::Isolate* isolate = CcTest::i_isolate();
75 5 : CpuProfilesCollection profiles(isolate);
76 5 : ProfileGenerator generator(&profiles);
77 : std::unique_ptr<ProfilerEventsProcessor> processor(
78 : new ProfilerEventsProcessor(isolate, &generator,
79 5 : v8::base::TimeDelta::FromMicroseconds(100)));
80 5 : processor->Start();
81 10 : processor->StopSynchronously();
82 5 : }
83 :
84 30 : static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
85 : i::Address frame1,
86 : i::Address frame2 = nullptr,
87 : i::Address frame3 = nullptr) {
88 30 : v8::TickSample* sample = proc->StartTickSample();
89 30 : sample->pc = frame1;
90 30 : sample->tos = frame1;
91 30 : sample->frames_count = 0;
92 30 : if (frame2 != nullptr) {
93 10 : sample->stack[0] = frame2;
94 10 : sample->frames_count = 1;
95 : }
96 30 : if (frame3 != nullptr) {
97 5 : sample->stack[1] = frame3;
98 5 : sample->frames_count = 2;
99 : }
100 : proc->FinishTickSample();
101 30 : }
102 :
103 : namespace {
104 :
105 : class TestSetup {
106 : public:
107 : TestSetup()
108 25 : : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
109 25 : i::FLAG_prof_browser_mode = false;
110 : }
111 :
112 : ~TestSetup() {
113 25 : i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
114 : }
115 :
116 : private:
117 : bool old_flag_prof_browser_mode_;
118 : };
119 :
120 : } // namespace
121 :
122 40 : i::AbstractCode* CreateCode(LocalContext* env) {
123 : static int counter = 0;
124 : i::EmbeddedVector<char, 256> script;
125 : i::EmbeddedVector<char, 32> name;
126 :
127 40 : i::SNPrintF(name, "function_%d", ++counter);
128 40 : const char* name_start = name.start();
129 : i::SNPrintF(script,
130 : "function %s() {\n"
131 : "var counter = 0;\n"
132 : "for (var i = 0; i < %d; ++i) counter += i;\n"
133 : "return '%s_' + counter;\n"
134 : "}\n"
135 40 : "%s();\n", name_start, counter, name_start, name_start);
136 40 : CompileRun(script.start());
137 :
138 : i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(
139 80 : v8::Utils::OpenHandle(*GetFunction(env->local(), name_start)));
140 40 : return fun->abstract_code();
141 : }
142 :
143 23723 : TEST(CodeEvents) {
144 5 : CcTest::InitializeVM();
145 5 : LocalContext env;
146 : i::Isolate* isolate = CcTest::i_isolate();
147 : i::Factory* factory = isolate->factory();
148 : TestSetup test_setup;
149 :
150 : i::HandleScope scope(isolate);
151 :
152 5 : i::AbstractCode* aaa_code = CreateCode(&env);
153 5 : i::AbstractCode* comment_code = CreateCode(&env);
154 5 : i::AbstractCode* comment2_code = CreateCode(&env);
155 5 : i::AbstractCode* moved_code = CreateCode(&env);
156 :
157 5 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
158 5 : ProfileGenerator* generator = new ProfileGenerator(profiles);
159 : ProfilerEventsProcessor* processor = new ProfilerEventsProcessor(
160 5 : isolate, generator, v8::base::TimeDelta::FromMicroseconds(100));
161 10 : CpuProfiler profiler(isolate, profiles, generator, processor);
162 5 : profiles->StartProfiling("", false);
163 5 : processor->Start();
164 10 : ProfilerListener profiler_listener(isolate);
165 5 : isolate->code_event_dispatcher()->AddListener(&profiler_listener);
166 5 : profiler_listener.AddObserver(&profiler);
167 :
168 : // Enqueue code creation events.
169 : const char* aaa_str = "aaa";
170 5 : i::Handle<i::String> aaa_name = factory->NewStringFromAsciiChecked(aaa_str);
171 : profiler_listener.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code,
172 5 : *aaa_name);
173 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code,
174 5 : "comment");
175 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code,
176 5 : "comment2");
177 5 : profiler_listener.CodeMoveEvent(comment2_code, moved_code->address());
178 :
179 : // Enqueue a tick event to enable code events processing.
180 5 : EnqueueTickSampleEvent(processor, aaa_code->address());
181 :
182 5 : profiler_listener.RemoveObserver(&profiler);
183 5 : isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
184 5 : processor->StopSynchronously();
185 :
186 : // Check the state of profile generator.
187 10 : CodeEntry* aaa = generator->code_map()->FindEntry(aaa_code->address());
188 5 : CHECK(aaa);
189 5 : CHECK_EQ(0, strcmp(aaa_str, aaa->name()));
190 :
191 5 : CodeEntry* comment =
192 5 : generator->code_map()->FindEntry(comment_code->address());
193 5 : CHECK(comment);
194 5 : CHECK_EQ(0, strcmp("comment", comment->name()));
195 :
196 5 : CHECK(!generator->code_map()->FindEntry(comment2_code->address()));
197 :
198 10 : CodeEntry* comment2 = generator->code_map()->FindEntry(moved_code->address());
199 5 : CHECK(comment2);
200 10 : CHECK_EQ(0, strcmp("comment2", comment2->name()));
201 5 : }
202 :
203 : template<typename T>
204 : static int CompareProfileNodes(const T* p1, const T* p2) {
205 : return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
206 : }
207 :
208 23723 : TEST(TickEvents) {
209 : TestSetup test_setup;
210 10 : LocalContext env;
211 : i::Isolate* isolate = CcTest::i_isolate();
212 : i::HandleScope scope(isolate);
213 :
214 5 : i::AbstractCode* frame1_code = CreateCode(&env);
215 5 : i::AbstractCode* frame2_code = CreateCode(&env);
216 5 : i::AbstractCode* frame3_code = CreateCode(&env);
217 :
218 5 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
219 5 : ProfileGenerator* generator = new ProfileGenerator(profiles);
220 : ProfilerEventsProcessor* processor =
221 : new ProfilerEventsProcessor(CcTest::i_isolate(), generator,
222 5 : v8::base::TimeDelta::FromMicroseconds(100));
223 10 : CpuProfiler profiler(isolate, profiles, generator, processor);
224 5 : profiles->StartProfiling("", false);
225 5 : processor->Start();
226 10 : ProfilerListener profiler_listener(isolate);
227 5 : isolate->code_event_dispatcher()->AddListener(&profiler_listener);
228 5 : profiler_listener.AddObserver(&profiler);
229 :
230 5 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
231 5 : profiler_listener.CodeCreateEvent(i::Logger::STUB_TAG, frame2_code, "ccc");
232 5 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame3_code, "ddd");
233 :
234 5 : EnqueueTickSampleEvent(processor, frame1_code->instruction_start());
235 : EnqueueTickSampleEvent(
236 : processor,
237 5 : frame2_code->instruction_start() + frame2_code->ExecutableSize() / 2,
238 10 : frame1_code->instruction_start() + frame1_code->ExecutableSize() / 2);
239 5 : EnqueueTickSampleEvent(processor, frame3_code->instruction_end() - 1,
240 5 : frame2_code->instruction_end() - 1,
241 15 : frame1_code->instruction_end() - 1);
242 :
243 5 : profiler_listener.RemoveObserver(&profiler);
244 5 : isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
245 5 : processor->StopSynchronously();
246 5 : CpuProfile* profile = profiles->StopProfiling("");
247 5 : CHECK(profile);
248 :
249 : // Check call trees.
250 5 : const std::vector<ProfileNode*>* top_down_root_children =
251 5 : profile->top_down()->root()->children();
252 5 : CHECK_EQ(1, top_down_root_children->size());
253 5 : CHECK_EQ(0, strcmp("bbb", top_down_root_children->back()->entry()->name()));
254 5 : const std::vector<ProfileNode*>* top_down_bbb_children =
255 : top_down_root_children->back()->children();
256 5 : CHECK_EQ(1, top_down_bbb_children->size());
257 5 : CHECK_EQ(0, strcmp("ccc", top_down_bbb_children->back()->entry()->name()));
258 5 : const std::vector<ProfileNode*>* top_down_stub_children =
259 : top_down_bbb_children->back()->children();
260 5 : CHECK_EQ(1, top_down_stub_children->size());
261 5 : CHECK_EQ(0, strcmp("ddd", top_down_stub_children->back()->entry()->name()));
262 : const std::vector<ProfileNode*>* top_down_ddd_children =
263 : top_down_stub_children->back()->children();
264 5 : CHECK(top_down_ddd_children->empty());
265 :
266 5 : isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
267 5 : }
268 :
269 : // http://crbug/51594
270 : // This test must not crash.
271 23723 : TEST(CrashIfStoppingLastNonExistentProfile) {
272 5 : CcTest::InitializeVM();
273 : TestSetup test_setup;
274 5 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(CcTest::i_isolate()));
275 5 : profiler->StartProfiling("1");
276 5 : profiler->StopProfiling("2");
277 5 : profiler->StartProfiling("1");
278 5 : profiler->StopProfiling("");
279 5 : }
280 :
281 : // http://code.google.com/p/v8/issues/detail?id=1398
282 : // Long stacks (exceeding max frames limit) must not be erased.
283 23723 : TEST(Issue1398) {
284 : TestSetup test_setup;
285 10 : LocalContext env;
286 : i::Isolate* isolate = CcTest::i_isolate();
287 : i::HandleScope scope(isolate);
288 :
289 5 : i::AbstractCode* code = CreateCode(&env);
290 :
291 5 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
292 5 : ProfileGenerator* generator = new ProfileGenerator(profiles);
293 : ProfilerEventsProcessor* processor =
294 : new ProfilerEventsProcessor(CcTest::i_isolate(), generator,
295 5 : v8::base::TimeDelta::FromMicroseconds(100));
296 10 : CpuProfiler profiler(isolate, profiles, generator, processor);
297 5 : profiles->StartProfiling("", false);
298 5 : processor->Start();
299 10 : ProfilerListener profiler_listener(isolate);
300 5 : isolate->code_event_dispatcher()->AddListener(&profiler_listener);
301 5 : profiler_listener.AddObserver(&profiler);
302 :
303 5 : profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
304 :
305 5 : v8::TickSample* sample = processor->StartTickSample();
306 5 : sample->pc = code->address();
307 5 : sample->tos = 0;
308 5 : sample->frames_count = v8::TickSample::kMaxFramesCount;
309 1280 : for (unsigned i = 0; i < sample->frames_count; ++i) {
310 1275 : sample->stack[i] = code->address();
311 : }
312 : processor->FinishTickSample();
313 :
314 5 : profiler_listener.RemoveObserver(&profiler);
315 5 : isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
316 5 : processor->StopSynchronously();
317 5 : CpuProfile* profile = profiles->StopProfiling("");
318 5 : CHECK(profile);
319 :
320 : unsigned actual_depth = 0;
321 5 : const ProfileNode* node = profile->top_down()->root();
322 1290 : while (!node->children()->empty()) {
323 1280 : node = node->children()->back();
324 1280 : ++actual_depth;
325 : }
326 :
327 5 : CHECK_EQ(1 + v8::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
328 5 : }
329 :
330 23723 : TEST(DeleteAllCpuProfiles) {
331 5 : CcTest::InitializeVM();
332 : TestSetup test_setup;
333 5 : std::unique_ptr<CpuProfiler> profiler(new CpuProfiler(CcTest::i_isolate()));
334 5 : CHECK_EQ(0, profiler->GetProfilesCount());
335 5 : profiler->DeleteAllProfiles();
336 5 : CHECK_EQ(0, profiler->GetProfilesCount());
337 :
338 5 : profiler->StartProfiling("1");
339 5 : profiler->StopProfiling("1");
340 5 : CHECK_EQ(1, profiler->GetProfilesCount());
341 5 : profiler->DeleteAllProfiles();
342 5 : CHECK_EQ(0, profiler->GetProfilesCount());
343 5 : profiler->StartProfiling("1");
344 5 : profiler->StartProfiling("2");
345 5 : profiler->StopProfiling("2");
346 5 : profiler->StopProfiling("1");
347 5 : CHECK_EQ(2, profiler->GetProfilesCount());
348 5 : profiler->DeleteAllProfiles();
349 5 : CHECK_EQ(0, profiler->GetProfilesCount());
350 :
351 : // Test profiling cancellation by the 'delete' command.
352 5 : profiler->StartProfiling("1");
353 5 : profiler->StartProfiling("2");
354 5 : CHECK_EQ(0, profiler->GetProfilesCount());
355 5 : profiler->DeleteAllProfiles();
356 5 : CHECK_EQ(0, profiler->GetProfilesCount());
357 5 : }
358 :
359 :
360 30 : static bool FindCpuProfile(v8::CpuProfiler* v8profiler,
361 : const v8::CpuProfile* v8profile) {
362 : i::CpuProfiler* profiler = reinterpret_cast<i::CpuProfiler*>(v8profiler);
363 : const i::CpuProfile* profile =
364 : reinterpret_cast<const i::CpuProfile*>(v8profile);
365 30 : int length = profiler->GetProfilesCount();
366 40 : for (int i = 0; i < length; i++) {
367 35 : if (profile == profiler->GetProfile(i))
368 : return true;
369 : }
370 : return false;
371 : }
372 :
373 :
374 23723 : TEST(DeleteCpuProfile) {
375 5 : LocalContext env;
376 10 : v8::HandleScope scope(env->GetIsolate());
377 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
378 : i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(cpu_profiler);
379 :
380 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
381 5 : v8::Local<v8::String> name1 = v8_str("1");
382 5 : cpu_profiler->StartProfiling(name1);
383 5 : v8::CpuProfile* p1 = cpu_profiler->StopProfiling(name1);
384 5 : CHECK(p1);
385 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
386 5 : CHECK(FindCpuProfile(cpu_profiler, p1));
387 5 : p1->Delete();
388 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
389 :
390 5 : v8::Local<v8::String> name2 = v8_str("2");
391 5 : cpu_profiler->StartProfiling(name2);
392 5 : v8::CpuProfile* p2 = cpu_profiler->StopProfiling(name2);
393 5 : CHECK(p2);
394 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
395 5 : CHECK(FindCpuProfile(cpu_profiler, p2));
396 5 : v8::Local<v8::String> name3 = v8_str("3");
397 5 : cpu_profiler->StartProfiling(name3);
398 5 : v8::CpuProfile* p3 = cpu_profiler->StopProfiling(name3);
399 5 : CHECK(p3);
400 5 : CHECK_EQ(2, iprofiler->GetProfilesCount());
401 5 : CHECK_NE(p2, p3);
402 5 : CHECK(FindCpuProfile(cpu_profiler, p3));
403 5 : CHECK(FindCpuProfile(cpu_profiler, p2));
404 5 : p2->Delete();
405 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
406 5 : CHECK(!FindCpuProfile(cpu_profiler, p2));
407 5 : CHECK(FindCpuProfile(cpu_profiler, p3));
408 5 : p3->Delete();
409 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
410 10 : cpu_profiler->Dispose();
411 5 : }
412 :
413 :
414 23723 : TEST(ProfileStartEndTime) {
415 5 : LocalContext env;
416 10 : v8::HandleScope scope(env->GetIsolate());
417 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
418 :
419 5 : v8::Local<v8::String> profile_name = v8_str("test");
420 5 : cpu_profiler->StartProfiling(profile_name);
421 5 : const v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
422 5 : CHECK(profile->GetStartTime() <= profile->GetEndTime());
423 10 : cpu_profiler->Dispose();
424 5 : }
425 :
426 : class ProfilerHelper {
427 : public:
428 59 : explicit ProfilerHelper(const v8::Local<v8::Context>& context)
429 : : context_(context),
430 118 : profiler_(v8::CpuProfiler::New(context->GetIsolate())) {
431 : i::ProfilerExtension::set_profiler(profiler_);
432 59 : }
433 : ~ProfilerHelper() {
434 : i::ProfilerExtension::set_profiler(static_cast<CpuProfiler*>(nullptr));
435 59 : profiler_->Dispose();
436 : }
437 :
438 : v8::CpuProfile* Run(v8::Local<v8::Function> function,
439 : v8::Local<v8::Value> argv[], int argc,
440 : unsigned min_js_samples = 0,
441 : unsigned min_external_samples = 0,
442 : bool collect_samples = false);
443 :
444 : v8::CpuProfiler* profiler() { return profiler_; }
445 :
446 : private:
447 : v8::Local<v8::Context> context_;
448 : v8::CpuProfiler* profiler_;
449 : };
450 :
451 40 : v8::CpuProfile* ProfilerHelper::Run(v8::Local<v8::Function> function,
452 : v8::Local<v8::Value> argv[], int argc,
453 : unsigned min_js_samples,
454 : unsigned min_external_samples,
455 : bool collect_samples) {
456 40 : v8::Local<v8::String> profile_name = v8_str("my_profile");
457 :
458 40 : profiler_->SetSamplingInterval(100);
459 40 : profiler_->StartProfiling(profile_name, collect_samples);
460 :
461 : v8::internal::CpuProfiler* iprofiler =
462 40 : reinterpret_cast<v8::internal::CpuProfiler*>(profiler_);
463 90 : v8::sampler::Sampler* sampler = iprofiler->processor()->sampler();
464 : sampler->StartCountingSamples();
465 50 : do {
466 150 : function->Call(context_, context_->Global(), argc, argv).ToLocalChecked();
467 90 : } while (sampler->js_sample_count() < min_js_samples ||
468 : sampler->external_sample_count() < min_external_samples);
469 :
470 40 : v8::CpuProfile* profile = profiler_->StopProfiling(profile_name);
471 :
472 40 : CHECK(profile);
473 : // Dump collected profile to have a better diagnostic in case of failure.
474 40 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
475 :
476 40 : return profile;
477 : }
478 :
479 163 : static const v8::CpuProfileNode* FindChild(v8::Local<v8::Context> context,
480 : const v8::CpuProfileNode* node,
481 : const char* name) {
482 163 : int count = node->GetChildrenCount();
483 163 : v8::Local<v8::String> name_handle = v8_str(name);
484 244 : for (int i = 0; i < count; i++) {
485 239 : const v8::CpuProfileNode* child = node->GetChild(i);
486 717 : if (name_handle->Equals(context, child->GetFunctionName()).FromJust()) {
487 : return child;
488 : }
489 : }
490 : return nullptr;
491 : }
492 :
493 :
494 158 : static const v8::CpuProfileNode* GetChild(v8::Local<v8::Context> context,
495 : const v8::CpuProfileNode* node,
496 : const char* name) {
497 158 : const v8::CpuProfileNode* result = FindChild(context, node, name);
498 158 : if (!result) {
499 : char buffer[100];
500 0 : i::SNPrintF(i::ArrayVector(buffer), "Failed to GetChild: %s", name);
501 0 : FATAL(buffer);
502 : }
503 158 : return result;
504 : }
505 :
506 :
507 0 : static void CheckSimpleBranch(v8::Local<v8::Context> context,
508 : const v8::CpuProfileNode* node,
509 : const char* names[], int length) {
510 0 : for (int i = 0; i < length; i++) {
511 0 : const char* name = names[i];
512 0 : node = GetChild(context, node, name);
513 : }
514 0 : }
515 :
516 :
517 9 : static const ProfileNode* GetSimpleBranch(v8::Local<v8::Context> context,
518 : v8::CpuProfile* profile,
519 : const char* names[], int length) {
520 9 : const v8::CpuProfileNode* node = profile->GetTopDownRoot();
521 27 : for (int i = 0; i < length; i++) {
522 18 : node = GetChild(context, node, names[i]);
523 : }
524 9 : return reinterpret_cast<const ProfileNode*>(node);
525 : }
526 :
527 : static const char* cpu_profiler_test_source =
528 : "%NeverOptimizeFunction(loop);\n"
529 : "%NeverOptimizeFunction(delay);\n"
530 : "%NeverOptimizeFunction(bar);\n"
531 : "%NeverOptimizeFunction(baz);\n"
532 : "%NeverOptimizeFunction(foo);\n"
533 : "%NeverOptimizeFunction(start);\n"
534 : "function loop(timeout) {\n"
535 : " this.mmm = 0;\n"
536 : " var start = Date.now();\n"
537 : " do {\n"
538 : " var n = 1000;\n"
539 : " while(n > 1) {\n"
540 : " n--;\n"
541 : " this.mmm += n * n * n;\n"
542 : " }\n"
543 : " } while (Date.now() - start < timeout);\n"
544 : "}\n"
545 : "function delay() { loop(10); }\n"
546 : "function bar() { delay(); }\n"
547 : "function baz() { delay(); }\n"
548 : "function foo() {\n"
549 : " delay();\n"
550 : " bar();\n"
551 : " delay();\n"
552 : " baz();\n"
553 : "}\n"
554 : "function start(duration) {\n"
555 : " var start = Date.now();\n"
556 : " do {\n"
557 : " foo();\n"
558 : " } while (Date.now() - start < duration);\n"
559 : "}\n";
560 :
561 : // Check that the profile tree for the script above will look like the
562 : // following:
563 : //
564 : // [Top down]:
565 : // 1062 0 (root) [-1]
566 : // 1054 0 start [-1]
567 : // 1054 1 foo [-1]
568 : // 265 0 baz [-1]
569 : // 265 1 delay [-1]
570 : // 264 264 loop [-1]
571 : // 525 3 delay [-1]
572 : // 522 522 loop [-1]
573 : // 263 0 bar [-1]
574 : // 263 1 delay [-1]
575 : // 262 262 loop [-1]
576 : // 2 2 (program) [-1]
577 : // 6 6 (garbage collector) [-1]
578 23718 : TEST(CollectCpuProfile) {
579 0 : i::FLAG_allow_natives_syntax = true;
580 0 : LocalContext env;
581 0 : v8::HandleScope scope(env->GetIsolate());
582 :
583 0 : CompileRun(cpu_profiler_test_source);
584 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
585 :
586 : int32_t profiling_interval_ms = 200;
587 : v8::Local<v8::Value> args[] = {
588 0 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
589 0 : ProfilerHelper helper(env.local());
590 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
591 :
592 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
593 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
594 0 : const v8::CpuProfileNode* foo_node = GetChild(env.local(), start_node, "foo");
595 :
596 0 : const char* bar_branch[] = {"bar", "delay", "loop"};
597 0 : CheckSimpleBranch(env.local(), foo_node, bar_branch, arraysize(bar_branch));
598 0 : const char* baz_branch[] = {"baz", "delay", "loop"};
599 0 : CheckSimpleBranch(env.local(), foo_node, baz_branch, arraysize(baz_branch));
600 0 : const char* delay_branch[] = {"delay", "loop"};
601 : CheckSimpleBranch(env.local(), foo_node, delay_branch,
602 0 : arraysize(delay_branch));
603 :
604 0 : profile->Delete();
605 0 : }
606 :
607 : static const char* hot_deopt_no_frame_entry_test_source =
608 : "%NeverOptimizeFunction(foo);\n"
609 : "%NeverOptimizeFunction(start);\n"
610 : "function foo(a, b) {\n"
611 : " return a + b;\n"
612 : "}\n"
613 : "function start(timeout) {\n"
614 : " var start = Date.now();\n"
615 : " do {\n"
616 : " for (var i = 1; i < 1000; ++i) foo(1, i);\n"
617 : " var duration = Date.now() - start;\n"
618 : " } while (duration < timeout);\n"
619 : " return duration;\n"
620 : "}\n";
621 :
622 : // Check that the profile tree for the script above will look like the
623 : // following:
624 : //
625 : // [Top down]:
626 : // 1062 0 (root) [-1]
627 : // 1054 0 start [-1]
628 : // 1054 1 foo [-1]
629 : // 2 2 (program) [-1]
630 : // 6 6 (garbage collector) [-1]
631 : //
632 : // The test checks no FP ranges are present in a deoptimized function.
633 : // If 'foo' has no ranges the samples falling into the prologue will miss the
634 : // 'start' function on the stack, so 'foo' will be attached to the (root).
635 23718 : TEST(HotDeoptNoFrameEntry) {
636 0 : i::FLAG_allow_natives_syntax = true;
637 0 : LocalContext env;
638 0 : v8::HandleScope scope(env->GetIsolate());
639 :
640 0 : CompileRun(hot_deopt_no_frame_entry_test_source);
641 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
642 :
643 : int32_t profiling_interval_ms = 200;
644 : v8::Local<v8::Value> args[] = {
645 0 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
646 0 : ProfilerHelper helper(env.local());
647 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
648 0 : function->Call(env.local(), env->Global(), arraysize(args), args)
649 0 : .ToLocalChecked();
650 :
651 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
652 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
653 0 : GetChild(env.local(), start_node, "foo");
654 :
655 0 : profile->Delete();
656 0 : }
657 :
658 23723 : TEST(CollectCpuProfileSamples) {
659 5 : i::FLAG_allow_natives_syntax = true;
660 5 : LocalContext env;
661 10 : v8::HandleScope scope(env->GetIsolate());
662 :
663 5 : CompileRun(cpu_profiler_test_source);
664 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
665 :
666 : int32_t profiling_interval_ms = 200;
667 : v8::Local<v8::Value> args[] = {
668 5 : v8::Integer::New(env->GetIsolate(), profiling_interval_ms)};
669 5 : ProfilerHelper helper(env.local());
670 : v8::CpuProfile* profile =
671 5 : helper.Run(function, args, arraysize(args), 1000, 0, true);
672 :
673 5 : CHECK_LE(200, profile->GetSamplesCount());
674 5 : uint64_t end_time = profile->GetEndTime();
675 5 : uint64_t current_time = profile->GetStartTime();
676 5 : CHECK_LE(current_time, end_time);
677 7922 : for (int i = 0; i < profile->GetSamplesCount(); i++) {
678 7922 : CHECK(profile->GetSample(i));
679 7922 : uint64_t timestamp = profile->GetSampleTimestamp(i);
680 7922 : CHECK_LE(current_time, timestamp);
681 7922 : CHECK_LE(timestamp, end_time);
682 : current_time = timestamp;
683 : }
684 :
685 10 : profile->Delete();
686 5 : }
687 :
688 : static const char* cpu_profiler_test_source2 =
689 : "%NeverOptimizeFunction(loop);\n"
690 : "%NeverOptimizeFunction(delay);\n"
691 : "%NeverOptimizeFunction(start);\n"
692 : "function loop() {}\n"
693 : "function delay() { loop(); }\n"
694 : "function start(duration) {\n"
695 : " var start = Date.now();\n"
696 : " do {\n"
697 : " for (var i = 0; i < 10000; ++i) delay();\n"
698 : " } while (Date.now() - start < duration);\n"
699 : "}";
700 :
701 : // Check that the profile tree doesn't contain unexpected traces:
702 : // - 'loop' can be called only by 'delay'
703 : // - 'delay' may be called only by 'start'
704 : // The profile will look like the following:
705 : //
706 : // [Top down]:
707 : // 135 0 (root) [-1] #1
708 : // 121 72 start [-1] #3
709 : // 49 33 delay [-1] #4
710 : // 16 16 loop [-1] #5
711 : // 14 14 (program) [-1] #2
712 23718 : TEST(SampleWhenFrameIsNotSetup) {
713 0 : i::FLAG_allow_natives_syntax = true;
714 0 : LocalContext env;
715 0 : v8::HandleScope scope(env->GetIsolate());
716 :
717 0 : CompileRun(cpu_profiler_test_source2);
718 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
719 :
720 : int32_t duration_ms = 100;
721 : v8::Local<v8::Value> args[] = {
722 0 : v8::Integer::New(env->GetIsolate(), duration_ms)};
723 0 : ProfilerHelper helper(env.local());
724 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
725 :
726 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
727 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
728 : const v8::CpuProfileNode* delay_node =
729 0 : GetChild(env.local(), start_node, "delay");
730 0 : GetChild(env.local(), delay_node, "loop");
731 :
732 0 : profile->Delete();
733 0 : }
734 :
735 : static const char* native_accessor_test_source = "function start(count) {\n"
736 : " for (var i = 0; i < count; i++) {\n"
737 : " var o = instance.foo;\n"
738 : " instance.foo = o + 1;\n"
739 : " }\n"
740 : "}\n";
741 :
742 : class TestApiCallbacks {
743 : public:
744 : explicit TestApiCallbacks(int min_duration_ms)
745 : : min_duration_ms_(min_duration_ms),
746 20 : is_warming_up_(false) {}
747 :
748 520 : static void Getter(v8::Local<v8::String> name,
749 : const v8::PropertyCallbackInfo<v8::Value>& info) {
750 : TestApiCallbacks* data = FromInfo(info);
751 520 : data->Wait();
752 520 : }
753 :
754 520 : static void Setter(v8::Local<v8::String> name,
755 : v8::Local<v8::Value> value,
756 : const v8::PropertyCallbackInfo<void>& info) {
757 : TestApiCallbacks* data = FromInfo(info);
758 520 : data->Wait();
759 520 : }
760 :
761 520 : static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
762 : TestApiCallbacks* data = FromInfo(info);
763 520 : data->Wait();
764 520 : }
765 :
766 20 : void set_warming_up(bool value) { is_warming_up_ = value; }
767 :
768 : private:
769 1560 : void Wait() {
770 3120 : if (is_warming_up_) return;
771 1515 : v8::Platform* platform = v8::internal::V8::GetCurrentPlatform();
772 1515 : double start = platform->CurrentClockTimeMillis();
773 : double duration = 0;
774 20041 : while (duration < min_duration_ms_) {
775 17011 : v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(1));
776 17011 : duration = platform->CurrentClockTimeMillis() - start;
777 : }
778 : }
779 :
780 : template <typename T>
781 520 : static TestApiCallbacks* FromInfo(const T& info) {
782 1560 : void* data = v8::External::Cast(*info.Data())->Value();
783 : return reinterpret_cast<TestApiCallbacks*>(data);
784 : }
785 :
786 : int min_duration_ms_;
787 : bool is_warming_up_;
788 : };
789 :
790 :
791 : // Test that native accessors are properly reported in the CPU profile.
792 : // This test checks the case when the long-running accessors are called
793 : // only once and the optimizer doesn't have chance to change the invocation
794 : // code.
795 23723 : TEST(NativeAccessorUninitializedIC) {
796 5 : LocalContext env;
797 5 : v8::Isolate* isolate = env->GetIsolate();
798 10 : v8::HandleScope scope(isolate);
799 :
800 : v8::Local<v8::FunctionTemplate> func_template =
801 5 : v8::FunctionTemplate::New(isolate);
802 : v8::Local<v8::ObjectTemplate> instance_template =
803 5 : func_template->InstanceTemplate();
804 :
805 : TestApiCallbacks accessors(100);
806 5 : v8::Local<v8::External> data = v8::External::New(isolate, &accessors);
807 : instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter,
808 10 : &TestApiCallbacks::Setter, data);
809 : v8::Local<v8::Function> func =
810 5 : func_template->GetFunction(env.local()).ToLocalChecked();
811 : v8::Local<v8::Object> instance =
812 5 : func->NewInstance(env.local()).ToLocalChecked();
813 25 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
814 :
815 5 : CompileRun(native_accessor_test_source);
816 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
817 :
818 5 : ProfilerHelper helper(env.local());
819 : int32_t repeat_count = 1;
820 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
821 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 100);
822 :
823 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
824 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
825 5 : GetChild(env.local(), start_node, "get foo");
826 5 : GetChild(env.local(), start_node, "set foo");
827 :
828 10 : profile->Delete();
829 5 : }
830 :
831 :
832 : // Test that native accessors are properly reported in the CPU profile.
833 : // This test makes sure that the accessors are called enough times to become
834 : // hot and to trigger optimizations.
835 23723 : TEST(NativeAccessorMonomorphicIC) {
836 5 : LocalContext env;
837 5 : v8::Isolate* isolate = env->GetIsolate();
838 10 : v8::HandleScope scope(isolate);
839 :
840 : v8::Local<v8::FunctionTemplate> func_template =
841 5 : v8::FunctionTemplate::New(isolate);
842 : v8::Local<v8::ObjectTemplate> instance_template =
843 5 : func_template->InstanceTemplate();
844 :
845 : TestApiCallbacks accessors(1);
846 : v8::Local<v8::External> data =
847 5 : v8::External::New(isolate, &accessors);
848 : instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter,
849 10 : &TestApiCallbacks::Setter, data);
850 : v8::Local<v8::Function> func =
851 5 : func_template->GetFunction(env.local()).ToLocalChecked();
852 : v8::Local<v8::Object> instance =
853 5 : func->NewInstance(env.local()).ToLocalChecked();
854 25 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
855 :
856 5 : CompileRun(native_accessor_test_source);
857 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
858 :
859 : {
860 : // Make sure accessors ICs are in monomorphic state before starting
861 : // profiling.
862 : accessors.set_warming_up(true);
863 : int32_t warm_up_iterations = 3;
864 : v8::Local<v8::Value> args[] = {
865 5 : v8::Integer::New(isolate, warm_up_iterations)};
866 15 : function->Call(env.local(), env->Global(), arraysize(args), args)
867 5 : .ToLocalChecked();
868 : accessors.set_warming_up(false);
869 : }
870 :
871 : int32_t repeat_count = 100;
872 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
873 5 : ProfilerHelper helper(env.local());
874 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 100);
875 :
876 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
877 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
878 5 : GetChild(env.local(), start_node, "get foo");
879 5 : GetChild(env.local(), start_node, "set foo");
880 :
881 10 : profile->Delete();
882 5 : }
883 :
884 :
885 : static const char* native_method_test_source = "function start(count) {\n"
886 : " for (var i = 0; i < count; i++) {\n"
887 : " instance.fooMethod();\n"
888 : " }\n"
889 : "}\n";
890 :
891 :
892 23723 : TEST(NativeMethodUninitializedIC) {
893 5 : LocalContext env;
894 5 : v8::Isolate* isolate = env->GetIsolate();
895 10 : v8::HandleScope scope(isolate);
896 :
897 : TestApiCallbacks callbacks(100);
898 5 : v8::Local<v8::External> data = v8::External::New(isolate, &callbacks);
899 :
900 : v8::Local<v8::FunctionTemplate> func_template =
901 5 : v8::FunctionTemplate::New(isolate);
902 5 : func_template->SetClassName(v8_str("Test_InstanceConstructor"));
903 : v8::Local<v8::ObjectTemplate> proto_template =
904 5 : func_template->PrototypeTemplate();
905 : v8::Local<v8::Signature> signature =
906 5 : v8::Signature::New(isolate, func_template);
907 : proto_template->Set(
908 : v8_str("fooMethod"),
909 : v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
910 15 : signature, 0));
911 :
912 : v8::Local<v8::Function> func =
913 5 : func_template->GetFunction(env.local()).ToLocalChecked();
914 : v8::Local<v8::Object> instance =
915 5 : func->NewInstance(env.local()).ToLocalChecked();
916 25 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
917 :
918 5 : CompileRun(native_method_test_source);
919 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
920 :
921 5 : ProfilerHelper helper(env.local());
922 : int32_t repeat_count = 1;
923 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
924 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 100);
925 :
926 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
927 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
928 5 : GetChild(env.local(), start_node, "fooMethod");
929 :
930 10 : profile->Delete();
931 5 : }
932 :
933 :
934 23723 : TEST(NativeMethodMonomorphicIC) {
935 5 : LocalContext env;
936 5 : v8::Isolate* isolate = env->GetIsolate();
937 10 : v8::HandleScope scope(isolate);
938 :
939 : TestApiCallbacks callbacks(1);
940 : v8::Local<v8::External> data =
941 5 : v8::External::New(isolate, &callbacks);
942 :
943 : v8::Local<v8::FunctionTemplate> func_template =
944 5 : v8::FunctionTemplate::New(isolate);
945 5 : func_template->SetClassName(v8_str("Test_InstanceCostructor"));
946 : v8::Local<v8::ObjectTemplate> proto_template =
947 5 : func_template->PrototypeTemplate();
948 : v8::Local<v8::Signature> signature =
949 5 : v8::Signature::New(isolate, func_template);
950 : proto_template->Set(
951 : v8_str("fooMethod"),
952 : v8::FunctionTemplate::New(isolate, &TestApiCallbacks::Callback, data,
953 15 : signature, 0));
954 :
955 : v8::Local<v8::Function> func =
956 5 : func_template->GetFunction(env.local()).ToLocalChecked();
957 : v8::Local<v8::Object> instance =
958 5 : func->NewInstance(env.local()).ToLocalChecked();
959 25 : env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust();
960 :
961 5 : CompileRun(native_method_test_source);
962 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
963 : {
964 : // Make sure method ICs are in monomorphic state before starting
965 : // profiling.
966 : callbacks.set_warming_up(true);
967 : int32_t warm_up_iterations = 3;
968 : v8::Local<v8::Value> args[] = {
969 5 : v8::Integer::New(isolate, warm_up_iterations)};
970 15 : function->Call(env.local(), env->Global(), arraysize(args), args)
971 5 : .ToLocalChecked();
972 : callbacks.set_warming_up(false);
973 : }
974 :
975 5 : ProfilerHelper helper(env.local());
976 : int32_t repeat_count = 100;
977 5 : v8::Local<v8::Value> args[] = {v8::Integer::New(isolate, repeat_count)};
978 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 0, 200);
979 :
980 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
981 5 : GetChild(env.local(), root, "start");
982 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
983 5 : GetChild(env.local(), start_node, "fooMethod");
984 :
985 10 : profile->Delete();
986 5 : }
987 :
988 :
989 : static const char* bound_function_test_source =
990 : "function foo() {\n"
991 : " startProfiling('my_profile');\n"
992 : "}\n"
993 : "function start() {\n"
994 : " var callback = foo.bind(this);\n"
995 : " callback();\n"
996 : "}";
997 :
998 :
999 23723 : TEST(BoundFunctionCall) {
1000 5 : v8::HandleScope scope(CcTest::isolate());
1001 5 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1002 : v8::Context::Scope context_scope(env);
1003 :
1004 5 : CompileRun(bound_function_test_source);
1005 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1006 :
1007 5 : ProfilerHelper helper(env);
1008 5 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0);
1009 :
1010 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1011 :
1012 5 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1013 5 : GetChild(env, start_node, "foo");
1014 :
1015 10 : profile->Delete();
1016 5 : }
1017 :
1018 : // This tests checks distribution of the samples through the source lines.
1019 10 : static void TickLines(bool optimize) {
1020 10 : if (!optimize) i::FLAG_opt = false;
1021 10 : CcTest::InitializeVM();
1022 10 : LocalContext env;
1023 10 : i::FLAG_allow_natives_syntax = true;
1024 : i::Isolate* isolate = CcTest::i_isolate();
1025 : i::Factory* factory = isolate->factory();
1026 : i::HandleScope scope(isolate);
1027 :
1028 : i::EmbeddedVector<char, 512> script;
1029 : i::EmbeddedVector<char, 64> optimize_call;
1030 :
1031 : const char* func_name = "func";
1032 10 : if (optimize) {
1033 : i::SNPrintF(optimize_call, "%%OptimizeFunctionOnNextCall(%s);\n",
1034 5 : func_name);
1035 : } else {
1036 5 : optimize_call[0] = '\0';
1037 : }
1038 : i::SNPrintF(script,
1039 : "function %s() {\n"
1040 : " var n = 0;\n"
1041 : " var m = 100*100;\n"
1042 : " while (m > 1) {\n"
1043 : " m--;\n"
1044 : " n += m * m * m;\n"
1045 : " }\n"
1046 : "}\n"
1047 : "%s();\n"
1048 : "%s"
1049 : "%s();\n",
1050 10 : func_name, func_name, optimize_call.start(), func_name);
1051 :
1052 10 : CompileRun(script.start());
1053 :
1054 : i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(
1055 20 : v8::Utils::OpenHandle(*GetFunction(env.local(), func_name)));
1056 10 : CHECK(func->shared());
1057 10 : CHECK(func->shared()->abstract_code());
1058 16 : CHECK(!optimize || func->IsOptimized() ||
1059 : !CcTest::i_isolate()->use_optimizer());
1060 10 : i::AbstractCode* code = func->abstract_code();
1061 10 : CHECK(code);
1062 10 : i::Address code_address = code->instruction_start();
1063 10 : CHECK(code_address);
1064 :
1065 10 : CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
1066 10 : ProfileGenerator* generator = new ProfileGenerator(profiles);
1067 : ProfilerEventsProcessor* processor =
1068 : new ProfilerEventsProcessor(CcTest::i_isolate(), generator,
1069 10 : v8::base::TimeDelta::FromMicroseconds(100));
1070 20 : CpuProfiler profiler(isolate, profiles, generator, processor);
1071 10 : profiles->StartProfiling("", false);
1072 10 : processor->Start();
1073 20 : ProfilerListener profiler_listener(isolate);
1074 10 : isolate->code_event_dispatcher()->AddListener(&profiler_listener);
1075 10 : profiler_listener.AddObserver(&profiler);
1076 :
1077 : // Enqueue code creation events.
1078 10 : i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name);
1079 : int line = 1;
1080 : int column = 1;
1081 : profiler_listener.CodeCreateEvent(i::Logger::FUNCTION_TAG, code,
1082 10 : func->shared(), *str, line, column);
1083 :
1084 : // Enqueue a tick event to enable code events processing.
1085 10 : EnqueueTickSampleEvent(processor, code_address);
1086 :
1087 10 : profiler_listener.RemoveObserver(&profiler);
1088 10 : isolate->code_event_dispatcher()->RemoveListener(&profiler_listener);
1089 10 : processor->StopSynchronously();
1090 :
1091 10 : CpuProfile* profile = profiles->StopProfiling("");
1092 10 : CHECK(profile);
1093 :
1094 : // Check the state of profile generator.
1095 30 : CodeEntry* func_entry = generator->code_map()->FindEntry(code_address);
1096 10 : CHECK(func_entry);
1097 10 : CHECK_EQ(0, strcmp(func_name, func_entry->name()));
1098 : const i::JITLineInfoTable* line_info = func_entry->line_info();
1099 10 : CHECK(line_info);
1100 10 : CHECK(!line_info->empty());
1101 :
1102 : // Check the hit source lines using V8 Public APIs.
1103 10 : const i::ProfileTree* tree = profile->top_down();
1104 : ProfileNode* root = tree->root();
1105 10 : CHECK(root);
1106 10 : ProfileNode* func_node = root->FindChild(func_entry);
1107 10 : CHECK(func_node);
1108 :
1109 : // Add 10 faked ticks to source line #5.
1110 : int hit_line = 5;
1111 : int hit_count = 10;
1112 100 : for (int i = 0; i < hit_count; i++) func_node->IncrementLineTicks(hit_line);
1113 :
1114 : unsigned int line_count = func_node->GetHitLineCount();
1115 10 : CHECK_EQ(2u, line_count); // Expect two hit source lines - #1 and #5.
1116 10 : ScopedVector<v8::CpuProfileNode::LineTick> entries(line_count);
1117 10 : CHECK(func_node->GetLineTicks(&entries[0], line_count));
1118 : int value = 0;
1119 10 : for (int i = 0; i < entries.length(); i++)
1120 40 : if (entries[i].line == hit_line) {
1121 10 : value = entries[i].hit_count;
1122 10 : break;
1123 : }
1124 20 : CHECK_EQ(hit_count, value);
1125 10 : }
1126 :
1127 23723 : TEST(TickLinesBaseline) { TickLines(false); }
1128 :
1129 23723 : TEST(TickLinesOptimized) { TickLines(true); }
1130 :
1131 : static const char* call_function_test_source =
1132 : "%NeverOptimizeFunction(bar);\n"
1133 : "%NeverOptimizeFunction(start);\n"
1134 : "function bar(n) {\n"
1135 : " var s = 0;\n"
1136 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1137 : " return s;\n"
1138 : "}\n"
1139 : "function start(duration) {\n"
1140 : " var start = Date.now();\n"
1141 : " do {\n"
1142 : " for (var i = 0; i < 100; ++i)\n"
1143 : " bar.call(this, 1000);\n"
1144 : " } while (Date.now() - start < duration);\n"
1145 : "}";
1146 :
1147 : // Test that if we sampled thread when it was inside FunctionCall buitin then
1148 : // its caller frame will be '(unresolved function)' as we have no reliable way
1149 : // to resolve it.
1150 : //
1151 : // [Top down]:
1152 : // 96 0 (root) [-1] #1
1153 : // 1 1 (garbage collector) [-1] #4
1154 : // 5 0 (unresolved function) [-1] #5
1155 : // 5 5 call [-1] #6
1156 : // 71 70 start [-1] #3
1157 : // 1 1 bar [-1] #7
1158 : // 19 19 (program) [-1] #2
1159 23723 : TEST(FunctionCallSample) {
1160 5 : i::FLAG_allow_natives_syntax = true;
1161 5 : LocalContext env;
1162 10 : v8::HandleScope scope(env->GetIsolate());
1163 :
1164 : // Collect garbage that might have be generated while installing
1165 : // extensions.
1166 5 : CcTest::CollectAllGarbage();
1167 :
1168 5 : CompileRun(call_function_test_source);
1169 5 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
1170 :
1171 5 : ProfilerHelper helper(env.local());
1172 : int32_t duration_ms = 100;
1173 : v8::Local<v8::Value> args[] = {
1174 5 : v8::Integer::New(env->GetIsolate(), duration_ms)};
1175 5 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
1176 :
1177 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1178 5 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
1179 5 : GetChild(env.local(), start_node, "bar");
1180 :
1181 : const v8::CpuProfileNode* unresolved_node =
1182 5 : FindChild(env.local(), root, i::CodeEntry::kUnresolvedFunctionName);
1183 5 : CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "call"));
1184 :
1185 10 : profile->Delete();
1186 5 : }
1187 :
1188 : static const char* function_apply_test_source =
1189 : "%NeverOptimizeFunction(bar);\n"
1190 : "%NeverOptimizeFunction(test);\n"
1191 : "%NeverOptimizeFunction(start);\n"
1192 : "function bar(n) {\n"
1193 : " var s = 0;\n"
1194 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1195 : " return s;\n"
1196 : "}\n"
1197 : "function test() {\n"
1198 : " bar.apply(this, [1000]);\n"
1199 : "}\n"
1200 : "function start(duration) {\n"
1201 : " var start = Date.now();\n"
1202 : " do {\n"
1203 : " for (var i = 0; i < 100; ++i) test();\n"
1204 : " } while (Date.now() - start < duration);\n"
1205 : "}";
1206 :
1207 : // [Top down]:
1208 : // 94 0 (root) [-1] #0 1
1209 : // 2 2 (garbage collector) [-1] #0 7
1210 : // 82 49 start [-1] #16 3
1211 : // 1 0 (unresolved function) [-1] #0 8
1212 : // 1 1 apply [-1] #0 9
1213 : // 32 21 test [-1] #16 4
1214 : // 2 2 bar [-1] #16 6
1215 : // 10 10 (program) [-1] #0 2
1216 23718 : TEST(FunctionApplySample) {
1217 0 : i::FLAG_allow_natives_syntax = true;
1218 0 : LocalContext env;
1219 0 : v8::HandleScope scope(env->GetIsolate());
1220 :
1221 0 : CompileRun(function_apply_test_source);
1222 0 : v8::Local<v8::Function> function = GetFunction(env.local(), "start");
1223 :
1224 0 : ProfilerHelper helper(env.local());
1225 : int32_t duration_ms = 100;
1226 : v8::Local<v8::Value> args[] = {
1227 0 : v8::Integer::New(env->GetIsolate(), duration_ms)};
1228 0 : v8::CpuProfile* profile = helper.Run(function, args, arraysize(args), 1000);
1229 :
1230 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1231 0 : const v8::CpuProfileNode* start_node = GetChild(env.local(), root, "start");
1232 : const v8::CpuProfileNode* test_node =
1233 0 : GetChild(env.local(), start_node, "test");
1234 0 : GetChild(env.local(), test_node, "bar");
1235 :
1236 : const v8::CpuProfileNode* unresolved_node =
1237 0 : FindChild(env.local(), start_node, CodeEntry::kUnresolvedFunctionName);
1238 0 : CHECK(!unresolved_node || GetChild(env.local(), unresolved_node, "apply"));
1239 :
1240 0 : profile->Delete();
1241 0 : }
1242 :
1243 : static const char* cpu_profiler_deep_stack_test_source =
1244 : "function foo(n) {\n"
1245 : " if (n)\n"
1246 : " foo(n - 1);\n"
1247 : " else\n"
1248 : " collectSample();\n"
1249 : "}\n"
1250 : "function start() {\n"
1251 : " startProfiling('my_profile');\n"
1252 : " foo(250);\n"
1253 : "}\n";
1254 :
1255 : // Check a deep stack
1256 : //
1257 : // [Top down]:
1258 : // 0 (root) 0 #1
1259 : // 2 (program) 0 #2
1260 : // 0 start 21 #3 no reason
1261 : // 0 foo 21 #4 no reason
1262 : // 0 foo 21 #5 no reason
1263 : // ....
1264 : // 0 foo 21 #254 no reason
1265 23718 : TEST(CpuProfileDeepStack) {
1266 0 : v8::HandleScope scope(CcTest::isolate());
1267 0 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1268 : v8::Context::Scope context_scope(env);
1269 0 : ProfilerHelper helper(env);
1270 :
1271 0 : CompileRun(cpu_profiler_deep_stack_test_source);
1272 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1273 :
1274 0 : v8::Local<v8::String> profile_name = v8_str("my_profile");
1275 0 : function->Call(env, env->Global(), 0, nullptr).ToLocalChecked();
1276 0 : v8::CpuProfile* profile = helper.profiler()->StopProfiling(profile_name);
1277 0 : CHECK(profile);
1278 : // Dump collected profile to have a better diagnostic in case of failure.
1279 0 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1280 :
1281 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1282 0 : const v8::CpuProfileNode* node = GetChild(env, root, "start");
1283 0 : for (int i = 0; i <= 250; ++i) {
1284 0 : node = GetChild(env, node, "foo");
1285 : }
1286 0 : CHECK(!FindChild(env, node, "foo"));
1287 :
1288 0 : profile->Delete();
1289 0 : }
1290 :
1291 : static const char* js_native_js_test_source =
1292 : "%NeverOptimizeFunction(foo);\n"
1293 : "%NeverOptimizeFunction(bar);\n"
1294 : "%NeverOptimizeFunction(start);\n"
1295 : "function foo(n) {\n"
1296 : " var s = 0;\n"
1297 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1298 : " return s;\n"
1299 : "}\n"
1300 : "function bar() {\n"
1301 : " foo(1000);\n"
1302 : "}\n"
1303 : "function start() {\n"
1304 : " CallJsFunction(bar);\n"
1305 : "}";
1306 :
1307 0 : static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1308 : v8::Local<v8::Function> function = info[0].As<v8::Function>();
1309 0 : v8::Local<v8::Value> argv[] = {info[1]};
1310 : function->Call(info.GetIsolate()->GetCurrentContext(), info.This(),
1311 0 : arraysize(argv), argv)
1312 0 : .ToLocalChecked();
1313 0 : }
1314 :
1315 : // [Top down]:
1316 : // 58 0 (root) #0 1
1317 : // 2 2 (program) #0 2
1318 : // 56 1 start #16 3
1319 : // 55 0 CallJsFunction #0 4
1320 : // 55 1 bar #16 5
1321 : // 54 54 foo #16 6
1322 23718 : TEST(JsNativeJsSample) {
1323 0 : i::FLAG_allow_natives_syntax = true;
1324 0 : v8::HandleScope scope(CcTest::isolate());
1325 0 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1326 : v8::Context::Scope context_scope(env);
1327 :
1328 : v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1329 0 : env->GetIsolate(), CallJsFunction);
1330 : v8::Local<v8::Function> func =
1331 0 : func_template->GetFunction(env).ToLocalChecked();
1332 0 : func->SetName(v8_str("CallJsFunction"));
1333 0 : env->Global()->Set(env, v8_str("CallJsFunction"), func).FromJust();
1334 :
1335 0 : CompileRun(js_native_js_test_source);
1336 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1337 :
1338 0 : ProfilerHelper helper(env);
1339 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 1000);
1340 :
1341 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1342 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1343 : const v8::CpuProfileNode* native_node =
1344 0 : GetChild(env, start_node, "CallJsFunction");
1345 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node, "bar");
1346 0 : GetChild(env, bar_node, "foo");
1347 :
1348 0 : profile->Delete();
1349 0 : }
1350 :
1351 : static const char* js_native_js_runtime_js_test_source =
1352 : "%NeverOptimizeFunction(foo);\n"
1353 : "%NeverOptimizeFunction(bar);\n"
1354 : "%NeverOptimizeFunction(start);\n"
1355 : "function foo(n) {\n"
1356 : " var s = 0;\n"
1357 : " for (var i = 0; i < n; i++) s += i * i * i;\n"
1358 : " return s;\n"
1359 : "}\n"
1360 : "var bound = foo.bind(this);\n"
1361 : "function bar() {\n"
1362 : " bound(1000);\n"
1363 : "}\n"
1364 : "function start() {\n"
1365 : " CallJsFunction(bar);\n"
1366 : "}";
1367 :
1368 : // [Top down]:
1369 : // 57 0 (root) #0 1
1370 : // 55 1 start #16 3
1371 : // 54 0 CallJsFunction #0 4
1372 : // 54 3 bar #16 5
1373 : // 51 51 foo #16 6
1374 : // 2 2 (program) #0 2
1375 23718 : TEST(JsNativeJsRuntimeJsSample) {
1376 0 : i::FLAG_allow_natives_syntax = true;
1377 0 : v8::HandleScope scope(CcTest::isolate());
1378 0 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1379 : v8::Context::Scope context_scope(env);
1380 :
1381 : v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1382 0 : env->GetIsolate(), CallJsFunction);
1383 : v8::Local<v8::Function> func =
1384 0 : func_template->GetFunction(env).ToLocalChecked();
1385 0 : func->SetName(v8_str("CallJsFunction"));
1386 0 : env->Global()->Set(env, v8_str("CallJsFunction"), func).FromJust();
1387 :
1388 0 : CompileRun(js_native_js_runtime_js_test_source);
1389 0 : ProfilerHelper helper(env);
1390 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1391 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 1000);
1392 :
1393 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1394 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1395 : const v8::CpuProfileNode* native_node =
1396 0 : GetChild(env, start_node, "CallJsFunction");
1397 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node, "bar");
1398 0 : GetChild(env, bar_node, "foo");
1399 :
1400 0 : profile->Delete();
1401 0 : }
1402 :
1403 0 : static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1404 0 : v8::base::OS::Print("In CallJsFunction2\n");
1405 0 : CallJsFunction(info);
1406 0 : }
1407 :
1408 : static const char* js_native1_js_native2_js_test_source =
1409 : "%NeverOptimizeFunction(foo);\n"
1410 : "%NeverOptimizeFunction(bar);\n"
1411 : "%NeverOptimizeFunction(start);\n"
1412 : "function foo() {\n"
1413 : " var s = 0;\n"
1414 : " for (var i = 0; i < 1000; i++) s += i * i * i;\n"
1415 : " return s;\n"
1416 : "}\n"
1417 : "function bar() {\n"
1418 : " CallJsFunction2(foo);\n"
1419 : "}\n"
1420 : "function start() {\n"
1421 : " CallJsFunction1(bar);\n"
1422 : "}";
1423 :
1424 : // [Top down]:
1425 : // 57 0 (root) #0 1
1426 : // 55 1 start #16 3
1427 : // 54 0 CallJsFunction1 #0 4
1428 : // 54 0 bar #16 5
1429 : // 54 0 CallJsFunction2 #0 6
1430 : // 54 54 foo #16 7
1431 : // 2 2 (program) #0 2
1432 23718 : TEST(JsNative1JsNative2JsSample) {
1433 0 : i::FLAG_allow_natives_syntax = true;
1434 0 : v8::HandleScope scope(CcTest::isolate());
1435 0 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1436 : v8::Context::Scope context_scope(env);
1437 :
1438 : v8::Local<v8::Function> func1 =
1439 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallJsFunction)
1440 0 : ->GetFunction(env)
1441 0 : .ToLocalChecked();
1442 0 : func1->SetName(v8_str("CallJsFunction1"));
1443 0 : env->Global()->Set(env, v8_str("CallJsFunction1"), func1).FromJust();
1444 :
1445 : v8::Local<v8::Function> func2 =
1446 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallJsFunction2)
1447 0 : ->GetFunction(env)
1448 0 : .ToLocalChecked();
1449 0 : func2->SetName(v8_str("CallJsFunction2"));
1450 0 : env->Global()->Set(env, v8_str("CallJsFunction2"), func2).FromJust();
1451 :
1452 0 : CompileRun(js_native1_js_native2_js_test_source);
1453 :
1454 0 : ProfilerHelper helper(env);
1455 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1456 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 1000);
1457 :
1458 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1459 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1460 : const v8::CpuProfileNode* native_node1 =
1461 0 : GetChild(env, start_node, "CallJsFunction1");
1462 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node1, "bar");
1463 : const v8::CpuProfileNode* native_node2 =
1464 0 : GetChild(env, bar_node, "CallJsFunction2");
1465 0 : GetChild(env, native_node2, "foo");
1466 :
1467 0 : profile->Delete();
1468 0 : }
1469 :
1470 : static const char* js_force_collect_sample_source =
1471 : "function start() {\n"
1472 : " CallCollectSample();\n"
1473 : "}";
1474 :
1475 5 : static void CallCollectSample(const v8::FunctionCallbackInfo<v8::Value>& info) {
1476 5 : i::ProfilerExtension::profiler()->CollectSample();
1477 5 : }
1478 :
1479 23723 : TEST(CollectSampleAPI) {
1480 5 : v8::HandleScope scope(CcTest::isolate());
1481 5 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1482 : v8::Context::Scope context_scope(env);
1483 :
1484 : v8::Local<v8::FunctionTemplate> func_template =
1485 5 : v8::FunctionTemplate::New(env->GetIsolate(), CallCollectSample);
1486 : v8::Local<v8::Function> func =
1487 5 : func_template->GetFunction(env).ToLocalChecked();
1488 5 : func->SetName(v8_str("CallCollectSample"));
1489 20 : env->Global()->Set(env, v8_str("CallCollectSample"), func).FromJust();
1490 :
1491 5 : CompileRun(js_force_collect_sample_source);
1492 5 : ProfilerHelper helper(env);
1493 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1494 5 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 0);
1495 :
1496 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1497 5 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1498 5 : CHECK_LE(1, start_node->GetChildrenCount());
1499 5 : GetChild(env, start_node, "CallCollectSample");
1500 :
1501 10 : profile->Delete();
1502 5 : }
1503 :
1504 : static const char* js_native_js_runtime_multiple_test_source =
1505 : "%NeverOptimizeFunction(foo);\n"
1506 : "%NeverOptimizeFunction(bar);\n"
1507 : "%NeverOptimizeFunction(start);\n"
1508 : "function foo() {\n"
1509 : " return Math.sin(Math.random());\n"
1510 : "}\n"
1511 : "var bound = foo.bind(this);\n"
1512 : "function bar() {\n"
1513 : " return bound();\n"
1514 : "}\n"
1515 : "function start() {\n"
1516 : " startProfiling('my_profile');\n"
1517 : " var startTime = Date.now();\n"
1518 : " do {\n"
1519 : " CallJsFunction(bar);\n"
1520 : " } while (Date.now() - startTime < 200);\n"
1521 : "}";
1522 :
1523 : // The test check multiple entrances/exits between JS and native code.
1524 : //
1525 : // [Top down]:
1526 : // (root) #0 1
1527 : // start #16 3
1528 : // CallJsFunction #0 4
1529 : // bar #16 5
1530 : // foo #16 6
1531 : // (program) #0 2
1532 23718 : TEST(JsNativeJsRuntimeJsSampleMultiple) {
1533 0 : i::FLAG_allow_natives_syntax = true;
1534 0 : v8::HandleScope scope(CcTest::isolate());
1535 0 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1536 : v8::Context::Scope context_scope(env);
1537 :
1538 : v8::Local<v8::FunctionTemplate> func_template =
1539 0 : v8::FunctionTemplate::New(env->GetIsolate(), CallJsFunction);
1540 : v8::Local<v8::Function> func =
1541 0 : func_template->GetFunction(env).ToLocalChecked();
1542 0 : func->SetName(v8_str("CallJsFunction"));
1543 0 : env->Global()->Set(env, v8_str("CallJsFunction"), func).FromJust();
1544 :
1545 0 : CompileRun(js_native_js_runtime_multiple_test_source);
1546 :
1547 0 : ProfilerHelper helper(env);
1548 0 : v8::Local<v8::Function> function = GetFunction(env, "start");
1549 0 : v8::CpuProfile* profile = helper.Run(function, nullptr, 0, 500, 500);
1550 :
1551 0 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1552 0 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1553 : const v8::CpuProfileNode* native_node =
1554 0 : GetChild(env, start_node, "CallJsFunction");
1555 0 : const v8::CpuProfileNode* bar_node = GetChild(env, native_node, "bar");
1556 0 : GetChild(env, bar_node, "foo");
1557 :
1558 0 : profile->Delete();
1559 0 : }
1560 :
1561 : static const char* inlining_test_source =
1562 : "%NeverOptimizeFunction(action);\n"
1563 : "%NeverOptimizeFunction(start);\n"
1564 : "level1()\n"
1565 : "%OptimizeFunctionOnNextCall(level1);\n"
1566 : "%OptimizeFunctionOnNextCall(level2);\n"
1567 : "%OptimizeFunctionOnNextCall(level3);\n"
1568 : "var finish = false;\n"
1569 : "function action(n) {\n"
1570 : " var s = 0;\n"
1571 : " for (var i = 0; i < n; ++i) s += i*i*i;\n"
1572 : " if (finish)\n"
1573 : " startProfiling('my_profile');\n"
1574 : " return s;\n"
1575 : "}\n"
1576 : "function level3() { return action(100); }\n"
1577 : "function level2() { return level3() * 2; }\n"
1578 : "function level1() { return level2(); }\n"
1579 : "function start() {\n"
1580 : " var n = 100;\n"
1581 : " while (--n)\n"
1582 : " level1();\n"
1583 : " finish = true;\n"
1584 : " level1();\n"
1585 : "}";
1586 :
1587 : // The test check multiple entrances/exits between JS and native code.
1588 : //
1589 : // [Top down]:
1590 : // (root) #0 1
1591 : // start #16 3
1592 : // level1 #0 4
1593 : // level2 #16 5
1594 : // level3 #16 6
1595 : // action #16 7
1596 : // (program) #0 2
1597 23723 : TEST(Inlining) {
1598 5 : i::FLAG_allow_natives_syntax = true;
1599 5 : v8::HandleScope scope(CcTest::isolate());
1600 5 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1601 : v8::Context::Scope context_scope(env);
1602 5 : ProfilerHelper helper(env);
1603 :
1604 5 : CompileRun(inlining_test_source);
1605 5 : v8::Local<v8::Function> function = GetFunction(env, "start");
1606 :
1607 5 : v8::Local<v8::String> profile_name = v8_str("my_profile");
1608 15 : function->Call(env, env->Global(), 0, nullptr).ToLocalChecked();
1609 5 : v8::CpuProfile* profile = helper.profiler()->StopProfiling(profile_name);
1610 5 : CHECK(profile);
1611 : // Dump collected profile to have a better diagnostic in case of failure.
1612 5 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1613 :
1614 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1615 5 : const v8::CpuProfileNode* start_node = GetChild(env, root, "start");
1616 5 : const v8::CpuProfileNode* level1_node = GetChild(env, start_node, "level1");
1617 5 : const v8::CpuProfileNode* level2_node = GetChild(env, level1_node, "level2");
1618 5 : const v8::CpuProfileNode* level3_node = GetChild(env, level2_node, "level3");
1619 5 : GetChild(env, level3_node, "action");
1620 :
1621 10 : profile->Delete();
1622 5 : }
1623 :
1624 : // [Top down]:
1625 : // 0 (root) #0 1
1626 : // 2 (program) #0 2
1627 : // 3 (idle) #0 3
1628 23723 : TEST(IdleTime) {
1629 5 : LocalContext env;
1630 10 : v8::HandleScope scope(env->GetIsolate());
1631 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
1632 :
1633 5 : v8::Local<v8::String> profile_name = v8_str("my_profile");
1634 5 : cpu_profiler->StartProfiling(profile_name);
1635 :
1636 : i::Isolate* isolate = CcTest::i_isolate();
1637 : i::ProfilerEventsProcessor* processor =
1638 : reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->processor();
1639 :
1640 5 : processor->AddCurrentStack(isolate, true);
1641 5 : cpu_profiler->SetIdle(true);
1642 20 : for (int i = 0; i < 3; i++) {
1643 15 : processor->AddCurrentStack(isolate, true);
1644 : }
1645 5 : cpu_profiler->SetIdle(false);
1646 5 : processor->AddCurrentStack(isolate, true);
1647 :
1648 5 : v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1649 5 : CHECK(profile);
1650 : // Dump collected profile to have a better diagnostic in case of failure.
1651 5 : reinterpret_cast<i::CpuProfile*>(profile)->Print();
1652 :
1653 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1654 : const v8::CpuProfileNode* program_node =
1655 5 : GetChild(env.local(), root, CodeEntry::kProgramEntryName);
1656 5 : CHECK_EQ(0, program_node->GetChildrenCount());
1657 5 : CHECK_GE(program_node->GetHitCount(), 2u);
1658 :
1659 : const v8::CpuProfileNode* idle_node =
1660 5 : GetChild(env.local(), root, CodeEntry::kIdleEntryName);
1661 5 : CHECK_EQ(0, idle_node->GetChildrenCount());
1662 5 : CHECK_GE(idle_node->GetHitCount(), 3u);
1663 :
1664 5 : profile->Delete();
1665 10 : cpu_profiler->Dispose();
1666 5 : }
1667 :
1668 20 : static void CheckFunctionDetails(v8::Isolate* isolate,
1669 : const v8::CpuProfileNode* node,
1670 : const char* name, const char* script_name,
1671 : int script_id, int line, int column) {
1672 20 : v8::Local<v8::Context> context = isolate->GetCurrentContext();
1673 80 : CHECK(v8_str(name)->Equals(context, node->GetFunctionName()).FromJust());
1674 20 : CHECK_EQ(0, strcmp(name, node->GetFunctionNameStr()));
1675 80 : CHECK(v8_str(script_name)
1676 : ->Equals(context, node->GetScriptResourceName())
1677 : .FromJust());
1678 20 : CHECK_EQ(0, strcmp(script_name, node->GetScriptResourceNameStr()));
1679 20 : CHECK_EQ(script_id, node->GetScriptId());
1680 20 : CHECK_EQ(line, node->GetLineNumber());
1681 20 : CHECK_EQ(column, node->GetColumnNumber());
1682 20 : }
1683 :
1684 :
1685 23723 : TEST(FunctionDetails) {
1686 5 : i::FLAG_allow_natives_syntax = true;
1687 5 : v8::HandleScope scope(CcTest::isolate());
1688 5 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1689 : v8::Context::Scope context_scope(env);
1690 5 : ProfilerHelper helper(env);
1691 :
1692 : v8::Local<v8::Script> script_a = CompileWithOrigin(
1693 : "%NeverOptimizeFunction(foo);\n"
1694 : "%NeverOptimizeFunction(bar);\n"
1695 : " function foo\n() { bar(); }\n"
1696 : " function bar() { startProfiling(); }\n",
1697 5 : "script_a");
1698 5 : script_a->Run(env).ToLocalChecked();
1699 : v8::Local<v8::Script> script_b = CompileWithOrigin(
1700 : "%NeverOptimizeFunction(baz);"
1701 : "\n\n function baz() { foo(); }\n"
1702 : "\n\nbaz();\n"
1703 : "stopProfiling();\n",
1704 5 : "script_b");
1705 5 : script_b->Run(env).ToLocalChecked();
1706 5 : const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1707 5 : const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1708 : reinterpret_cast<ProfileNode*>(
1709 5 : const_cast<v8::CpuProfileNode*>(current))->Print(0);
1710 : // The tree should look like this:
1711 : // 0 (root) 0 #1
1712 : // 0 "" 19 #2 no reason script_b:1
1713 : // 0 baz 19 #3 TryCatchStatement script_b:3
1714 : // 0 foo 18 #4 TryCatchStatement script_a:2
1715 : // 1 bar 18 #5 no reason script_a:3
1716 5 : const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1717 5 : const v8::CpuProfileNode* script = GetChild(env, root, "");
1718 : CheckFunctionDetails(env->GetIsolate(), script, "", "script_b",
1719 15 : script_b->GetUnboundScript()->GetId(), 1, 1);
1720 5 : const v8::CpuProfileNode* baz = GetChild(env, script, "baz");
1721 : CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b",
1722 15 : script_b->GetUnboundScript()->GetId(), 3, 16);
1723 5 : const v8::CpuProfileNode* foo = GetChild(env, baz, "foo");
1724 : CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a",
1725 15 : script_a->GetUnboundScript()->GetId(), 4, 1);
1726 5 : const v8::CpuProfileNode* bar = GetChild(env, foo, "bar");
1727 : CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a",
1728 20 : script_a->GetUnboundScript()->GetId(), 5, 14);
1729 5 : }
1730 :
1731 :
1732 23723 : TEST(DontStopOnFinishedProfileDelete) {
1733 5 : v8::HandleScope scope(CcTest::isolate());
1734 5 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1735 : v8::Context::Scope context_scope(env);
1736 :
1737 5 : v8::CpuProfiler* profiler = v8::CpuProfiler::New(env->GetIsolate());
1738 : i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
1739 :
1740 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
1741 5 : v8::Local<v8::String> outer = v8_str("outer");
1742 5 : profiler->StartProfiling(outer);
1743 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
1744 :
1745 5 : v8::Local<v8::String> inner = v8_str("inner");
1746 5 : profiler->StartProfiling(inner);
1747 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
1748 :
1749 5 : v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
1750 5 : CHECK(inner_profile);
1751 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
1752 5 : inner_profile->Delete();
1753 : inner_profile = nullptr;
1754 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
1755 :
1756 5 : v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
1757 5 : CHECK(outer_profile);
1758 5 : CHECK_EQ(1, iprofiler->GetProfilesCount());
1759 5 : outer_profile->Delete();
1760 : outer_profile = nullptr;
1761 5 : CHECK_EQ(0, iprofiler->GetProfilesCount());
1762 10 : profiler->Dispose();
1763 5 : }
1764 :
1765 :
1766 0 : const char* GetBranchDeoptReason(v8::Local<v8::Context> context,
1767 : i::CpuProfile* iprofile, const char* branch[],
1768 : int length) {
1769 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
1770 : const ProfileNode* iopt_function = nullptr;
1771 0 : iopt_function = GetSimpleBranch(context, profile, branch, length);
1772 0 : CHECK_EQ(1U, iopt_function->deopt_infos().size());
1773 0 : return iopt_function->deopt_infos()[0].deopt_reason;
1774 : }
1775 :
1776 :
1777 : // deopt at top function
1778 23718 : TEST(CollectDeoptEvents) {
1779 0 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
1780 0 : i::FLAG_allow_natives_syntax = true;
1781 0 : v8::HandleScope scope(CcTest::isolate());
1782 0 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1783 : v8::Context::Scope context_scope(env);
1784 0 : ProfilerHelper helper(env);
1785 : i::CpuProfiler* iprofiler =
1786 0 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
1787 :
1788 : const char opt_source[] =
1789 : "function opt_function%d(value, depth) {\n"
1790 : " if (depth) return opt_function%d(value, depth - 1);\n"
1791 : "\n"
1792 : " return 10 / value;\n"
1793 : "}\n"
1794 0 : "\n";
1795 :
1796 0 : for (int i = 0; i < 3; ++i) {
1797 : i::EmbeddedVector<char, sizeof(opt_source) + 100> buffer;
1798 0 : i::SNPrintF(buffer, opt_source, i, i);
1799 0 : v8::Script::Compile(env, v8_str(buffer.start()))
1800 0 : .ToLocalChecked()
1801 : ->Run(env)
1802 0 : .ToLocalChecked();
1803 : }
1804 :
1805 : const char* source =
1806 : "startProfiling();\n"
1807 : "\n"
1808 : "opt_function0(1, 1);\n"
1809 : "\n"
1810 : "%OptimizeFunctionOnNextCall(opt_function0)\n"
1811 : "\n"
1812 : "opt_function0(1, 1);\n"
1813 : "\n"
1814 : "opt_function0(undefined, 1);\n"
1815 : "\n"
1816 : "opt_function1(1, 1);\n"
1817 : "\n"
1818 : "%OptimizeFunctionOnNextCall(opt_function1)\n"
1819 : "\n"
1820 : "opt_function1(1, 1);\n"
1821 : "\n"
1822 : "opt_function1(NaN, 1);\n"
1823 : "\n"
1824 : "opt_function2(1, 1);\n"
1825 : "\n"
1826 : "%OptimizeFunctionOnNextCall(opt_function2)\n"
1827 : "\n"
1828 : "opt_function2(1, 1);\n"
1829 : "\n"
1830 : "opt_function2(0, 1);\n"
1831 : "\n"
1832 : "stopProfiling();\n"
1833 : "\n";
1834 :
1835 0 : v8::Script::Compile(env, v8_str(source))
1836 0 : .ToLocalChecked()
1837 : ->Run(env)
1838 0 : .ToLocalChecked();
1839 0 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
1840 0 : iprofile->Print();
1841 : /* The expected profile
1842 : [Top down]:
1843 : 0 (root) 0 #1
1844 : 23 32 #2
1845 : 1 opt_function2 31 #7
1846 : 1 opt_function2 31 #8
1847 : ;;; deopted at script_id: 31 position: 106 with reason
1848 : 'division by zero'.
1849 : 2 opt_function0 29 #3
1850 : 4 opt_function0 29 #4
1851 : ;;; deopted at script_id: 29 position: 108 with reason 'not a
1852 : heap number'.
1853 : 0 opt_function1 30 #5
1854 : 1 opt_function1 30 #6
1855 : ;;; deopted at script_id: 30 position: 108 with reason 'lost
1856 : precision or NaN'.
1857 : */
1858 :
1859 : {
1860 0 : const char* branch[] = {"", "opt_function0", "opt_function0"};
1861 : const char* deopt_reason =
1862 0 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch));
1863 0 : if (deopt_reason != reason(i::DeoptimizeReason::kNotAHeapNumber) &&
1864 : deopt_reason != reason(i::DeoptimizeReason::kNotASmi)) {
1865 0 : FATAL(deopt_reason);
1866 : }
1867 : }
1868 : {
1869 0 : const char* branch[] = {"", "opt_function1", "opt_function1"};
1870 : const char* deopt_reason =
1871 0 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch));
1872 0 : if (deopt_reason != reason(i::DeoptimizeReason::kNaN) &&
1873 0 : deopt_reason != reason(i::DeoptimizeReason::kLostPrecisionOrNaN) &&
1874 : deopt_reason != reason(i::DeoptimizeReason::kNotASmi)) {
1875 0 : FATAL(deopt_reason);
1876 : }
1877 : }
1878 : {
1879 0 : const char* branch[] = {"", "opt_function2", "opt_function2"};
1880 0 : CHECK_EQ(reason(i::DeoptimizeReason::kDivisionByZero),
1881 : GetBranchDeoptReason(env, iprofile, branch, arraysize(branch)));
1882 : }
1883 0 : iprofiler->DeleteProfile(iprofile);
1884 : }
1885 :
1886 :
1887 23723 : TEST(SourceLocation) {
1888 5 : i::FLAG_always_opt = true;
1889 5 : LocalContext env;
1890 10 : v8::HandleScope scope(CcTest::isolate());
1891 :
1892 : const char* source =
1893 : "function CompareStatementWithThis() {\n"
1894 : " if (this === 1) {}\n"
1895 : "}\n"
1896 : "CompareStatementWithThis();\n";
1897 :
1898 5 : v8::Script::Compile(env.local(), v8_str(source))
1899 5 : .ToLocalChecked()
1900 5 : ->Run(env.local())
1901 10 : .ToLocalChecked();
1902 5 : }
1903 :
1904 : static const char* inlined_source =
1905 : "function opt_function(left, right) { var k = left*right; return k + 1; "
1906 : "}\n";
1907 : // 0.........1.........2.........3.........4....*....5.........6......*..7
1908 :
1909 :
1910 : // deopt at the first level inlined function
1911 23723 : TEST(DeoptAtFirstLevelInlinedSource) {
1912 7 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
1913 3 : i::FLAG_allow_natives_syntax = true;
1914 3 : v8::HandleScope scope(CcTest::isolate());
1915 3 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1916 : v8::Context::Scope context_scope(env);
1917 3 : ProfilerHelper helper(env);
1918 : i::CpuProfiler* iprofiler =
1919 3 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
1920 :
1921 : // 0.........1.........2.........3.........4.........5.........6.........7
1922 : const char* source =
1923 : "function test(left, right) { return opt_function(left, right); }\n"
1924 : "\n"
1925 : "startProfiling();\n"
1926 : "\n"
1927 : "test(10, 10);\n"
1928 : "\n"
1929 : "%OptimizeFunctionOnNextCall(test)\n"
1930 : "\n"
1931 : "test(10, 10);\n"
1932 : "\n"
1933 : "test(undefined, 1e9);\n"
1934 : "\n"
1935 : "stopProfiling();\n"
1936 : "\n";
1937 :
1938 3 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
1939 3 : inlined_script->Run(env).ToLocalChecked();
1940 6 : int inlined_script_id = inlined_script->GetUnboundScript()->GetId();
1941 :
1942 : v8::Local<v8::Script> script = v8_compile(source);
1943 3 : script->Run(env).ToLocalChecked();
1944 6 : int script_id = script->GetUnboundScript()->GetId();
1945 :
1946 3 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
1947 3 : iprofile->Print();
1948 : /* The expected profile output
1949 : [Top down]:
1950 : 0 (root) 0 #1
1951 : 10 30 #2
1952 : 1 test 30 #3
1953 : ;;; deopted at script_id: 29 position: 45 with reason 'not a
1954 : heap number'.
1955 : ;;; Inline point: script_id 30 position: 36.
1956 : 4 opt_function 29 #4
1957 : */
1958 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
1959 :
1960 3 : const char* branch[] = {"", "test"};
1961 : const ProfileNode* itest_node =
1962 3 : GetSimpleBranch(env, profile, branch, arraysize(branch));
1963 3 : const std::vector<v8::CpuProfileDeoptInfo>& deopt_infos =
1964 : itest_node->deopt_infos();
1965 3 : CHECK_EQ(1U, deopt_infos.size());
1966 :
1967 : const v8::CpuProfileDeoptInfo& info = deopt_infos[0];
1968 3 : CHECK(reason(i::DeoptimizeReason::kNotASmi) == info.deopt_reason ||
1969 : reason(i::DeoptimizeReason::kNotAHeapNumber) == info.deopt_reason);
1970 6 : CHECK_EQ(2U, info.stack.size());
1971 3 : CHECK_EQ(inlined_script_id, info.stack[0].script_id);
1972 6 : CHECK_LE(dist(offset(inlined_source, "*right"), info.stack[0].position), 1);
1973 3 : CHECK_EQ(script_id, info.stack[1].script_id);
1974 3 : CHECK_EQ(offset(source, "opt_function(left,"), info.stack[1].position);
1975 :
1976 6 : iprofiler->DeleteProfile(iprofile);
1977 : }
1978 :
1979 :
1980 : // deopt at the second level inlined function
1981 23723 : TEST(DeoptAtSecondLevelInlinedSource) {
1982 7 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
1983 3 : i::FLAG_allow_natives_syntax = true;
1984 3 : v8::HandleScope scope(CcTest::isolate());
1985 3 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1986 : v8::Context::Scope context_scope(env);
1987 3 : ProfilerHelper helper(env);
1988 : i::CpuProfiler* iprofiler =
1989 3 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
1990 :
1991 : // 0.........1.........2.........3.........4.........5.........6.........7
1992 : const char* source =
1993 : "function test2(left, right) { return opt_function(left, right); }\n"
1994 : "function test1(left, right) { return test2(left, right); } \n"
1995 : "\n"
1996 : "startProfiling();\n"
1997 : "\n"
1998 : "test1(10, 10);\n"
1999 : "\n"
2000 : "%OptimizeFunctionOnNextCall(test1)\n"
2001 : "\n"
2002 : "test1(10, 10);\n"
2003 : "\n"
2004 : "test1(undefined, 1e9);\n"
2005 : "\n"
2006 : "stopProfiling();\n"
2007 : "\n";
2008 :
2009 3 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2010 3 : inlined_script->Run(env).ToLocalChecked();
2011 6 : int inlined_script_id = inlined_script->GetUnboundScript()->GetId();
2012 :
2013 : v8::Local<v8::Script> script = v8_compile(source);
2014 3 : script->Run(env).ToLocalChecked();
2015 6 : int script_id = script->GetUnboundScript()->GetId();
2016 :
2017 3 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2018 3 : iprofile->Print();
2019 : /* The expected profile output
2020 : [Top down]:
2021 : 0 (root) 0 #1
2022 : 11 30 #2
2023 : 1 test1 30 #3
2024 : ;;; deopted at script_id: 29 position: 45 with reason 'not a
2025 : heap number'.
2026 : ;;; Inline point: script_id 30 position: 37.
2027 : ;;; Inline point: script_id 30 position: 103.
2028 : 1 test2 30 #4
2029 : 3 opt_function 29 #5
2030 : */
2031 :
2032 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2033 :
2034 3 : const char* branch[] = {"", "test1"};
2035 : const ProfileNode* itest_node =
2036 3 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2037 3 : const std::vector<v8::CpuProfileDeoptInfo>& deopt_infos =
2038 : itest_node->deopt_infos();
2039 3 : CHECK_EQ(1U, deopt_infos.size());
2040 :
2041 : const v8::CpuProfileDeoptInfo info = deopt_infos[0];
2042 3 : CHECK(reason(i::DeoptimizeReason::kNotASmi) == info.deopt_reason ||
2043 : reason(i::DeoptimizeReason::kNotAHeapNumber) == info.deopt_reason);
2044 6 : CHECK_EQ(3U, info.stack.size());
2045 3 : CHECK_EQ(inlined_script_id, info.stack[0].script_id);
2046 6 : CHECK_LE(dist(offset(inlined_source, "*right"), info.stack[0].position), 1);
2047 3 : CHECK_EQ(script_id, info.stack[1].script_id);
2048 3 : CHECK_EQ(offset(source, "opt_function(left,"), info.stack[1].position);
2049 3 : CHECK_EQ(offset(source, "test2(left, right);"), info.stack[2].position);
2050 :
2051 6 : iprofiler->DeleteProfile(iprofile);
2052 : }
2053 :
2054 :
2055 : // deopt in untracked function
2056 23723 : TEST(DeoptUntrackedFunction) {
2057 7 : if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
2058 3 : i::FLAG_allow_natives_syntax = true;
2059 3 : v8::HandleScope scope(CcTest::isolate());
2060 3 : v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
2061 : v8::Context::Scope context_scope(env);
2062 3 : ProfilerHelper helper(env);
2063 : i::CpuProfiler* iprofiler =
2064 3 : reinterpret_cast<i::CpuProfiler*>(helper.profiler());
2065 :
2066 : // 0.........1.........2.........3.........4.........5.........6.........7
2067 : const char* source =
2068 : "function test(left, right) { return opt_function(left, right); }\n"
2069 : "\n"
2070 : "test(10, 10);\n"
2071 : "\n"
2072 : "%OptimizeFunctionOnNextCall(test)\n"
2073 : "\n"
2074 : "test(10, 10);\n"
2075 : "\n"
2076 : "startProfiling();\n" // profiler started after compilation.
2077 : "\n"
2078 : "test(undefined, 10);\n"
2079 : "\n"
2080 : "stopProfiling();\n"
2081 : "\n";
2082 :
2083 3 : v8::Local<v8::Script> inlined_script = v8_compile(inlined_source);
2084 3 : inlined_script->Run(env).ToLocalChecked();
2085 :
2086 : v8::Local<v8::Script> script = v8_compile(source);
2087 3 : script->Run(env).ToLocalChecked();
2088 :
2089 3 : i::CpuProfile* iprofile = iprofiler->GetProfile(0);
2090 3 : iprofile->Print();
2091 : v8::CpuProfile* profile = reinterpret_cast<v8::CpuProfile*>(iprofile);
2092 :
2093 3 : const char* branch[] = {"", "test"};
2094 : const ProfileNode* itest_node =
2095 3 : GetSimpleBranch(env, profile, branch, arraysize(branch));
2096 6 : CHECK_EQ(0U, itest_node->deopt_infos().size());
2097 :
2098 6 : iprofiler->DeleteProfile(iprofile);
2099 : }
2100 :
2101 : using v8::platform::tracing::TraceBuffer;
2102 : using v8::platform::tracing::TraceConfig;
2103 : using v8::platform::tracing::TraceObject;
2104 :
2105 : namespace {
2106 :
2107 15 : class CpuProfileEventChecker : public v8::platform::tracing::TraceWriter {
2108 : public:
2109 20 : void AppendTraceEvent(TraceObject* trace_event) override {
2110 15 : if (trace_event->name() != std::string("Profile") &&
2111 5 : trace_event->name() != std::string("ProfileChunk"))
2112 5 : return;
2113 5 : CHECK(!profile_id_ || trace_event->id() == profile_id_);
2114 5 : CHECK_EQ(1, trace_event->num_args());
2115 5 : CHECK_EQ(TRACE_VALUE_TYPE_CONVERTABLE, trace_event->arg_types()[0]);
2116 5 : profile_id_ = trace_event->id();
2117 : v8::ConvertableToTraceFormat* arg =
2118 : trace_event->arg_convertables()[0].get();
2119 5 : arg->AppendAsTraceFormat(&result_json_);
2120 : }
2121 5 : void Flush() override {}
2122 :
2123 5 : std::string result_json() const { return result_json_; }
2124 :
2125 : private:
2126 : std::string result_json_;
2127 : uint64_t profile_id_ = 0;
2128 : };
2129 :
2130 : } // namespace
2131 :
2132 23723 : TEST(TracingCpuProfiler) {
2133 5 : v8::Platform* old_platform = i::V8::GetCurrentPlatform();
2134 5 : v8::Platform* default_platform = v8::platform::CreateDefaultPlatform();
2135 5 : i::V8::SetPlatformForTesting(default_platform);
2136 :
2137 5 : v8::platform::tracing::TracingController tracing_controller;
2138 : static_cast<v8::platform::DefaultPlatform*>(default_platform)
2139 5 : ->SetTracingController(&tracing_controller);
2140 :
2141 5 : CpuProfileEventChecker* event_checker = new CpuProfileEventChecker();
2142 : TraceBuffer* ring_buffer =
2143 5 : TraceBuffer::CreateTraceBufferRingBuffer(1, event_checker);
2144 5 : tracing_controller.Initialize(ring_buffer);
2145 5 : TraceConfig* trace_config = new TraceConfig();
2146 : trace_config->AddIncludedCategory(
2147 5 : TRACE_DISABLED_BY_DEFAULT("v8.cpu_profiler"));
2148 :
2149 10 : LocalContext env;
2150 10 : v8::HandleScope scope(env->GetIsolate());
2151 : {
2152 5 : tracing_controller.StartTracing(trace_config);
2153 5 : auto profiler = v8::TracingCpuProfiler::Create(env->GetIsolate());
2154 : CompileRun("function foo() { } foo();");
2155 5 : tracing_controller.StopTracing();
2156 : CompileRun("function bar() { } bar();");
2157 : }
2158 :
2159 : const char* profile_checker =
2160 : "function checkProfile(profile) {\n"
2161 : " if (typeof profile['startTime'] !== 'number') return 'startTime';\n"
2162 : " return '';\n"
2163 : "}\n"
2164 : "checkProfile(";
2165 : std::string profile_json = event_checker->result_json();
2166 5 : CHECK_LT(0u, profile_json.length());
2167 : printf("Profile JSON: %s\n", profile_json.c_str());
2168 10 : std::string code = profile_checker + profile_json + ")";
2169 : v8::Local<v8::Value> result =
2170 5 : CompileRunChecked(CcTest::isolate(), code.c_str());
2171 10 : v8::String::Utf8Value value(CcTest::isolate(), result);
2172 5 : printf("Check result: %*s\n", value.length(), *value);
2173 5 : CHECK_EQ(0, value.length());
2174 :
2175 10 : i::V8::SetPlatformForTesting(old_platform);
2176 5 : }
2177 :
2178 23723 : TEST(Issue763073) {
2179 : class AllowNativesSyntax {
2180 : public:
2181 : AllowNativesSyntax()
2182 : : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
2183 5 : trace_deopt_(i::FLAG_trace_deopt) {
2184 5 : i::FLAG_allow_natives_syntax = true;
2185 5 : i::FLAG_trace_deopt = true;
2186 : }
2187 :
2188 : ~AllowNativesSyntax() {
2189 5 : i::FLAG_allow_natives_syntax = allow_natives_syntax_;
2190 5 : i::FLAG_trace_deopt = trace_deopt_;
2191 : }
2192 :
2193 : private:
2194 : bool allow_natives_syntax_;
2195 : bool trace_deopt_;
2196 : };
2197 :
2198 : AllowNativesSyntax allow_natives_syntax_scope;
2199 10 : LocalContext env;
2200 10 : v8::HandleScope scope(env->GetIsolate());
2201 :
2202 : CompileRun(
2203 : "function f() { return function g(x) { }; }"
2204 : // Create first closure, optimize it, and deoptimize it.
2205 : "var g = f();"
2206 : "g(1);"
2207 : "%OptimizeFunctionOnNextCall(g);"
2208 : "g(1);"
2209 : "%DeoptimizeFunction(g);"
2210 : // Create second closure, and optimize it. This will create another
2211 : // optimized code object and put in the (shared) type feedback vector.
2212 : "var h = f();"
2213 : "h(1);"
2214 : "%OptimizeFunctionOnNextCall(h);"
2215 : "h(1);");
2216 :
2217 : // Start profiling.
2218 5 : v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(env->GetIsolate());
2219 5 : v8::Local<v8::String> profile_name = v8_str("test");
2220 :
2221 : // Here we test that the heap iteration upon profiling start is not
2222 : // confused by having a deoptimized code object for a closure while
2223 : // having a different optimized code object in the type feedback vector.
2224 5 : cpu_profiler->StartProfiling(profile_name);
2225 5 : v8::CpuProfile* p = cpu_profiler->StopProfiling(profile_name);
2226 5 : p->Delete();
2227 5 : cpu_profiler->Dispose();
2228 5 : }
2229 :
2230 : } // namespace test_cpu_profiler
2231 : } // namespace internal
2232 71154 : } // namespace v8
|