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
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.
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.
28 // Tests of profiles generator and utilities.
32 #include "include/v8-profiler.h"
33 #include "src/base/platform/platform.h"
34 #include "src/cpu-profiler-inl.h"
35 #include "src/smart-pointers.h"
36 #include "src/utils.h"
37 #include "test/cctest/cctest.h"
38 #include "test/cctest/profiler-extension.h"
42 using i::CpuProfilesCollection;
44 using i::ProfileGenerator;
46 using i::ProfilerEventsProcessor;
47 using i::ScopedVector;
48 using i::SmartPointer;
53 i::Isolate* isolate = CcTest::i_isolate();
54 CpuProfilesCollection profiles(isolate->heap());
55 ProfileGenerator generator(&profiles);
56 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
57 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
59 processor->StopSynchronously();
63 static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
65 i::Address frame2 = NULL,
66 i::Address frame3 = NULL) {
67 i::TickSample* sample = proc->StartTickSample();
70 sample->frames_count = 0;
72 sample->stack[0] = frame2;
73 sample->frames_count = 1;
76 sample->stack[1] = frame3;
77 sample->frames_count = 2;
79 proc->FinishTickSample();
87 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
88 i::FLAG_prof_browser_mode = false;
92 i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
96 bool old_flag_prof_browser_mode_;
102 i::Code* CreateCode(LocalContext* env) {
103 static int counter = 0;
104 i::EmbeddedVector<char, 256> script;
105 i::EmbeddedVector<char, 32> name;
107 i::SNPrintF(name, "function_%d", ++counter);
108 const char* name_start = name.start();
112 "for (var i = 0; i < %d; ++i) counter += i;\n"
113 "return '%s_' + counter;\n"
115 "%s();\n", name_start, counter, name_start, name_start);
116 CompileRun(script.start());
117 i::Handle<i::JSFunction> fun = v8::Utils::OpenHandle(
118 *v8::Local<v8::Function>::Cast(
119 (*env)->Global()->Get(v8_str(name_start))));
125 CcTest::InitializeVM();
127 i::Isolate* isolate = CcTest::i_isolate();
128 i::Factory* factory = isolate->factory();
129 TestSetup test_setup;
131 i::HandleScope scope(isolate);
133 i::Code* aaa_code = CreateCode(&env);
134 i::Code* comment_code = CreateCode(&env);
135 i::Code* args5_code = CreateCode(&env);
136 i::Code* comment2_code = CreateCode(&env);
137 i::Code* moved_code = CreateCode(&env);
138 i::Code* args3_code = CreateCode(&env);
139 i::Code* args4_code = CreateCode(&env);
141 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
142 profiles->StartProfiling("", false);
143 ProfileGenerator generator(profiles);
144 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
145 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
147 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
149 // Enqueue code creation events.
150 const char* aaa_str = "aaa";
151 i::Handle<i::String> aaa_name = factory->NewStringFromAsciiChecked(aaa_str);
152 profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code, *aaa_name);
153 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code, "comment");
154 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args5_code, 5);
155 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code, "comment2");
156 profiler.CodeMoveEvent(comment2_code->address(), moved_code->address());
157 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args3_code, 3);
158 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args4_code, 4);
160 // Enqueue a tick event to enable code events processing.
161 EnqueueTickSampleEvent(processor.get(), aaa_code->address());
163 processor->StopSynchronously();
165 // Check the state of profile generator.
166 CodeEntry* aaa = generator.code_map()->FindEntry(aaa_code->address());
168 CHECK_EQ(aaa_str, aaa->name());
170 CodeEntry* comment = generator.code_map()->FindEntry(comment_code->address());
171 CHECK_NE(NULL, comment);
172 CHECK_EQ("comment", comment->name());
174 CodeEntry* args5 = generator.code_map()->FindEntry(args5_code->address());
175 CHECK_NE(NULL, args5);
176 CHECK_EQ("5", args5->name());
178 CHECK_EQ(NULL, generator.code_map()->FindEntry(comment2_code->address()));
180 CodeEntry* comment2 = generator.code_map()->FindEntry(moved_code->address());
181 CHECK_NE(NULL, comment2);
182 CHECK_EQ("comment2", comment2->name());
187 static int CompareProfileNodes(const T* p1, const T* p2) {
188 return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
193 TestSetup test_setup;
195 i::Isolate* isolate = CcTest::i_isolate();
196 i::HandleScope scope(isolate);
198 i::Code* frame1_code = CreateCode(&env);
199 i::Code* frame2_code = CreateCode(&env);
200 i::Code* frame3_code = CreateCode(&env);
202 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
203 profiles->StartProfiling("", false);
204 ProfileGenerator generator(profiles);
205 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
206 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
208 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
210 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
211 profiler.CodeCreateEvent(i::Logger::STUB_TAG, frame2_code, 5);
212 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame3_code, "ddd");
214 EnqueueTickSampleEvent(processor.get(), frame1_code->instruction_start());
215 EnqueueTickSampleEvent(
217 frame2_code->instruction_start() + frame2_code->ExecutableSize() / 2,
218 frame1_code->instruction_start() + frame2_code->ExecutableSize() / 2);
219 EnqueueTickSampleEvent(
221 frame3_code->instruction_end() - 1,
222 frame2_code->instruction_end() - 1,
223 frame1_code->instruction_end() - 1);
225 processor->StopSynchronously();
226 CpuProfile* profile = profiles->StopProfiling("");
227 CHECK_NE(NULL, profile);
230 const i::List<ProfileNode*>* top_down_root_children =
231 profile->top_down()->root()->children();
232 CHECK_EQ(1, top_down_root_children->length());
233 CHECK_EQ("bbb", top_down_root_children->last()->entry()->name());
234 const i::List<ProfileNode*>* top_down_bbb_children =
235 top_down_root_children->last()->children();
236 CHECK_EQ(1, top_down_bbb_children->length());
237 CHECK_EQ("5", top_down_bbb_children->last()->entry()->name());
238 const i::List<ProfileNode*>* top_down_stub_children =
239 top_down_bbb_children->last()->children();
240 CHECK_EQ(1, top_down_stub_children->length());
241 CHECK_EQ("ddd", top_down_stub_children->last()->entry()->name());
242 const i::List<ProfileNode*>* top_down_ddd_children =
243 top_down_stub_children->last()->children();
244 CHECK_EQ(0, top_down_ddd_children->length());
248 // http://crbug/51594
249 // This test must not crash.
250 TEST(CrashIfStoppingLastNonExistentProfile) {
251 CcTest::InitializeVM();
252 TestSetup test_setup;
253 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
254 profiler->StartProfiling("1");
255 profiler->StopProfiling("2");
256 profiler->StartProfiling("1");
257 profiler->StopProfiling("");
261 // http://code.google.com/p/v8/issues/detail?id=1398
262 // Long stacks (exceeding max frames limit) must not be erased.
264 TestSetup test_setup;
266 i::Isolate* isolate = CcTest::i_isolate();
267 i::HandleScope scope(isolate);
269 i::Code* code = CreateCode(&env);
271 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
272 profiles->StartProfiling("", false);
273 ProfileGenerator generator(profiles);
274 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
275 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
277 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
279 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
281 i::TickSample* sample = processor->StartTickSample();
282 sample->pc = code->address();
284 sample->frames_count = i::TickSample::kMaxFramesCount;
285 for (unsigned i = 0; i < sample->frames_count; ++i) {
286 sample->stack[i] = code->address();
288 processor->FinishTickSample();
290 processor->StopSynchronously();
291 CpuProfile* profile = profiles->StopProfiling("");
292 CHECK_NE(NULL, profile);
294 int actual_depth = 0;
295 const ProfileNode* node = profile->top_down()->root();
296 while (node->children()->length() > 0) {
297 node = node->children()->last();
301 CHECK_EQ(1 + i::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
305 TEST(DeleteAllCpuProfiles) {
306 CcTest::InitializeVM();
307 TestSetup test_setup;
308 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
309 CHECK_EQ(0, profiler->GetProfilesCount());
310 profiler->DeleteAllProfiles();
311 CHECK_EQ(0, profiler->GetProfilesCount());
313 profiler->StartProfiling("1");
314 profiler->StopProfiling("1");
315 CHECK_EQ(1, profiler->GetProfilesCount());
316 profiler->DeleteAllProfiles();
317 CHECK_EQ(0, profiler->GetProfilesCount());
318 profiler->StartProfiling("1");
319 profiler->StartProfiling("2");
320 profiler->StopProfiling("2");
321 profiler->StopProfiling("1");
322 CHECK_EQ(2, profiler->GetProfilesCount());
323 profiler->DeleteAllProfiles();
324 CHECK_EQ(0, profiler->GetProfilesCount());
326 // Test profiling cancellation by the 'delete' command.
327 profiler->StartProfiling("1");
328 profiler->StartProfiling("2");
329 CHECK_EQ(0, profiler->GetProfilesCount());
330 profiler->DeleteAllProfiles();
331 CHECK_EQ(0, profiler->GetProfilesCount());
335 static bool FindCpuProfile(v8::CpuProfiler* v8profiler,
336 const v8::CpuProfile* v8profile) {
337 i::CpuProfiler* profiler = reinterpret_cast<i::CpuProfiler*>(v8profiler);
338 const i::CpuProfile* profile =
339 reinterpret_cast<const i::CpuProfile*>(v8profile);
340 int length = profiler->GetProfilesCount();
341 for (int i = 0; i < length; i++) {
342 if (profile == profiler->GetProfile(i))
349 TEST(DeleteCpuProfile) {
351 v8::HandleScope scope(env->GetIsolate());
352 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
353 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(cpu_profiler);
355 CHECK_EQ(0, iprofiler->GetProfilesCount());
356 v8::Local<v8::String> name1 = v8::String::NewFromUtf8(env->GetIsolate(), "1");
357 cpu_profiler->StartProfiling(name1);
358 v8::CpuProfile* p1 = cpu_profiler->StopProfiling(name1);
360 CHECK_EQ(1, iprofiler->GetProfilesCount());
361 CHECK(FindCpuProfile(cpu_profiler, p1));
363 CHECK_EQ(0, iprofiler->GetProfilesCount());
365 v8::Local<v8::String> name2 = v8::String::NewFromUtf8(env->GetIsolate(), "2");
366 cpu_profiler->StartProfiling(name2);
367 v8::CpuProfile* p2 = cpu_profiler->StopProfiling(name2);
369 CHECK_EQ(1, iprofiler->GetProfilesCount());
370 CHECK(FindCpuProfile(cpu_profiler, p2));
371 v8::Local<v8::String> name3 = v8::String::NewFromUtf8(env->GetIsolate(), "3");
372 cpu_profiler->StartProfiling(name3);
373 v8::CpuProfile* p3 = cpu_profiler->StopProfiling(name3);
375 CHECK_EQ(2, iprofiler->GetProfilesCount());
377 CHECK(FindCpuProfile(cpu_profiler, p3));
378 CHECK(FindCpuProfile(cpu_profiler, p2));
380 CHECK_EQ(1, iprofiler->GetProfilesCount());
381 CHECK(!FindCpuProfile(cpu_profiler, p2));
382 CHECK(FindCpuProfile(cpu_profiler, p3));
384 CHECK_EQ(0, iprofiler->GetProfilesCount());
388 TEST(ProfileStartEndTime) {
390 v8::HandleScope scope(env->GetIsolate());
391 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
393 v8::Local<v8::String> profile_name =
394 v8::String::NewFromUtf8(env->GetIsolate(), "test");
395 cpu_profiler->StartProfiling(profile_name);
396 const v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
397 CHECK(profile->GetStartTime() <= profile->GetEndTime());
401 static v8::CpuProfile* RunProfiler(
402 v8::Handle<v8::Context> env, v8::Handle<v8::Function> function,
403 v8::Handle<v8::Value> argv[], int argc,
404 unsigned min_js_samples, bool collect_samples = false) {
405 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
406 v8::Local<v8::String> profile_name =
407 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
409 cpu_profiler->StartProfiling(profile_name, collect_samples);
411 i::Sampler* sampler =
412 reinterpret_cast<i::Isolate*>(env->GetIsolate())->logger()->sampler();
413 sampler->StartCountingSamples();
415 function->Call(env->Global(), argc, argv);
416 } while (sampler->js_and_external_sample_count() < min_js_samples);
418 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
420 CHECK_NE(NULL, profile);
421 // Dump collected profile to have a better diagnostic in case of failure.
422 reinterpret_cast<i::CpuProfile*>(profile)->Print();
428 static bool ContainsString(v8::Handle<v8::String> string,
429 const Vector<v8::Handle<v8::String> >& vector) {
430 for (int i = 0; i < vector.length(); i++) {
431 if (string->Equals(vector[i]))
438 static void CheckChildrenNames(const v8::CpuProfileNode* node,
439 const Vector<v8::Handle<v8::String> >& names) {
440 int count = node->GetChildrenCount();
441 for (int i = 0; i < count; i++) {
442 v8::Handle<v8::String> name = node->GetChild(i)->GetFunctionName();
443 CHECK(ContainsString(name, names));
444 // Check that there are no duplicates.
445 for (int j = 0; j < count; j++) {
446 if (j == i) continue;
447 CHECK_NE(name, node->GetChild(j)->GetFunctionName());
453 static const v8::CpuProfileNode* FindChild(v8::Isolate* isolate,
454 const v8::CpuProfileNode* node,
456 int count = node->GetChildrenCount();
457 v8::Handle<v8::String> nameHandle = v8::String::NewFromUtf8(isolate, name);
458 for (int i = 0; i < count; i++) {
459 const v8::CpuProfileNode* child = node->GetChild(i);
460 if (nameHandle->Equals(child->GetFunctionName())) return child;
466 static const v8::CpuProfileNode* GetChild(v8::Isolate* isolate,
467 const v8::CpuProfileNode* node,
469 const v8::CpuProfileNode* result = FindChild(isolate, node, name);
472 i::SNPrintF(Vector<char>(buffer, ARRAY_SIZE(buffer)),
473 "Failed to GetChild: %s", name);
480 static void CheckSimpleBranch(v8::Isolate* isolate,
481 const v8::CpuProfileNode* node,
482 const char* names[], int length) {
483 for (int i = 0; i < length; i++) {
484 const char* name = names[i];
485 node = GetChild(isolate, node, name);
486 int expectedChildrenCount = (i == length - 1) ? 0 : 1;
487 CHECK_EQ(expectedChildrenCount, node->GetChildrenCount());
492 static const char* cpu_profiler_test_source = "function loop(timeout) {\n"
494 " var start = Date.now();\n"
495 " while (Date.now() - start < timeout) {\n"
496 " var n = 100*1000;\n"
499 " this.mmm += n * n * n;\n"
503 "function delay() { try { loop(10); } catch(e) { } }\n"
504 "function bar() { delay(); }\n"
505 "function baz() { delay(); }\n"
514 "function start(timeout) {\n"
515 " var start = Date.now();\n"
518 " var duration = Date.now() - start;\n"
519 " } while (duration < timeout);\n"
520 " return duration;\n"
524 // Check that the profile tree for the script above will look like the
528 // 1062 0 (root) [-1]
539 // 2 2 (program) [-1]
540 // 6 6 (garbage collector) [-1]
541 TEST(CollectCpuProfile) {
543 v8::HandleScope scope(env->GetIsolate());
545 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
546 cpu_profiler_test_source))->Run();
547 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
548 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
550 int32_t profiling_interval_ms = 200;
551 v8::Handle<v8::Value> args[] = {
552 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
554 v8::CpuProfile* profile =
555 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
556 function->Call(env->Global(), ARRAY_SIZE(args), args);
558 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
560 ScopedVector<v8::Handle<v8::String> > names(3);
561 names[0] = v8::String::NewFromUtf8(
562 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
563 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
564 ProfileGenerator::kProgramEntryName);
565 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
566 CheckChildrenNames(root, names);
568 const v8::CpuProfileNode* startNode =
569 GetChild(env->GetIsolate(), root, "start");
570 CHECK_EQ(1, startNode->GetChildrenCount());
572 const v8::CpuProfileNode* fooNode =
573 GetChild(env->GetIsolate(), startNode, "foo");
574 CHECK_EQ(3, fooNode->GetChildrenCount());
576 const char* barBranch[] = { "bar", "delay", "loop" };
577 CheckSimpleBranch(env->GetIsolate(), fooNode, barBranch,
578 ARRAY_SIZE(barBranch));
579 const char* bazBranch[] = { "baz", "delay", "loop" };
580 CheckSimpleBranch(env->GetIsolate(), fooNode, bazBranch,
581 ARRAY_SIZE(bazBranch));
582 const char* delayBranch[] = { "delay", "loop" };
583 CheckSimpleBranch(env->GetIsolate(), fooNode, delayBranch,
584 ARRAY_SIZE(delayBranch));
590 static const char* hot_deopt_no_frame_entry_test_source =
591 "function foo(a, b) {\n"
596 "function start(timeout) {\n"
597 " var start = Date.now();\n"
599 " for (var i = 1; i < 1000; ++i) foo(1, i);\n"
600 " var duration = Date.now() - start;\n"
601 " } while (duration < timeout);\n"
602 " return duration;\n"
605 // Check that the profile tree for the script above will look like the
609 // 1062 0 (root) [-1]
612 // 2 2 (program) [-1]
613 // 6 6 (garbage collector) [-1]
615 // The test checks no FP ranges are present in a deoptimized funcion.
616 // If 'foo' has no ranges the samples falling into the prologue will miss the
617 // 'start' function on the stack, so 'foo' will be attached to the (root).
618 TEST(HotDeoptNoFrameEntry) {
620 v8::HandleScope scope(env->GetIsolate());
622 v8::Script::Compile(v8::String::NewFromUtf8(
624 hot_deopt_no_frame_entry_test_source))->Run();
625 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
626 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
628 int32_t profiling_interval_ms = 200;
629 v8::Handle<v8::Value> args[] = {
630 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
632 v8::CpuProfile* profile =
633 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
634 function->Call(env->Global(), ARRAY_SIZE(args), args);
636 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
638 ScopedVector<v8::Handle<v8::String> > names(3);
639 names[0] = v8::String::NewFromUtf8(
640 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
641 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
642 ProfileGenerator::kProgramEntryName);
643 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
644 CheckChildrenNames(root, names);
646 const v8::CpuProfileNode* startNode =
647 GetChild(env->GetIsolate(), root, "start");
648 CHECK_EQ(1, startNode->GetChildrenCount());
650 GetChild(env->GetIsolate(), startNode, "foo");
656 TEST(CollectCpuProfileSamples) {
658 v8::HandleScope scope(env->GetIsolate());
660 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
661 cpu_profiler_test_source))->Run();
662 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
663 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
665 int32_t profiling_interval_ms = 200;
666 v8::Handle<v8::Value> args[] = {
667 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
669 v8::CpuProfile* profile =
670 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200, true);
672 CHECK_LE(200, profile->GetSamplesCount());
673 uint64_t end_time = profile->GetEndTime();
674 uint64_t current_time = profile->GetStartTime();
675 CHECK_LE(current_time, end_time);
676 for (int i = 0; i < profile->GetSamplesCount(); i++) {
677 CHECK_NE(NULL, profile->GetSample(i));
678 uint64_t timestamp = profile->GetSampleTimestamp(i);
679 CHECK_LE(current_time, timestamp);
680 CHECK_LE(timestamp, end_time);
681 current_time = timestamp;
688 static const char* cpu_profiler_test_source2 = "function loop() {}\n"
689 "function delay() { loop(); }\n"
690 "function start(count) {\n"
694 " } while (++k < count*100*1000);\n"
697 // Check that the profile tree doesn't contain unexpected traces:
698 // - 'loop' can be called only by 'delay'
699 // - 'delay' may be called only by 'start'
700 // The profile will look like the following:
703 // 135 0 (root) [-1] #1
704 // 121 72 start [-1] #3
705 // 49 33 delay [-1] #4
706 // 16 16 loop [-1] #5
707 // 14 14 (program) [-1] #2
708 TEST(SampleWhenFrameIsNotSetup) {
710 v8::HandleScope scope(env->GetIsolate());
712 v8::Script::Compile(v8::String::NewFromUtf8(
713 env->GetIsolate(), cpu_profiler_test_source2))->Run();
714 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
715 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
717 int32_t repeat_count = 100;
718 #if defined(USE_SIMULATOR)
719 // Simulators are much slower.
722 v8::Handle<v8::Value> args[] = {
723 v8::Integer::New(env->GetIsolate(), repeat_count)
725 v8::CpuProfile* profile =
726 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
728 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
730 ScopedVector<v8::Handle<v8::String> > names(3);
731 names[0] = v8::String::NewFromUtf8(
732 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
733 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
734 ProfileGenerator::kProgramEntryName);
735 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
736 CheckChildrenNames(root, names);
738 const v8::CpuProfileNode* startNode =
739 FindChild(env->GetIsolate(), root, "start");
740 // On slow machines there may be no meaningfull samples at all, skip the
742 if (startNode && startNode->GetChildrenCount() > 0) {
743 CHECK_EQ(1, startNode->GetChildrenCount());
744 const v8::CpuProfileNode* delayNode =
745 GetChild(env->GetIsolate(), startNode, "delay");
746 if (delayNode->GetChildrenCount() > 0) {
747 CHECK_EQ(1, delayNode->GetChildrenCount());
748 GetChild(env->GetIsolate(), delayNode, "loop");
756 static const char* native_accessor_test_source = "function start(count) {\n"
757 " for (var i = 0; i < count; i++) {\n"
758 " var o = instance.foo;\n"
759 " instance.foo = o + 1;\n"
764 class TestApiCallbacks {
766 explicit TestApiCallbacks(int min_duration_ms)
767 : min_duration_ms_(min_duration_ms),
768 is_warming_up_(false) {}
770 static void Getter(v8::Local<v8::String> name,
771 const v8::PropertyCallbackInfo<v8::Value>& info) {
772 TestApiCallbacks* data = fromInfo(info);
776 static void Setter(v8::Local<v8::String> name,
777 v8::Local<v8::Value> value,
778 const v8::PropertyCallbackInfo<void>& info) {
779 TestApiCallbacks* data = fromInfo(info);
783 static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
784 TestApiCallbacks* data = fromInfo(info);
788 void set_warming_up(bool value) { is_warming_up_ = value; }
792 if (is_warming_up_) return;
793 double start = v8::base::OS::TimeCurrentMillis();
795 while (duration < min_duration_ms_) {
796 v8::base::OS::Sleep(1);
797 duration = v8::base::OS::TimeCurrentMillis() - start;
802 static TestApiCallbacks* fromInfo(const T& info) {
803 void* data = v8::External::Cast(*info.Data())->Value();
804 return reinterpret_cast<TestApiCallbacks*>(data);
807 int min_duration_ms_;
812 // Test that native accessors are properly reported in the CPU profile.
813 // This test checks the case when the long-running accessors are called
814 // only once and the optimizer doesn't have chance to change the invocation
816 TEST(NativeAccessorUninitializedIC) {
818 v8::Isolate* isolate = env->GetIsolate();
819 v8::HandleScope scope(isolate);
821 v8::Local<v8::FunctionTemplate> func_template =
822 v8::FunctionTemplate::New(isolate);
823 v8::Local<v8::ObjectTemplate> instance_template =
824 func_template->InstanceTemplate();
826 TestApiCallbacks accessors(100);
827 v8::Local<v8::External> data =
828 v8::External::New(isolate, &accessors);
829 instance_template->SetAccessor(
830 v8::String::NewFromUtf8(isolate, "foo"),
831 &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data);
832 v8::Local<v8::Function> func = func_template->GetFunction();
833 v8::Local<v8::Object> instance = func->NewInstance();
834 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
838 v8::String::NewFromUtf8(isolate, native_accessor_test_source))
840 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
841 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
843 int32_t repeat_count = 1;
844 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
845 v8::CpuProfile* profile =
846 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 180);
848 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
849 const v8::CpuProfileNode* startNode =
850 GetChild(isolate, root, "start");
851 GetChild(isolate, startNode, "get foo");
852 GetChild(isolate, startNode, "set foo");
858 // Test that native accessors are properly reported in the CPU profile.
859 // This test makes sure that the accessors are called enough times to become
860 // hot and to trigger optimizations.
861 TEST(NativeAccessorMonomorphicIC) {
863 v8::Isolate* isolate = env->GetIsolate();
864 v8::HandleScope scope(isolate);
866 v8::Local<v8::FunctionTemplate> func_template =
867 v8::FunctionTemplate::New(isolate);
868 v8::Local<v8::ObjectTemplate> instance_template =
869 func_template->InstanceTemplate();
871 TestApiCallbacks accessors(1);
872 v8::Local<v8::External> data =
873 v8::External::New(isolate, &accessors);
874 instance_template->SetAccessor(
875 v8::String::NewFromUtf8(isolate, "foo"),
876 &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data);
877 v8::Local<v8::Function> func = func_template->GetFunction();
878 v8::Local<v8::Object> instance = func->NewInstance();
879 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
883 v8::String::NewFromUtf8(isolate, native_accessor_test_source))
885 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
886 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
889 // Make sure accessors ICs are in monomorphic state before starting
891 accessors.set_warming_up(true);
892 int32_t warm_up_iterations = 3;
893 v8::Handle<v8::Value> args[] = {
894 v8::Integer::New(isolate, warm_up_iterations)
896 function->Call(env->Global(), ARRAY_SIZE(args), args);
897 accessors.set_warming_up(false);
900 int32_t repeat_count = 100;
901 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
902 v8::CpuProfile* profile =
903 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
905 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
906 const v8::CpuProfileNode* startNode =
907 GetChild(isolate, root, "start");
908 GetChild(isolate, startNode, "get foo");
909 GetChild(isolate, startNode, "set foo");
915 static const char* native_method_test_source = "function start(count) {\n"
916 " for (var i = 0; i < count; i++) {\n"
917 " instance.fooMethod();\n"
922 TEST(NativeMethodUninitializedIC) {
924 v8::Isolate* isolate = env->GetIsolate();
925 v8::HandleScope scope(isolate);
927 TestApiCallbacks callbacks(100);
928 v8::Local<v8::External> data =
929 v8::External::New(isolate, &callbacks);
931 v8::Local<v8::FunctionTemplate> func_template =
932 v8::FunctionTemplate::New(isolate);
933 func_template->SetClassName(
934 v8::String::NewFromUtf8(isolate, "Test_InstanceCostructor"));
935 v8::Local<v8::ObjectTemplate> proto_template =
936 func_template->PrototypeTemplate();
937 v8::Local<v8::Signature> signature =
938 v8::Signature::New(isolate, func_template);
939 proto_template->Set(v8::String::NewFromUtf8(isolate, "fooMethod"),
940 v8::FunctionTemplate::New(isolate,
941 &TestApiCallbacks::Callback,
942 data, signature, 0));
944 v8::Local<v8::Function> func = func_template->GetFunction();
945 v8::Local<v8::Object> instance = func->NewInstance();
946 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
949 v8::Script::Compile(v8::String::NewFromUtf8(
950 isolate, native_method_test_source))->Run();
951 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
952 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
954 int32_t repeat_count = 1;
955 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
956 v8::CpuProfile* profile =
957 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
959 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
960 const v8::CpuProfileNode* startNode =
961 GetChild(isolate, root, "start");
962 GetChild(isolate, startNode, "fooMethod");
968 TEST(NativeMethodMonomorphicIC) {
970 v8::Isolate* isolate = env->GetIsolate();
971 v8::HandleScope scope(isolate);
973 TestApiCallbacks callbacks(1);
974 v8::Local<v8::External> data =
975 v8::External::New(isolate, &callbacks);
977 v8::Local<v8::FunctionTemplate> func_template =
978 v8::FunctionTemplate::New(isolate);
979 func_template->SetClassName(
980 v8::String::NewFromUtf8(isolate, "Test_InstanceCostructor"));
981 v8::Local<v8::ObjectTemplate> proto_template =
982 func_template->PrototypeTemplate();
983 v8::Local<v8::Signature> signature =
984 v8::Signature::New(isolate, func_template);
985 proto_template->Set(v8::String::NewFromUtf8(isolate, "fooMethod"),
986 v8::FunctionTemplate::New(isolate,
987 &TestApiCallbacks::Callback,
988 data, signature, 0));
990 v8::Local<v8::Function> func = func_template->GetFunction();
991 v8::Local<v8::Object> instance = func->NewInstance();
992 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
995 v8::Script::Compile(v8::String::NewFromUtf8(
996 isolate, native_method_test_source))->Run();
997 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
998 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
1000 // Make sure method ICs are in monomorphic state before starting
1002 callbacks.set_warming_up(true);
1003 int32_t warm_up_iterations = 3;
1004 v8::Handle<v8::Value> args[] = {
1005 v8::Integer::New(isolate, warm_up_iterations)
1007 function->Call(env->Global(), ARRAY_SIZE(args), args);
1008 callbacks.set_warming_up(false);
1011 int32_t repeat_count = 100;
1012 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
1013 v8::CpuProfile* profile =
1014 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1016 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1017 GetChild(isolate, root, "start");
1018 const v8::CpuProfileNode* startNode =
1019 GetChild(isolate, root, "start");
1020 GetChild(isolate, startNode, "fooMethod");
1026 static const char* bound_function_test_source =
1027 "function foo() {\n"
1028 " startProfiling('my_profile');\n"
1030 "function start() {\n"
1031 " var callback = foo.bind(this);\n"
1036 TEST(BoundFunctionCall) {
1037 v8::HandleScope scope(CcTest::isolate());
1038 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1039 v8::Context::Scope context_scope(env);
1041 v8::Script::Compile(
1042 v8::String::NewFromUtf8(env->GetIsolate(), bound_function_test_source))
1044 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1045 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1047 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1049 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1050 ScopedVector<v8::Handle<v8::String> > names(3);
1051 names[0] = v8::String::NewFromUtf8(
1052 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1053 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1054 ProfileGenerator::kProgramEntryName);
1055 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1056 // Don't allow |foo| node to be at the top level.
1057 CheckChildrenNames(root, names);
1059 const v8::CpuProfileNode* startNode =
1060 GetChild(env->GetIsolate(), root, "start");
1061 GetChild(env->GetIsolate(), startNode, "foo");
1067 static const char* call_function_test_source = "function bar(iterations) {\n"
1069 "function start(duration) {\n"
1070 " var start = Date.now();\n"
1071 " while (Date.now() - start < duration) {\n"
1073 " bar.call(this, 10 * 1000);\n"
1079 // Test that if we sampled thread when it was inside FunctionCall buitin then
1080 // its caller frame will be '(unresolved function)' as we have no reliable way
1084 // 96 0 (root) [-1] #1
1085 // 1 1 (garbage collector) [-1] #4
1086 // 5 0 (unresolved function) [-1] #5
1088 // 71 70 start [-1] #3
1090 // 19 19 (program) [-1] #2
1091 TEST(FunctionCallSample) {
1093 v8::HandleScope scope(env->GetIsolate());
1095 // Collect garbage that might have be generated while installing extensions.
1096 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1098 v8::Script::Compile(v8::String::NewFromUtf8(
1099 env->GetIsolate(), call_function_test_source))->Run();
1100 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1101 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1103 int32_t duration_ms = 100;
1104 v8::Handle<v8::Value> args[] = {
1105 v8::Integer::New(env->GetIsolate(), duration_ms)
1107 v8::CpuProfile* profile =
1108 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1110 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1112 ScopedVector<v8::Handle<v8::String> > names(4);
1113 names[0] = v8::String::NewFromUtf8(
1114 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1115 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1116 ProfileGenerator::kProgramEntryName);
1117 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1118 names[3] = v8::String::NewFromUtf8(
1119 env->GetIsolate(), i::ProfileGenerator::kUnresolvedFunctionName);
1120 // Don't allow |bar| and |call| nodes to be at the top level.
1121 CheckChildrenNames(root, names);
1124 // In case of GC stress tests all samples may be in GC phase and there
1125 // won't be |start| node in the profiles.
1126 bool is_gc_stress_testing =
1127 (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction;
1128 const v8::CpuProfileNode* startNode =
1129 FindChild(env->GetIsolate(), root, "start");
1130 CHECK(is_gc_stress_testing || startNode);
1132 ScopedVector<v8::Handle<v8::String> > names(2);
1133 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1134 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1135 CheckChildrenNames(startNode, names);
1138 const v8::CpuProfileNode* unresolvedNode = FindChild(
1139 env->GetIsolate(), root, i::ProfileGenerator::kUnresolvedFunctionName);
1140 if (unresolvedNode) {
1141 ScopedVector<v8::Handle<v8::String> > names(1);
1142 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1143 CheckChildrenNames(unresolvedNode, names);
1150 static const char* function_apply_test_source = "function bar(iterations) {\n"
1152 "function test() {\n"
1153 " bar.apply(this, [10 * 1000]);\n"
1155 "function start(duration) {\n"
1156 " var start = Date.now();\n"
1157 " while (Date.now() - start < duration) {\n"
1166 // 94 0 (root) [-1] #0 1
1167 // 2 2 (garbage collector) [-1] #0 7
1168 // 82 49 start [-1] #16 3
1169 // 1 0 (unresolved function) [-1] #0 8
1170 // 1 1 apply [-1] #0 9
1171 // 32 21 test [-1] #16 4
1172 // 2 2 bar [-1] #16 6
1173 // 9 9 apply [-1] #0 5
1174 // 10 10 (program) [-1] #0 2
1175 TEST(FunctionApplySample) {
1177 v8::HandleScope scope(env->GetIsolate());
1179 v8::Script::Compile(
1180 v8::String::NewFromUtf8(env->GetIsolate(), function_apply_test_source))
1182 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1183 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1185 int32_t duration_ms = 100;
1186 v8::Handle<v8::Value> args[] = {
1187 v8::Integer::New(env->GetIsolate(), duration_ms)
1190 v8::CpuProfile* profile =
1191 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1193 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1195 ScopedVector<v8::Handle<v8::String> > names(3);
1196 names[0] = v8::String::NewFromUtf8(
1197 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1198 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1199 ProfileGenerator::kProgramEntryName);
1200 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1201 // Don't allow |test|, |bar| and |apply| nodes to be at the top level.
1202 CheckChildrenNames(root, names);
1205 const v8::CpuProfileNode* startNode =
1206 FindChild(env->GetIsolate(), root, "start");
1209 ScopedVector<v8::Handle<v8::String> > names(2);
1210 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "test");
1211 names[1] = v8::String::NewFromUtf8(
1212 env->GetIsolate(), ProfileGenerator::kUnresolvedFunctionName);
1213 CheckChildrenNames(startNode, names);
1216 const v8::CpuProfileNode* testNode =
1217 FindChild(env->GetIsolate(), startNode, "test");
1219 ScopedVector<v8::Handle<v8::String> > names(3);
1220 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1221 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1222 // apply calls "get length" before invoking the function itself
1223 // and we may get hit into it.
1224 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "get length");
1225 CheckChildrenNames(testNode, names);
1228 if (const v8::CpuProfileNode* unresolvedNode =
1229 FindChild(env->GetIsolate(), startNode,
1230 ProfileGenerator::kUnresolvedFunctionName)) {
1231 ScopedVector<v8::Handle<v8::String> > names(1);
1232 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1233 CheckChildrenNames(unresolvedNode, names);
1234 GetChild(env->GetIsolate(), unresolvedNode, "apply");
1242 static const char* cpu_profiler_deep_stack_test_source =
1243 "function foo(n) {\n"
1247 " startProfiling('my_profile');\n"
1249 "function start() {\n"
1254 // Check a deep stack
1259 // 0 start 21 #3 no reason
1260 // 0 foo 21 #4 no reason
1261 // 0 foo 21 #5 no reason
1263 // 0 foo 21 #253 no reason
1264 // 1 startProfiling 0 #254
1265 TEST(CpuProfileDeepStack) {
1266 v8::HandleScope scope(CcTest::isolate());
1267 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1268 v8::Context::Scope context_scope(env);
1270 v8::Script::Compile(v8::String::NewFromUtf8(
1271 env->GetIsolate(), cpu_profiler_deep_stack_test_source))->Run();
1272 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1273 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1275 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1276 v8::Local<v8::String> profile_name =
1277 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1278 function->Call(env->Global(), 0, NULL);
1279 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1280 CHECK_NE(NULL, profile);
1281 // Dump collected profile to have a better diagnostic in case of failure.
1282 reinterpret_cast<i::CpuProfile*>(profile)->Print();
1284 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1286 ScopedVector<v8::Handle<v8::String> > names(3);
1287 names[0] = v8::String::NewFromUtf8(
1288 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1289 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1290 ProfileGenerator::kProgramEntryName);
1291 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1292 CheckChildrenNames(root, names);
1295 const v8::CpuProfileNode* node =
1296 GetChild(env->GetIsolate(), root, "start");
1297 for (int i = 0; i < 250; ++i) {
1298 node = GetChild(env->GetIsolate(), node, "foo");
1301 // In theory there must be one more 'foo' and a 'startProfiling' nodes,
1302 // but due to unstable top frame extraction these might be missing.
1308 static const char* js_native_js_test_source =
1309 "function foo() {\n"
1310 " startProfiling('my_profile');\n"
1312 "function bar() {\n"
1313 " try { foo(); } catch(e) {}\n"
1315 "function start() {\n"
1317 " CallJsFunction(bar);\n"
1321 static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1322 v8::Handle<v8::Function> function = info[0].As<v8::Function>();
1323 v8::Handle<v8::Value> argv[] = { info[1] };
1324 function->Call(info.This(), ARRAY_SIZE(argv), argv);
1330 // 2 2 (program) #0 2
1332 // 55 0 CallJsFunction #0 4
1335 TEST(JsNativeJsSample) {
1336 v8::HandleScope scope(CcTest::isolate());
1337 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1338 v8::Context::Scope context_scope(env);
1340 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1341 env->GetIsolate(), CallJsFunction);
1342 v8::Local<v8::Function> func = func_template->GetFunction();
1343 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1345 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1347 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
1348 js_native_js_test_source))->Run();
1349 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1350 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1352 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1354 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1356 ScopedVector<v8::Handle<v8::String> > names(3);
1357 names[0] = v8::String::NewFromUtf8(
1358 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1359 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1360 ProfileGenerator::kProgramEntryName);
1361 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1362 CheckChildrenNames(root, names);
1365 const v8::CpuProfileNode* startNode =
1366 GetChild(env->GetIsolate(), root, "start");
1367 CHECK_EQ(1, startNode->GetChildrenCount());
1368 const v8::CpuProfileNode* nativeFunctionNode =
1369 GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1371 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1372 const v8::CpuProfileNode* barNode =
1373 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1375 CHECK_EQ(1, barNode->GetChildrenCount());
1376 GetChild(env->GetIsolate(), barNode, "foo");
1382 static const char* js_native_js_runtime_js_test_source =
1383 "function foo() {\n"
1384 " startProfiling('my_profile');\n"
1386 "var bound = foo.bind(this);\n"
1387 "function bar() {\n"
1388 " try { bound(); } catch(e) {}\n"
1390 "function start() {\n"
1392 " CallJsFunction(bar);\n"
1400 // 54 0 CallJsFunction #0 4
1403 // 2 2 (program) #0 2
1404 TEST(JsNativeJsRuntimeJsSample) {
1405 v8::HandleScope scope(CcTest::isolate());
1406 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1407 v8::Context::Scope context_scope(env);
1409 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1410 env->GetIsolate(), CallJsFunction);
1411 v8::Local<v8::Function> func = func_template->GetFunction();
1412 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1414 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1416 v8::Script::Compile(
1417 v8::String::NewFromUtf8(env->GetIsolate(),
1418 js_native_js_runtime_js_test_source))->Run();
1419 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1420 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1422 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1424 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1425 ScopedVector<v8::Handle<v8::String> > names(3);
1426 names[0] = v8::String::NewFromUtf8(
1427 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1428 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1429 ProfileGenerator::kProgramEntryName);
1430 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1431 CheckChildrenNames(root, names);
1433 const v8::CpuProfileNode* startNode =
1434 GetChild(env->GetIsolate(), root, "start");
1435 CHECK_EQ(1, startNode->GetChildrenCount());
1436 const v8::CpuProfileNode* nativeFunctionNode =
1437 GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1439 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1440 const v8::CpuProfileNode* barNode =
1441 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1443 // The child is in fact a bound foo.
1444 // A bound function has a wrapper that may make calls to
1445 // other functions e.g. "get length".
1446 CHECK_LE(1, barNode->GetChildrenCount());
1447 CHECK_GE(2, barNode->GetChildrenCount());
1448 GetChild(env->GetIsolate(), barNode, "foo");
1454 static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1455 v8::base::OS::Print("In CallJsFunction2\n");
1456 CallJsFunction(info);
1460 static const char* js_native1_js_native2_js_test_source =
1461 "function foo() {\n"
1463 " startProfiling('my_profile');\n"
1466 "function bar() {\n"
1467 " CallJsFunction2(foo);\n"
1469 "function start() {\n"
1471 " CallJsFunction1(bar);\n"
1479 // 54 0 CallJsFunction1 #0 4
1481 // 54 0 CallJsFunction2 #0 6
1483 // 2 2 (program) #0 2
1484 TEST(JsNative1JsNative2JsSample) {
1485 v8::HandleScope scope(CcTest::isolate());
1486 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1487 v8::Context::Scope context_scope(env);
1489 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1490 env->GetIsolate(), CallJsFunction);
1491 v8::Local<v8::Function> func1 = func_template->GetFunction();
1492 func1->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"));
1494 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"), func1);
1496 v8::Local<v8::Function> func2 = v8::FunctionTemplate::New(
1497 env->GetIsolate(), CallJsFunction2)->GetFunction();
1498 func2->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"));
1500 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"), func2);
1502 v8::Script::Compile(
1503 v8::String::NewFromUtf8(env->GetIsolate(),
1504 js_native1_js_native2_js_test_source))->Run();
1505 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1506 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1508 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1510 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1511 ScopedVector<v8::Handle<v8::String> > names(3);
1512 names[0] = v8::String::NewFromUtf8(
1513 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1514 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1515 ProfileGenerator::kProgramEntryName);
1516 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1517 CheckChildrenNames(root, names);
1519 const v8::CpuProfileNode* startNode =
1520 GetChild(env->GetIsolate(), root, "start");
1521 CHECK_EQ(1, startNode->GetChildrenCount());
1522 const v8::CpuProfileNode* nativeNode1 =
1523 GetChild(env->GetIsolate(), startNode, "CallJsFunction1");
1525 CHECK_EQ(1, nativeNode1->GetChildrenCount());
1526 const v8::CpuProfileNode* barNode =
1527 GetChild(env->GetIsolate(), nativeNode1, "bar");
1529 CHECK_EQ(1, barNode->GetChildrenCount());
1530 const v8::CpuProfileNode* nativeNode2 =
1531 GetChild(env->GetIsolate(), barNode, "CallJsFunction2");
1533 CHECK_EQ(1, nativeNode2->GetChildrenCount());
1534 GetChild(env->GetIsolate(), nativeNode2, "foo");
1542 // 3 3 (program) #0 2
1546 v8::HandleScope scope(env->GetIsolate());
1547 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1549 v8::Local<v8::String> profile_name =
1550 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1551 cpu_profiler->StartProfiling(profile_name);
1553 i::Isolate* isolate = CcTest::i_isolate();
1554 i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
1555 processor->AddCurrentStack(isolate);
1557 cpu_profiler->SetIdle(true);
1559 for (int i = 0; i < 3; i++) {
1560 processor->AddCurrentStack(isolate);
1563 cpu_profiler->SetIdle(false);
1564 processor->AddCurrentStack(isolate);
1567 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1568 CHECK_NE(NULL, profile);
1569 // Dump collected profile to have a better diagnostic in case of failure.
1570 reinterpret_cast<i::CpuProfile*>(profile)->Print();
1572 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1573 ScopedVector<v8::Handle<v8::String> > names(3);
1574 names[0] = v8::String::NewFromUtf8(
1575 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1576 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1577 ProfileGenerator::kProgramEntryName);
1578 names[2] = v8::String::NewFromUtf8(env->GetIsolate(),
1579 ProfileGenerator::kIdleEntryName);
1580 CheckChildrenNames(root, names);
1582 const v8::CpuProfileNode* programNode =
1583 GetChild(env->GetIsolate(), root, ProfileGenerator::kProgramEntryName);
1584 CHECK_EQ(0, programNode->GetChildrenCount());
1585 CHECK_GE(programNode->GetHitCount(), 3);
1587 const v8::CpuProfileNode* idleNode =
1588 GetChild(env->GetIsolate(), root, ProfileGenerator::kIdleEntryName);
1589 CHECK_EQ(0, idleNode->GetChildrenCount());
1590 CHECK_GE(idleNode->GetHitCount(), 3);
1596 static void CheckFunctionDetails(v8::Isolate* isolate,
1597 const v8::CpuProfileNode* node,
1598 const char* name, const char* script_name,
1599 int script_id, int line, int column) {
1600 CHECK_EQ(v8::String::NewFromUtf8(isolate, name),
1601 node->GetFunctionName());
1602 CHECK_EQ(v8::String::NewFromUtf8(isolate, script_name),
1603 node->GetScriptResourceName());
1604 CHECK_EQ(script_id, node->GetScriptId());
1605 CHECK_EQ(line, node->GetLineNumber());
1606 CHECK_EQ(column, node->GetColumnNumber());
1610 TEST(FunctionDetails) {
1611 v8::HandleScope scope(CcTest::isolate());
1612 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1613 v8::Context::Scope context_scope(env);
1615 v8::Handle<v8::Script> script_a = CompileWithOrigin(
1616 " function foo\n() { try { bar(); } catch(e) {} }\n"
1617 " function bar() { startProfiling(); }\n",
1620 v8::Handle<v8::Script> script_b = CompileWithOrigin(
1621 "\n\n function baz() { try { foo(); } catch(e) {} }\n"
1623 "stopProfiling();\n",
1626 const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1627 const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1628 reinterpret_cast<ProfileNode*>(
1629 const_cast<v8::CpuProfileNode*>(current))->Print(0);
1630 // The tree should look like this:
1632 // 0 "" 19 #2 no reason script_b:1
1633 // 0 baz 19 #3 TryCatchStatement script_b:3
1634 // 0 foo 18 #4 TryCatchStatement script_a:2
1635 // 1 bar 18 #5 no reason script_a:3
1636 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1637 const v8::CpuProfileNode* script = GetChild(env->GetIsolate(), root, "");
1638 CheckFunctionDetails(env->GetIsolate(), script, "", "script_b",
1639 script_b->GetUnboundScript()->GetId(), 1, 1);
1640 const v8::CpuProfileNode* baz = GetChild(env->GetIsolate(), script, "baz");
1641 CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b",
1642 script_b->GetUnboundScript()->GetId(), 3, 16);
1643 const v8::CpuProfileNode* foo = GetChild(env->GetIsolate(), baz, "foo");
1644 CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a",
1645 script_a->GetUnboundScript()->GetId(), 2, 1);
1646 const v8::CpuProfileNode* bar = GetChild(env->GetIsolate(), foo, "bar");
1647 CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a",
1648 script_a->GetUnboundScript()->GetId(), 3, 14);
1652 TEST(DontStopOnFinishedProfileDelete) {
1653 v8::HandleScope scope(CcTest::isolate());
1654 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1655 v8::Context::Scope context_scope(env);
1656 v8::Isolate* isolate = env->GetIsolate();
1658 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler();
1659 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
1661 CHECK_EQ(0, iprofiler->GetProfilesCount());
1662 v8::Handle<v8::String> outer = v8::String::NewFromUtf8(isolate, "outer");
1663 profiler->StartProfiling(outer);
1664 CHECK_EQ(0, iprofiler->GetProfilesCount());
1666 v8::Handle<v8::String> inner = v8::String::NewFromUtf8(isolate, "inner");
1667 profiler->StartProfiling(inner);
1668 CHECK_EQ(0, iprofiler->GetProfilesCount());
1670 v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
1671 CHECK(inner_profile);
1672 CHECK_EQ(1, iprofiler->GetProfilesCount());
1673 inner_profile->Delete();
1674 inner_profile = NULL;
1675 CHECK_EQ(0, iprofiler->GetProfilesCount());
1677 v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
1678 CHECK(outer_profile);
1679 CHECK_EQ(1, iprofiler->GetProfilesCount());
1680 outer_profile->Delete();
1681 outer_profile = NULL;
1682 CHECK_EQ(0, iprofiler->GetProfilesCount());