1 // Copyright 2010 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //
28 // Tests of profiles generator and utilities.
29
30 #include "src/v8.h"
31
32 #include "include/v8-profiler.h"
33 #include "src/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"
39 using i::CodeEntry;
40 using i::CpuProfile;
41 using i::CpuProfiler;
42 using i::CpuProfilesCollection;
43 using i::Heap;
44 using i::ProfileGenerator;
45 using i::ProfileNode;
46 using i::ProfilerEventsProcessor;
47 using i::ScopedVector;
48 using i::SmartPointer;
49 using i::Vector;
50
51
TEST(StartStop)52 TEST(StartStop) {
53 i::Isolate* isolate = CcTest::i_isolate();
54 CpuProfilesCollection profiles(isolate->heap());
55 ProfileGenerator generator(&profiles);
56 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
57 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
58 processor->Start();
59 processor->StopSynchronously();
60 }
61
62
EnqueueTickSampleEvent(ProfilerEventsProcessor * proc,i::Address frame1,i::Address frame2=NULL,i::Address frame3=NULL)63 static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
64 i::Address frame1,
65 i::Address frame2 = NULL,
66 i::Address frame3 = NULL) {
67 i::TickSample* sample = proc->StartTickSample();
68 sample->pc = frame1;
69 sample->tos = frame1;
70 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 }
79 proc->FinishTickSample();
80 }
81
82 namespace {
83
84 class TestSetup {
85 public:
TestSetup()86 TestSetup()
87 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
88 i::FLAG_prof_browser_mode = false;
89 }
90
~TestSetup()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
101
CreateCode(LocalContext * env)102 i::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
TEST(CodeEvents)124 TEST(CodeEvents) {
125 CcTest::InitializeVM();
126 LocalContext env;
127 i::Isolate* isolate = CcTest::i_isolate();
128 i::Factory* factory = isolate->factory();
129 TestSetup test_setup;
130
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());
148
149 // Enqueue code creation events.
150 const char* aaa_str = "aaa";
151 i::Handle<i::String> aaa_name = factory->NewStringFromAsciiChecked(aaa_str);
152 profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code, *aaa_name);
153 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code, "comment");
154 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args5_code, 5);
155 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code, "comment2");
156 profiler.CodeMoveEvent(comment2_code->address(), moved_code->address());
157 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args3_code, 3);
158 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args4_code, 4);
159
160 // Enqueue a tick event to enable code events processing.
161 EnqueueTickSampleEvent(processor.get(), aaa_code->address());
162
163 processor->StopSynchronously();
164
165 // Check the state of profile generator.
166 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());
183 }
184
185
186 template<typename T>
CompareProfileNodes(const T * p1,const T * p2)187 static int CompareProfileNodes(const T* p1, const T* p2) {
188 return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
189 }
190
191
TEST(TickEvents)192 TEST(TickEvents) {
193 TestSetup test_setup;
194 LocalContext env;
195 i::Isolate* isolate = CcTest::i_isolate();
196 i::HandleScope scope(isolate);
197
198 i::Code* frame1_code = CreateCode(&env);
199 i::Code* frame2_code = CreateCode(&env);
200 i::Code* frame3_code = CreateCode(&env);
201
202 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
203 profiles->StartProfiling("", false);
204 ProfileGenerator generator(profiles);
205 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
206 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
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("");
227 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());
245 }
246
247
248 // http://crbug/51594
249 // This test must not crash.
TEST(CrashIfStoppingLastNonExistentProfile)250 TEST(CrashIfStoppingLastNonExistentProfile) {
251 CcTest::InitializeVM();
252 TestSetup test_setup;
253 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
254 profiler->StartProfiling("1");
255 profiler->StopProfiling("2");
256 profiler->StartProfiling("1");
257 profiler->StopProfiling("");
258 }
259
260
261 // http://code.google.com/p/v8/issues/detail?id=1398
262 // Long stacks (exceeding max frames limit) must not be erased.
TEST(Issue1398)263 TEST(Issue1398) {
264 TestSetup test_setup;
265 LocalContext env;
266 i::Isolate* isolate = CcTest::i_isolate();
267 i::HandleScope scope(isolate);
268
269 i::Code* code = CreateCode(&env);
270
271 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
272 profiles->StartProfiling("", false);
273 ProfileGenerator generator(profiles);
274 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
275 &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100)));
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();
283 sample->tos = 0;
284 sample->frames_count = i::TickSample::kMaxFramesCount;
285 for (unsigned i = 0; i < sample->frames_count; ++i) {
286 sample->stack[i] = code->address();
287 }
288 processor->FinishTickSample();
289
290 processor->StopSynchronously();
291 CpuProfile* profile = profiles->StopProfiling("");
292 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
TEST(DeleteAllCpuProfiles)305 TEST(DeleteAllCpuProfiles) {
306 CcTest::InitializeVM();
307 TestSetup test_setup;
308 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
309 CHECK_EQ(0, profiler->GetProfilesCount());
310 profiler->DeleteAllProfiles();
311 CHECK_EQ(0, profiler->GetProfilesCount());
312
313 profiler->StartProfiling("1");
314 profiler->StopProfiling("1");
315 CHECK_EQ(1, profiler->GetProfilesCount());
316 profiler->DeleteAllProfiles();
317 CHECK_EQ(0, profiler->GetProfilesCount());
318 profiler->StartProfiling("1");
319 profiler->StartProfiling("2");
320 profiler->StopProfiling("2");
321 profiler->StopProfiling("1");
322 CHECK_EQ(2, profiler->GetProfilesCount());
323 profiler->DeleteAllProfiles();
324 CHECK_EQ(0, profiler->GetProfilesCount());
325
326 // Test profiling cancellation by the 'delete' command.
327 profiler->StartProfiling("1");
328 profiler->StartProfiling("2");
329 CHECK_EQ(0, profiler->GetProfilesCount());
330 profiler->DeleteAllProfiles();
331 CHECK_EQ(0, profiler->GetProfilesCount());
332 }
333
334
FindCpuProfile(v8::CpuProfiler * v8profiler,const v8::CpuProfile * v8profile)335 static bool FindCpuProfile(v8::CpuProfiler* v8profiler,
336 const v8::CpuProfile* v8profile) {
337 i::CpuProfiler* profiler = reinterpret_cast<i::CpuProfiler*>(v8profiler);
338 const i::CpuProfile* profile =
339 reinterpret_cast<const i::CpuProfile*>(v8profile);
340 int length = profiler->GetProfilesCount();
341 for (int i = 0; i < length; i++) {
342 if (profile == profiler->GetProfile(i))
343 return true;
344 }
345 return false;
346 }
347
348
TEST(DeleteCpuProfile)349 TEST(DeleteCpuProfile) {
350 LocalContext env;
351 v8::HandleScope scope(env->GetIsolate());
352 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
353 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(cpu_profiler);
354
355 CHECK_EQ(0, iprofiler->GetProfilesCount());
356 v8::Local<v8::String> name1 = v8::String::NewFromUtf8(env->GetIsolate(), "1");
357 cpu_profiler->StartProfiling(name1);
358 v8::CpuProfile* p1 = cpu_profiler->StopProfiling(name1);
359 CHECK_NE(NULL, p1);
360 CHECK_EQ(1, iprofiler->GetProfilesCount());
361 CHECK(FindCpuProfile(cpu_profiler, p1));
362 p1->Delete();
363 CHECK_EQ(0, iprofiler->GetProfilesCount());
364
365 v8::Local<v8::String> name2 = v8::String::NewFromUtf8(env->GetIsolate(), "2");
366 cpu_profiler->StartProfiling(name2);
367 v8::CpuProfile* p2 = cpu_profiler->StopProfiling(name2);
368 CHECK_NE(NULL, p2);
369 CHECK_EQ(1, iprofiler->GetProfilesCount());
370 CHECK(FindCpuProfile(cpu_profiler, p2));
371 v8::Local<v8::String> name3 = v8::String::NewFromUtf8(env->GetIsolate(), "3");
372 cpu_profiler->StartProfiling(name3);
373 v8::CpuProfile* p3 = cpu_profiler->StopProfiling(name3);
374 CHECK_NE(NULL, p3);
375 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());
385 }
386
387
TEST(ProfileStartEndTime)388 TEST(ProfileStartEndTime) {
389 LocalContext env;
390 v8::HandleScope scope(env->GetIsolate());
391 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
392
393 v8::Local<v8::String> profile_name =
394 v8::String::NewFromUtf8(env->GetIsolate(), "test");
395 cpu_profiler->StartProfiling(profile_name);
396 const v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
397 CHECK(profile->GetStartTime() <= profile->GetEndTime());
398 }
399
400
RunProfiler(v8::Handle<v8::Context> env,v8::Handle<v8::Function> function,v8::Handle<v8::Value> argv[],int argc,unsigned min_js_samples,bool collect_samples=false)401 static v8::CpuProfile* RunProfiler(
402 v8::Handle<v8::Context> env, v8::Handle<v8::Function> function,
403 v8::Handle<v8::Value> argv[], int argc,
404 unsigned min_js_samples, bool collect_samples = false) {
405 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
406 v8::Local<v8::String> profile_name =
407 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
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
ContainsString(v8::Handle<v8::String> string,const Vector<v8::Handle<v8::String>> & vector)428 static bool ContainsString(v8::Handle<v8::String> string,
429 const Vector<v8::Handle<v8::String> >& vector) {
430 for (int i = 0; i < vector.length(); i++) {
431 if (string->Equals(vector[i]))
432 return true;
433 }
434 return false;
435 }
436
437
CheckChildrenNames(const v8::CpuProfileNode * node,const Vector<v8::Handle<v8::String>> & names)438 static void CheckChildrenNames(const v8::CpuProfileNode* node,
439 const Vector<v8::Handle<v8::String> >& names) {
440 int count = node->GetChildrenCount();
441 for (int i = 0; i < count; i++) {
442 v8::Handle<v8::String> name = node->GetChild(i)->GetFunctionName();
443 CHECK(ContainsString(name, names));
444 // Check that there are no duplicates.
445 for (int j = 0; j < count; j++) {
446 if (j == i) continue;
447 CHECK_NE(name, node->GetChild(j)->GetFunctionName());
448 }
449 }
450 }
451
452
FindChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)453 static 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
GetChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)466 static 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
CheckSimpleBranch(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * names[],int length)480 static void CheckSimpleBranch(v8::Isolate* isolate,
481 const v8::CpuProfileNode* node,
482 const char* names[], int length) {
483 for (int i = 0; i < length; i++) {
484 const char* name = names[i];
485 node = GetChild(isolate, node, name);
486 int expectedChildrenCount = (i == length - 1) ? 0 : 1;
487 CHECK_EQ(expectedChildrenCount, node->GetChildrenCount());
488 }
489 }
490
491
492 static 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]
TEST(CollectCpuProfile)541 TEST(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
590 static 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).
TEST(HotDeoptNoFrameEntry)618 TEST(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
TEST(CollectCpuProfileSamples)656 TEST(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
688 static 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
TEST(SampleWhenFrameIsNotSetup)708 TEST(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
756 static const char* native_accessor_test_source = "function start(count) {\n"
757 " for (var i = 0; i < count; i++) {\n"
758 " var o = instance.foo;\n"
759 " instance.foo = o + 1;\n"
760 " }\n"
761 "}\n";
762
763
764 class TestApiCallbacks {
765 public:
TestApiCallbacks(int min_duration_ms)766 explicit TestApiCallbacks(int min_duration_ms)
767 : min_duration_ms_(min_duration_ms),
768 is_warming_up_(false) {}
769
Getter(v8::Local<v8::String> name,const v8::PropertyCallbackInfo<v8::Value> & info)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
Setter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)776 static void Setter(v8::Local<v8::String> name,
777 v8::Local<v8::Value> value,
778 const v8::PropertyCallbackInfo<void>& info) {
779 TestApiCallbacks* data = fromInfo(info);
780 data->Wait();
781 }
782
Callback(const v8::FunctionCallbackInfo<v8::Value> & info)783 static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
784 TestApiCallbacks* data = fromInfo(info);
785 data->Wait();
786 }
787
set_warming_up(bool value)788 void set_warming_up(bool value) { is_warming_up_ = value; }
789
790 private:
Wait()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>
fromInfo(const T & info)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.
TEST(NativeAccessorUninitializedIC)816 TEST(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.
TEST(NativeAccessorMonomorphicIC)861 TEST(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
915 static const char* native_method_test_source = "function start(count) {\n"
916 " for (var i = 0; i < count; i++) {\n"
917 " instance.fooMethod();\n"
918 " }\n"
919 "}\n";
920
921
TEST(NativeMethodUninitializedIC)922 TEST(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
TEST(NativeMethodMonomorphicIC)968 TEST(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
1026 static 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
TEST(BoundFunctionCall)1036 TEST(BoundFunctionCall) {
1037 v8::HandleScope scope(CcTest::isolate());
1038 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1039 v8::Context::Scope context_scope(env);
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
1067 static 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
TEST(FunctionCallSample)1091 TEST(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
1150 static 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
TEST(FunctionApplySample)1175 TEST(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
1242 static 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
TEST(CpuProfileDeepStack)1265 TEST(CpuProfileDeepStack) {
1266 v8::HandleScope scope(CcTest::isolate());
1267 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1268 v8::Context::Scope context_scope(env);
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
1308 static 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
CallJsFunction(const v8::FunctionCallbackInfo<v8::Value> & info)1321 static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1322 v8::Handle<v8::Function> function = info[0].As<v8::Function>();
1323 v8::Handle<v8::Value> argv[] = { info[1] };
1324 function->Call(info.This(), 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
TEST(JsNativeJsSample)1335 TEST(JsNativeJsSample) {
1336 v8::HandleScope scope(CcTest::isolate());
1337 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1338 v8::Context::Scope context_scope(env);
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
1382 static 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
TEST(JsNativeJsRuntimeJsSample)1404 TEST(JsNativeJsRuntimeJsSample) {
1405 v8::HandleScope scope(CcTest::isolate());
1406 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1407 v8::Context::Scope context_scope(env);
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
CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value> & info)1454 static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1455 v8::base::OS::Print("In CallJsFunction2\n");
1456 CallJsFunction(info);
1457 }
1458
1459
1460 static 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
TEST(JsNative1JsNative2JsSample)1484 TEST(JsNative1JsNative2JsSample) {
1485 v8::HandleScope scope(CcTest::isolate());
1486 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1487 v8::Context::Scope context_scope(env);
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
TEST(IdleTime)1544 TEST(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
CheckFunctionDetails(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name,const char * script_name,int script_id,int line,int column)1596 static void CheckFunctionDetails(v8::Isolate* isolate,
1597 const v8::CpuProfileNode* node,
1598 const char* name, const char* script_name,
1599 int script_id, int line, int column) {
1600 CHECK_EQ(v8::String::NewFromUtf8(isolate, name),
1601 node->GetFunctionName());
1602 CHECK_EQ(v8::String::NewFromUtf8(isolate, script_name),
1603 node->GetScriptResourceName());
1604 CHECK_EQ(script_id, node->GetScriptId());
1605 CHECK_EQ(line, node->GetLineNumber());
1606 CHECK_EQ(column, node->GetColumnNumber());
1607 }
1608
1609
TEST(FunctionDetails)1610 TEST(FunctionDetails) {
1611 v8::HandleScope scope(CcTest::isolate());
1612 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1613 v8::Context::Scope context_scope(env);
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
TEST(DontStopOnFinishedProfileDelete)1652 TEST(DontStopOnFinishedProfileDelete) {
1653 v8::HandleScope scope(CcTest::isolate());
1654 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1655 v8::Context::Scope context_scope(env);
1656 v8::Isolate* isolate = env->GetIsolate();
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());
1683 }
1684