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/cpu-profiler-inl.h"
34 #include "src/platform.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::TimeDelta;
50 using i::Vector;
51
52
TEST(StartStop)53 TEST(StartStop) {
54 i::Isolate* isolate = CcTest::i_isolate();
55 CpuProfilesCollection profiles(isolate->heap());
56 ProfileGenerator generator(&profiles);
57 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
58 &generator, NULL, TimeDelta::FromMicroseconds(100)));
59 processor->Start();
60 processor->StopSynchronously();
61 }
62
63
EnqueueTickSampleEvent(ProfilerEventsProcessor * proc,i::Address frame1,i::Address frame2=NULL,i::Address frame3=NULL)64 static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
65 i::Address frame1,
66 i::Address frame2 = NULL,
67 i::Address frame3 = NULL) {
68 i::TickSample* sample = proc->StartTickSample();
69 sample->pc = frame1;
70 sample->tos = frame1;
71 sample->frames_count = 0;
72 if (frame2 != NULL) {
73 sample->stack[0] = frame2;
74 sample->frames_count = 1;
75 }
76 if (frame3 != NULL) {
77 sample->stack[1] = frame3;
78 sample->frames_count = 2;
79 }
80 proc->FinishTickSample();
81 }
82
83 namespace {
84
85 class TestSetup {
86 public:
TestSetup()87 TestSetup()
88 : old_flag_prof_browser_mode_(i::FLAG_prof_browser_mode) {
89 i::FLAG_prof_browser_mode = false;
90 }
91
~TestSetup()92 ~TestSetup() {
93 i::FLAG_prof_browser_mode = old_flag_prof_browser_mode_;
94 }
95
96 private:
97 bool old_flag_prof_browser_mode_;
98 };
99
100 } // namespace
101
102
CreateCode(LocalContext * env)103 i::Code* CreateCode(LocalContext* env) {
104 static int counter = 0;
105 i::EmbeddedVector<char, 256> script;
106 i::EmbeddedVector<char, 32> name;
107
108 i::SNPrintF(name, "function_%d", ++counter);
109 const char* name_start = name.start();
110 i::SNPrintF(script,
111 "function %s() {\n"
112 "var counter = 0;\n"
113 "for (var i = 0; i < %d; ++i) counter += i;\n"
114 "return '%s_' + counter;\n"
115 "}\n"
116 "%s();\n", name_start, counter, name_start, name_start);
117 CompileRun(script.start());
118 i::Handle<i::JSFunction> fun = v8::Utils::OpenHandle(
119 *v8::Local<v8::Function>::Cast(
120 (*env)->Global()->Get(v8_str(name_start))));
121 return fun->code();
122 }
123
124
TEST(CodeEvents)125 TEST(CodeEvents) {
126 CcTest::InitializeVM();
127 LocalContext env;
128 i::Isolate* isolate = CcTest::i_isolate();
129 i::Factory* factory = isolate->factory();
130 TestSetup test_setup;
131
132 i::HandleScope scope(isolate);
133
134 i::Code* aaa_code = CreateCode(&env);
135 i::Code* comment_code = CreateCode(&env);
136 i::Code* args5_code = CreateCode(&env);
137 i::Code* comment2_code = CreateCode(&env);
138 i::Code* moved_code = CreateCode(&env);
139 i::Code* args3_code = CreateCode(&env);
140 i::Code* args4_code = CreateCode(&env);
141
142 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
143 profiles->StartProfiling("", false);
144 ProfileGenerator generator(profiles);
145 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
146 &generator, NULL, TimeDelta::FromMicroseconds(100)));
147 processor->Start();
148 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
149
150 // Enqueue code creation events.
151 const char* aaa_str = "aaa";
152 i::Handle<i::String> aaa_name = factory->NewStringFromAsciiChecked(aaa_str);
153 profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, aaa_code, *aaa_name);
154 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment_code, "comment");
155 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args5_code, 5);
156 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, comment2_code, "comment2");
157 profiler.CodeMoveEvent(comment2_code->address(), moved_code->address());
158 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args3_code, 3);
159 profiler.CodeCreateEvent(i::Logger::STUB_TAG, args4_code, 4);
160
161 // Enqueue a tick event to enable code events processing.
162 EnqueueTickSampleEvent(processor.get(), aaa_code->address());
163
164 processor->StopSynchronously();
165
166 // Check the state of profile generator.
167 CodeEntry* aaa = generator.code_map()->FindEntry(aaa_code->address());
168 CHECK_NE(NULL, aaa);
169 CHECK_EQ(aaa_str, aaa->name());
170
171 CodeEntry* comment = generator.code_map()->FindEntry(comment_code->address());
172 CHECK_NE(NULL, comment);
173 CHECK_EQ("comment", comment->name());
174
175 CodeEntry* args5 = generator.code_map()->FindEntry(args5_code->address());
176 CHECK_NE(NULL, args5);
177 CHECK_EQ("5", args5->name());
178
179 CHECK_EQ(NULL, generator.code_map()->FindEntry(comment2_code->address()));
180
181 CodeEntry* comment2 = generator.code_map()->FindEntry(moved_code->address());
182 CHECK_NE(NULL, comment2);
183 CHECK_EQ("comment2", comment2->name());
184 }
185
186
187 template<typename T>
CompareProfileNodes(const T * p1,const T * p2)188 static int CompareProfileNodes(const T* p1, const T* p2) {
189 return strcmp((*p1)->entry()->name(), (*p2)->entry()->name());
190 }
191
192
TEST(TickEvents)193 TEST(TickEvents) {
194 TestSetup test_setup;
195 LocalContext env;
196 i::Isolate* isolate = CcTest::i_isolate();
197 i::HandleScope scope(isolate);
198
199 i::Code* frame1_code = CreateCode(&env);
200 i::Code* frame2_code = CreateCode(&env);
201 i::Code* frame3_code = CreateCode(&env);
202
203 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
204 profiles->StartProfiling("", false);
205 ProfileGenerator generator(profiles);
206 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
207 &generator, NULL, TimeDelta::FromMicroseconds(100)));
208 processor->Start();
209 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
210
211 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame1_code, "bbb");
212 profiler.CodeCreateEvent(i::Logger::STUB_TAG, frame2_code, 5);
213 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, frame3_code, "ddd");
214
215 EnqueueTickSampleEvent(processor.get(), frame1_code->instruction_start());
216 EnqueueTickSampleEvent(
217 processor.get(),
218 frame2_code->instruction_start() + frame2_code->ExecutableSize() / 2,
219 frame1_code->instruction_start() + frame2_code->ExecutableSize() / 2);
220 EnqueueTickSampleEvent(
221 processor.get(),
222 frame3_code->instruction_end() - 1,
223 frame2_code->instruction_end() - 1,
224 frame1_code->instruction_end() - 1);
225
226 processor->StopSynchronously();
227 CpuProfile* profile = profiles->StopProfiling("");
228 CHECK_NE(NULL, profile);
229
230 // Check call trees.
231 const i::List<ProfileNode*>* top_down_root_children =
232 profile->top_down()->root()->children();
233 CHECK_EQ(1, top_down_root_children->length());
234 CHECK_EQ("bbb", top_down_root_children->last()->entry()->name());
235 const i::List<ProfileNode*>* top_down_bbb_children =
236 top_down_root_children->last()->children();
237 CHECK_EQ(1, top_down_bbb_children->length());
238 CHECK_EQ("5", top_down_bbb_children->last()->entry()->name());
239 const i::List<ProfileNode*>* top_down_stub_children =
240 top_down_bbb_children->last()->children();
241 CHECK_EQ(1, top_down_stub_children->length());
242 CHECK_EQ("ddd", top_down_stub_children->last()->entry()->name());
243 const i::List<ProfileNode*>* top_down_ddd_children =
244 top_down_stub_children->last()->children();
245 CHECK_EQ(0, top_down_ddd_children->length());
246 }
247
248
249 // http://crbug/51594
250 // This test must not crash.
TEST(CrashIfStoppingLastNonExistentProfile)251 TEST(CrashIfStoppingLastNonExistentProfile) {
252 CcTest::InitializeVM();
253 TestSetup test_setup;
254 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
255 profiler->StartProfiling("1");
256 profiler->StopProfiling("2");
257 profiler->StartProfiling("1");
258 profiler->StopProfiling("");
259 }
260
261
262 // http://code.google.com/p/v8/issues/detail?id=1398
263 // Long stacks (exceeding max frames limit) must not be erased.
TEST(Issue1398)264 TEST(Issue1398) {
265 TestSetup test_setup;
266 LocalContext env;
267 i::Isolate* isolate = CcTest::i_isolate();
268 i::HandleScope scope(isolate);
269
270 i::Code* code = CreateCode(&env);
271
272 CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
273 profiles->StartProfiling("", false);
274 ProfileGenerator generator(profiles);
275 SmartPointer<ProfilerEventsProcessor> processor(new ProfilerEventsProcessor(
276 &generator, NULL, TimeDelta::FromMicroseconds(100)));
277 processor->Start();
278 CpuProfiler profiler(isolate, profiles, &generator, processor.get());
279
280 profiler.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
281
282 i::TickSample* sample = processor->StartTickSample();
283 sample->pc = code->address();
284 sample->tos = 0;
285 sample->frames_count = i::TickSample::kMaxFramesCount;
286 for (int i = 0; i < sample->frames_count; ++i) {
287 sample->stack[i] = code->address();
288 }
289 processor->FinishTickSample();
290
291 processor->StopSynchronously();
292 CpuProfile* profile = profiles->StopProfiling("");
293 CHECK_NE(NULL, profile);
294
295 int actual_depth = 0;
296 const ProfileNode* node = profile->top_down()->root();
297 while (node->children()->length() > 0) {
298 node = node->children()->last();
299 ++actual_depth;
300 }
301
302 CHECK_EQ(1 + i::TickSample::kMaxFramesCount, actual_depth); // +1 for PC.
303 }
304
305
TEST(DeleteAllCpuProfiles)306 TEST(DeleteAllCpuProfiles) {
307 CcTest::InitializeVM();
308 TestSetup test_setup;
309 CpuProfiler* profiler = CcTest::i_isolate()->cpu_profiler();
310 CHECK_EQ(0, profiler->GetProfilesCount());
311 profiler->DeleteAllProfiles();
312 CHECK_EQ(0, profiler->GetProfilesCount());
313
314 profiler->StartProfiling("1");
315 profiler->StopProfiling("1");
316 CHECK_EQ(1, profiler->GetProfilesCount());
317 profiler->DeleteAllProfiles();
318 CHECK_EQ(0, profiler->GetProfilesCount());
319 profiler->StartProfiling("1");
320 profiler->StartProfiling("2");
321 profiler->StopProfiling("2");
322 profiler->StopProfiling("1");
323 CHECK_EQ(2, profiler->GetProfilesCount());
324 profiler->DeleteAllProfiles();
325 CHECK_EQ(0, profiler->GetProfilesCount());
326
327 // Test profiling cancellation by the 'delete' command.
328 profiler->StartProfiling("1");
329 profiler->StartProfiling("2");
330 CHECK_EQ(0, profiler->GetProfilesCount());
331 profiler->DeleteAllProfiles();
332 CHECK_EQ(0, profiler->GetProfilesCount());
333 }
334
335
FindCpuProfile(v8::CpuProfiler * v8profiler,const v8::CpuProfile * v8profile)336 static bool FindCpuProfile(v8::CpuProfiler* v8profiler,
337 const v8::CpuProfile* v8profile) {
338 i::CpuProfiler* profiler = reinterpret_cast<i::CpuProfiler*>(v8profiler);
339 const i::CpuProfile* profile =
340 reinterpret_cast<const i::CpuProfile*>(v8profile);
341 int length = profiler->GetProfilesCount();
342 for (int i = 0; i < length; i++) {
343 if (profile == profiler->GetProfile(i))
344 return true;
345 }
346 return false;
347 }
348
349
TEST(DeleteCpuProfile)350 TEST(DeleteCpuProfile) {
351 LocalContext env;
352 v8::HandleScope scope(env->GetIsolate());
353 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
354 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(cpu_profiler);
355
356 CHECK_EQ(0, iprofiler->GetProfilesCount());
357 v8::Local<v8::String> name1 = v8::String::NewFromUtf8(env->GetIsolate(), "1");
358 cpu_profiler->StartProfiling(name1);
359 v8::CpuProfile* p1 = cpu_profiler->StopProfiling(name1);
360 CHECK_NE(NULL, p1);
361 CHECK_EQ(1, iprofiler->GetProfilesCount());
362 CHECK(FindCpuProfile(cpu_profiler, p1));
363 p1->Delete();
364 CHECK_EQ(0, iprofiler->GetProfilesCount());
365
366 v8::Local<v8::String> name2 = v8::String::NewFromUtf8(env->GetIsolate(), "2");
367 cpu_profiler->StartProfiling(name2);
368 v8::CpuProfile* p2 = cpu_profiler->StopProfiling(name2);
369 CHECK_NE(NULL, p2);
370 CHECK_EQ(1, iprofiler->GetProfilesCount());
371 CHECK(FindCpuProfile(cpu_profiler, p2));
372 v8::Local<v8::String> name3 = v8::String::NewFromUtf8(env->GetIsolate(), "3");
373 cpu_profiler->StartProfiling(name3);
374 v8::CpuProfile* p3 = cpu_profiler->StopProfiling(name3);
375 CHECK_NE(NULL, p3);
376 CHECK_EQ(2, iprofiler->GetProfilesCount());
377 CHECK_NE(p2, p3);
378 CHECK(FindCpuProfile(cpu_profiler, p3));
379 CHECK(FindCpuProfile(cpu_profiler, p2));
380 p2->Delete();
381 CHECK_EQ(1, iprofiler->GetProfilesCount());
382 CHECK(!FindCpuProfile(cpu_profiler, p2));
383 CHECK(FindCpuProfile(cpu_profiler, p3));
384 p3->Delete();
385 CHECK_EQ(0, iprofiler->GetProfilesCount());
386 }
387
388
TEST(ProfileStartEndTime)389 TEST(ProfileStartEndTime) {
390 LocalContext env;
391 v8::HandleScope scope(env->GetIsolate());
392 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
393
394 v8::Local<v8::String> profile_name =
395 v8::String::NewFromUtf8(env->GetIsolate(), "test");
396 cpu_profiler->StartProfiling(profile_name);
397 const v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
398 CHECK(profile->GetStartTime() <= profile->GetEndTime());
399 }
400
401
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)402 static v8::CpuProfile* RunProfiler(
403 v8::Handle<v8::Context> env, v8::Handle<v8::Function> function,
404 v8::Handle<v8::Value> argv[], int argc,
405 unsigned min_js_samples, bool collect_samples = false) {
406 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
407 v8::Local<v8::String> profile_name =
408 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
409
410 cpu_profiler->StartProfiling(profile_name, collect_samples);
411
412 i::Sampler* sampler =
413 reinterpret_cast<i::Isolate*>(env->GetIsolate())->logger()->sampler();
414 sampler->StartCountingSamples();
415 do {
416 function->Call(env->Global(), argc, argv);
417 } while (sampler->js_and_external_sample_count() < min_js_samples);
418
419 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
420
421 CHECK_NE(NULL, profile);
422 // Dump collected profile to have a better diagnostic in case of failure.
423 reinterpret_cast<i::CpuProfile*>(profile)->Print();
424
425 return profile;
426 }
427
428
ContainsString(v8::Handle<v8::String> string,const Vector<v8::Handle<v8::String>> & vector)429 static bool ContainsString(v8::Handle<v8::String> string,
430 const Vector<v8::Handle<v8::String> >& vector) {
431 for (int i = 0; i < vector.length(); i++) {
432 if (string->Equals(vector[i]))
433 return true;
434 }
435 return false;
436 }
437
438
CheckChildrenNames(const v8::CpuProfileNode * node,const Vector<v8::Handle<v8::String>> & names)439 static void CheckChildrenNames(const v8::CpuProfileNode* node,
440 const Vector<v8::Handle<v8::String> >& names) {
441 int count = node->GetChildrenCount();
442 for (int i = 0; i < count; i++) {
443 v8::Handle<v8::String> name = node->GetChild(i)->GetFunctionName();
444 CHECK(ContainsString(name, names));
445 // Check that there are no duplicates.
446 for (int j = 0; j < count; j++) {
447 if (j == i) continue;
448 CHECK_NE(name, node->GetChild(j)->GetFunctionName());
449 }
450 }
451 }
452
453
FindChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)454 static const v8::CpuProfileNode* FindChild(v8::Isolate* isolate,
455 const v8::CpuProfileNode* node,
456 const char* name) {
457 int count = node->GetChildrenCount();
458 v8::Handle<v8::String> nameHandle = v8::String::NewFromUtf8(isolate, name);
459 for (int i = 0; i < count; i++) {
460 const v8::CpuProfileNode* child = node->GetChild(i);
461 if (nameHandle->Equals(child->GetFunctionName())) return child;
462 }
463 return NULL;
464 }
465
466
GetChild(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name)467 static const v8::CpuProfileNode* GetChild(v8::Isolate* isolate,
468 const v8::CpuProfileNode* node,
469 const char* name) {
470 const v8::CpuProfileNode* result = FindChild(isolate, node, name);
471 if (!result) {
472 char buffer[100];
473 i::SNPrintF(Vector<char>(buffer, ARRAY_SIZE(buffer)),
474 "Failed to GetChild: %s", name);
475 FATAL(buffer);
476 }
477 return result;
478 }
479
480
CheckSimpleBranch(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * names[],int length)481 static void CheckSimpleBranch(v8::Isolate* isolate,
482 const v8::CpuProfileNode* node,
483 const char* names[], int length) {
484 for (int i = 0; i < length; i++) {
485 const char* name = names[i];
486 node = GetChild(isolate, node, name);
487 int expectedChildrenCount = (i == length - 1) ? 0 : 1;
488 CHECK_EQ(expectedChildrenCount, node->GetChildrenCount());
489 }
490 }
491
492
493 static const char* cpu_profiler_test_source = "function loop(timeout) {\n"
494 " this.mmm = 0;\n"
495 " var start = Date.now();\n"
496 " while (Date.now() - start < timeout) {\n"
497 " var n = 100*1000;\n"
498 " while(n > 1) {\n"
499 " n--;\n"
500 " this.mmm += n * n * n;\n"
501 " }\n"
502 " }\n"
503 "}\n"
504 "function delay() { try { loop(10); } catch(e) { } }\n"
505 "function bar() { delay(); }\n"
506 "function baz() { delay(); }\n"
507 "function foo() {\n"
508 " try {\n"
509 " delay();\n"
510 " bar();\n"
511 " delay();\n"
512 " baz();\n"
513 " } catch (e) { }\n"
514 "}\n"
515 "function start(timeout) {\n"
516 " var start = Date.now();\n"
517 " do {\n"
518 " foo();\n"
519 " var duration = Date.now() - start;\n"
520 " } while (duration < timeout);\n"
521 " return duration;\n"
522 "}\n";
523
524
525 // Check that the profile tree for the script above will look like the
526 // following:
527 //
528 // [Top down]:
529 // 1062 0 (root) [-1]
530 // 1054 0 start [-1]
531 // 1054 1 foo [-1]
532 // 265 0 baz [-1]
533 // 265 1 delay [-1]
534 // 264 264 loop [-1]
535 // 525 3 delay [-1]
536 // 522 522 loop [-1]
537 // 263 0 bar [-1]
538 // 263 1 delay [-1]
539 // 262 262 loop [-1]
540 // 2 2 (program) [-1]
541 // 6 6 (garbage collector) [-1]
TEST(CollectCpuProfile)542 TEST(CollectCpuProfile) {
543 LocalContext env;
544 v8::HandleScope scope(env->GetIsolate());
545
546 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
547 cpu_profiler_test_source))->Run();
548 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
549 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
550
551 int32_t profiling_interval_ms = 200;
552 v8::Handle<v8::Value> args[] = {
553 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
554 };
555 v8::CpuProfile* profile =
556 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
557 function->Call(env->Global(), ARRAY_SIZE(args), args);
558
559 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
560
561 ScopedVector<v8::Handle<v8::String> > names(3);
562 names[0] = v8::String::NewFromUtf8(
563 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
564 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
565 ProfileGenerator::kProgramEntryName);
566 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
567 CheckChildrenNames(root, names);
568
569 const v8::CpuProfileNode* startNode =
570 GetChild(env->GetIsolate(), root, "start");
571 CHECK_EQ(1, startNode->GetChildrenCount());
572
573 const v8::CpuProfileNode* fooNode =
574 GetChild(env->GetIsolate(), startNode, "foo");
575 CHECK_EQ(3, fooNode->GetChildrenCount());
576
577 const char* barBranch[] = { "bar", "delay", "loop" };
578 CheckSimpleBranch(env->GetIsolate(), fooNode, barBranch,
579 ARRAY_SIZE(barBranch));
580 const char* bazBranch[] = { "baz", "delay", "loop" };
581 CheckSimpleBranch(env->GetIsolate(), fooNode, bazBranch,
582 ARRAY_SIZE(bazBranch));
583 const char* delayBranch[] = { "delay", "loop" };
584 CheckSimpleBranch(env->GetIsolate(), fooNode, delayBranch,
585 ARRAY_SIZE(delayBranch));
586
587 profile->Delete();
588 }
589
590
591 static const char* hot_deopt_no_frame_entry_test_source =
592 "function foo(a, b) {\n"
593 " try {\n"
594 " return a + b;\n"
595 " } catch (e) { }\n"
596 "}\n"
597 "function start(timeout) {\n"
598 " var start = Date.now();\n"
599 " do {\n"
600 " for (var i = 1; i < 1000; ++i) foo(1, i);\n"
601 " var duration = Date.now() - start;\n"
602 " } while (duration < timeout);\n"
603 " return duration;\n"
604 "}\n";
605
606 // Check that the profile tree for the script above will look like the
607 // following:
608 //
609 // [Top down]:
610 // 1062 0 (root) [-1]
611 // 1054 0 start [-1]
612 // 1054 1 foo [-1]
613 // 2 2 (program) [-1]
614 // 6 6 (garbage collector) [-1]
615 //
616 // The test checks no FP ranges are present in a deoptimized funcion.
617 // If 'foo' has no ranges the samples falling into the prologue will miss the
618 // 'start' function on the stack, so 'foo' will be attached to the (root).
TEST(HotDeoptNoFrameEntry)619 TEST(HotDeoptNoFrameEntry) {
620 LocalContext env;
621 v8::HandleScope scope(env->GetIsolate());
622
623 v8::Script::Compile(v8::String::NewFromUtf8(
624 env->GetIsolate(),
625 hot_deopt_no_frame_entry_test_source))->Run();
626 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
627 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
628
629 int32_t profiling_interval_ms = 200;
630 v8::Handle<v8::Value> args[] = {
631 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
632 };
633 v8::CpuProfile* profile =
634 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
635 function->Call(env->Global(), ARRAY_SIZE(args), args);
636
637 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
638
639 ScopedVector<v8::Handle<v8::String> > names(3);
640 names[0] = v8::String::NewFromUtf8(
641 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
642 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
643 ProfileGenerator::kProgramEntryName);
644 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
645 CheckChildrenNames(root, names);
646
647 const v8::CpuProfileNode* startNode =
648 GetChild(env->GetIsolate(), root, "start");
649 CHECK_EQ(1, startNode->GetChildrenCount());
650
651 GetChild(env->GetIsolate(), startNode, "foo");
652
653 profile->Delete();
654 }
655
656
TEST(CollectCpuProfileSamples)657 TEST(CollectCpuProfileSamples) {
658 LocalContext env;
659 v8::HandleScope scope(env->GetIsolate());
660
661 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
662 cpu_profiler_test_source))->Run();
663 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
664 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
665
666 int32_t profiling_interval_ms = 200;
667 v8::Handle<v8::Value> args[] = {
668 v8::Integer::New(env->GetIsolate(), profiling_interval_ms)
669 };
670 v8::CpuProfile* profile =
671 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200, true);
672
673 CHECK_LE(200, profile->GetSamplesCount());
674 uint64_t end_time = profile->GetEndTime();
675 uint64_t current_time = profile->GetStartTime();
676 CHECK_LE(current_time, end_time);
677 for (int i = 0; i < profile->GetSamplesCount(); i++) {
678 CHECK_NE(NULL, profile->GetSample(i));
679 uint64_t timestamp = profile->GetSampleTimestamp(i);
680 CHECK_LE(current_time, timestamp);
681 CHECK_LE(timestamp, end_time);
682 current_time = timestamp;
683 }
684
685 profile->Delete();
686 }
687
688
689 static const char* cpu_profiler_test_source2 = "function loop() {}\n"
690 "function delay() { loop(); }\n"
691 "function start(count) {\n"
692 " var k = 0;\n"
693 " do {\n"
694 " delay();\n"
695 " } while (++k < count*100*1000);\n"
696 "}\n";
697
698 // Check that the profile tree doesn't contain unexpected traces:
699 // - 'loop' can be called only by 'delay'
700 // - 'delay' may be called only by 'start'
701 // The profile will look like the following:
702 //
703 // [Top down]:
704 // 135 0 (root) [-1] #1
705 // 121 72 start [-1] #3
706 // 49 33 delay [-1] #4
707 // 16 16 loop [-1] #5
708 // 14 14 (program) [-1] #2
TEST(SampleWhenFrameIsNotSetup)709 TEST(SampleWhenFrameIsNotSetup) {
710 LocalContext env;
711 v8::HandleScope scope(env->GetIsolate());
712
713 v8::Script::Compile(v8::String::NewFromUtf8(
714 env->GetIsolate(), cpu_profiler_test_source2))->Run();
715 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
716 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
717
718 int32_t repeat_count = 100;
719 #if defined(USE_SIMULATOR)
720 // Simulators are much slower.
721 repeat_count = 1;
722 #endif
723 v8::Handle<v8::Value> args[] = {
724 v8::Integer::New(env->GetIsolate(), repeat_count)
725 };
726 v8::CpuProfile* profile =
727 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
728
729 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
730
731 ScopedVector<v8::Handle<v8::String> > names(3);
732 names[0] = v8::String::NewFromUtf8(
733 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
734 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
735 ProfileGenerator::kProgramEntryName);
736 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
737 CheckChildrenNames(root, names);
738
739 const v8::CpuProfileNode* startNode =
740 FindChild(env->GetIsolate(), root, "start");
741 // On slow machines there may be no meaningfull samples at all, skip the
742 // check there.
743 if (startNode && startNode->GetChildrenCount() > 0) {
744 CHECK_EQ(1, startNode->GetChildrenCount());
745 const v8::CpuProfileNode* delayNode =
746 GetChild(env->GetIsolate(), startNode, "delay");
747 if (delayNode->GetChildrenCount() > 0) {
748 CHECK_EQ(1, delayNode->GetChildrenCount());
749 GetChild(env->GetIsolate(), delayNode, "loop");
750 }
751 }
752
753 profile->Delete();
754 }
755
756
757 static const char* native_accessor_test_source = "function start(count) {\n"
758 " for (var i = 0; i < count; i++) {\n"
759 " var o = instance.foo;\n"
760 " instance.foo = o + 1;\n"
761 " }\n"
762 "}\n";
763
764
765 class TestApiCallbacks {
766 public:
TestApiCallbacks(int min_duration_ms)767 explicit TestApiCallbacks(int min_duration_ms)
768 : min_duration_ms_(min_duration_ms),
769 is_warming_up_(false) {}
770
Getter(v8::Local<v8::String> name,const v8::PropertyCallbackInfo<v8::Value> & info)771 static void Getter(v8::Local<v8::String> name,
772 const v8::PropertyCallbackInfo<v8::Value>& info) {
773 TestApiCallbacks* data = fromInfo(info);
774 data->Wait();
775 }
776
Setter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<void> & info)777 static void Setter(v8::Local<v8::String> name,
778 v8::Local<v8::Value> value,
779 const v8::PropertyCallbackInfo<void>& info) {
780 TestApiCallbacks* data = fromInfo(info);
781 data->Wait();
782 }
783
Callback(const v8::FunctionCallbackInfo<v8::Value> & info)784 static void Callback(const v8::FunctionCallbackInfo<v8::Value>& info) {
785 TestApiCallbacks* data = fromInfo(info);
786 data->Wait();
787 }
788
set_warming_up(bool value)789 void set_warming_up(bool value) { is_warming_up_ = value; }
790
791 private:
Wait()792 void Wait() {
793 if (is_warming_up_) return;
794 double start = i::OS::TimeCurrentMillis();
795 double duration = 0;
796 while (duration < min_duration_ms_) {
797 i::OS::Sleep(1);
798 duration = i::OS::TimeCurrentMillis() - start;
799 }
800 }
801
802 template<typename T>
fromInfo(const T & info)803 static TestApiCallbacks* fromInfo(const T& info) {
804 void* data = v8::External::Cast(*info.Data())->Value();
805 return reinterpret_cast<TestApiCallbacks*>(data);
806 }
807
808 int min_duration_ms_;
809 bool is_warming_up_;
810 };
811
812
813 // Test that native accessors are properly reported in the CPU profile.
814 // This test checks the case when the long-running accessors are called
815 // only once and the optimizer doesn't have chance to change the invocation
816 // code.
TEST(NativeAccessorUninitializedIC)817 TEST(NativeAccessorUninitializedIC) {
818 LocalContext env;
819 v8::Isolate* isolate = env->GetIsolate();
820 v8::HandleScope scope(isolate);
821
822 v8::Local<v8::FunctionTemplate> func_template =
823 v8::FunctionTemplate::New(isolate);
824 v8::Local<v8::ObjectTemplate> instance_template =
825 func_template->InstanceTemplate();
826
827 TestApiCallbacks accessors(100);
828 v8::Local<v8::External> data =
829 v8::External::New(isolate, &accessors);
830 instance_template->SetAccessor(
831 v8::String::NewFromUtf8(isolate, "foo"),
832 &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data);
833 v8::Local<v8::Function> func = func_template->GetFunction();
834 v8::Local<v8::Object> instance = func->NewInstance();
835 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
836 instance);
837
838 v8::Script::Compile(
839 v8::String::NewFromUtf8(isolate, native_accessor_test_source))
840 ->Run();
841 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
842 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
843
844 int32_t repeat_count = 1;
845 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
846 v8::CpuProfile* profile =
847 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 180);
848
849 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
850 const v8::CpuProfileNode* startNode =
851 GetChild(isolate, root, "start");
852 GetChild(isolate, startNode, "get foo");
853 GetChild(isolate, startNode, "set foo");
854
855 profile->Delete();
856 }
857
858
859 // Test that native accessors are properly reported in the CPU profile.
860 // This test makes sure that the accessors are called enough times to become
861 // hot and to trigger optimizations.
TEST(NativeAccessorMonomorphicIC)862 TEST(NativeAccessorMonomorphicIC) {
863 LocalContext env;
864 v8::Isolate* isolate = env->GetIsolate();
865 v8::HandleScope scope(isolate);
866
867 v8::Local<v8::FunctionTemplate> func_template =
868 v8::FunctionTemplate::New(isolate);
869 v8::Local<v8::ObjectTemplate> instance_template =
870 func_template->InstanceTemplate();
871
872 TestApiCallbacks accessors(1);
873 v8::Local<v8::External> data =
874 v8::External::New(isolate, &accessors);
875 instance_template->SetAccessor(
876 v8::String::NewFromUtf8(isolate, "foo"),
877 &TestApiCallbacks::Getter, &TestApiCallbacks::Setter, data);
878 v8::Local<v8::Function> func = func_template->GetFunction();
879 v8::Local<v8::Object> instance = func->NewInstance();
880 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
881 instance);
882
883 v8::Script::Compile(
884 v8::String::NewFromUtf8(isolate, native_accessor_test_source))
885 ->Run();
886 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
887 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
888
889 {
890 // Make sure accessors ICs are in monomorphic state before starting
891 // profiling.
892 accessors.set_warming_up(true);
893 int32_t warm_up_iterations = 3;
894 v8::Handle<v8::Value> args[] = {
895 v8::Integer::New(isolate, warm_up_iterations)
896 };
897 function->Call(env->Global(), ARRAY_SIZE(args), args);
898 accessors.set_warming_up(false);
899 }
900
901 int32_t repeat_count = 100;
902 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
903 v8::CpuProfile* profile =
904 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 200);
905
906 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
907 const v8::CpuProfileNode* startNode =
908 GetChild(isolate, root, "start");
909 GetChild(isolate, startNode, "get foo");
910 GetChild(isolate, startNode, "set foo");
911
912 profile->Delete();
913 }
914
915
916 static const char* native_method_test_source = "function start(count) {\n"
917 " for (var i = 0; i < count; i++) {\n"
918 " instance.fooMethod();\n"
919 " }\n"
920 "}\n";
921
922
TEST(NativeMethodUninitializedIC)923 TEST(NativeMethodUninitializedIC) {
924 LocalContext env;
925 v8::Isolate* isolate = env->GetIsolate();
926 v8::HandleScope scope(isolate);
927
928 TestApiCallbacks callbacks(100);
929 v8::Local<v8::External> data =
930 v8::External::New(isolate, &callbacks);
931
932 v8::Local<v8::FunctionTemplate> func_template =
933 v8::FunctionTemplate::New(isolate);
934 func_template->SetClassName(
935 v8::String::NewFromUtf8(isolate, "Test_InstanceCostructor"));
936 v8::Local<v8::ObjectTemplate> proto_template =
937 func_template->PrototypeTemplate();
938 v8::Local<v8::Signature> signature =
939 v8::Signature::New(isolate, func_template);
940 proto_template->Set(v8::String::NewFromUtf8(isolate, "fooMethod"),
941 v8::FunctionTemplate::New(isolate,
942 &TestApiCallbacks::Callback,
943 data, signature, 0));
944
945 v8::Local<v8::Function> func = func_template->GetFunction();
946 v8::Local<v8::Object> instance = func->NewInstance();
947 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
948 instance);
949
950 v8::Script::Compile(v8::String::NewFromUtf8(
951 isolate, native_method_test_source))->Run();
952 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
953 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
954
955 int32_t repeat_count = 1;
956 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
957 v8::CpuProfile* profile =
958 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
959
960 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
961 const v8::CpuProfileNode* startNode =
962 GetChild(isolate, root, "start");
963 GetChild(isolate, startNode, "fooMethod");
964
965 profile->Delete();
966 }
967
968
TEST(NativeMethodMonomorphicIC)969 TEST(NativeMethodMonomorphicIC) {
970 LocalContext env;
971 v8::Isolate* isolate = env->GetIsolate();
972 v8::HandleScope scope(isolate);
973
974 TestApiCallbacks callbacks(1);
975 v8::Local<v8::External> data =
976 v8::External::New(isolate, &callbacks);
977
978 v8::Local<v8::FunctionTemplate> func_template =
979 v8::FunctionTemplate::New(isolate);
980 func_template->SetClassName(
981 v8::String::NewFromUtf8(isolate, "Test_InstanceCostructor"));
982 v8::Local<v8::ObjectTemplate> proto_template =
983 func_template->PrototypeTemplate();
984 v8::Local<v8::Signature> signature =
985 v8::Signature::New(isolate, func_template);
986 proto_template->Set(v8::String::NewFromUtf8(isolate, "fooMethod"),
987 v8::FunctionTemplate::New(isolate,
988 &TestApiCallbacks::Callback,
989 data, signature, 0));
990
991 v8::Local<v8::Function> func = func_template->GetFunction();
992 v8::Local<v8::Object> instance = func->NewInstance();
993 env->Global()->Set(v8::String::NewFromUtf8(isolate, "instance"),
994 instance);
995
996 v8::Script::Compile(v8::String::NewFromUtf8(
997 isolate, native_method_test_source))->Run();
998 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
999 env->Global()->Get(v8::String::NewFromUtf8(isolate, "start")));
1000 {
1001 // Make sure method ICs are in monomorphic state before starting
1002 // profiling.
1003 callbacks.set_warming_up(true);
1004 int32_t warm_up_iterations = 3;
1005 v8::Handle<v8::Value> args[] = {
1006 v8::Integer::New(isolate, warm_up_iterations)
1007 };
1008 function->Call(env->Global(), ARRAY_SIZE(args), args);
1009 callbacks.set_warming_up(false);
1010 }
1011
1012 int32_t repeat_count = 100;
1013 v8::Handle<v8::Value> args[] = { v8::Integer::New(isolate, repeat_count) };
1014 v8::CpuProfile* profile =
1015 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1016
1017 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1018 GetChild(isolate, root, "start");
1019 const v8::CpuProfileNode* startNode =
1020 GetChild(isolate, root, "start");
1021 GetChild(isolate, startNode, "fooMethod");
1022
1023 profile->Delete();
1024 }
1025
1026
1027 static const char* bound_function_test_source = "function foo(iterations) {\n"
1028 " var r = 0;\n"
1029 " for (var i = 0; i < iterations; i++) { r += i; }\n"
1030 " return r;\n"
1031 "}\n"
1032 "function start(duration) {\n"
1033 " var callback = foo.bind(this);\n"
1034 " var start = Date.now();\n"
1035 " while (Date.now() - start < duration) {\n"
1036 " callback(10 * 1000);\n"
1037 " }\n"
1038 "}";
1039
1040
TEST(BoundFunctionCall)1041 TEST(BoundFunctionCall) {
1042 LocalContext env;
1043 v8::HandleScope scope(env->GetIsolate());
1044
1045 v8::Script::Compile(
1046 v8::String::NewFromUtf8(env->GetIsolate(), bound_function_test_source))
1047 ->Run();
1048 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1049 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1050
1051 int32_t duration_ms = 100;
1052 v8::Handle<v8::Value> args[] = {
1053 v8::Integer::New(env->GetIsolate(), duration_ms)
1054 };
1055 v8::CpuProfile* profile =
1056 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1057
1058 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1059 ScopedVector<v8::Handle<v8::String> > names(3);
1060 names[0] = v8::String::NewFromUtf8(
1061 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1062 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1063 ProfileGenerator::kProgramEntryName);
1064 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1065 // Don't allow |foo| node to be at the top level.
1066 CheckChildrenNames(root, names);
1067
1068 const v8::CpuProfileNode* startNode =
1069 GetChild(env->GetIsolate(), root, "start");
1070 GetChild(env->GetIsolate(), startNode, "foo");
1071
1072 profile->Delete();
1073 }
1074
1075
1076 static const char* call_function_test_source = "function bar(iterations) {\n"
1077 "}\n"
1078 "function start(duration) {\n"
1079 " var start = Date.now();\n"
1080 " while (Date.now() - start < duration) {\n"
1081 " try {\n"
1082 " bar.call(this, 10 * 1000);\n"
1083 " } catch(e) {}\n"
1084 " }\n"
1085 "}";
1086
1087
1088 // Test that if we sampled thread when it was inside FunctionCall buitin then
1089 // its caller frame will be '(unresolved function)' as we have no reliable way
1090 // to resolve it.
1091 //
1092 // [Top down]:
1093 // 96 0 (root) [-1] #1
1094 // 1 1 (garbage collector) [-1] #4
1095 // 5 0 (unresolved function) [-1] #5
1096 // 5 5 call [-1] #6
1097 // 71 70 start [-1] #3
1098 // 1 1 bar [-1] #7
1099 // 19 19 (program) [-1] #2
TEST(FunctionCallSample)1100 TEST(FunctionCallSample) {
1101 LocalContext env;
1102 v8::HandleScope scope(env->GetIsolate());
1103
1104 // Collect garbage that might have be generated while installing extensions.
1105 CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
1106
1107 v8::Script::Compile(v8::String::NewFromUtf8(
1108 env->GetIsolate(), call_function_test_source))->Run();
1109 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1110 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1111
1112 int32_t duration_ms = 100;
1113 v8::Handle<v8::Value> args[] = {
1114 v8::Integer::New(env->GetIsolate(), duration_ms)
1115 };
1116 v8::CpuProfile* profile =
1117 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1118
1119 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1120 {
1121 ScopedVector<v8::Handle<v8::String> > names(4);
1122 names[0] = v8::String::NewFromUtf8(
1123 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1124 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1125 ProfileGenerator::kProgramEntryName);
1126 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1127 names[3] = v8::String::NewFromUtf8(
1128 env->GetIsolate(), i::ProfileGenerator::kUnresolvedFunctionName);
1129 // Don't allow |bar| and |call| nodes to be at the top level.
1130 CheckChildrenNames(root, names);
1131 }
1132
1133 // In case of GC stress tests all samples may be in GC phase and there
1134 // won't be |start| node in the profiles.
1135 bool is_gc_stress_testing =
1136 (i::FLAG_gc_interval != -1) || i::FLAG_stress_compaction;
1137 const v8::CpuProfileNode* startNode =
1138 FindChild(env->GetIsolate(), root, "start");
1139 CHECK(is_gc_stress_testing || startNode);
1140 if (startNode) {
1141 ScopedVector<v8::Handle<v8::String> > names(2);
1142 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1143 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1144 CheckChildrenNames(startNode, names);
1145 }
1146
1147 const v8::CpuProfileNode* unresolvedNode = FindChild(
1148 env->GetIsolate(), root, i::ProfileGenerator::kUnresolvedFunctionName);
1149 if (unresolvedNode) {
1150 ScopedVector<v8::Handle<v8::String> > names(1);
1151 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "call");
1152 CheckChildrenNames(unresolvedNode, names);
1153 }
1154
1155 profile->Delete();
1156 }
1157
1158
1159 static const char* function_apply_test_source = "function bar(iterations) {\n"
1160 "}\n"
1161 "function test() {\n"
1162 " bar.apply(this, [10 * 1000]);\n"
1163 "}\n"
1164 "function start(duration) {\n"
1165 " var start = Date.now();\n"
1166 " while (Date.now() - start < duration) {\n"
1167 " try {\n"
1168 " test();\n"
1169 " } catch(e) {}\n"
1170 " }\n"
1171 "}";
1172
1173
1174 // [Top down]:
1175 // 94 0 (root) [-1] #0 1
1176 // 2 2 (garbage collector) [-1] #0 7
1177 // 82 49 start [-1] #16 3
1178 // 1 0 (unresolved function) [-1] #0 8
1179 // 1 1 apply [-1] #0 9
1180 // 32 21 test [-1] #16 4
1181 // 2 2 bar [-1] #16 6
1182 // 9 9 apply [-1] #0 5
1183 // 10 10 (program) [-1] #0 2
TEST(FunctionApplySample)1184 TEST(FunctionApplySample) {
1185 LocalContext env;
1186 v8::HandleScope scope(env->GetIsolate());
1187
1188 v8::Script::Compile(
1189 v8::String::NewFromUtf8(env->GetIsolate(), function_apply_test_source))
1190 ->Run();
1191 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1192 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1193
1194 int32_t duration_ms = 100;
1195 v8::Handle<v8::Value> args[] = {
1196 v8::Integer::New(env->GetIsolate(), duration_ms)
1197 };
1198
1199 v8::CpuProfile* profile =
1200 RunProfiler(env.local(), function, args, ARRAY_SIZE(args), 100);
1201
1202 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1203 {
1204 ScopedVector<v8::Handle<v8::String> > names(3);
1205 names[0] = v8::String::NewFromUtf8(
1206 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1207 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1208 ProfileGenerator::kProgramEntryName);
1209 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1210 // Don't allow |test|, |bar| and |apply| nodes to be at the top level.
1211 CheckChildrenNames(root, names);
1212 }
1213
1214 const v8::CpuProfileNode* startNode =
1215 FindChild(env->GetIsolate(), root, "start");
1216 if (startNode) {
1217 {
1218 ScopedVector<v8::Handle<v8::String> > names(2);
1219 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "test");
1220 names[1] = v8::String::NewFromUtf8(
1221 env->GetIsolate(), ProfileGenerator::kUnresolvedFunctionName);
1222 CheckChildrenNames(startNode, names);
1223 }
1224
1225 const v8::CpuProfileNode* testNode =
1226 FindChild(env->GetIsolate(), startNode, "test");
1227 if (testNode) {
1228 ScopedVector<v8::Handle<v8::String> > names(3);
1229 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "bar");
1230 names[1] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1231 // apply calls "get length" before invoking the function itself
1232 // and we may get hit into it.
1233 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "get length");
1234 CheckChildrenNames(testNode, names);
1235 }
1236
1237 if (const v8::CpuProfileNode* unresolvedNode =
1238 FindChild(env->GetIsolate(), startNode,
1239 ProfileGenerator::kUnresolvedFunctionName)) {
1240 ScopedVector<v8::Handle<v8::String> > names(1);
1241 names[0] = v8::String::NewFromUtf8(env->GetIsolate(), "apply");
1242 CheckChildrenNames(unresolvedNode, names);
1243 GetChild(env->GetIsolate(), unresolvedNode, "apply");
1244 }
1245 }
1246
1247 profile->Delete();
1248 }
1249
1250
1251 static const char* js_native_js_test_source =
1252 "var is_profiling = false;\n"
1253 "function foo(iterations) {\n"
1254 " if (!is_profiling) {\n"
1255 " is_profiling = true;\n"
1256 " startProfiling('my_profile');\n"
1257 " }\n"
1258 " var r = 0;\n"
1259 " for (var i = 0; i < iterations; i++) { r += i; }\n"
1260 " return r;\n"
1261 "}\n"
1262 "function bar(iterations) {\n"
1263 " try { foo(iterations); } catch(e) {}\n"
1264 "}\n"
1265 "function start(duration) {\n"
1266 " var start = Date.now();\n"
1267 " while (Date.now() - start < duration) {\n"
1268 " try {\n"
1269 " CallJsFunction(bar, 10 * 1000);\n"
1270 " } catch(e) {}\n"
1271 " }\n"
1272 "}";
1273
CallJsFunction(const v8::FunctionCallbackInfo<v8::Value> & info)1274 static void CallJsFunction(const v8::FunctionCallbackInfo<v8::Value>& info) {
1275 v8::Handle<v8::Function> function = info[0].As<v8::Function>();
1276 v8::Handle<v8::Value> argv[] = { info[1] };
1277 function->Call(info.This(), ARRAY_SIZE(argv), argv);
1278 }
1279
1280
1281 // [Top down]:
1282 // 58 0 (root) #0 1
1283 // 2 2 (program) #0 2
1284 // 56 1 start #16 3
1285 // 55 0 CallJsFunction #0 4
1286 // 55 1 bar #16 5
1287 // 54 54 foo #16 6
TEST(JsNativeJsSample)1288 TEST(JsNativeJsSample) {
1289 v8::HandleScope scope(CcTest::isolate());
1290 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1291 v8::Context::Scope context_scope(env);
1292
1293 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1294 env->GetIsolate(), CallJsFunction);
1295 v8::Local<v8::Function> func = func_template->GetFunction();
1296 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1297 env->Global()->Set(
1298 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1299
1300 v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(),
1301 js_native_js_test_source))->Run();
1302 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1303 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1304
1305 int32_t duration_ms = 20;
1306 v8::Handle<v8::Value> args[] = {
1307 v8::Integer::New(env->GetIsolate(), duration_ms)
1308 };
1309 v8::CpuProfile* profile =
1310 RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1311
1312 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1313 {
1314 ScopedVector<v8::Handle<v8::String> > names(3);
1315 names[0] = v8::String::NewFromUtf8(
1316 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1317 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1318 ProfileGenerator::kProgramEntryName);
1319 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1320 CheckChildrenNames(root, names);
1321 }
1322
1323 const v8::CpuProfileNode* startNode =
1324 GetChild(env->GetIsolate(), root, "start");
1325 CHECK_EQ(1, startNode->GetChildrenCount());
1326 const v8::CpuProfileNode* nativeFunctionNode =
1327 GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1328
1329 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1330 const v8::CpuProfileNode* barNode =
1331 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1332
1333 CHECK_EQ(1, barNode->GetChildrenCount());
1334 GetChild(env->GetIsolate(), barNode, "foo");
1335
1336 profile->Delete();
1337 }
1338
1339
1340 static const char* js_native_js_runtime_js_test_source =
1341 "var is_profiling = false;\n"
1342 "function foo(iterations) {\n"
1343 " if (!is_profiling) {\n"
1344 " is_profiling = true;\n"
1345 " startProfiling('my_profile');\n"
1346 " }\n"
1347 " var r = 0;\n"
1348 " for (var i = 0; i < iterations; i++) { r += i; }\n"
1349 " return r;\n"
1350 "}\n"
1351 "var bound = foo.bind(this);\n"
1352 "function bar(iterations) {\n"
1353 " try { bound(iterations); } catch(e) {}\n"
1354 "}\n"
1355 "function start(duration) {\n"
1356 " var start = Date.now();\n"
1357 " while (Date.now() - start < duration) {\n"
1358 " try {\n"
1359 " CallJsFunction(bar, 10 * 1000);\n"
1360 " } catch(e) {}\n"
1361 " }\n"
1362 "}";
1363
1364
1365 // [Top down]:
1366 // 57 0 (root) #0 1
1367 // 55 1 start #16 3
1368 // 54 0 CallJsFunction #0 4
1369 // 54 3 bar #16 5
1370 // 51 51 foo #16 6
1371 // 2 2 (program) #0 2
TEST(JsNativeJsRuntimeJsSample)1372 TEST(JsNativeJsRuntimeJsSample) {
1373 v8::HandleScope scope(CcTest::isolate());
1374 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1375 v8::Context::Scope context_scope(env);
1376
1377 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1378 env->GetIsolate(), CallJsFunction);
1379 v8::Local<v8::Function> func = func_template->GetFunction();
1380 func->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"));
1381 env->Global()->Set(
1382 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction"), func);
1383
1384 v8::Script::Compile(
1385 v8::String::NewFromUtf8(env->GetIsolate(),
1386 js_native_js_runtime_js_test_source))->Run();
1387 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1388 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1389
1390 int32_t duration_ms = 20;
1391 v8::Handle<v8::Value> args[] = {
1392 v8::Integer::New(env->GetIsolate(), duration_ms)
1393 };
1394 v8::CpuProfile* profile =
1395 RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1396
1397 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1398 ScopedVector<v8::Handle<v8::String> > names(3);
1399 names[0] = v8::String::NewFromUtf8(
1400 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1401 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1402 ProfileGenerator::kProgramEntryName);
1403 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1404 CheckChildrenNames(root, names);
1405
1406 const v8::CpuProfileNode* startNode =
1407 GetChild(env->GetIsolate(), root, "start");
1408 CHECK_EQ(1, startNode->GetChildrenCount());
1409 const v8::CpuProfileNode* nativeFunctionNode =
1410 GetChild(env->GetIsolate(), startNode, "CallJsFunction");
1411
1412 CHECK_EQ(1, nativeFunctionNode->GetChildrenCount());
1413 const v8::CpuProfileNode* barNode =
1414 GetChild(env->GetIsolate(), nativeFunctionNode, "bar");
1415
1416 // The child is in fact a bound foo.
1417 // A bound function has a wrapper that may make calls to
1418 // other functions e.g. "get length".
1419 CHECK_LE(1, barNode->GetChildrenCount());
1420 CHECK_GE(2, barNode->GetChildrenCount());
1421 GetChild(env->GetIsolate(), barNode, "foo");
1422
1423 profile->Delete();
1424 }
1425
1426
CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value> & info)1427 static void CallJsFunction2(const v8::FunctionCallbackInfo<v8::Value>& info) {
1428 CallJsFunction(info);
1429 }
1430
1431
1432 static const char* js_native1_js_native2_js_test_source =
1433 "var is_profiling = false;\n"
1434 "function foo(iterations) {\n"
1435 " if (!is_profiling) {\n"
1436 " is_profiling = true;\n"
1437 " startProfiling('my_profile');\n"
1438 " }\n"
1439 " var r = 0;\n"
1440 " for (var i = 0; i < iterations; i++) { r += i; }\n"
1441 " return r;\n"
1442 "}\n"
1443 "function bar(iterations) {\n"
1444 " CallJsFunction2(foo, iterations);\n"
1445 "}\n"
1446 "function start(duration) {\n"
1447 " var start = Date.now();\n"
1448 " while (Date.now() - start < duration) {\n"
1449 " try {\n"
1450 " CallJsFunction1(bar, 10 * 1000);\n"
1451 " } catch(e) {}\n"
1452 " }\n"
1453 "}";
1454
1455
1456 // [Top down]:
1457 // 57 0 (root) #0 1
1458 // 55 1 start #16 3
1459 // 54 0 CallJsFunction1 #0 4
1460 // 54 0 bar #16 5
1461 // 54 0 CallJsFunction2 #0 6
1462 // 54 54 foo #16 7
1463 // 2 2 (program) #0 2
TEST(JsNative1JsNative2JsSample)1464 TEST(JsNative1JsNative2JsSample) {
1465 v8::HandleScope scope(CcTest::isolate());
1466 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1467 v8::Context::Scope context_scope(env);
1468
1469 v8::Local<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(
1470 env->GetIsolate(), CallJsFunction);
1471 v8::Local<v8::Function> func1 = func_template->GetFunction();
1472 func1->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"));
1473 env->Global()->Set(
1474 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction1"), func1);
1475
1476 v8::Local<v8::Function> func2 = v8::FunctionTemplate::New(
1477 env->GetIsolate(), CallJsFunction2)->GetFunction();
1478 func2->SetName(v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"));
1479 env->Global()->Set(
1480 v8::String::NewFromUtf8(env->GetIsolate(), "CallJsFunction2"), func2);
1481
1482 v8::Script::Compile(
1483 v8::String::NewFromUtf8(env->GetIsolate(),
1484 js_native1_js_native2_js_test_source))->Run();
1485 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
1486 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "start")));
1487
1488 int32_t duration_ms = 20;
1489 v8::Handle<v8::Value> args[] = {
1490 v8::Integer::New(env->GetIsolate(), duration_ms)
1491 };
1492 v8::CpuProfile* profile =
1493 RunProfiler(env, function, args, ARRAY_SIZE(args), 10);
1494
1495 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1496 ScopedVector<v8::Handle<v8::String> > names(3);
1497 names[0] = v8::String::NewFromUtf8(
1498 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1499 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1500 ProfileGenerator::kProgramEntryName);
1501 names[2] = v8::String::NewFromUtf8(env->GetIsolate(), "start");
1502 CheckChildrenNames(root, names);
1503
1504 const v8::CpuProfileNode* startNode =
1505 GetChild(env->GetIsolate(), root, "start");
1506 CHECK_EQ(1, startNode->GetChildrenCount());
1507 const v8::CpuProfileNode* nativeNode1 =
1508 GetChild(env->GetIsolate(), startNode, "CallJsFunction1");
1509
1510 CHECK_EQ(1, nativeNode1->GetChildrenCount());
1511 const v8::CpuProfileNode* barNode =
1512 GetChild(env->GetIsolate(), nativeNode1, "bar");
1513
1514 CHECK_EQ(1, barNode->GetChildrenCount());
1515 const v8::CpuProfileNode* nativeNode2 =
1516 GetChild(env->GetIsolate(), barNode, "CallJsFunction2");
1517
1518 CHECK_EQ(1, nativeNode2->GetChildrenCount());
1519 GetChild(env->GetIsolate(), nativeNode2, "foo");
1520
1521 profile->Delete();
1522 }
1523
1524
1525 // [Top down]:
1526 // 6 0 (root) #0 1
1527 // 3 3 (program) #0 2
1528 // 3 3 (idle) #0 3
TEST(IdleTime)1529 TEST(IdleTime) {
1530 LocalContext env;
1531 v8::HandleScope scope(env->GetIsolate());
1532 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler();
1533
1534 v8::Local<v8::String> profile_name =
1535 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile");
1536 cpu_profiler->StartProfiling(profile_name);
1537
1538 i::Isolate* isolate = CcTest::i_isolate();
1539 i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor();
1540 processor->AddCurrentStack(isolate);
1541
1542 cpu_profiler->SetIdle(true);
1543
1544 for (int i = 0; i < 3; i++) {
1545 processor->AddCurrentStack(isolate);
1546 }
1547
1548 cpu_profiler->SetIdle(false);
1549 processor->AddCurrentStack(isolate);
1550
1551
1552 v8::CpuProfile* profile = cpu_profiler->StopProfiling(profile_name);
1553 CHECK_NE(NULL, profile);
1554 // Dump collected profile to have a better diagnostic in case of failure.
1555 reinterpret_cast<i::CpuProfile*>(profile)->Print();
1556
1557 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1558 ScopedVector<v8::Handle<v8::String> > names(3);
1559 names[0] = v8::String::NewFromUtf8(
1560 env->GetIsolate(), ProfileGenerator::kGarbageCollectorEntryName);
1561 names[1] = v8::String::NewFromUtf8(env->GetIsolate(),
1562 ProfileGenerator::kProgramEntryName);
1563 names[2] = v8::String::NewFromUtf8(env->GetIsolate(),
1564 ProfileGenerator::kIdleEntryName);
1565 CheckChildrenNames(root, names);
1566
1567 const v8::CpuProfileNode* programNode =
1568 GetChild(env->GetIsolate(), root, ProfileGenerator::kProgramEntryName);
1569 CHECK_EQ(0, programNode->GetChildrenCount());
1570 CHECK_GE(programNode->GetHitCount(), 3);
1571
1572 const v8::CpuProfileNode* idleNode =
1573 GetChild(env->GetIsolate(), root, ProfileGenerator::kIdleEntryName);
1574 CHECK_EQ(0, idleNode->GetChildrenCount());
1575 CHECK_GE(idleNode->GetHitCount(), 3);
1576
1577 profile->Delete();
1578 }
1579
1580
CheckFunctionDetails(v8::Isolate * isolate,const v8::CpuProfileNode * node,const char * name,const char * script_name,int script_id,int line,int column)1581 static void CheckFunctionDetails(v8::Isolate* isolate,
1582 const v8::CpuProfileNode* node,
1583 const char* name, const char* script_name,
1584 int script_id, int line, int column) {
1585 CHECK_EQ(v8::String::NewFromUtf8(isolate, name),
1586 node->GetFunctionName());
1587 CHECK_EQ(v8::String::NewFromUtf8(isolate, script_name),
1588 node->GetScriptResourceName());
1589 CHECK_EQ(script_id, node->GetScriptId());
1590 CHECK_EQ(line, node->GetLineNumber());
1591 CHECK_EQ(column, node->GetColumnNumber());
1592 }
1593
1594
TEST(FunctionDetails)1595 TEST(FunctionDetails) {
1596 v8::HandleScope scope(CcTest::isolate());
1597 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1598 v8::Context::Scope context_scope(env);
1599
1600 v8::Handle<v8::Script> script_a = CompileWithOrigin(
1601 " function foo\n() { try { bar(); } catch(e) {} }\n"
1602 " function bar() { startProfiling(); }\n",
1603 "script_a");
1604 script_a->Run();
1605 v8::Handle<v8::Script> script_b = CompileWithOrigin(
1606 "\n\n function baz() { try { foo(); } catch(e) {} }\n"
1607 "\n\nbaz();\n"
1608 "stopProfiling();\n",
1609 "script_b");
1610 script_b->Run();
1611 const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
1612 const v8::CpuProfileNode* current = profile->GetTopDownRoot();
1613 reinterpret_cast<ProfileNode*>(
1614 const_cast<v8::CpuProfileNode*>(current))->Print(0);
1615 // The tree should look like this:
1616 // 0 (root) 0 #1
1617 // 0 (anonymous function) 19 #2 no reason script_b:1
1618 // 0 baz 19 #3 TryCatchStatement script_b:3
1619 // 0 foo 18 #4 TryCatchStatement script_a:2
1620 // 1 bar 18 #5 no reason script_a:3
1621 const v8::CpuProfileNode* root = profile->GetTopDownRoot();
1622 const v8::CpuProfileNode* script = GetChild(env->GetIsolate(), root,
1623 ProfileGenerator::kAnonymousFunctionName);
1624 CheckFunctionDetails(env->GetIsolate(), script,
1625 ProfileGenerator::kAnonymousFunctionName, "script_b",
1626 script_b->GetUnboundScript()->GetId(), 1, 1);
1627 const v8::CpuProfileNode* baz = GetChild(env->GetIsolate(), script, "baz");
1628 CheckFunctionDetails(env->GetIsolate(), baz, "baz", "script_b",
1629 script_b->GetUnboundScript()->GetId(), 3, 16);
1630 const v8::CpuProfileNode* foo = GetChild(env->GetIsolate(), baz, "foo");
1631 CheckFunctionDetails(env->GetIsolate(), foo, "foo", "script_a",
1632 script_a->GetUnboundScript()->GetId(), 2, 1);
1633 const v8::CpuProfileNode* bar = GetChild(env->GetIsolate(), foo, "bar");
1634 CheckFunctionDetails(env->GetIsolate(), bar, "bar", "script_a",
1635 script_a->GetUnboundScript()->GetId(), 3, 14);
1636 }
1637
1638
TEST(DontStopOnFinishedProfileDelete)1639 TEST(DontStopOnFinishedProfileDelete) {
1640 v8::HandleScope scope(CcTest::isolate());
1641 v8::Local<v8::Context> env = CcTest::NewContext(PROFILER_EXTENSION);
1642 v8::Context::Scope context_scope(env);
1643 v8::Isolate* isolate = env->GetIsolate();
1644
1645 v8::CpuProfiler* profiler = env->GetIsolate()->GetCpuProfiler();
1646 i::CpuProfiler* iprofiler = reinterpret_cast<i::CpuProfiler*>(profiler);
1647
1648 CHECK_EQ(0, iprofiler->GetProfilesCount());
1649 v8::Handle<v8::String> outer = v8::String::NewFromUtf8(isolate, "outer");
1650 profiler->StartProfiling(outer);
1651 CHECK_EQ(0, iprofiler->GetProfilesCount());
1652
1653 v8::Handle<v8::String> inner = v8::String::NewFromUtf8(isolate, "inner");
1654 profiler->StartProfiling(inner);
1655 CHECK_EQ(0, iprofiler->GetProfilesCount());
1656
1657 v8::CpuProfile* inner_profile = profiler->StopProfiling(inner);
1658 CHECK(inner_profile);
1659 CHECK_EQ(1, iprofiler->GetProfilesCount());
1660 inner_profile->Delete();
1661 inner_profile = NULL;
1662 CHECK_EQ(0, iprofiler->GetProfilesCount());
1663
1664 v8::CpuProfile* outer_profile = profiler->StopProfiling(outer);
1665 CHECK(outer_profile);
1666 CHECK_EQ(1, iprofiler->GetProfilesCount());
1667 outer_profile->Delete();
1668 outer_profile = NULL;
1669 CHECK_EQ(0, iprofiler->GetProfilesCount());
1670 }
1671