blob: 0e2dd91219ff91d0d1078e01ca0c2300f326ff36 [file] [log] [blame]
Steve Block6ded16b2010-05-10 14:33:55 +01001// Copyright 2010 the V8 project authors. All rights reserved.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002// 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.
Steve Block6ded16b2010-05-10 14:33:55 +010027//
28// Tests of profiles generator and utilities.
29
Ben Murdochb8a8cc12014-11-26 15:28:44 +000030#include "src/v8.h"
Steve Block6ded16b2010-05-10 14:33:55 +010031
Ben Murdochb8a8cc12014-11-26 15:28:44 +000032#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"
Steve Block6ded16b2010-05-10 14:33:55 +010039using i::CodeEntry;
40using i::CpuProfile;
Iain Merrick75681382010-08-19 15:07:18 +010041using i::CpuProfiler;
Steve Block6ded16b2010-05-10 14:33:55 +010042using i::CpuProfilesCollection;
Ben Murdochb8a8cc12014-11-26 15:28:44 +000043using i::Heap;
Steve Block6ded16b2010-05-10 14:33:55 +010044using i::ProfileGenerator;
45using i::ProfileNode;
46using i::ProfilerEventsProcessor;
Ben Murdochb8a8cc12014-11-26 15:28:44 +000047using i::ScopedVector;
48using i::SmartPointer;
49using i::Vector;
Steve Block6ded16b2010-05-10 14:33:55 +010050
51
52TEST(StartStop) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000053 i::Isolate* isolate = CcTest::i_isolate();
54 CpuProfilesCollection profiles(isolate->heap());
Steve Block6ded16b2010-05-10 14:33:55 +010055 ProfileGenerator generator(&profiles);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000056 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
57 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
58 processor->Start();
59 processor->StopSynchronously();
Steve Block6ded16b2010-05-10 14:33:55 +010060}
61
Steve Block6ded16b2010-05-10 14:33:55 +010062
63static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
64 i::Address frame1,
65 i::Address frame2 = NULL,
66 i::Address frame3 = NULL) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000067 i::TickSample* sample = proc->StartTickSample();
Steve Block6ded16b2010-05-10 14:33:55 +010068 sample->pc = frame1;
Ben Murdoche0cee9b2011-05-25 10:26:03 +010069 sample->tos = frame1;
Steve Block6ded16b2010-05-10 14:33:55 +010070 sample->frames_count = 0;
71 if (frame2 != NULL) {
72 sample->stack[0] = frame2;
73 sample->frames_count = 1;
74 }
75 if (frame3 != NULL) {
76 sample->stack[1] = frame3;
77 sample->frames_count = 2;
78 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +000079 proc->FinishTickSample();
Steve Block6ded16b2010-05-10 14:33:55 +010080}
81
82namespace {
83
84class TestSetup {
85 public:
86 TestSetup()
87 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
88 i::FLAG_prof_browser_mode = false;
89 }
90
91 ~TestSetup() {
92 i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
93 }
94
95 private:
96 bool old_flag_prof_browser_mode_;
97};
98
99} // namespace
100
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000101
102i::Code* CreateCode(LocalContext* env) {
103 static int counter = 0;
104 i::EmbeddedVector<char, 256> script;
105 i::EmbeddedVector<char, 32> name;
106
107 i::SNPrintF(name, "function_%d", ++counter);
108 const char* name_start = name.start();
109 i::SNPrintF(script,
110 "function %s() {\n"
111 "var counter = 0;\n"
112 "for (var i = 0; i < %d; ++i) counter += i;\n"
113 "return '%s_' + counter;\n"
114 "}\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))));
120 return fun->code();
121}
122
123
Steve Block6ded16b2010-05-10 14:33:55 +0100124TEST(CodeEvents) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000125 CcTest::InitializeVM();
126 LocalContext env;
127 i::Isolate* isolate = CcTest::i_isolate();
128 i::Factory* factory = isolate->factory();
Steve Block6ded16b2010-05-10 14:33:55 +0100129 TestSetup test_setup;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000130
131 i::HandleScope scope(isolate);
132
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);
140
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)));
146 processor->Start();
147 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
Steve Block6ded16b2010-05-10 14:33:55 +0100148
149 // Enqueue code creation events.
Steve Block6ded16b2010-05-10 14:33:55 +0100150 const char* aaa_str = "aaa";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000151 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);
Steve Block6ded16b2010-05-10 14:33:55 +0100159
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000160 // Enqueue a tick event to enable code events processing.
161 EnqueueTickSampleEvent(processor.get(), aaa_code->address());
162
163 processor->StopSynchronously();
Steve Block6ded16b2010-05-10 14:33:55 +0100164
165 // Check the state of profile generator.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000166 CodeEntry* aaa = generator.code_map()->FindEntry(aaa_code->address());
167 CHECK_NE(NULL, aaa);
168 CHECK_EQ(aaa_str, aaa->name());
169
170 CodeEntry* comment = generator.code_map()->FindEntry(comment_code->address());
171 CHECK_NE(NULL, comment);
172 CHECK_EQ("comment", comment->name());
173
174 CodeEntry* args5 = generator.code_map()->FindEntry(args5_code->address());
175 CHECK_NE(NULL, args5);
176 CHECK_EQ("5", args5->name());
177
178 CHECK_EQ(NULL, generator.code_map()->FindEntry(comment2_code->address()));
179
180 CodeEntry* comment2 = generator.code_map()->FindEntry(moved_code->address());
181 CHECK_NE(NULL, comment2);
182 CHECK_EQ("comment2", comment2->name());
Steve Block6ded16b2010-05-10 14:33:55 +0100183}
184
185
186template<typename T>
187static int CompareProfileNodes(const T* p1, const T* p2) {
188 return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
189}
190
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000191
Steve Block6ded16b2010-05-10 14:33:55 +0100192TEST(TickEvents) {
193 TestSetup test_setup;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000194 LocalContext env;
195 i::Isolate* isolate = CcTest::i_isolate();
196 i::HandleScope scope(isolate);
Steve Block6ded16b2010-05-10 14:33:55 +0100197
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000198 i::Code* frame1_code = CreateCode(&env);
199 i::Code* frame2_code = CreateCode(&env);
200 i::Code* frame3_code = CreateCode(&env);
Steve Block6ded16b2010-05-10 14:33:55 +0100201
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000202 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)));
207 processor->Start();
208 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
209
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");
213
214 EnqueueTickSampleEvent(processor.get(), frame1_code->instruction_start());
215 EnqueueTickSampleEvent(
216 processor.get(),
217 frame2_code->instruction_start() + frame2_code->ExecutableSize() / 2,
218 frame1_code->instruction_start() + frame2_code->ExecutableSize() / 2);
219 EnqueueTickSampleEvent(
220 processor.get(),
221 frame3_code->instruction_end() - 1,
222 frame2_code->instruction_end() - 1,
223 frame1_code->instruction_end() - 1);
224
225 processor->StopSynchronously();
226 CpuProfile* profile = profiles->StopProfiling("");
Steve Block6ded16b2010-05-10 14:33:55 +0100227 CHECK_NE(NULL, profile);
228
229 // Check call trees.
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());
Steve Block6ded16b2010-05-10 14:33:55 +0100245}
246
Iain Merrick75681382010-08-19 15:07:18 +0100247
248// http://crbug/51594
249// This test must not crash.
250TEST(CrashIfStoppingLastNonExistentProfile) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000251 CcTest::InitializeVM();
Iain Merrick75681382010-08-19 15:07:18 +0100252 TestSetup test_setup;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000253 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
254 profiler->StartProfiling("1");
255 profiler->StopProfiling("2");
256 profiler->StartProfiling("1");
257 profiler->StopProfiling("");
Iain Merrick75681382010-08-19 15:07:18 +0100258}
259
Steve Block44f0eee2011-05-26 01:26:41 +0100260
Steve Block053d10c2011-06-13 19:13:29 +0100261// http://code.google.com/p/v8/issues/detail?id=1398
262// Long stacks (exceeding max frames limit) must not be erased.
263TEST(Issue1398) {
264 TestSetup test_setup;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000265 LocalContext env;
266 i::Isolate* isolate = CcTest::i_isolate();
267 i::HandleScope scope(isolate);
Steve Block053d10c2011-06-13 19:13:29 +0100268
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000269 i::Code* code = CreateCode(&env);
Steve Block053d10c2011-06-13 19:13:29 +0100270
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000271 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)));
276 processor->Start();
277 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
278
279 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
280
281 i::TickSample* sample = processor->StartTickSample();
282 sample->pc = code->address();
Steve Block053d10c2011-06-13 19:13:29 +0100283 sample->tos = 0;
284 sample->frames_count = i::TickSample::kMaxFramesCount;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000285 for (unsigned i = 0; i < sample->frames_count; ++i) {
286 sample->stack[i] = code->address();
Steve Block053d10c2011-06-13 19:13:29 +0100287 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000288 processor->FinishTickSample();
Steve Block053d10c2011-06-13 19:13:29 +0100289
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000290 processor->StopSynchronously();
291 CpuProfile* profile = profiles->StopProfiling("");
Steve Block053d10c2011-06-13 19:13:29 +0100292 CHECK_NE(NULL, profile);
293
294 int actual_depth = 0;
295 const ProfileNode* node = profile->top_down()->root();
296 while (node->children()->length() > 0) {
297 node = node->children()->last();
298 ++actual_depth;
299 }
300
301 CHECK_EQ(1 + i::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
302}
303
304
Steve Block44f0eee2011-05-26 01:26:41 +0100305TEST(DeleteAllCpuProfiles) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000306 CcTest::InitializeVM();
Steve Block44f0eee2011-05-26 01:26:41 +0100307 TestSetup test_setup;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000308 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
309 CHECK_EQ(0, profiler->GetProfilesCount());
310 profiler->DeleteAllProfiles();
311 CHECK_EQ(0, profiler->GetProfilesCount());
Steve Block44f0eee2011-05-26 01:26:41 +0100312
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000313 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());
Steve Block44f0eee2011-05-26 01:26:41 +0100325
326 // Test profiling cancellation by the 'delete' command.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000327 profiler->StartProfiling("1");
328 profiler->StartProfiling("2");
329 CHECK_EQ(0, profiler->GetProfilesCount());
330 profiler->DeleteAllProfiles();
331 CHECK_EQ(0, profiler->GetProfilesCount());
332}
Steve Block44f0eee2011-05-26 01:26:41 +0100333
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000334
335static 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))
343 return true;
344 }
345 return false;
Steve Block44f0eee2011-05-26 01:26:41 +0100346}
347
348
349TEST(DeleteCpuProfile) {
Steve Block44f0eee2011-05-26 01:26:41 +0100350 LocalContext env;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000351 v8::HandleScope scope(env->GetIsolate());
352 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
353 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(cpu_profiler);
Steve Block44f0eee2011-05-26 01:26:41 +0100354
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000355 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);
Steve Block44f0eee2011-05-26 01:26:41 +0100359 CHECK_NE(NULL, p1);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000360 CHECK_EQ(1, iprofiler->GetProfilesCount());
361 CHECK(FindCpuProfile(cpu_profiler, p1));
362 p1->Delete();
363 CHECK_EQ(0, iprofiler->GetProfilesCount());
Steve Block44f0eee2011-05-26 01:26:41 +0100364
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000365 v8::Local<v8::String> name2 = v8::String::NewFromUtf8(env->GetIsolate(), "2");
366 cpu_profiler->StartProfiling(name2);
367 v8::CpuProfile* p2 = cpu_profiler->StopProfiling(name2);
Steve Block44f0eee2011-05-26 01:26:41 +0100368 CHECK_NE(NULL, p2);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000369 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);
Steve Block44f0eee2011-05-26 01:26:41 +0100374 CHECK_NE(NULL, p3);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000375 CHECK_EQ(2, iprofiler->GetProfilesCount());
376 CHECK_NE(p2, p3);
377 CHECK(FindCpuProfile(cpu_profiler, p3));
378 CHECK(FindCpuProfile(cpu_profiler, p2));
379 p2->Delete();
380 CHECK_EQ(1, iprofiler->GetProfilesCount());
381 CHECK(!FindCpuProfile(cpu_profiler, p2));
382 CHECK(FindCpuProfile(cpu_profiler, p3));
383 p3->Delete();
384 CHECK_EQ(0, iprofiler->GetProfilesCount());
Steve Block44f0eee2011-05-26 01:26:41 +0100385}
386
387
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000388TEST(ProfileStartEndTime) {
Steve Block44f0eee2011-05-26 01:26:41 +0100389 LocalContext env;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000390 v8::HandleScope scope(env->GetIsolate());
391 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
Steve Block44f0eee2011-05-26 01:26:41 +0100392
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000393 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());
398}
Steve Block44f0eee2011-05-26 01:26:41 +0100399
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000400
401static 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");
408
409 cpu_profiler->StartProfiling(profile_name, collect_samples);
410
411 i::Sampler* sampler =
412 reinterpret_cast<i::Isolate*>(env->GetIsolate())->logger()->sampler();
413 sampler->StartCountingSamples();
414 do {
415 function->Call(env->Global(), argc, argv);
416 } while (sampler->js_and_external_sample_count() < min_js_samples);
417
418 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
419
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();
423
424 return profile;
425}
426
427
428static 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]))
432 return true;
433 }
434 return false;
435}
436
437
438static 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());
448 }
449 }
450}
451
452
453static const v8::CpuProfileNode* FindChild(v8::Isolate* isolate,
454 const v8::CpuProfileNode* node,
455 const char* name) {
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;
461 }
462 return NULL;
463}
464
465
466static const v8::CpuProfileNode* GetChild(v8::Isolate* isolate,
467 const v8::CpuProfileNode* node,
468 const char* name) {
469 const v8::CpuProfileNode* result = FindChild(isolate, node, name);
470 if (!result) {
471 char buffer[100];
472 i::SNPrintF(Vector<char>(buffer, arraysize(buffer)),
473 "Failed to GetChild: %s", name);
474 FATAL(buffer);
475 }
476 return result;
477}
478
479
480static 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());
488 }
489}
490
491
492static const char* cpu_profiler_test_source = "function loop(timeout) {\n"
493" this.mmm = 0;\n"
494" var start = Date.now();\n"
495" while (Date.now() - start < timeout) {\n"
496" var n = 100*1000;\n"
497" while(n > 1) {\n"
498" n--;\n"
499" this.mmm += n * n * n;\n"
500" }\n"
501" }\n"
502"}\n"
503"function delay() { try { loop(10); } catch(e) { } }\n"
504"function bar() { delay(); }\n"
505"function baz() { delay(); }\n"
506"function foo() {\n"
507" try {\n"
508" delay();\n"
509" bar();\n"
510" delay();\n"
511" baz();\n"
512" } catch (e) { }\n"
513"}\n"
514"function start(timeout) {\n"
515" var start = Date.now();\n"
516" do {\n"
517" foo();\n"
518" var duration = Date.now() - start;\n"
519" } while (duration < timeout);\n"
520" return duration;\n"
521"}\n";
522
523
524// Check that the profile tree for the script above will look like the
525// following:
526//
527// [Top down]:
528// 1062 0 (root) [-1]
529// 1054 0 start [-1]
530// 1054 1 foo [-1]
531// 265 0 baz [-1]
532// 265 1 delay [-1]
533// 264 264 loop [-1]
534// 525 3 delay [-1]
535// 522 522 loop [-1]
536// 263 0 bar [-1]
537// 263 1 delay [-1]
538// 262 262 loop [-1]
539// 2 2 (program) [-1]
540// 6 6 (garbage collector) [-1]
541TEST(CollectCpuProfile) {
542 LocalContext env;
543 v8::HandleScope scope(env->GetIsolate());
544
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")));
549
550 int32_t profiling_interval_ms = 200;
551 v8::Handle<v8::Value> args[] = {
552 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
553 };
554 v8::CpuProfile* profile =
555 RunProfiler(env.local(), function, args, arraysize(args), 200);
556 function->Call(env->Global(), arraysize(args), args);
557
558 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
559
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);
567
568 const v8::CpuProfileNode* startNode =
569 GetChild(env->GetIsolate(), root, "start");
570 CHECK_EQ(1, startNode->GetChildrenCount());
571
572 const v8::CpuProfileNode* fooNode =
573 GetChild(env->GetIsolate(), startNode, "foo");
574 CHECK_EQ(3, fooNode->GetChildrenCount());
575
576 const char* barBranch[] = { "bar", "delay", "loop" };
577 CheckSimpleBranch(env->GetIsolate(), fooNode, barBranch,
578 arraysize(barBranch));
579 const char* bazBranch[] = { "baz", "delay", "loop" };
580 CheckSimpleBranch(env->GetIsolate(), fooNode, bazBranch,
581 arraysize(bazBranch));
582 const char* delayBranch[] = { "delay", "loop" };
583 CheckSimpleBranch(env->GetIsolate(), fooNode, delayBranch,
584 arraysize(delayBranch));
585
586 profile->Delete();
587}
588
589
590static const char* hot_deopt_no_frame_entry_test_source =
591"function foo(a, b) {\n"
592" try {\n"
593" return a + b;\n"
594" } catch (e) { }\n"
595"}\n"
596"function start(timeout) {\n"
597" var start = Date.now();\n"
598" do {\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"
603"}\n";
604
605// Check that the profile tree for the script above will look like the
606// following:
607//
608// [Top down]:
609// 1062 0 (root) [-1]
610// 1054 0 start [-1]
611// 1054 1 foo [-1]
612// 2 2 (program) [-1]
613// 6 6 (garbage collector) [-1]
614//
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).
618TEST(HotDeoptNoFrameEntry) {
619 LocalContext env;
620 v8::HandleScope scope(env->GetIsolate());
621
622 v8::Script::Compile(v8::String::NewFromUtf8(
623 env->GetIsolate(),
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")));
627
628 int32_t profiling_interval_ms = 200;
629 v8::Handle<v8::Value> args[] = {
630 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
631 };
632 v8::CpuProfile* profile =
633 RunProfiler(env.local(), function, args, arraysize(args), 200);
634 function->Call(env->Global(), arraysize(args), args);
635
636 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
637
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);
645
646 const v8::CpuProfileNode* startNode =
647 GetChild(env->GetIsolate(), root, "start");
648 CHECK_EQ(1, startNode->GetChildrenCount());
649
650 GetChild(env->GetIsolate(), startNode, "foo");
651
652 profile->Delete();
653}
654
655
656TEST(CollectCpuProfileSamples) {
657 LocalContext env;
658 v8::HandleScope scope(env->GetIsolate());
659
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")));
664
665 int32_t profiling_interval_ms = 200;
666 v8::Handle<v8::Value> args[] = {
667 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
668 };
669 v8::CpuProfile* profile =
670 RunProfiler(env.local(), function, args, arraysize(args), 200, true);
671
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;
682 }
683
684 profile->Delete();
685}
686
687
688static const char* cpu_profiler_test_source2 = "function loop() {}\n"
689"function delay() { loop(); }\n"
690"function start(count) {\n"
691" var k = 0;\n"
692" do {\n"
693" delay();\n"
694" } while (++k < count*100*1000);\n"
695"}\n";
696
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:
701//
702// [Top down]:
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
708TEST(SampleWhenFrameIsNotSetup) {
709 LocalContext env;
710 v8::HandleScope scope(env->GetIsolate());
711
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")));
716
717 int32_t repeat_count = 100;
718#if defined(USE_SIMULATOR)
719 // Simulators are much slower.
720 repeat_count = 1;
721#endif
722 v8::Handle<v8::Value> args[] = {
723 v8::Integer::New(env->GetIsolate(), repeat_count)
724 };
725 v8::CpuProfile* profile =
726 RunProfiler(env.local(), function, args, arraysize(args), 100);
727
728 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
729
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);
737
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
741 // check there.
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");
749 }
750 }
751
752 profile->Delete();
753}
754
755
756static 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"
760" }\n"
761"}\n";
762
763
764class TestApiCallbacks {
765 public:
766 explicit TestApiCallbacks(int min_duration_ms)
767 : min_duration_ms_(min_duration_ms),
768 is_warming_up_(false) {}
769
770 static void Getter(v8::Local<v8::String> name,
771 const v8::PropertyCallbackInfo<v8::Value>& info) {
772 TestApiCallbacks* data = fromInfo(info);
773 data->Wait();
774 }
775
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);
780 data->Wait();
781 }
782
783 static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
784 TestApiCallbacks* data = fromInfo(info);
785 data->Wait();
786 }
787
788 void set_warming_up(bool value) { is_warming_up_ = value; }
789
790 private:
791 void Wait() {
792 if (is_warming_up_) return;
793 double start = v8::base::OS::TimeCurrentMillis();
794 double duration = 0;
795 while (duration < min_duration_ms_) {
796 v8::base::OS::Sleep(1);
797 duration = v8::base::OS::TimeCurrentMillis() - start;
798 }
799 }
800
801 template<typename T>
802 static TestApiCallbacks* fromInfo(const T& info) {
803 void* data = v8::External::Cast(*info.Data())->Value();
804 return reinterpret_cast<TestApiCallbacks*>(data);
805 }
806
807 int min_duration_ms_;
808 bool is_warming_up_;
809};
810
811
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
815// code.
816TEST(NativeAccessorUninitializedIC) {
817 LocalContext env;
818 v8::Isolate* isolate = env->GetIsolate();
819 v8::HandleScope scope(isolate);
820
821 v8::Local<v8::FunctionTemplate> func_template =
822 v8::FunctionTemplate::New(isolate);
823 v8::Local<v8::ObjectTemplate> instance_template =
824 func_template->InstanceTemplate();
825
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"),
835 instance);
836
837 v8::Script::Compile(
838 v8::String::NewFromUtf8(isolate, native_accessor_test_source))
839 ->Run();
840 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
841 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
842
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, arraysize(args), 180);
847
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");
853
854 profile->Delete();
855}
856
857
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.
861TEST(NativeAccessorMonomorphicIC) {
862 LocalContext env;
863 v8::Isolate* isolate = env->GetIsolate();
864 v8::HandleScope scope(isolate);
865
866 v8::Local<v8::FunctionTemplate> func_template =
867 v8::FunctionTemplate::New(isolate);
868 v8::Local<v8::ObjectTemplate> instance_template =
869 func_template->InstanceTemplate();
870
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"),
880 instance);
881
882 v8::Script::Compile(
883 v8::String::NewFromUtf8(isolate, native_accessor_test_source))
884 ->Run();
885 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
886 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
887
888 {
889 // Make sure accessors ICs are in monomorphic state before starting
890 // profiling.
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)
895 };
896 function->Call(env->Global(), arraysize(args), args);
897 accessors.set_warming_up(false);
898 }
899
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, arraysize(args), 200);
904
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");
910
911 profile->Delete();
912}
913
914
915static const char* native_method_test_source = "function start(count) {\n"
916" for (var i = 0; i < count; i++) {\n"
917" instance.fooMethod();\n"
918" }\n"
919"}\n";
920
921
922TEST(NativeMethodUninitializedIC) {
923 LocalContext env;
924 v8::Isolate* isolate = env->GetIsolate();
925 v8::HandleScope scope(isolate);
926
927 TestApiCallbacks callbacks(100);
928 v8::Local<v8::External> data =
929 v8::External::New(isolate, &callbacks);
930
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));
943
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"),
947 instance);
948
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")));
953
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, arraysize(args), 100);
958
959 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
960 const v8::CpuProfileNode* startNode =
961 GetChild(isolate, root, "start");
962 GetChild(isolate, startNode, "fooMethod");
963
964 profile->Delete();
965}
966
967
968TEST(NativeMethodMonomorphicIC) {
969 LocalContext env;
970 v8::Isolate* isolate = env->GetIsolate();
971 v8::HandleScope scope(isolate);
972
973 TestApiCallbacks callbacks(1);
974 v8::Local<v8::External> data =
975 v8::External::New(isolate, &callbacks);
976
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));
989
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"),
993 instance);
994
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")));
999 {
1000 // Make sure method ICs are in monomorphic state before starting
1001 // profiling.
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)
1006 };
1007 function->Call(env->Global(), arraysize(args), args);
1008 callbacks.set_warming_up(false);
1009 }
1010
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, arraysize(args), 100);
1015
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");
1021
1022 profile->Delete();
1023}
1024
1025
1026static const char* bound_function_test_source =
1027 "function foo() {\n"
1028 " startProfiling('my_profile');\n"
1029 "}\n"
1030 "function start() {\n"
1031 " var callback = foo.bind(this);\n"
1032 " callback();\n"
1033 "}";
1034
1035
1036TEST(BoundFunctionCall) {
1037 v8::HandleScope scope(CcTest::isolate());
1038 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1039 v8::Context::Scope context_scope(env);
1040
1041 v8::Script::Compile(
1042 v8::String::NewFromUtf8(env->GetIsolate(), bound_function_test_source))
1043 ->Run();
1044 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1045 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1046
1047 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1048
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);
1058
1059 const v8::CpuProfileNode* startNode =
1060 GetChild(env->GetIsolate(), root, "start");
1061 GetChild(env->GetIsolate(), startNode, "foo");
1062
1063 profile->Delete();
1064}
1065
1066
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001067// This tests checks distribution of the samples through the source lines.
1068TEST(TickLines) {
1069 CcTest::InitializeVM();
1070 LocalContext env;
1071 i::FLAG_turbo_source_positions = true;
1072 i::Isolate* isolate = CcTest::i_isolate();
1073 i::Factory* factory = isolate->factory();
1074 i::HandleScope scope(isolate);
1075
1076 i::EmbeddedVector<char, 512> script;
1077
1078 const char* func_name = "func";
1079 i::SNPrintF(script,
1080 "function %s() {\n"
1081 " var n = 0;\n"
1082 " var m = 100*100;\n"
1083 " while (m > 1) {\n"
1084 " m--;\n"
1085 " n += m * m * m;\n"
1086 " }\n"
1087 "}\n"
1088 "%s();\n",
1089 func_name, func_name);
1090
1091 CompileRun(script.start());
1092
1093 i::Handle<i::JSFunction> func = v8::Utils::OpenHandle(
1094 *v8::Local<v8::Function>::Cast((*env)->Global()->Get(v8_str(func_name))));
1095 CHECK_NE(NULL, func->shared());
1096 CHECK_NE(NULL, func->shared()->code());
1097 i::Code* code = NULL;
1098 if (func->code()->is_optimized_code()) {
1099 code = func->code();
1100 } else {
1101 CHECK(func->shared()->code() == func->code() || !i::FLAG_crankshaft);
1102 code = func->shared()->code();
1103 }
1104 CHECK_NE(NULL, code);
1105 i::Address code_address = code->instruction_start();
1106 CHECK_NE(NULL, code_address);
1107
1108 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
1109 profiles->StartProfiling("", false);
1110 ProfileGenerator generator(profiles);
1111 ProfilerEventsProcessor* processor = new ProfilerEventsProcessor(
1112 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100));
1113 processor->Start();
1114 CpuProfiler profiler(isolate, profiles, &generator, processor);
1115
1116 // Enqueue code creation events.
1117 i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name);
1118 int line = 1;
1119 int column = 1;
1120 profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, code, func->shared(), NULL,
1121 *str, line, column);
1122
1123 // Enqueue a tick event to enable code events processing.
1124 EnqueueTickSampleEvent(processor, code_address);
1125
1126 processor->StopSynchronously();
1127
1128 CpuProfile* profile = profiles->StopProfiling("");
1129 CHECK_NE(NULL, profile);
1130
1131 // Check the state of profile generator.
1132 CodeEntry* func_entry = generator.code_map()->FindEntry(code_address);
1133 CHECK_NE(NULL, func_entry);
1134 CHECK_EQ(func_name, func_entry->name());
1135 const i::JITLineInfoTable* line_info = func_entry->line_info();
1136 CHECK_NE(NULL, line_info);
1137 CHECK(!line_info->empty());
1138
1139 // Check the hit source lines using V8 Public APIs.
1140 const i::ProfileTree* tree = profile->top_down();
1141 ProfileNode* root = tree->root();
1142 CHECK_NE(NULL, root);
1143 ProfileNode* func_node = root->FindChild(func_entry);
1144 CHECK_NE(NULL, func_node);
1145
1146 // Add 10 faked ticks to source line #5.
1147 int hit_line = 5;
1148 int hit_count = 10;
1149 for (int i = 0; i < hit_count; i++) func_node->IncrementLineTicks(hit_line);
1150
1151 unsigned int line_count = func_node->GetHitLineCount();
1152 CHECK_EQ(2, line_count); // Expect two hit source lines - #1 and #5.
1153 ScopedVector<v8::CpuProfileNode::LineTick> entries(line_count);
1154 CHECK(func_node->GetLineTicks(&entries[0], line_count));
1155 int value = 0;
1156 for (int i = 0; i < entries.length(); i++)
1157 if (entries[i].line == hit_line) {
1158 value = entries[i].hit_count;
1159 break;
1160 }
1161 CHECK_EQ(hit_count, value);
1162}
1163
1164
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001165static const char* call_function_test_source = "function bar(iterations) {\n"
1166"}\n"
1167"function start(duration) {\n"
1168" var start = Date.now();\n"
1169" while (Date.now() - start < duration) {\n"
1170" try {\n"
1171" bar.call(this, 10 * 1000);\n"
1172" } catch(e) {}\n"
1173" }\n"
1174"}";
1175
1176
1177// Test that if we sampled thread when it was inside FunctionCall buitin then
1178// its caller frame will be '(unresolved function)' as we have no reliable way
1179// to resolve it.
1180//
1181// [Top down]:
1182// 96 0 (root) [-1] #1
1183// 1 1 (garbage collector) [-1] #4
1184// 5 0 (unresolved function) [-1] #5
1185// 5 5 call [-1] #6
1186// 71 70 start [-1] #3
1187// 1 1 bar [-1] #7
1188// 19 19 (program) [-1] #2
1189TEST(FunctionCallSample) {
1190 LocalContext env;
1191 v8::HandleScope scope(env->GetIsolate());
1192
1193 // Collect garbage that might have be generated while installing extensions.
1194 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1195
1196 v8::Script::Compile(v8::String::NewFromUtf8(
1197 env->GetIsolate(), call_function_test_source))->Run();
1198 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1199 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1200
1201 int32_t duration_ms = 100;
1202 v8::Handle<v8::Value> args[] = {
1203 v8::Integer::New(env->GetIsolate(), duration_ms)
1204 };
1205 v8::CpuProfile* profile =
1206 RunProfiler(env.local(), function, args, arraysize(args), 100);
1207
1208 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1209 {
1210 ScopedVector<v8::Handle<v8::String> > names(4);
1211 names[0] = v8::String::NewFromUtf8(
1212 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1213 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1214 ProfileGenerator::kProgramEntryName);
1215 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1216 names[3] = v8::String::NewFromUtf8(
1217 env->GetIsolate(), i::ProfileGenerator::kUnresolvedFunctionName);
1218 // Don't allow |bar| and |call| nodes to be at the top level.
1219 CheckChildrenNames(root, names);
1220 }
1221
1222 // In case of GC stress tests all samples may be in GC phase and there
1223 // won't be |start| node in the profiles.
1224 bool is_gc_stress_testing =
1225 (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction;
1226 const v8::CpuProfileNode* startNode =
1227 FindChild(env->GetIsolate(), root, "start");
1228 CHECK(is_gc_stress_testing || startNode);
1229 if (startNode) {
1230 ScopedVector<v8::Handle<v8::String> > names(2);
1231 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1232 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1233 CheckChildrenNames(startNode, names);
1234 }
1235
1236 const v8::CpuProfileNode* unresolvedNode = FindChild(
1237 env->GetIsolate(), root, i::ProfileGenerator::kUnresolvedFunctionName);
1238 if (unresolvedNode) {
1239 ScopedVector<v8::Handle<v8::String> > names(1);
1240 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1241 CheckChildrenNames(unresolvedNode, names);
1242 }
1243
1244 profile->Delete();
1245}
1246
1247
1248static const char* function_apply_test_source = "function bar(iterations) {\n"
1249"}\n"
1250"function test() {\n"
1251" bar.apply(this, [10 * 1000]);\n"
1252"}\n"
1253"function start(duration) {\n"
1254" var start = Date.now();\n"
1255" while (Date.now() - start < duration) {\n"
1256" try {\n"
1257" test();\n"
1258" } catch(e) {}\n"
1259" }\n"
1260"}";
1261
1262
1263// [Top down]:
1264// 94 0 (root) [-1] #0 1
1265// 2 2 (garbage collector) [-1] #0 7
1266// 82 49 start [-1] #16 3
1267// 1 0 (unresolved function) [-1] #0 8
1268// 1 1 apply [-1] #0 9
1269// 32 21 test [-1] #16 4
1270// 2 2 bar [-1] #16 6
1271// 9 9 apply [-1] #0 5
1272// 10 10 (program) [-1] #0 2
1273TEST(FunctionApplySample) {
1274 LocalContext env;
1275 v8::HandleScope scope(env->GetIsolate());
1276
1277 v8::Script::Compile(
1278 v8::String::NewFromUtf8(env->GetIsolate(), function_apply_test_source))
1279 ->Run();
1280 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1281 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1282
1283 int32_t duration_ms = 100;
1284 v8::Handle<v8::Value> args[] = {
1285 v8::Integer::New(env->GetIsolate(), duration_ms)
1286 };
1287
1288 v8::CpuProfile* profile =
1289 RunProfiler(env.local(), function, args, arraysize(args), 100);
1290
1291 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1292 {
1293 ScopedVector<v8::Handle<v8::String> > names(3);
1294 names[0] = v8::String::NewFromUtf8(
1295 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1296 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1297 ProfileGenerator::kProgramEntryName);
1298 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1299 // Don't allow |test|, |bar| and |apply| nodes to be at the top level.
1300 CheckChildrenNames(root, names);
1301 }
1302
1303 const v8::CpuProfileNode* startNode =
1304 FindChild(env->GetIsolate(), root, "start");
1305 if (startNode) {
1306 {
1307 ScopedVector<v8::Handle<v8::String> > names(2);
1308 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "test");
1309 names[1] = v8::String::NewFromUtf8(
1310 env->GetIsolate(), ProfileGenerator::kUnresolvedFunctionName);
1311 CheckChildrenNames(startNode, names);
1312 }
1313
1314 const v8::CpuProfileNode* testNode =
1315 FindChild(env->GetIsolate(), startNode, "test");
1316 if (testNode) {
1317 ScopedVector<v8::Handle<v8::String> > names(3);
1318 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1319 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1320 // apply calls "get length" before invoking the function itself
1321 // and we may get hit into it.
1322 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "get length");
1323 CheckChildrenNames(testNode, names);
1324 }
1325
1326 if (const v8::CpuProfileNode* unresolvedNode =
1327 FindChild(env->GetIsolate(), startNode,
1328 ProfileGenerator::kUnresolvedFunctionName)) {
1329 ScopedVector<v8::Handle<v8::String> > names(1);
1330 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1331 CheckChildrenNames(unresolvedNode, names);
1332 GetChild(env->GetIsolate(), unresolvedNode, "apply");
1333 }
1334 }
1335
1336 profile->Delete();
1337}
1338
1339
1340static const char* cpu_profiler_deep_stack_test_source =
1341"function foo(n) {\n"
1342" if (n)\n"
1343" foo(n - 1);\n"
1344" else\n"
1345" startProfiling('my_profile');\n"
1346"}\n"
1347"function start() {\n"
1348" foo(250);\n"
1349"}\n";
1350
1351
1352// Check a deep stack
1353//
1354// [Top down]:
1355// 0 (root) 0 #1
1356// 2 (program) 0 #2
1357// 0 start 21 #3 no reason
1358// 0 foo 21 #4 no reason
1359// 0 foo 21 #5 no reason
1360// ....
1361// 0 foo 21 #253 no reason
1362// 1 startProfiling 0 #254
1363TEST(CpuProfileDeepStack) {
1364 v8::HandleScope scope(CcTest::isolate());
1365 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1366 v8::Context::Scope context_scope(env);
1367
1368 v8::Script::Compile(v8::String::NewFromUtf8(
1369 env->GetIsolate(), cpu_profiler_deep_stack_test_source))->Run();
1370 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1371 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1372
1373 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1374 v8::Local<v8::String> profile_name =
1375 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1376 function->Call(env->Global(), 0, NULL);
1377 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1378 CHECK_NE(NULL, profile);
1379 // Dump collected profile to have a better diagnostic in case of failure.
1380 reinterpret_cast<i::CpuProfile*>(profile)->Print();
1381
1382 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1383 {
1384 ScopedVector<v8::Handle<v8::String> > names(3);
1385 names[0] = v8::String::NewFromUtf8(
1386 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1387 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1388 ProfileGenerator::kProgramEntryName);
1389 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1390 CheckChildrenNames(root, names);
1391 }
1392
1393 const v8::CpuProfileNode* node =
1394 GetChild(env->GetIsolate(), root, "start");
1395 for (int i = 0; i < 250; ++i) {
1396 node = GetChild(env->GetIsolate(), node, "foo");
1397 }
1398 // TODO(alph):
1399 // In theory there must be one more 'foo' and a 'startProfiling' nodes,
1400 // but due to unstable top frame extraction these might be missing.
1401
1402 profile->Delete();
1403}
1404
1405
1406static const char* js_native_js_test_source =
1407 "function foo() {\n"
1408 " startProfiling('my_profile');\n"
1409 "}\n"
1410 "function bar() {\n"
1411 " try { foo(); } catch(e) {}\n"
1412 "}\n"
1413 "function start() {\n"
1414 " try {\n"
1415 " CallJsFunction(bar);\n"
1416 " } catch(e) {}\n"
1417 "}";
1418
1419static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1420 v8::Handle<v8::Function> function = info[0].As<v8::Function>();
1421 v8::Handle<v8::Value> argv[] = { info[1] };
1422 function->Call(info.This(), arraysize(argv), argv);
1423}
1424
1425
1426// [Top down]:
1427// 58 0 (root) #0 1
1428// 2 2 (program) #0 2
1429// 56 1 start #16 3
1430// 55 0 CallJsFunction #0 4
1431// 55 1 bar #16 5
1432// 54 54 foo #16 6
1433TEST(JsNativeJsSample) {
1434 v8::HandleScope scope(CcTest::isolate());
1435 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1436 v8::Context::Scope context_scope(env);
1437
1438 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1439 env->GetIsolate(), CallJsFunction);
1440 v8::Local<v8::Function> func = func_template->GetFunction();
1441 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1442 env->Global()->Set(
1443 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1444
1445 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
1446 js_native_js_test_source))->Run();
1447 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1448 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1449
1450 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1451
1452 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1453 {
1454 ScopedVector<v8::Handle<v8::String> > names(3);
1455 names[0] = v8::String::NewFromUtf8(
1456 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1457 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1458 ProfileGenerator::kProgramEntryName);
1459 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1460 CheckChildrenNames(root, names);
1461 }
1462
1463 const v8::CpuProfileNode* startNode =
1464 GetChild(env->GetIsolate(), root, "start");
1465 CHECK_EQ(1, startNode->GetChildrenCount());
1466 const v8::CpuProfileNode* nativeFunctionNode =
1467 GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1468
1469 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1470 const v8::CpuProfileNode* barNode =
1471 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1472
1473 CHECK_EQ(1, barNode->GetChildrenCount());
1474 GetChild(env->GetIsolate(), barNode, "foo");
1475
1476 profile->Delete();
1477}
1478
1479
1480static const char* js_native_js_runtime_js_test_source =
1481 "function foo() {\n"
1482 " startProfiling('my_profile');\n"
1483 "}\n"
1484 "var bound = foo.bind(this);\n"
1485 "function bar() {\n"
1486 " try { bound(); } catch(e) {}\n"
1487 "}\n"
1488 "function start() {\n"
1489 " try {\n"
1490 " CallJsFunction(bar);\n"
1491 " } catch(e) {}\n"
1492 "}";
1493
1494
1495// [Top down]:
1496// 57 0 (root) #0 1
1497// 55 1 start #16 3
1498// 54 0 CallJsFunction #0 4
1499// 54 3 bar #16 5
1500// 51 51 foo #16 6
1501// 2 2 (program) #0 2
1502TEST(JsNativeJsRuntimeJsSample) {
1503 v8::HandleScope scope(CcTest::isolate());
1504 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1505 v8::Context::Scope context_scope(env);
1506
1507 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1508 env->GetIsolate(), CallJsFunction);
1509 v8::Local<v8::Function> func = func_template->GetFunction();
1510 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1511 env->Global()->Set(
1512 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1513
1514 v8::Script::Compile(
1515 v8::String::NewFromUtf8(env->GetIsolate(),
1516 js_native_js_runtime_js_test_source))->Run();
1517 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1518 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1519
1520 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1521
1522 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1523 ScopedVector<v8::Handle<v8::String> > names(3);
1524 names[0] = v8::String::NewFromUtf8(
1525 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1526 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1527 ProfileGenerator::kProgramEntryName);
1528 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1529 CheckChildrenNames(root, names);
1530
1531 const v8::CpuProfileNode* startNode =
1532 GetChild(env->GetIsolate(), root, "start");
1533 CHECK_EQ(1, startNode->GetChildrenCount());
1534 const v8::CpuProfileNode* nativeFunctionNode =
1535 GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1536
1537 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1538 const v8::CpuProfileNode* barNode =
1539 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1540
1541 // The child is in fact a bound foo.
1542 // A bound function has a wrapper that may make calls to
1543 // other functions e.g. "get length".
1544 CHECK_LE(1, barNode->GetChildrenCount());
1545 CHECK_GE(2, barNode->GetChildrenCount());
1546 GetChild(env->GetIsolate(), barNode, "foo");
1547
1548 profile->Delete();
1549}
1550
1551
1552static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1553 v8::base::OS::Print("In CallJsFunction2\n");
1554 CallJsFunction(info);
1555}
1556
1557
1558static const char* js_native1_js_native2_js_test_source =
1559 "function foo() {\n"
1560 " try {\n"
1561 " startProfiling('my_profile');\n"
1562 " } catch(e) {}\n"
1563 "}\n"
1564 "function bar() {\n"
1565 " CallJsFunction2(foo);\n"
1566 "}\n"
1567 "function start() {\n"
1568 " try {\n"
1569 " CallJsFunction1(bar);\n"
1570 " } catch(e) {}\n"
1571 "}";
1572
1573
1574// [Top down]:
1575// 57 0 (root) #0 1
1576// 55 1 start #16 3
1577// 54 0 CallJsFunction1 #0 4
1578// 54 0 bar #16 5
1579// 54 0 CallJsFunction2 #0 6
1580// 54 54 foo #16 7
1581// 2 2 (program) #0 2
1582TEST(JsNative1JsNative2JsSample) {
1583 v8::HandleScope scope(CcTest::isolate());
1584 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1585 v8::Context::Scope context_scope(env);
1586
1587 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1588 env->GetIsolate(), CallJsFunction);
1589 v8::Local<v8::Function> func1 = func_template->GetFunction();
1590 func1->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"));
1591 env->Global()->Set(
1592 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"), func1);
1593
1594 v8::Local<v8::Function> func2 = v8::FunctionTemplate::New(
1595 env->GetIsolate(), CallJsFunction2)->GetFunction();
1596 func2->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"));
1597 env->Global()->Set(
1598 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"), func2);
1599
1600 v8::Script::Compile(
1601 v8::String::NewFromUtf8(env->GetIsolate(),
1602 js_native1_js_native2_js_test_source))->Run();
1603 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1604 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1605
1606 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1607
1608 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1609 ScopedVector<v8::Handle<v8::String> > names(3);
1610 names[0] = v8::String::NewFromUtf8(
1611 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1612 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1613 ProfileGenerator::kProgramEntryName);
1614 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1615 CheckChildrenNames(root, names);
1616
1617 const v8::CpuProfileNode* startNode =
1618 GetChild(env->GetIsolate(), root, "start");
1619 CHECK_EQ(1, startNode->GetChildrenCount());
1620 const v8::CpuProfileNode* nativeNode1 =
1621 GetChild(env->GetIsolate(), startNode, "CallJsFunction1");
1622
1623 CHECK_EQ(1, nativeNode1->GetChildrenCount());
1624 const v8::CpuProfileNode* barNode =
1625 GetChild(env->GetIsolate(), nativeNode1, "bar");
1626
1627 CHECK_EQ(1, barNode->GetChildrenCount());
1628 const v8::CpuProfileNode* nativeNode2 =
1629 GetChild(env->GetIsolate(), barNode, "CallJsFunction2");
1630
1631 CHECK_EQ(1, nativeNode2->GetChildrenCount());
1632 GetChild(env->GetIsolate(), nativeNode2, "foo");
1633
1634 profile->Delete();
1635}
1636
1637
1638// [Top down]:
1639// 6 0 (root) #0 1
1640// 3 3 (program) #0 2
1641// 3 3 (idle) #0 3
1642TEST(IdleTime) {
1643 LocalContext env;
1644 v8::HandleScope scope(env->GetIsolate());
1645 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1646
1647 v8::Local<v8::String> profile_name =
1648 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1649 cpu_profiler->StartProfiling(profile_name);
1650
1651 i::Isolate* isolate = CcTest::i_isolate();
1652 i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
1653 processor->AddCurrentStack(isolate);
1654
1655 cpu_profiler->SetIdle(true);
1656
1657 for (int i = 0; i < 3; i++) {
1658 processor->AddCurrentStack(isolate);
1659 }
1660
1661 cpu_profiler->SetIdle(false);
1662 processor->AddCurrentStack(isolate);
1663
1664
1665 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1666 CHECK_NE(NULL, profile);
1667 // Dump collected profile to have a better diagnostic in case of failure.
1668 reinterpret_cast<i::CpuProfile*>(profile)->Print();
1669
1670 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1671 ScopedVector<v8::Handle<v8::String> > names(3);
1672 names[0] = v8::String::NewFromUtf8(
1673 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1674 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1675 ProfileGenerator::kProgramEntryName);
1676 names[2] = v8::String::NewFromUtf8(env->GetIsolate(),
1677 ProfileGenerator::kIdleEntryName);
1678 CheckChildrenNames(root, names);
1679
1680 const v8::CpuProfileNode* programNode =
1681 GetChild(env->GetIsolate(), root, ProfileGenerator::kProgramEntryName);
1682 CHECK_EQ(0, programNode->GetChildrenCount());
1683 CHECK_GE(programNode->GetHitCount(), 3);
1684
1685 const v8::CpuProfileNode* idleNode =
1686 GetChild(env->GetIsolate(), root, ProfileGenerator::kIdleEntryName);
1687 CHECK_EQ(0, idleNode->GetChildrenCount());
1688 CHECK_GE(idleNode->GetHitCount(), 3);
1689
1690 profile->Delete();
1691}
1692
1693
1694static void CheckFunctionDetails(v8::Isolate* isolate,
1695 const v8::CpuProfileNode* node,
1696 const char* name, const char* script_name,
1697 int script_id, int line, int column) {
1698 CHECK_EQ(v8::String::NewFromUtf8(isolate, name),
1699 node->GetFunctionName());
1700 CHECK_EQ(v8::String::NewFromUtf8(isolate, script_name),
1701 node->GetScriptResourceName());
1702 CHECK_EQ(script_id, node->GetScriptId());
1703 CHECK_EQ(line, node->GetLineNumber());
1704 CHECK_EQ(column, node->GetColumnNumber());
1705}
1706
1707
1708TEST(FunctionDetails) {
1709 v8::HandleScope scope(CcTest::isolate());
1710 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1711 v8::Context::Scope context_scope(env);
1712
1713 v8::Handle<v8::Script> script_a = CompileWithOrigin(
1714 " function foo\n() { try { bar(); } catch(e) {} }\n"
1715 " function bar() { startProfiling(); }\n",
1716 "script_a");
1717 script_a->Run();
1718 v8::Handle<v8::Script> script_b = CompileWithOrigin(
1719 "\n\n function baz() { try { foo(); } catch(e) {} }\n"
1720 "\n\nbaz();\n"
1721 "stopProfiling();\n",
1722 "script_b");
1723 script_b->Run();
1724 const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1725 const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1726 reinterpret_cast<ProfileNode*>(
1727 const_cast<v8::CpuProfileNode*>(current))->Print(0);
1728 // The tree should look like this:
1729 // 0 (root) 0 #1
1730 // 0 "" 19 #2 no reason script_b:1
1731 // 0 baz 19 #3 TryCatchStatement script_b:3
1732 // 0 foo 18 #4 TryCatchStatement script_a:2
1733 // 1 bar 18 #5 no reason script_a:3
1734 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1735 const v8::CpuProfileNode* script = GetChild(env->GetIsolate(), root, "");
1736 CheckFunctionDetails(env->GetIsolate(), script, "", "script_b",
1737 script_b->GetUnboundScript()->GetId(), 1, 1);
1738 const v8::CpuProfileNode* baz = GetChild(env->GetIsolate(), script, "baz");
1739 CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b",
1740 script_b->GetUnboundScript()->GetId(), 3, 16);
1741 const v8::CpuProfileNode* foo = GetChild(env->GetIsolate(), baz, "foo");
1742 CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a",
1743 script_a->GetUnboundScript()->GetId(), 2, 1);
1744 const v8::CpuProfileNode* bar = GetChild(env->GetIsolate(), foo, "bar");
1745 CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a",
1746 script_a->GetUnboundScript()->GetId(), 3, 14);
1747}
1748
1749
1750TEST(DontStopOnFinishedProfileDelete) {
1751 v8::HandleScope scope(CcTest::isolate());
1752 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1753 v8::Context::Scope context_scope(env);
1754 v8::Isolate* isolate = env->GetIsolate();
1755
1756 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler();
1757 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
1758
1759 CHECK_EQ(0, iprofiler->GetProfilesCount());
1760 v8::Handle<v8::String> outer = v8::String::NewFromUtf8(isolate, "outer");
1761 profiler->StartProfiling(outer);
1762 CHECK_EQ(0, iprofiler->GetProfilesCount());
1763
1764 v8::Handle<v8::String> inner = v8::String::NewFromUtf8(isolate, "inner");
1765 profiler->StartProfiling(inner);
1766 CHECK_EQ(0, iprofiler->GetProfilesCount());
1767
1768 v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
1769 CHECK(inner_profile);
1770 CHECK_EQ(1, iprofiler->GetProfilesCount());
1771 inner_profile->Delete();
1772 inner_profile = NULL;
1773 CHECK_EQ(0, iprofiler->GetProfilesCount());
1774
1775 v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
1776 CHECK(outer_profile);
1777 CHECK_EQ(1, iprofiler->GetProfilesCount());
1778 outer_profile->Delete();
1779 outer_profile = NULL;
1780 CHECK_EQ(0, iprofiler->GetProfilesCount());
Steve Block44f0eee2011-05-26 01:26:41 +01001781}