blob: 8d429d2e21f35f7dc0f380ae495d1430900b9865 [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
1067static const char* call_function_test_source = "function bar(iterations) {\n"
1068"}\n"
1069"function start(duration) {\n"
1070" var start = Date.now();\n"
1071" while (Date.now() - start < duration) {\n"
1072" try {\n"
1073" bar.call(this, 10 * 1000);\n"
1074" } catch(e) {}\n"
1075" }\n"
1076"}";
1077
1078
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
1081// to resolve it.
1082//
1083// [Top down]:
1084// 96 0 (root) [-1] #1
1085// 1 1 (garbage collector) [-1] #4
1086// 5 0 (unresolved function) [-1] #5
1087// 5 5 call [-1] #6
1088// 71 70 start [-1] #3
1089// 1 1 bar [-1] #7
1090// 19 19 (program) [-1] #2
1091TEST(FunctionCallSample) {
1092 LocalContext env;
1093 v8::HandleScope scope(env->GetIsolate());
1094
1095 // Collect garbage that might have be generated while installing extensions.
1096 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1097
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")));
1102
1103 int32_t duration_ms = 100;
1104 v8::Handle<v8::Value> args[] = {
1105 v8::Integer::New(env->GetIsolate(), duration_ms)
1106 };
1107 v8::CpuProfile* profile =
1108 RunProfiler(env.local(), function, args, arraysize(args), 100);
1109
1110 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1111 {
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);
1122 }
1123
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);
1131 if (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);
1136 }
1137
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);
1144 }
1145
1146 profile->Delete();
1147}
1148
1149
1150static const char* function_apply_test_source = "function bar(iterations) {\n"
1151"}\n"
1152"function test() {\n"
1153" bar.apply(this, [10 * 1000]);\n"
1154"}\n"
1155"function start(duration) {\n"
1156" var start = Date.now();\n"
1157" while (Date.now() - start < duration) {\n"
1158" try {\n"
1159" test();\n"
1160" } catch(e) {}\n"
1161" }\n"
1162"}";
1163
1164
1165// [Top down]:
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
1175TEST(FunctionApplySample) {
1176 LocalContext env;
1177 v8::HandleScope scope(env->GetIsolate());
1178
1179 v8::Script::Compile(
1180 v8::String::NewFromUtf8(env->GetIsolate(), function_apply_test_source))
1181 ->Run();
1182 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1183 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1184
1185 int32_t duration_ms = 100;
1186 v8::Handle<v8::Value> args[] = {
1187 v8::Integer::New(env->GetIsolate(), duration_ms)
1188 };
1189
1190 v8::CpuProfile* profile =
1191 RunProfiler(env.local(), function, args, arraysize(args), 100);
1192
1193 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1194 {
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);
1203 }
1204
1205 const v8::CpuProfileNode* startNode =
1206 FindChild(env->GetIsolate(), root, "start");
1207 if (startNode) {
1208 {
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);
1214 }
1215
1216 const v8::CpuProfileNode* testNode =
1217 FindChild(env->GetIsolate(), startNode, "test");
1218 if (testNode) {
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);
1226 }
1227
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");
1235 }
1236 }
1237
1238 profile->Delete();
1239}
1240
1241
1242static const char* cpu_profiler_deep_stack_test_source =
1243"function foo(n) {\n"
1244" if (n)\n"
1245" foo(n - 1);\n"
1246" else\n"
1247" startProfiling('my_profile');\n"
1248"}\n"
1249"function start() {\n"
1250" foo(250);\n"
1251"}\n";
1252
1253
1254// Check a deep stack
1255//
1256// [Top down]:
1257// 0 (root) 0 #1
1258// 2 (program) 0 #2
1259// 0 start 21 #3 no reason
1260// 0 foo 21 #4 no reason
1261// 0 foo 21 #5 no reason
1262// ....
1263// 0 foo 21 #253 no reason
1264// 1 startProfiling 0 #254
1265TEST(CpuProfileDeepStack) {
1266 v8::HandleScope scope(CcTest::isolate());
1267 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1268 v8::Context::Scope context_scope(env);
1269
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")));
1274
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();
1283
1284 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1285 {
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);
1293 }
1294
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");
1299 }
1300 // TODO(alph):
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.
1303
1304 profile->Delete();
1305}
1306
1307
1308static const char* js_native_js_test_source =
1309 "function foo() {\n"
1310 " startProfiling('my_profile');\n"
1311 "}\n"
1312 "function bar() {\n"
1313 " try { foo(); } catch(e) {}\n"
1314 "}\n"
1315 "function start() {\n"
1316 " try {\n"
1317 " CallJsFunction(bar);\n"
1318 " } catch(e) {}\n"
1319 "}";
1320
1321static 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(), arraysize(argv), argv);
1325}
1326
1327
1328// [Top down]:
1329// 58 0 (root) #0 1
1330// 2 2 (program) #0 2
1331// 56 1 start #16 3
1332// 55 0 CallJsFunction #0 4
1333// 55 1 bar #16 5
1334// 54 54 foo #16 6
1335TEST(JsNativeJsSample) {
1336 v8::HandleScope scope(CcTest::isolate());
1337 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1338 v8::Context::Scope context_scope(env);
1339
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"));
1344 env->Global()->Set(
1345 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1346
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")));
1351
1352 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1353
1354 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1355 {
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);
1363 }
1364
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");
1370
1371 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1372 const v8::CpuProfileNode* barNode =
1373 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1374
1375 CHECK_EQ(1, barNode->GetChildrenCount());
1376 GetChild(env->GetIsolate(), barNode, "foo");
1377
1378 profile->Delete();
1379}
1380
1381
1382static const char* js_native_js_runtime_js_test_source =
1383 "function foo() {\n"
1384 " startProfiling('my_profile');\n"
1385 "}\n"
1386 "var bound = foo.bind(this);\n"
1387 "function bar() {\n"
1388 " try { bound(); } catch(e) {}\n"
1389 "}\n"
1390 "function start() {\n"
1391 " try {\n"
1392 " CallJsFunction(bar);\n"
1393 " } catch(e) {}\n"
1394 "}";
1395
1396
1397// [Top down]:
1398// 57 0 (root) #0 1
1399// 55 1 start #16 3
1400// 54 0 CallJsFunction #0 4
1401// 54 3 bar #16 5
1402// 51 51 foo #16 6
1403// 2 2 (program) #0 2
1404TEST(JsNativeJsRuntimeJsSample) {
1405 v8::HandleScope scope(CcTest::isolate());
1406 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1407 v8::Context::Scope context_scope(env);
1408
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"));
1413 env->Global()->Set(
1414 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1415
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")));
1421
1422 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1423
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);
1432
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");
1438
1439 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1440 const v8::CpuProfileNode* barNode =
1441 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1442
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");
1449
1450 profile->Delete();
1451}
1452
1453
1454static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1455 v8::base::OS::Print("In CallJsFunction2\n");
1456 CallJsFunction(info);
1457}
1458
1459
1460static const char* js_native1_js_native2_js_test_source =
1461 "function foo() {\n"
1462 " try {\n"
1463 " startProfiling('my_profile');\n"
1464 " } catch(e) {}\n"
1465 "}\n"
1466 "function bar() {\n"
1467 " CallJsFunction2(foo);\n"
1468 "}\n"
1469 "function start() {\n"
1470 " try {\n"
1471 " CallJsFunction1(bar);\n"
1472 " } catch(e) {}\n"
1473 "}";
1474
1475
1476// [Top down]:
1477// 57 0 (root) #0 1
1478// 55 1 start #16 3
1479// 54 0 CallJsFunction1 #0 4
1480// 54 0 bar #16 5
1481// 54 0 CallJsFunction2 #0 6
1482// 54 54 foo #16 7
1483// 2 2 (program) #0 2
1484TEST(JsNative1JsNative2JsSample) {
1485 v8::HandleScope scope(CcTest::isolate());
1486 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1487 v8::Context::Scope context_scope(env);
1488
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"));
1493 env->Global()->Set(
1494 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"), func1);
1495
1496 v8::Local<v8::Function> func2 = v8::FunctionTemplate::New(
1497 env->GetIsolate(), CallJsFunction2)->GetFunction();
1498 func2->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"));
1499 env->Global()->Set(
1500 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"), func2);
1501
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")));
1507
1508 v8::CpuProfile* profile = RunProfiler(env, function, NULL, 0, 0);
1509
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);
1518
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");
1524
1525 CHECK_EQ(1, nativeNode1->GetChildrenCount());
1526 const v8::CpuProfileNode* barNode =
1527 GetChild(env->GetIsolate(), nativeNode1, "bar");
1528
1529 CHECK_EQ(1, barNode->GetChildrenCount());
1530 const v8::CpuProfileNode* nativeNode2 =
1531 GetChild(env->GetIsolate(), barNode, "CallJsFunction2");
1532
1533 CHECK_EQ(1, nativeNode2->GetChildrenCount());
1534 GetChild(env->GetIsolate(), nativeNode2, "foo");
1535
1536 profile->Delete();
1537}
1538
1539
1540// [Top down]:
1541// 6 0 (root) #0 1
1542// 3 3 (program) #0 2
1543// 3 3 (idle) #0 3
1544TEST(IdleTime) {
1545 LocalContext env;
1546 v8::HandleScope scope(env->GetIsolate());
1547 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1548
1549 v8::Local<v8::String> profile_name =
1550 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1551 cpu_profiler->StartProfiling(profile_name);
1552
1553 i::Isolate* isolate = CcTest::i_isolate();
1554 i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
1555 processor->AddCurrentStack(isolate);
1556
1557 cpu_profiler->SetIdle(true);
1558
1559 for (int i = 0; i < 3; i++) {
1560 processor->AddCurrentStack(isolate);
1561 }
1562
1563 cpu_profiler->SetIdle(false);
1564 processor->AddCurrentStack(isolate);
1565
1566
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();
1571
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);
1581
1582 const v8::CpuProfileNode* programNode =
1583 GetChild(env->GetIsolate(), root, ProfileGenerator::kProgramEntryName);
1584 CHECK_EQ(0, programNode->GetChildrenCount());
1585 CHECK_GE(programNode->GetHitCount(), 3);
1586
1587 const v8::CpuProfileNode* idleNode =
1588 GetChild(env->GetIsolate(), root, ProfileGenerator::kIdleEntryName);
1589 CHECK_EQ(0, idleNode->GetChildrenCount());
1590 CHECK_GE(idleNode->GetHitCount(), 3);
1591
1592 profile->Delete();
1593}
1594
1595
1596static 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());
1607}
1608
1609
1610TEST(FunctionDetails) {
1611 v8::HandleScope scope(CcTest::isolate());
1612 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1613 v8::Context::Scope context_scope(env);
1614
1615 v8::Handle<v8::Script> script_a = CompileWithOrigin(
1616 " function foo\n() { try { bar(); } catch(e) {} }\n"
1617 " function bar() { startProfiling(); }\n",
1618 "script_a");
1619 script_a->Run();
1620 v8::Handle<v8::Script> script_b = CompileWithOrigin(
1621 "\n\n function baz() { try { foo(); } catch(e) {} }\n"
1622 "\n\nbaz();\n"
1623 "stopProfiling();\n",
1624 "script_b");
1625 script_b->Run();
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:
1631 // 0 (root) 0 #1
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);
1649}
1650
1651
1652TEST(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();
1657
1658 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler();
1659 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
1660
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());
1665
1666 v8::Handle<v8::String> inner = v8::String::NewFromUtf8(isolate, "inner");
1667 profiler->StartProfiling(inner);
1668 CHECK_EQ(0, iprofiler->GetProfilesCount());
1669
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());
1676
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());
Steve Block44f0eee2011-05-26 01:26:41 +01001683}