Flutter Engine
The Flutter Engine
profiler_test.cc
Go to the documentation of this file.
1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "platform/assert.h"
6
7#include "vm/dart_api_impl.h"
8#include "vm/dart_api_state.h"
9#include "vm/globals.h"
10#include "vm/profiler.h"
11#include "vm/profiler_service.h"
12#include "vm/source_report.h"
13#include "vm/symbols.h"
14#include "vm/unit_test.h"
15
16namespace dart {
17
18#ifndef PRODUCT
19
20DECLARE_FLAG(bool, profile_vm);
21DECLARE_FLAG(bool, profile_vm_allocation);
22DECLARE_FLAG(int, max_profile_depth);
23DECLARE_FLAG(int, optimization_counter_threshold);
24
25// SampleVisitor ignores samples with timestamp == 0.
26const int64_t kValidTimeStamp = 1;
27
28// SampleVisitor ignores samples with pc == 0.
29const uword kValidPc = 0xFF;
30
31// Some tests are written assuming native stack trace profiling is disabled.
33 public:
35 : FLAG_profile_vm_(FLAG_profile_vm),
36 FLAG_profile_vm_allocation_(FLAG_profile_vm_allocation) {
37 FLAG_profile_vm = false;
38 FLAG_profile_vm_allocation = false;
39 }
40
42 FLAG_profile_vm = FLAG_profile_vm_;
43 FLAG_profile_vm_allocation = FLAG_profile_vm_allocation_;
44 }
45
46 private:
47 const bool FLAG_profile_vm_;
48 const bool FLAG_profile_vm_allocation_;
49};
50
51// Temporarily adjust the maximum profile depth.
53 public:
54 explicit MaxProfileDepthScope(intptr_t new_max_depth)
55 : FLAG_max_profile_depth_(FLAG_max_profile_depth) {
56 Profiler::SetSampleDepth(new_max_depth);
57 }
58
59 ~MaxProfileDepthScope() { Profiler::SetSampleDepth(FLAG_max_profile_depth_); }
60
61 private:
62 const intptr_t FLAG_max_profile_depth_;
63};
64
66 public:
69
70 void VisitSample(Sample* sample) { sum_ += sample->At(0); }
71
72 void Reset() {
73 sum_ = 0;
75 }
76
77 intptr_t sum() const { return sum_; }
78
79 private:
80 intptr_t sum_ = 0;
81};
82
84 visitor->Reset();
85 buffer->VisitSamples(visitor);
86}
87
89 public:
91 : override_(buffer) {
94 }
95
98 delete override_;
99 }
100
101 private:
102 SampleBlockBuffer* orig_;
103 SampleBlockBuffer* override_;
104};
105
106TEST_CASE(Profiler_SampleBufferWrapTest) {
107 Isolate* isolate = Isolate::Current();
108
111
112 Dart_Port i = 123;
114
115 VisitSamples(sample_buffer, &visitor);
116 EXPECT_EQ(0, visitor.sum());
117 Sample* s;
118
119 s = sample_buffer->ReserveCPUSample(isolate);
120 EXPECT_NOTNULL(s);
121 s->Init(i, kValidTimeStamp, 0);
122 s->SetAt(0, 2);
123 VisitSamples(sample_buffer, &visitor);
124 EXPECT_EQ(2, visitor.sum());
125
126 s = sample_buffer->ReserveCPUSample(isolate);
127 EXPECT_NOTNULL(s);
128 s->Init(i, kValidTimeStamp, 0);
129 s->SetAt(0, 4);
130 VisitSamples(sample_buffer, &visitor);
131 EXPECT_EQ(6, visitor.sum());
132
133 s = sample_buffer->ReserveCPUSample(isolate);
134 EXPECT_NOTNULL(s);
135 s->Init(i, kValidTimeStamp, 0);
136 s->SetAt(0, 6);
137 VisitSamples(sample_buffer, &visitor);
138 EXPECT_EQ(12, visitor.sum());
139
140 // Mark the completed blocks as free so they can be re-used.
141 sample_buffer->FreeCompletedBlocks();
142
143 s = sample_buffer->ReserveCPUSample(isolate);
144 EXPECT_NOTNULL(s);
145 s->Init(i, kValidTimeStamp, 0);
146 s->SetAt(0, 8);
147 VisitSamples(sample_buffer, &visitor);
148 EXPECT_EQ(18, visitor.sum());
149
150 // The overridden sample buffer will be freed before isolate shutdown.
151 isolate->set_current_sample_block(nullptr);
152}
153
154TEST_CASE(Profiler_SampleBufferIterateTest) {
155 Isolate* isolate = Isolate::Current();
156
159
160 Dart_Port i = 123;
162
163 sample_buffer->VisitSamples(&visitor);
164 EXPECT_EQ(0, visitor.visited());
165 Sample* s;
166 s = sample_buffer->ReserveCPUSample(isolate);
167 EXPECT_NOTNULL(s);
168 s->Init(i, kValidTimeStamp, 0);
169 s->SetAt(0, kValidPc);
170 VisitSamples(sample_buffer, &visitor);
171 EXPECT_EQ(1, visitor.visited());
172
173 s = sample_buffer->ReserveCPUSample(isolate);
174 EXPECT_NOTNULL(s);
175 s->Init(i, kValidTimeStamp, 0);
176 s->SetAt(0, kValidPc);
177 VisitSamples(sample_buffer, &visitor);
178 EXPECT_EQ(2, visitor.visited());
179
180 s = sample_buffer->ReserveCPUSample(isolate);
181 EXPECT_NOTNULL(s);
182 s->Init(i, kValidTimeStamp, 0);
183 s->SetAt(0, kValidPc);
184 VisitSamples(sample_buffer, &visitor);
185 EXPECT_EQ(3, visitor.visited());
186
187 s = sample_buffer->ReserveCPUSample(isolate);
188 EXPECT_NOTNULL(s);
189 s->Init(i, kValidTimeStamp, 0);
190 s->SetAt(0, kValidPc);
191 VisitSamples(sample_buffer, &visitor);
192 EXPECT_EQ(3, visitor.visited());
193
194 // The overridden sample buffer will be freed before isolate shutdown.
195 isolate->set_current_sample_block(nullptr);
196}
197
198TEST_CASE(Profiler_AllocationSampleTest) {
199 Isolate* isolate = Isolate::Current();
200 SampleBlockBuffer* sample_buffer = new SampleBlockBuffer(1, 1);
201 Sample* sample = sample_buffer->ReserveAllocationSample(isolate);
202 sample->Init(isolate->main_port(), 0, 0);
203 sample->set_metadata(99);
204 sample->set_is_allocation_sample(true);
205 EXPECT_EQ(99, sample->allocation_cid());
206 isolate->set_current_allocation_sample_block(nullptr);
207 delete sample_buffer;
208}
209
210static LibraryPtr LoadTestScript(const char* script) {
211 Dart_Handle api_lib;
212 {
214 api_lib = TestCase::LoadTestScript(script, nullptr);
215 EXPECT_VALID(api_lib);
216 }
217 Library& lib = Library::Handle();
218 lib ^= Api::UnwrapHandle(api_lib);
219 return lib.ptr();
220}
221
222static ClassPtr GetClass(const Library& lib, const char* name) {
223 Thread* thread = Thread::Current();
224 const Class& cls = Class::Handle(
225 lib.LookupClassAllowPrivate(String::Handle(Symbols::New(thread, name))));
226 EXPECT(!cls.IsNull()); // No ambiguity error expected.
227 return cls.ptr();
228}
229
230static FunctionPtr GetFunction(const Library& lib, const char* name) {
231 Thread* thread = Thread::Current();
232 const Function& func = Function::Handle(lib.LookupFunctionAllowPrivate(
233 String::Handle(Symbols::New(thread, name))));
234 EXPECT(!func.IsNull()); // No ambiguity error expected.
235 return func.ptr();
236}
237
238static void Invoke(const Library& lib,
239 const char* name,
240 intptr_t argc = 0,
241 Dart_Handle* argv = nullptr) {
242 Thread* thread = Thread::Current();
243 Dart_Handle api_lib = Api::NewHandle(thread, lib.ptr());
244 TransitionVMToNative transition(thread);
245 Dart_Handle result = Dart_Invoke(api_lib, NewString(name), argc, argv);
247}
248
250 public:
252 intptr_t cid,
253 int64_t time_origin_micros = -1,
254 int64_t time_extent_micros = -1)
256 Thread::kMutatorTask,
257 time_origin_micros,
258 time_extent_micros),
259 cid_(cid),
260 enable_vm_ticks_(false) {}
261
262 bool FilterSample(Sample* sample) {
263 if (!enable_vm_ticks_ && (sample->vm_tag() == VMTag::kVMTagId)) {
264 // We don't want to see embedder ticks in the test.
265 return false;
266 }
267 return sample->is_allocation_sample() && (sample->allocation_cid() == cid_);
268 }
269
270 void set_enable_vm_ticks(bool enable) { enable_vm_ticks_ = enable; }
271
272 private:
273 intptr_t cid_;
274 bool enable_vm_ticks_;
275};
276
277static void EnableProfiler() {
278 if (!FLAG_profiler) {
279 FLAG_profiler = true;
281 }
282}
283
285 public:
286 explicit ProfileStackWalker(Profile* profile, bool as_func = false)
287 : profile_(profile),
288 as_functions_(as_func),
289 index_(0),
290 sample_(profile->SampleAt(0)) {
291 ClearInliningData();
292 }
293
294 bool Down() {
295 if (as_functions_) {
296 return UpdateFunctionIndex();
297 } else {
298 ++index_;
299 return (index_ < sample_->length());
300 }
301 }
302
303 const char* CurrentName() {
304 if (as_functions_) {
305 ProfileFunction* func = GetFunction();
306 EXPECT(func != nullptr);
307 return func->Name();
308 } else {
309 ProfileCode* code = GetCode();
310 EXPECT(code != nullptr);
311 return code->name();
312 }
313 }
314
315 const char* CurrentToken() {
316 if (!as_functions_) {
317 return nullptr;
318 }
319 ProfileFunction* func = GetFunction();
320 const Function& function = *(func->function());
321 if (function.IsNull()) {
322 // No function.
323 return nullptr;
324 }
325 Zone* zone = Thread::Current()->zone();
326 const Script& script = Script::Handle(zone, function.script());
327 if (script.IsNull()) {
328 // No script.
329 return nullptr;
330 }
331 ProfileFunctionSourcePosition pfsp(TokenPosition::kNoSource);
332 if (!func->GetSinglePosition(&pfsp)) {
333 // Not exactly one source position.
334 return nullptr;
335 }
336
337 const TokenPosition& token_pos = pfsp.token_pos();
338 intptr_t line, column;
339 if (script.GetTokenLocation(token_pos, &line, &column)) {
340 const intptr_t token_len = script.GetTokenLength(token_pos);
341 const auto& str = String::Handle(
342 zone, script.GetSnippet(line, column, line, column + token_len));
343 if (!str.IsNull()) return str.ToCString();
344 }
345 // Couldn't get line/number information.
346 return nullptr;
347 }
348
350 if (as_functions_) {
351 ProfileFunction* func = GetFunction();
352 EXPECT(func != nullptr);
353 return func->inclusive_ticks();
354 } else {
355 ProfileCode* code = GetCode();
356 ASSERT(code != nullptr);
357 return code->inclusive_ticks();
358 }
359 }
360
362 if (as_functions_) {
363 ProfileFunction* func = GetFunction();
364 EXPECT(func != nullptr);
365 return func->exclusive_ticks();
366 } else {
367 ProfileCode* code = GetCode();
368 ASSERT(code != nullptr);
369 return code->exclusive_ticks();
370 }
371 }
372
373 const char* VMTagName() { return VMTag::TagName(sample_->vm_tag()); }
374
375 private:
376 ProfileCode* GetCode() {
377 uword pc = sample_->At(index_);
378 int64_t timestamp = sample_->timestamp();
379 return profile_->GetCodeFromPC(pc, timestamp);
380 }
381
382 static constexpr intptr_t kInvalidInlinedIndex = -1;
383
384 bool UpdateFunctionIndex() {
385 if (inlined_index_ != kInvalidInlinedIndex) {
386 if (inlined_index_ - 1 >= 0) {
387 --inlined_index_;
388 return true;
389 }
390 ClearInliningData();
391 }
392 ++index_;
393 return (index_ < sample_->length());
394 }
395
396 void ClearInliningData() {
397 inlined_index_ = kInvalidInlinedIndex;
398 inlined_functions_ = nullptr;
399 inlined_token_positions_ = nullptr;
400 }
401
402 ProfileFunction* GetFunction() {
403 // Check to see if we're currently processing inlined functions. If so,
404 // return the next inlined function.
405 ProfileFunction* function = GetInlinedFunction();
406 if (function != nullptr) {
407 return function;
408 }
409
410 const uword pc = sample_->At(index_);
411 ProfileCode* profile_code =
412 profile_->GetCodeFromPC(pc, sample_->timestamp());
413 ASSERT(profile_code != nullptr);
414 function = profile_code->function();
415 ASSERT(function != nullptr);
416
417 TokenPosition token_position = TokenPosition::kNoSource;
418 Code& code = Code::ZoneHandle();
419 if (profile_code->code().IsCode()) {
420 code ^= profile_code->code().ptr();
421 inlined_functions_cache_.Get(pc, code, sample_, index_,
422 &inlined_functions_,
423 &inlined_token_positions_, &token_position);
424 }
425
426 if (code.IsNull() || (inlined_functions_ == nullptr) ||
427 (inlined_functions_->length() <= 1)) {
428 ClearInliningData();
429 // No inlined functions.
430 return function;
431 }
432
433 ASSERT(code.is_optimized());
434 inlined_index_ = inlined_functions_->length() - 1;
435 function = GetInlinedFunction();
436 ASSERT(function != nullptr);
437 return function;
438 }
439
440 ProfileFunction* GetInlinedFunction() {
441 if ((inlined_index_ != kInvalidInlinedIndex) &&
442 (inlined_index_ < inlined_functions_->length())) {
443 return profile_->FindFunction(*(*inlined_functions_)[inlined_index_]);
444 }
445 return nullptr;
446 }
447
448 Profile* profile_;
449 bool as_functions_;
450 intptr_t index_;
451 ProcessedSample* sample_;
452 ProfileCodeInlinedFunctionsCache inlined_functions_cache_;
453 GrowableArray<const Function*>* inlined_functions_;
454 GrowableArray<TokenPosition>* inlined_token_positions_;
455 intptr_t inlined_index_;
456};
457
458ISOLATE_UNIT_TEST_CASE(Profiler_TrivialRecordAllocation) {
462 const char* kScript =
463 "class A {\n"
464 " var a;\n"
465 " var b;\n"
466 "}\n"
467 "class B {\n"
468 " static boo() {\n"
469 " return new A();\n"
470 " }\n"
471 "}\n"
472 "main() {\n"
473 " return B.boo();\n"
474 "}\n";
475
476 const Library& root_library = Library::Handle(LoadTestScript(kScript));
477
478 const int64_t before_allocations_micros = Dart_TimelineGetMicros();
479 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
480 EXPECT(!class_a.IsNull());
481 class_a.SetTraceAllocation(true);
482
483 Invoke(root_library, "main");
484
485 const int64_t after_allocations_micros = Dart_TimelineGetMicros();
486 const int64_t allocation_extent_micros =
487 after_allocations_micros - before_allocations_micros;
488 {
489 Thread* thread = Thread::Current();
490 Isolate* isolate = thread->isolate();
491 StackZone zone(thread);
493 // Filter for the class in the time range.
494 AllocationFilter filter(isolate->main_port(), class_a.id(),
495 before_allocations_micros,
496 allocation_extent_micros);
497 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
498 // We should have 1 allocation sample.
499 EXPECT_EQ(1, profile.sample_count());
501
502 // Move down from the root.
503 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
504#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
505 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
506#else
507 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
508#endif
509 EXPECT(walker.Down());
510 EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
511 EXPECT(walker.Down());
512 EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
513 EXPECT(!walker.Down());
514 }
515
516 // Query with a time filter where no allocations occurred.
517 {
518 Thread* thread = Thread::Current();
519 Isolate* isolate = thread->isolate();
520 StackZone zone(thread);
522 AllocationFilter filter(isolate->main_port(), class_a.id(),
523 Dart_TimelineGetMicros(), 16000);
524 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
525 // We should have no allocation samples because none occurred within
526 // the specified time range.
527 EXPECT_EQ(0, profile.sample_count());
528 }
529}
530
531ISOLATE_UNIT_TEST_CASE(Profiler_NullSampleBuffer) {
532 Isolate* isolate = thread->isolate();
533
534 SampleFilter filter(isolate->main_port(), Thread::kMutatorTask, -1, -1);
536 profile.Build(thread, isolate, &filter, nullptr);
537
538 EXPECT_EQ(0, profile.sample_count());
539}
540
541ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
543
546 const char* kScript =
547 "class A {\n"
548 " var a;\n"
549 " var b;\n"
550 "}\n"
551 "class B {\n"
552 " static boo() {\n"
553 " return new A();\n"
554 " }\n"
555 "}\n"
556 "main() {\n"
557 " return B.boo();\n"
558 "}\n";
559
560 const Library& root_library = Library::Handle(LoadTestScript(kScript));
561
562 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
563 EXPECT(!class_a.IsNull());
564
565 Invoke(root_library, "main");
566
567 {
568 Thread* thread = Thread::Current();
569 Isolate* isolate = thread->isolate();
570 StackZone zone(thread);
572 AllocationFilter filter(isolate->main_port(), class_a.id());
573 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
574 // We should have no allocation samples.
575 EXPECT_EQ(0, profile.sample_count());
576 }
577
578 // Turn on allocation tracing for A.
579 class_a.SetTraceAllocation(true);
580
581 Invoke(root_library, "main");
582
583 {
584 Thread* thread = Thread::Current();
585 Isolate* isolate = thread->isolate();
586 StackZone zone(thread);
588 AllocationFilter filter(isolate->main_port(), class_a.id());
589 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
590 // We should have one allocation sample.
591 EXPECT_EQ(1, profile.sample_count());
593
594 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
595#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
596 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
597#else
598 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
599#endif
600 EXPECT(walker.Down());
601 EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
602 EXPECT(walker.Down());
603 EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
604 EXPECT(!walker.Down());
605 }
606
607 // Turn off allocation tracing for A.
608 class_a.SetTraceAllocation(false);
609
610 Invoke(root_library, "main");
611
612 {
613 Thread* thread = Thread::Current();
614 Isolate* isolate = thread->isolate();
615 StackZone zone(thread);
617 AllocationFilter filter(isolate->main_port(), class_a.id());
618 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
619 // We should still only have one allocation sample.
620 EXPECT_EQ(1, profile.sample_count());
621 }
622}
623
624ISOLATE_UNIT_TEST_CASE(Profiler_CodeTicks) {
628 const char* kScript =
629 "class A {\n"
630 " var a;\n"
631 " var b;\n"
632 "}\n"
633 "class B {\n"
634 " static boo() {\n"
635 " return new A();\n"
636 " }\n"
637 "}\n"
638 "main() {\n"
639 " return B.boo();\n"
640 "}\n";
641
642 const Library& root_library = Library::Handle(LoadTestScript(kScript));
643
644 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
645 EXPECT(!class_a.IsNull());
646
647 Invoke(root_library, "main");
648
649 {
650 Thread* thread = Thread::Current();
651 Isolate* isolate = thread->isolate();
652 StackZone zone(thread);
654 AllocationFilter filter(isolate->main_port(), class_a.id());
655 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
656 // We should have no allocation samples.
657 EXPECT_EQ(0, profile.sample_count());
658 }
659
660 // Turn on allocation tracing for A.
661 class_a.SetTraceAllocation(true);
662
663 // Allocate three times.
664 Invoke(root_library, "main");
665 Invoke(root_library, "main");
666 Invoke(root_library, "main");
667
668 {
669 Thread* thread = Thread::Current();
670 Isolate* isolate = thread->isolate();
671 StackZone zone(thread);
673 AllocationFilter filter(isolate->main_port(), class_a.id());
674 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
675 // We should have three allocation samples.
676 EXPECT_EQ(3, profile.sample_count());
678
679 // Move down from the root.
680 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
681#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
682 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
683#else
684 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
685#endif
686 EXPECT_EQ(3, walker.CurrentExclusiveTicks());
687 EXPECT(walker.Down());
688 EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
689 EXPECT_EQ(3, walker.CurrentInclusiveTicks());
690 EXPECT(walker.Down());
691 EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
692 EXPECT_EQ(3, walker.CurrentInclusiveTicks());
693 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
694 EXPECT(!walker.Down());
695 }
696}
697ISOLATE_UNIT_TEST_CASE(Profiler_FunctionTicks) {
701 const char* kScript =
702 "class A {\n"
703 " var a;\n"
704 " var b;\n"
705 "}\n"
706 "class B {\n"
707 " static boo() {\n"
708 " return new A();\n"
709 " }\n"
710 "}\n"
711 "main() {\n"
712 " return B.boo();\n"
713 "}\n";
714
715 const Library& root_library = Library::Handle(LoadTestScript(kScript));
716
717 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
718 EXPECT(!class_a.IsNull());
719
720 Invoke(root_library, "main");
721
722 {
723 Thread* thread = Thread::Current();
724 Isolate* isolate = thread->isolate();
725 StackZone zone(thread);
727 AllocationFilter filter(isolate->main_port(), class_a.id());
728 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
729 // We should have no allocation samples.
730 EXPECT_EQ(0, profile.sample_count());
731 }
732
733 // Turn on allocation tracing for A.
734 class_a.SetTraceAllocation(true);
735
736 // Allocate three times.
737 Invoke(root_library, "main");
738 Invoke(root_library, "main");
739 Invoke(root_library, "main");
740
741 {
742 Thread* thread = Thread::Current();
743 Isolate* isolate = thread->isolate();
744 StackZone zone(thread);
746 AllocationFilter filter(isolate->main_port(), class_a.id());
747 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
748 // We should have three allocation samples.
749 EXPECT_EQ(3, profile.sample_count());
750 ProfileStackWalker walker(&profile, true);
751
752 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
753
754#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
755 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
756#else
757 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
758#endif
759 EXPECT_EQ(3, walker.CurrentExclusiveTicks());
760 EXPECT(walker.Down());
761 EXPECT_STREQ("B.boo", walker.CurrentName());
762 EXPECT_EQ(3, walker.CurrentInclusiveTicks());
763 EXPECT(walker.Down());
764 EXPECT_STREQ("main", walker.CurrentName());
765 EXPECT_EQ(3, walker.CurrentInclusiveTicks());
766 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
767 EXPECT(!walker.Down());
768 }
769}
770
771ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
775 const char* kScript = "double foo(double a, double b) => a + b;";
776 const Library& root_library = Library::Handle(LoadTestScript(kScript));
777 Isolate* isolate = thread->isolate();
778
779 const Class& double_class =
780 Class::Handle(isolate->group()->object_store()->double_class());
781 EXPECT(!double_class.IsNull());
782
783 Dart_Handle args[2];
784 {
785 TransitionVMToNative transition(thread);
786 args[0] = Dart_NewDouble(1.0);
787 args[1] = Dart_NewDouble(2.0);
788 }
789
790 Invoke(root_library, "foo", 2, &args[0]);
791
792 {
793 StackZone zone(thread);
795 AllocationFilter filter(isolate->main_port(), double_class.id());
796 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
797 // We should have no allocation samples.
798 EXPECT_EQ(0, profile.sample_count());
799 }
800
801 double_class.SetTraceAllocation(true);
802 Invoke(root_library, "foo", 2, &args[0]);
803
804 {
805 StackZone zone(thread);
807 AllocationFilter filter(isolate->main_port(), double_class.id());
808 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
809 // We should have one allocation sample.
810 EXPECT_EQ(1, profile.sample_count());
812
813 EXPECT_STREQ("Double_add", walker.VMTagName());
814 EXPECT_STREQ("[Unoptimized] double._add", walker.CurrentName());
815 EXPECT(walker.Down());
816 EXPECT_STREQ("[Unoptimized] double.+", walker.CurrentName());
817 EXPECT(walker.Down());
818 EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
819 EXPECT(!walker.Down());
820 }
821
822 double_class.SetTraceAllocation(false);
823 Invoke(root_library, "foo", 2, &args[0]);
824
825 {
826 StackZone zone(thread);
828 AllocationFilter filter(isolate->main_port(), double_class.id());
829 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
830 // We should still only have one allocation sample.
831 EXPECT_EQ(1, profile.sample_count());
832 }
833}
834
835ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
839 const char* kScript =
840 "List foo() => List.filled(4, null);\n"
841 "List bar() => List.filled(0, null, growable: true);\n";
842 const Library& root_library = Library::Handle(LoadTestScript(kScript));
843 Isolate* isolate = thread->isolate();
844
845 const Class& array_class =
846 Class::Handle(isolate->group()->object_store()->array_class());
847 EXPECT(!array_class.IsNull());
848
849 Invoke(root_library, "foo");
850
851 {
852 StackZone zone(thread);
854 AllocationFilter filter(isolate->main_port(), array_class.id());
855 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
856 // We should have no allocation samples.
857 EXPECT_EQ(0, profile.sample_count());
858 }
859
860 array_class.SetTraceAllocation(true);
861 Invoke(root_library, "foo");
862
863 {
864 StackZone zone(thread);
866 AllocationFilter filter(isolate->main_port(), array_class.id());
867 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
868 // We should have one allocation sample.
869 EXPECT_EQ(1, profile.sample_count());
871
872 EXPECT_STREQ("DRT_AllocateArray", walker.VMTagName());
873 EXPECT_STREQ("[Stub] AllocateArray", walker.CurrentName());
874 EXPECT(walker.Down());
875 EXPECT_STREQ("[Unoptimized] new _List", walker.CurrentName());
876 EXPECT(walker.Down());
877 EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
878 EXPECT(!walker.Down());
879 }
880
881 array_class.SetTraceAllocation(false);
882 Invoke(root_library, "foo");
883
884 {
885 StackZone zone(thread);
887 AllocationFilter filter(isolate->main_port(), array_class.id());
888 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
889 // We should still only have one allocation sample.
890 EXPECT_EQ(1, profile.sample_count());
891 }
892
893 // Clear the samples.
895
896 // Compile bar (many List objects allocated).
897 Invoke(root_library, "bar");
898
899 // Enable again.
900 array_class.SetTraceAllocation(true);
901
902 // Run bar.
903 Invoke(root_library, "bar");
904
905 {
906 StackZone zone(thread);
908 AllocationFilter filter(isolate->main_port(), array_class.id());
909 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
910 // We should have no allocation samples, since empty
911 // growable lists use a shared backing.
912 EXPECT_EQ(0, profile.sample_count());
913 }
914}
915
916ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
920 const char* kScript =
921 "var msg1 = 'a';\n"
922 "foo() {\n"
923 " var msg = msg1 + msg1;\n"
924 " return (x) { return '$msg + $msg'; }(msg);\n"
925 "}\n";
926 const Library& root_library = Library::Handle(LoadTestScript(kScript));
927 Isolate* isolate = thread->isolate();
928
929 const Class& context_class = Class::Handle(Object::context_class());
930 EXPECT(!context_class.IsNull());
931
932 Invoke(root_library, "foo");
933
934 {
935 StackZone zone(thread);
937 AllocationFilter filter(isolate->main_port(), context_class.id());
938 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
939 // We should have no allocation samples.
940 EXPECT_EQ(0, profile.sample_count());
941 }
942
943 context_class.SetTraceAllocation(true);
944 Invoke(root_library, "foo");
945
946 {
947 StackZone zone(thread);
949 AllocationFilter filter(isolate->main_port(), context_class.id());
950 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
951 // We should have one allocation sample.
952 EXPECT_EQ(1, profile.sample_count());
954
955 EXPECT_STREQ("DRT_AllocateContext", walker.VMTagName());
956 EXPECT_STREQ("[Stub] AllocateContext", walker.CurrentName());
957 EXPECT(walker.Down());
958 EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
959 EXPECT(!walker.Down());
960 }
961
962 context_class.SetTraceAllocation(false);
963 Invoke(root_library, "foo");
964
965 {
966 StackZone zone(thread);
968 AllocationFilter filter(isolate->main_port(), context_class.id());
969 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
970 // We should still only have one allocation sample.
971 EXPECT_EQ(1, profile.sample_count());
972 }
973}
974
975ISOLATE_UNIT_TEST_CASE(Profiler_ClosureAllocation) {
979 const char* kScript =
980 "var msg1 = 'a';\n"
981 "\n"
982 "foo() {\n"
983 " var msg = msg1 + msg1;\n"
984 " var msg2 = msg + msg;\n"
985 " return (x, y, z, w) { return '$x + $y + $z'; }(msg, msg2, msg, msg);\n"
986 "}\n"
987 "bar() {\n"
988 " var msg = msg1 + msg1;\n"
989 " var msg2 = msg + msg;\n"
990 " return (x, y) { return '$x + $y'; }(msg, msg2);\n"
991 "}\n";
992
993 const Library& root_library = Library::Handle(LoadTestScript(kScript));
994 Isolate* isolate = thread->isolate();
995
996 const Class& closure_class =
997 Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
998 EXPECT(!closure_class.IsNull());
999 closure_class.SetTraceAllocation(true);
1000
1001 // Invoke "foo" which during compilation, triggers a closure allocation.
1002 Invoke(root_library, "foo");
1003
1004 {
1005 StackZone zone(thread);
1007 AllocationFilter filter(isolate->main_port(), closure_class.id());
1008 filter.set_enable_vm_ticks(true);
1009 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1010 // We should have one allocation sample.
1011 EXPECT_EQ(1, profile.sample_count());
1012 ProfileStackWalker walker(&profile);
1013
1014 EXPECT_SUBSTRING("DRT_AllocateClosure", walker.VMTagName());
1015 EXPECT_STREQ("[Stub] AllocateClosure", walker.CurrentName());
1016 EXPECT(walker.Down());
1017 EXPECT_SUBSTRING("foo", walker.CurrentName());
1018 EXPECT(!walker.Down());
1019 }
1020
1021 // Disable allocation tracing for Closure.
1022 closure_class.SetTraceAllocation(false);
1023
1024 // Invoke "bar" which during compilation, triggers a closure allocation.
1025 Invoke(root_library, "bar");
1026
1027 {
1028 StackZone zone(thread);
1030 AllocationFilter filter(isolate->main_port(), closure_class.id());
1031 filter.set_enable_vm_ticks(true);
1032 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1033 // We should still only have one allocation sample.
1034 EXPECT_EQ(1, profile.sample_count());
1035 }
1036}
1037
1038ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
1042 const char* kScript =
1043 "import 'dart:typed_data';\n"
1044 "List foo() => new Float32List(4);\n";
1045 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1046 Isolate* isolate = thread->isolate();
1047
1048 const Library& typed_data_library =
1049 Library::Handle(isolate->group()->object_store()->typed_data_library());
1050
1051 const Class& float32_list_class =
1052 Class::Handle(GetClass(typed_data_library, "_Float32List"));
1053 EXPECT(!float32_list_class.IsNull());
1054
1055 Invoke(root_library, "foo");
1056
1057 {
1058 StackZone zone(thread);
1060 AllocationFilter filter(isolate->main_port(), float32_list_class.id());
1061 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1062 // We should have no allocation samples.
1063 EXPECT_EQ(0, profile.sample_count());
1064 }
1065
1066 float32_list_class.SetTraceAllocation(true);
1067 Invoke(root_library, "foo");
1068
1069 {
1070 StackZone zone(thread);
1072 AllocationFilter filter(isolate->main_port(), float32_list_class.id());
1073 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1074 // We should have one allocation sample.
1075 EXPECT_EQ(1, profile.sample_count());
1076 ProfileStackWalker walker(&profile);
1077
1078 EXPECT_STREQ("DRT_AllocateTypedData", walker.VMTagName());
1079 EXPECT_STREQ("[Stub] AllocateFloat32Array", walker.CurrentName());
1080 EXPECT(walker.Down());
1081 EXPECT_STREQ("[Unoptimized] new Float32List", walker.CurrentName());
1082 EXPECT(walker.Down());
1083 EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
1084 EXPECT(!walker.Down());
1085 }
1086
1087 float32_list_class.SetTraceAllocation(false);
1088 Invoke(root_library, "foo");
1089
1090 {
1091 StackZone zone(thread);
1093 AllocationFilter filter(isolate->main_port(), float32_list_class.id());
1094 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1095 // We should still only have one allocation sample.
1096 EXPECT_EQ(1, profile.sample_count());
1097 }
1098
1099 float32_list_class.SetTraceAllocation(true);
1100 Invoke(root_library, "foo");
1101
1102 {
1103 StackZone zone(thread);
1105 AllocationFilter filter(isolate->main_port(), float32_list_class.id());
1106 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1107 // We should now have two allocation samples.
1108 EXPECT_EQ(2, profile.sample_count());
1109 }
1110}
1111
1112ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
1116 const char* kScript = "String foo(String a, String b) => a + b;";
1117 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1118 Isolate* isolate = thread->isolate();
1119
1120 const Class& one_byte_string_class =
1121 Class::Handle(isolate->group()->object_store()->one_byte_string_class());
1122 EXPECT(!one_byte_string_class.IsNull());
1123
1124 Dart_Handle args[2];
1125 {
1126 TransitionVMToNative transition(thread);
1127 args[0] = NewString("a");
1128 args[1] = NewString("b");
1129 }
1130
1131 Invoke(root_library, "foo", 2, &args[0]);
1132
1133 {
1134 StackZone zone(thread);
1136 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1137 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1138 // We should have no allocation samples.
1139 EXPECT_EQ(0, profile.sample_count());
1140 }
1141
1142 one_byte_string_class.SetTraceAllocation(true);
1143 Invoke(root_library, "foo", 2, &args[0]);
1144
1145 {
1146 StackZone zone(thread);
1148 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1149 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1150 // We should still only have one allocation sample.
1151 EXPECT_EQ(1, profile.sample_count());
1152 ProfileStackWalker walker(&profile);
1153
1154 EXPECT_STREQ("String_concat", walker.VMTagName());
1155 EXPECT_STREQ("[Unoptimized] _StringBase.+", walker.CurrentName());
1156 EXPECT(walker.Down());
1157 EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
1158 EXPECT(!walker.Down());
1159 }
1160
1161 one_byte_string_class.SetTraceAllocation(false);
1162 Invoke(root_library, "foo", 2, &args[0]);
1163
1164 {
1165 StackZone zone(thread);
1167 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1168 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1169 // We should still only have one allocation sample.
1170 EXPECT_EQ(1, profile.sample_count());
1171 }
1172
1173 one_byte_string_class.SetTraceAllocation(true);
1174 Invoke(root_library, "foo", 2, &args[0]);
1175
1176 {
1177 StackZone zone(thread);
1179 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1180 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1181 // We should now have two allocation samples.
1182 EXPECT_EQ(2, profile.sample_count());
1183 }
1184}
1185
1186ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
1190 const char* kScript = "String foo(String a, String b) => '$a | $b';";
1191 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1192 Isolate* isolate = thread->isolate();
1193
1194 const Class& one_byte_string_class =
1195 Class::Handle(isolate->group()->object_store()->one_byte_string_class());
1196 EXPECT(!one_byte_string_class.IsNull());
1197
1198 Dart_Handle args[2];
1199 {
1200 TransitionVMToNative transition(thread);
1201 args[0] = NewString("a");
1202 args[1] = NewString("b");
1203 }
1204
1205 Invoke(root_library, "foo", 2, &args[0]);
1206
1207 {
1208 StackZone zone(thread);
1210 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1211 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1212 // We should have no allocation samples.
1213 EXPECT_EQ(0, profile.sample_count());
1214 }
1215
1216 one_byte_string_class.SetTraceAllocation(true);
1217 Invoke(root_library, "foo", 2, &args[0]);
1218
1219 {
1220 StackZone zone(thread);
1222 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1223 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1224 // We should still only have one allocation sample.
1225 EXPECT_EQ(1, profile.sample_count());
1226 ProfileStackWalker walker(&profile);
1227
1228 EXPECT_STREQ("Internal_allocateOneByteString", walker.VMTagName());
1229 EXPECT(walker.Down());
1230 EXPECT_STREQ("[Unoptimized] String._allocate", walker.CurrentName());
1231 EXPECT(walker.Down());
1232 EXPECT_STREQ("[Unoptimized] String._concatAll", walker.CurrentName());
1233 EXPECT(walker.Down());
1234 EXPECT_STREQ("[Unoptimized] _StringBase._interpolate",
1235 walker.CurrentName());
1236 EXPECT(walker.Down());
1237 EXPECT_STREQ("[Unoptimized] foo", walker.CurrentName());
1238 EXPECT(!walker.Down());
1239 }
1240
1241 one_byte_string_class.SetTraceAllocation(false);
1242 Invoke(root_library, "foo", 2, &args[0]);
1243
1244 {
1245 StackZone zone(thread);
1247 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1248 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1249 // We should still only have one allocation sample.
1250 EXPECT_EQ(1, profile.sample_count());
1251 }
1252
1253 one_byte_string_class.SetTraceAllocation(true);
1254 Invoke(root_library, "foo", 2, &args[0]);
1255
1256 {
1257 StackZone zone(thread);
1259 AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
1260 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1261 // We should now have two allocation samples.
1262 EXPECT_EQ(2, profile.sample_count());
1263 }
1264}
1265
1266ISOLATE_UNIT_TEST_CASE(Profiler_FunctionInline) {
1270 SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 30000);
1271
1272 const char* kScript =
1273 "class A {\n"
1274 " var a;\n"
1275 " var b;\n"
1276 "}\n"
1277 "class B {\n"
1278 " static choo(bool alloc) {\n"
1279 " if (alloc) return new A();\n"
1280 " return alloc && alloc && !alloc;\n"
1281 " }\n"
1282 " static foo(bool alloc) {\n"
1283 " choo(alloc);\n"
1284 " }\n"
1285 " static boo(bool alloc) {\n"
1286 " for (var i = 0; i < 50000; i++) {\n"
1287 " foo(alloc);\n"
1288 " }\n"
1289 " }\n"
1290 "}\n"
1291 "main() {\n"
1292 " B.boo(false);\n"
1293 "}\n"
1294 "mainA() {\n"
1295 " B.boo(true);\n"
1296 "}\n";
1297
1298 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1299
1300 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1301 EXPECT(!class_a.IsNull());
1302
1303 // Compile "main".
1304 Invoke(root_library, "main");
1305 // Compile "mainA".
1306 Invoke(root_library, "mainA");
1307 // At this point B.boo should be optimized and inlined B.foo and B.choo.
1308
1309 {
1310 Thread* thread = Thread::Current();
1311 Isolate* isolate = thread->isolate();
1312 StackZone zone(thread);
1314 AllocationFilter filter(isolate->main_port(), class_a.id());
1315 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1316 // We should have no allocation samples.
1317 EXPECT_EQ(0, profile.sample_count());
1318 }
1319
1320 // Turn on allocation tracing for A.
1321 class_a.SetTraceAllocation(true);
1322
1323 // Allocate 50,000 instances of A.
1324 Invoke(root_library, "mainA");
1325
1326 {
1327 Thread* thread = Thread::Current();
1328 Isolate* isolate = thread->isolate();
1329 StackZone zone(thread);
1331 AllocationFilter filter(isolate->main_port(), class_a.id());
1332 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1333 // We should have 50,000 allocation samples.
1334 EXPECT_EQ(50000, profile.sample_count());
1335 {
1336 ProfileStackWalker walker(&profile);
1337 // We have two code objects: mainA and B.boo.
1338 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1339#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1340 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1341#else
1342 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1343#endif
1344 EXPECT_EQ(50000, walker.CurrentExclusiveTicks());
1345 EXPECT(walker.Down());
1346 EXPECT_STREQ("[Optimized] B.boo", walker.CurrentName());
1347 EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
1348 EXPECT(walker.Down());
1349 EXPECT_STREQ("[Unoptimized] mainA", walker.CurrentName());
1350 EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
1351 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1352 EXPECT(!walker.Down());
1353 }
1354 {
1355 ProfileStackWalker walker(&profile, true);
1356 // Inline expansion should show us the complete call chain:
1357 // mainA -> B.boo -> B.foo -> B.choo.
1358 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1359#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1360 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1361#else
1362 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1363#endif
1364 EXPECT_EQ(50000, walker.CurrentExclusiveTicks());
1365 EXPECT(walker.Down());
1366 EXPECT_STREQ("B.choo", walker.CurrentName());
1367 EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
1368 EXPECT(walker.Down());
1369 EXPECT_STREQ("B.foo", walker.CurrentName());
1370 EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
1371 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1372 EXPECT(walker.Down());
1373 EXPECT_STREQ("B.boo", walker.CurrentName());
1374 EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
1375 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1376 EXPECT(walker.Down());
1377 EXPECT_STREQ("mainA", walker.CurrentName());
1378 EXPECT_EQ(50000, walker.CurrentInclusiveTicks());
1379 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1380 EXPECT(!walker.Down());
1381 }
1382 }
1383}
1384
1385ISOLATE_UNIT_TEST_CASE(Profiler_InliningIntervalBoundary) {
1386 // The PC of frames below the top frame is a call's return address,
1387 // which can belong to a different inlining interval than the call.
1388 // This test checks the profiler service takes this into account; see
1389 // ProfileBuilder::ProcessFrame.
1390
1394 SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 30000);
1395
1396 const char* kScript =
1397 "class A {\n"
1398 "}\n"
1399 "bool alloc = false;"
1400 "maybeAlloc() {\n"
1401 " try {\n"
1402 " if (alloc) new A();\n"
1403 " } catch (e) {\n"
1404 " }\n"
1405 "}\n"
1406 "right() => maybeAlloc();\n"
1407 "doNothing() {\n"
1408 " try {\n"
1409 " } catch (e) {\n"
1410 " }\n"
1411 "}\n"
1412 "wrong() => doNothing();\n"
1413 "a() {\n"
1414 " try {\n"
1415 " right();\n"
1416 " wrong();\n"
1417 " } catch (e) {\n"
1418 " }\n"
1419 "}\n"
1420 "mainNoAlloc() {\n"
1421 " for (var i = 0; i < 20000; i++) {\n"
1422 " a();\n"
1423 " }\n"
1424 "}\n"
1425 "mainAlloc() {\n"
1426 " alloc = true;\n"
1427 " a();\n"
1428 "}\n";
1429
1430 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1431
1432 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1433 EXPECT(!class_a.IsNull());
1434
1435 // Compile and optimize.
1436 Invoke(root_library, "mainNoAlloc");
1437 Invoke(root_library, "mainAlloc");
1438
1439 // At this point a should be optimized and have inlined both right and wrong,
1440 // but not maybeAllocate or doNothing.
1441 Function& func = Function::Handle();
1442 func = GetFunction(root_library, "a");
1443 EXPECT(!func.is_inlinable());
1444 EXPECT(func.HasOptimizedCode());
1445 func = GetFunction(root_library, "right");
1446 EXPECT(func.is_inlinable());
1447 func = GetFunction(root_library, "wrong");
1448 EXPECT(func.is_inlinable());
1449 func = GetFunction(root_library, "doNothing");
1450 EXPECT(!func.is_inlinable());
1451 func = GetFunction(root_library, "maybeAlloc");
1452 EXPECT(!func.is_inlinable());
1453
1454 {
1455 Thread* thread = Thread::Current();
1456 Isolate* isolate = thread->isolate();
1457 StackZone zone(thread);
1459 AllocationFilter filter(isolate->main_port(), class_a.id());
1460 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1461 // We should have no allocation samples.
1462 EXPECT_EQ(0, profile.sample_count());
1463 }
1464
1465 // Turn on allocation tracing for A.
1466 class_a.SetTraceAllocation(true);
1467
1468 Invoke(root_library, "mainAlloc");
1469
1470 {
1471 Thread* thread = Thread::Current();
1472 Isolate* isolate = thread->isolate();
1473 StackZone zone(thread);
1475 AllocationFilter filter(isolate->main_port(), class_a.id());
1476 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1477 EXPECT_EQ(1, profile.sample_count());
1478 ProfileStackWalker walker(&profile, true);
1479
1480 // Inline expansion should show us the complete call chain:
1481 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1482#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1483 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1484#else
1485 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1486#endif
1487 EXPECT(walker.Down());
1488 EXPECT_STREQ("maybeAlloc", walker.CurrentName());
1489 EXPECT(walker.Down());
1490 EXPECT_STREQ("right", walker.CurrentName());
1491 EXPECT(walker.Down());
1492 EXPECT_STREQ("a", walker.CurrentName());
1493 EXPECT(walker.Down());
1494 EXPECT_STREQ("mainAlloc", walker.CurrentName());
1495 }
1496}
1497
1498ISOLATE_UNIT_TEST_CASE(Profiler_ChainedSamples) {
1500 MaxProfileDepthScope mpds(32);
1502
1503 // Each sample holds 8 stack frames.
1504 // This chain is 20 stack frames deep.
1505 const char* kScript =
1506 "class A {\n"
1507 " var a;\n"
1508 " var b;\n"
1509 "}\n"
1510 "class B {\n"
1511 " static boo() {\n"
1512 " return new A();\n"
1513 " }\n"
1514 "}\n"
1515 "go() => init();\n"
1516 "init() => secondInit();\n"
1517 "secondInit() => apple();\n"
1518 "apple() => banana();\n"
1519 "banana() => cantaloupe();\n"
1520 "cantaloupe() => dog();\n"
1521 "dog() => elephant();\n"
1522 "elephant() => fred();\n"
1523 "fred() => granola();\n"
1524 "granola() => haystack();\n"
1525 "haystack() => ice();\n"
1526 "ice() => jeep();\n"
1527 "jeep() => kindle();\n"
1528 "kindle() => lemon();\n"
1529 "lemon() => mayo();\n"
1530 "mayo() => napkin();\n"
1531 "napkin() => orange();\n"
1532 "orange() => B.boo();\n"
1533 "main() {\n"
1534 " return go();\n"
1535 "}\n";
1536
1537 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1538
1539 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1540 EXPECT(!class_a.IsNull());
1541 class_a.SetTraceAllocation(true);
1542
1543 Invoke(root_library, "main");
1544
1545 {
1546 Thread* thread = Thread::Current();
1547 Isolate* isolate = thread->isolate();
1548 StackZone zone(thread);
1550 AllocationFilter filter(isolate->main_port(), class_a.id());
1551 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1552 // We should have 1 allocation sample.
1553 EXPECT_EQ(1, profile.sample_count());
1554 ProfileStackWalker walker(&profile);
1555
1556 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1557#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1558 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1559#else
1560 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1561#endif
1562 EXPECT(walker.Down());
1563 EXPECT_STREQ("[Unoptimized] B.boo", walker.CurrentName());
1564 EXPECT(walker.Down());
1565 EXPECT_STREQ("[Unoptimized] orange", walker.CurrentName());
1566 EXPECT(walker.Down());
1567 EXPECT_STREQ("[Unoptimized] napkin", walker.CurrentName());
1568 EXPECT(walker.Down());
1569 EXPECT_STREQ("[Unoptimized] mayo", walker.CurrentName());
1570 EXPECT(walker.Down());
1571 EXPECT_STREQ("[Unoptimized] lemon", walker.CurrentName());
1572 EXPECT(walker.Down());
1573 EXPECT_STREQ("[Unoptimized] kindle", walker.CurrentName());
1574 EXPECT(walker.Down());
1575 EXPECT_STREQ("[Unoptimized] jeep", walker.CurrentName());
1576 EXPECT(walker.Down());
1577 EXPECT_STREQ("[Unoptimized] ice", walker.CurrentName());
1578 EXPECT(walker.Down());
1579 EXPECT_STREQ("[Unoptimized] haystack", walker.CurrentName());
1580 EXPECT(walker.Down());
1581 EXPECT_STREQ("[Unoptimized] granola", walker.CurrentName());
1582 EXPECT(walker.Down());
1583 EXPECT_STREQ("[Unoptimized] fred", walker.CurrentName());
1584 EXPECT(walker.Down());
1585 EXPECT_STREQ("[Unoptimized] elephant", walker.CurrentName());
1586 EXPECT(walker.Down());
1587 EXPECT_STREQ("[Unoptimized] dog", walker.CurrentName());
1588 EXPECT(walker.Down());
1589 EXPECT_STREQ("[Unoptimized] cantaloupe", walker.CurrentName());
1590 EXPECT(walker.Down());
1591 EXPECT_STREQ("[Unoptimized] banana", walker.CurrentName());
1592 EXPECT(walker.Down());
1593 EXPECT_STREQ("[Unoptimized] apple", walker.CurrentName());
1594 EXPECT(walker.Down());
1595 EXPECT_STREQ("[Unoptimized] secondInit", walker.CurrentName());
1596 EXPECT(walker.Down());
1597 EXPECT_STREQ("[Unoptimized] init", walker.CurrentName());
1598 EXPECT(walker.Down());
1599 EXPECT_STREQ("[Unoptimized] go", walker.CurrentName());
1600 EXPECT(walker.Down());
1601 EXPECT_STREQ("[Unoptimized] main", walker.CurrentName());
1602 EXPECT(!walker.Down());
1603 }
1604}
1605
1606ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePosition) {
1610 const char* kScript =
1611 "class A {\n"
1612 " var a;\n"
1613 " var b;\n"
1614 " @pragma('vm:never-inline') A() { }\n"
1615 "}\n"
1616 "class B {\n"
1617 " @pragma('vm:prefer-inline')\n"
1618 " static boo() {\n"
1619 " return new A();\n"
1620 " }\n"
1621 "}\n"
1622 "main() {\n"
1623 " B.boo();\n"
1624 "}\n";
1625
1626 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1627
1628 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1629 EXPECT(!class_a.IsNull());
1630
1631 Invoke(root_library, "main");
1632
1633 // Turn on allocation tracing for A.
1634 class_a.SetTraceAllocation(true);
1635
1636 // Allocate one time.
1637 Invoke(root_library, "main");
1638
1639 {
1640 Thread* thread = Thread::Current();
1641 Isolate* isolate = thread->isolate();
1642 StackZone zone(thread);
1644 AllocationFilter filter(isolate->main_port(), class_a.id());
1645 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1646 // We should have one allocation samples.
1647 EXPECT_EQ(1, profile.sample_count());
1648 ProfileStackWalker walker(&profile, true);
1649
1650 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1651#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1652 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1653#else
1654 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1655#endif
1656 EXPECT_EQ(1, walker.CurrentExclusiveTicks());
1657 EXPECT(walker.Down());
1658 EXPECT_STREQ("B.boo", walker.CurrentName());
1659 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1660 EXPECT_STREQ("A", walker.CurrentToken());
1661 EXPECT(walker.Down());
1662 EXPECT_STREQ("main", walker.CurrentName());
1663 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1664 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1665 EXPECT_STREQ("boo", walker.CurrentToken());
1666 EXPECT(!walker.Down());
1667 }
1668}
1669
1670ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePositionOptimized) {
1674 const char* kScript =
1675 "class A {\n"
1676 " var a;\n"
1677 " var b;\n"
1678 " @pragma('vm:never-inline') A() { }\n"
1679 "}\n"
1680 "class B {\n"
1681 " @pragma('vm:prefer-inline')\n"
1682 " static boo() {\n"
1683 " return new A();\n"
1684 " }\n"
1685 "}\n"
1686 "main() {\n"
1687 " B.boo();\n"
1688 "}\n";
1689
1690 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1691
1692 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1693 EXPECT(!class_a.IsNull());
1694
1695 const Function& main = Function::Handle(GetFunction(root_library, "main"));
1696 EXPECT(!main.IsNull());
1697
1698 // Optimize quickly.
1699 SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 5);
1700 // Warm up function.
1701 while (true) {
1702 Invoke(root_library, "main");
1703 const Code& code = Code::Handle(main.CurrentCode());
1704 if (code.is_optimized()) {
1705 // Warmed up.
1706 break;
1707 }
1708 }
1709
1710 // Turn on allocation tracing for A.
1711 class_a.SetTraceAllocation(true);
1712
1713 // Allocate one time.
1714 Invoke(root_library, "main");
1715
1716 // Still optimized.
1717 const Code& code = Code::Handle(main.CurrentCode());
1718 EXPECT(code.is_optimized());
1719
1720 {
1721 Thread* thread = Thread::Current();
1722 Isolate* isolate = thread->isolate();
1723 StackZone zone(thread);
1725 AllocationFilter filter(isolate->main_port(), class_a.id());
1726 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1727 // We should have one allocation samples.
1728 EXPECT_EQ(1, profile.sample_count());
1729 ProfileStackWalker walker(&profile, true);
1730
1731 // Move down from the root.
1732 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1733#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1734 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1735#else
1736 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1737#endif
1738 EXPECT_EQ(1, walker.CurrentExclusiveTicks());
1739 EXPECT(walker.Down());
1740 EXPECT_STREQ("B.boo", walker.CurrentName());
1741 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1742 EXPECT_STREQ("A", walker.CurrentToken());
1743 EXPECT(walker.Down());
1744 EXPECT_STREQ("main", walker.CurrentName());
1745 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1746 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1747 EXPECT_STREQ("boo", walker.CurrentToken());
1748 EXPECT(!walker.Down());
1749 }
1750}
1751
1752ISOLATE_UNIT_TEST_CASE(Profiler_SourcePosition) {
1756 const char* kScript =
1757 "class A {\n"
1758 " var a;\n"
1759 " var b;\n"
1760 " @pragma('vm:never-inline') A() { }\n"
1761 "}\n"
1762 "class B {\n"
1763 " @pragma('vm:never-inline')\n"
1764 " static oats() {\n"
1765 " return boo();\n"
1766 " }\n"
1767 " @pragma('vm:prefer-inline')\n"
1768 " static boo() {\n"
1769 " return new A();\n"
1770 " }\n"
1771 "}\n"
1772 "class C {\n"
1773 " @pragma('vm:never-inline') bacon() {\n"
1774 " return fox();\n"
1775 " }\n"
1776 " @pragma('vm:prefer-inline') fox() {\n"
1777 " return B.oats();\n"
1778 " }\n"
1779 "}\n"
1780 "main() {\n"
1781 " new C()..bacon();\n"
1782 "}\n";
1783
1784 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1785
1786 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1787 EXPECT(!class_a.IsNull());
1788
1789 Invoke(root_library, "main");
1790
1791 // Turn on allocation tracing for A.
1792 class_a.SetTraceAllocation(true);
1793
1794 // Allocate one time.
1795 Invoke(root_library, "main");
1796
1797 {
1798 Thread* thread = Thread::Current();
1799 Isolate* isolate = thread->isolate();
1800 StackZone zone(thread);
1802 AllocationFilter filter(isolate->main_port(), class_a.id());
1803 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1804 // We should have one allocation samples.
1805 EXPECT_EQ(1, profile.sample_count());
1806 ProfileStackWalker walker(&profile, true);
1807
1808 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1809#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1810 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1811#else
1812 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1813#endif
1814 EXPECT_EQ(1, walker.CurrentExclusiveTicks());
1815 EXPECT(walker.Down());
1816 EXPECT_STREQ("B.boo", walker.CurrentName());
1817 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1818 EXPECT_STREQ("A", walker.CurrentToken());
1819 EXPECT(walker.Down());
1820 EXPECT_STREQ("B.oats", walker.CurrentName());
1821 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1822 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1823 EXPECT_STREQ("boo", walker.CurrentToken());
1824 EXPECT(walker.Down());
1825 EXPECT_STREQ("C.fox", walker.CurrentName());
1826 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1827 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1828 EXPECT_STREQ("oats", walker.CurrentToken());
1829 EXPECT(walker.Down());
1830 EXPECT_STREQ("C.bacon", walker.CurrentName());
1831 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1832 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1833 EXPECT_STREQ("fox", walker.CurrentToken());
1834 EXPECT(walker.Down());
1835 EXPECT_STREQ("main", walker.CurrentName());
1836 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1837 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1838 EXPECT_STREQ("bacon", walker.CurrentToken());
1839 EXPECT(!walker.Down());
1840 }
1841}
1842
1843ISOLATE_UNIT_TEST_CASE(Profiler_SourcePositionOptimized) {
1847 const char* kScript =
1848 "class A {\n"
1849 " var a;\n"
1850 " var b;\n"
1851 " @pragma('vm:never-inline') A() { }\n"
1852 "}\n"
1853 "class B {\n"
1854 " @pragma('vm:never-inline')\n"
1855 " static oats() {\n"
1856 " return boo();\n"
1857 " }\n"
1858 " @pragma('vm:prefer-inline')\n"
1859 " static boo() {\n"
1860 " return new A();\n"
1861 " }\n"
1862 "}\n"
1863 "class C {\n"
1864 " @pragma('vm:never-inline') bacon() {\n"
1865 " return fox();\n"
1866 " }\n"
1867 " @pragma('vm:prefer-inline') fox() {\n"
1868 " return B.oats();\n"
1869 " }\n"
1870 "}\n"
1871 "main() {\n"
1872 " new C()..bacon();\n"
1873 "}\n";
1874
1875 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1876
1877 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1878 EXPECT(!class_a.IsNull());
1879
1880 const Function& main = Function::Handle(GetFunction(root_library, "main"));
1881 EXPECT(!main.IsNull());
1882
1883 // Optimize quickly.
1884 SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 5);
1885 // Warm up function.
1886 while (true) {
1887 Invoke(root_library, "main");
1888 const Code& code = Code::Handle(main.CurrentCode());
1889 if (code.is_optimized()) {
1890 // Warmed up.
1891 break;
1892 }
1893 }
1894
1895 // Turn on allocation tracing for A.
1896 class_a.SetTraceAllocation(true);
1897
1898 // Allocate one time.
1899 Invoke(root_library, "main");
1900
1901 // Still optimized.
1902 const Code& code = Code::Handle(main.CurrentCode());
1903 EXPECT(code.is_optimized());
1904
1905 {
1906 Thread* thread = Thread::Current();
1907 Isolate* isolate = thread->isolate();
1908 StackZone zone(thread);
1910 AllocationFilter filter(isolate->main_port(), class_a.id());
1911 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
1912 // We should have one allocation samples.
1913 EXPECT_EQ(1, profile.sample_count());
1914 ProfileStackWalker walker(&profile, true);
1915
1916 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
1917#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
1918 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
1919#else
1920 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
1921#endif
1922 EXPECT_EQ(1, walker.CurrentExclusiveTicks());
1923 EXPECT(walker.Down());
1924 EXPECT_STREQ("B.boo", walker.CurrentName());
1925 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1926 EXPECT_STREQ("A", walker.CurrentToken());
1927 EXPECT(walker.Down());
1928 EXPECT_STREQ("B.oats", walker.CurrentName());
1929 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1930 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1931 EXPECT_STREQ("boo", walker.CurrentToken());
1932 EXPECT(walker.Down());
1933 EXPECT_STREQ("C.fox", walker.CurrentName());
1934 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1935 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1936 EXPECT_STREQ("oats", walker.CurrentToken());
1937 EXPECT(walker.Down());
1938 EXPECT_STREQ("C.bacon", walker.CurrentName());
1939 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1940 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1941 EXPECT_STREQ("fox", walker.CurrentToken());
1942 EXPECT(walker.Down());
1943 EXPECT_STREQ("main", walker.CurrentName());
1944 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
1945 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
1946 EXPECT_STREQ("bacon", walker.CurrentToken());
1947 EXPECT(!walker.Down());
1948 }
1949}
1950
1951ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePosition) {
1955 const char* kScript =
1956 "class A {\n"
1957 " var a;\n"
1958 " var b;\n"
1959 " @pragma('vm:never-inline') A() { }\n"
1960 "}\n"
1961 "class B {\n"
1962 " @pragma('vm:never-inline')\n"
1963 " static oats() {\n"
1964 " return boo();\n"
1965 " }\n"
1966 " @pragma('vm:prefer-inline')\n"
1967 " static boo() {\n"
1968 " return new A();\n"
1969 " }\n"
1970 "}\n"
1971 "class C {\n"
1972 " @pragma('vm:never-inline') bacon() {\n"
1973 " return this + this;\n"
1974 " }\n"
1975 " @pragma('vm:prefer-inline') operator+(C other) {\n"
1976 " return fox();\n"
1977 " }\n"
1978 " @pragma('vm:prefer-inline') fox() {\n"
1979 " return B.oats();\n"
1980 " }\n"
1981 "}\n"
1982 "main() {\n"
1983 " new C()..bacon();\n"
1984 "}\n";
1985
1986 const Library& root_library = Library::Handle(LoadTestScript(kScript));
1987
1988 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
1989 EXPECT(!class_a.IsNull());
1990
1991 Invoke(root_library, "main");
1992
1993 // Turn on allocation tracing for A.
1994 class_a.SetTraceAllocation(true);
1995
1996 // Allocate one time.
1997 Invoke(root_library, "main");
1998
1999 {
2000 Thread* thread = Thread::Current();
2001 Isolate* isolate = thread->isolate();
2002 StackZone zone(thread);
2004 AllocationFilter filter(isolate->main_port(), class_a.id());
2005 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
2006 // We should have one allocation samples.
2007 EXPECT_EQ(1, profile.sample_count());
2008 ProfileStackWalker walker(&profile, true);
2009
2010 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
2011#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
2012 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
2013#else
2014 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
2015#endif
2016 EXPECT_EQ(1, walker.CurrentExclusiveTicks());
2017 EXPECT(walker.Down());
2018 EXPECT_STREQ("B.boo", walker.CurrentName());
2019 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2020 EXPECT_STREQ("A", walker.CurrentToken());
2021 EXPECT(walker.Down());
2022 EXPECT_STREQ("B.oats", walker.CurrentName());
2023 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2024 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2025 EXPECT_STREQ("boo", walker.CurrentToken());
2026 EXPECT(walker.Down());
2027 EXPECT_STREQ("C.fox", walker.CurrentName());
2028 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2029 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2030 EXPECT_STREQ("oats", walker.CurrentToken());
2031 EXPECT(walker.Down());
2032 EXPECT_STREQ("C.+", walker.CurrentName());
2033 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2034 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2035 EXPECT_STREQ("fox", walker.CurrentToken());
2036 EXPECT(walker.Down());
2037 EXPECT_STREQ("C.bacon", walker.CurrentName());
2038 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2039 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2040 EXPECT_STREQ("+", walker.CurrentToken());
2041 EXPECT(walker.Down());
2042 EXPECT_STREQ("main", walker.CurrentName());
2043 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2044 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2045 EXPECT_STREQ("bacon", walker.CurrentToken());
2046 EXPECT(!walker.Down());
2047 }
2048}
2049
2050ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePositionOptimized) {
2054
2055 const char* kScript =
2056 "class A {\n"
2057 " var a;\n"
2058 " var b;\n"
2059 " @pragma('vm:never-inline') A() { }\n"
2060 "}\n"
2061 "class B {\n"
2062 " @pragma('vm:never-inline')\n"
2063 " static oats() {\n"
2064 " return boo();\n"
2065 " }\n"
2066 " @pragma('vm:prefer-inline')\n"
2067 " static boo() {\n"
2068 " return new A();\n"
2069 " }\n"
2070 "}\n"
2071 "class C {\n"
2072 " @pragma('vm:never-inline') bacon() {\n"
2073 " return this + this;\n"
2074 " }\n"
2075 " @pragma('vm:prefer-inline') operator+(C other) {\n"
2076 " return fox();\n"
2077 " }\n"
2078 " @pragma('vm:prefer-inline') fox() {\n"
2079 " return B.oats();\n"
2080 " }\n"
2081 "}\n"
2082 "main() {\n"
2083 " new C()..bacon();\n"
2084 "}\n";
2085
2086 const Library& root_library = Library::Handle(LoadTestScript(kScript));
2087
2088 const Class& class_a = Class::Handle(GetClass(root_library, "A"));
2089 EXPECT(!class_a.IsNull());
2090
2091 const Function& main = Function::Handle(GetFunction(root_library, "main"));
2092 EXPECT(!main.IsNull());
2093
2094 // Optimize quickly.
2095 SetFlagScope<int> sfs(&FLAG_optimization_counter_threshold, 5);
2096 // Warm up function.
2097 while (true) {
2098 Invoke(root_library, "main");
2099 const Code& code = Code::Handle(main.CurrentCode());
2100 if (code.is_optimized()) {
2101 // Warmed up.
2102 break;
2103 }
2104 }
2105
2106 // Turn on allocation tracing for A.
2107 class_a.SetTraceAllocation(true);
2108
2109 // Allocate one time.
2110 Invoke(root_library, "main");
2111
2112 // Still optimized.
2113 const Code& code = Code::Handle(main.CurrentCode());
2114 EXPECT(code.is_optimized());
2115
2116 {
2117 Thread* thread = Thread::Current();
2118 Isolate* isolate = thread->isolate();
2119 StackZone zone(thread);
2121 AllocationFilter filter(isolate->main_port(), class_a.id());
2122 profile.Build(thread, isolate, &filter, Profiler::sample_block_buffer());
2123 // We should have one allocation samples.
2124 EXPECT_EQ(1, profile.sample_count());
2125 ProfileStackWalker walker(&profile, true);
2126
2127 EXPECT_STREQ("DRT_AllocateObject", walker.VMTagName());
2128#if defined(TARGET_ARCH_IA32) // Alloc. stub not impl. for ia32.
2129 EXPECT_STREQ("[Stub] Allocate A", walker.CurrentName());
2130#else
2131 EXPECT_STREQ("[Stub] AllocateObjectSlow", walker.CurrentName());
2132#endif
2133 EXPECT_EQ(1, walker.CurrentExclusiveTicks());
2134 EXPECT(walker.Down());
2135 EXPECT_STREQ("B.boo", walker.CurrentName());
2136 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2137 EXPECT_STREQ("A", walker.CurrentToken());
2138 EXPECT(walker.Down());
2139 EXPECT_STREQ("B.oats", walker.CurrentName());
2140 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2141 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2142 EXPECT_STREQ("boo", walker.CurrentToken());
2143 EXPECT(walker.Down());
2144 EXPECT_STREQ("C.fox", walker.CurrentName());
2145 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2146 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2147 EXPECT_STREQ("oats", walker.CurrentToken());
2148 EXPECT(walker.Down());
2149 EXPECT_STREQ("C.+", walker.CurrentName());
2150 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2151 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2152 EXPECT_STREQ("fox", walker.CurrentToken());
2153 EXPECT(walker.Down());
2154 EXPECT_STREQ("C.bacon", walker.CurrentName());
2155 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2156 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2157 EXPECT_STREQ("+", walker.CurrentToken());
2158 EXPECT(walker.Down());
2159 EXPECT_STREQ("main", walker.CurrentName());
2160 EXPECT_EQ(1, walker.CurrentInclusiveTicks());
2161 EXPECT_EQ(0, walker.CurrentExclusiveTicks());
2162 EXPECT_STREQ("bacon", walker.CurrentToken());
2163 EXPECT(!walker.Down());
2164 }
2165}
2166
2167static void InsertFakeSample(uword* pc_offsets) {
2168 Isolate* isolate = Isolate::Current();
2171 ASSERT(sample != nullptr);
2172 sample->Init(isolate->main_port(), OS::GetCurrentMonotonicMicros(),
2173 OSThread::Current()->trace_id());
2175
2176 intptr_t i = 0;
2177 while (pc_offsets[i] != 0) {
2178 // When we collect a real stack trace, all PCs collected aside from the
2179 // executing one (i == 0) are actually return addresses. Return addresses
2180 // are one byte beyond the call instruction that is executing. The profiler
2181 // accounts for this and subtracts one from these addresses when querying
2182 // inline and token position ranges. To be consistent with real stack
2183 // traces, we add one byte to all PCs except the executing one.
2184 // See OffsetForPC in profiler_service.cc for more context.
2185 const intptr_t return_address_offset = i > 0 ? 1 : 0;
2186 sample->SetAt(i, pc_offsets[i] + return_address_offset);
2187 i++;
2188 }
2189 sample->SetAt(i, 0);
2190}
2191
2194 GrowableArray<TokenPosition> token_positions;
2195 for (uword pc_offset = 0; pc_offset < code.Size(); pc_offset++) {
2196 code.GetInlinedFunctionsAtInstruction(pc_offset, &functions,
2197 &token_positions);
2198 if (token_positions[0] == tp) {
2199 return code.PayloadStart() + pc_offset;
2200 }
2201 }
2202
2203 return 0;
2204}
2205
2206ISOLATE_UNIT_TEST_CASE(Profiler_GetSourceReport) {
2208 const char* kScript =
2209 "int doWork(i) => i * i;\n"
2210 "int main() {\n"
2211 " int sum = 0;\n"
2212 " for (int i = 0; i < 100; i++) {\n"
2213 " sum += doWork(i);\n"
2214 " }\n"
2215 " return sum;\n"
2216 "}\n";
2217
2218 // Token position of * in `i * i`.
2219 const TokenPosition squarePosition = TokenPosition::Deserialize(19);
2220
2221 // Token position of the call to `doWork`.
2222 const TokenPosition callPosition = TokenPosition::Deserialize(95);
2223
2225 // Disable profiling for this thread.
2227
2229
2230 SampleBlockBuffer* sample_block_buffer = Profiler::sample_block_buffer();
2231 ASSERT(sample_block_buffer != nullptr);
2232
2233 const Library& root_library = Library::Handle(LoadTestScript(kScript));
2234
2235 // Invoke main so that it gets compiled.
2236 Invoke(root_library, "main");
2237
2238 {
2239 // Clear the profile for this isolate.
2241 sample_block_buffer->VisitSamples(&cpv);
2242 }
2243
2244 // Query the code object for main and determine the PC at some token
2245 // positions.
2246 const Function& main = Function::Handle(GetFunction(root_library, "main"));
2247 EXPECT(!main.IsNull());
2248
2249 const Function& do_work =
2250 Function::Handle(GetFunction(root_library, "doWork"));
2251 EXPECT(!do_work.IsNull());
2252
2253 const Script& script = Script::Handle(main.script());
2254 EXPECT(!script.IsNull());
2255
2256 const Code& main_code = Code::Handle(main.CurrentCode());
2257 EXPECT(!main_code.IsNull());
2258
2259 const Code& do_work_code = Code::Handle(do_work.CurrentCode());
2260 EXPECT(!do_work_code.IsNull());
2261
2262 // Dump code source map.
2263 do_work_code.DumpSourcePositions();
2264 main_code.DumpSourcePositions();
2265
2266 // Look up some source token position's pc.
2267 uword squarePositionPc = FindPCForTokenPosition(do_work_code, squarePosition);
2268 EXPECT(squarePositionPc != 0);
2269
2270 uword callPositionPc = FindPCForTokenPosition(main_code, callPosition);
2271 EXPECT(callPositionPc != 0);
2272
2273 // Look up some classifying token position's pc.
2274 uword controlFlowPc =
2275 FindPCForTokenPosition(do_work_code, TokenPosition::kControlFlow);
2276 EXPECT(controlFlowPc != 0);
2277
2278 // Insert fake samples.
2279
2280 // Sample 1:
2281 // squarePositionPc exclusive.
2282 // callPositionPc inclusive.
2283 uword sample1[] = {squarePositionPc, // doWork.
2284 callPositionPc, // main.
2285 0};
2286
2287 // Sample 2:
2288 // squarePositionPc exclusive.
2289 uword sample2[] = {
2290 squarePositionPc, // doWork.
2291 0,
2292 };
2293
2294 // Sample 3:
2295 // controlFlowPc exclusive.
2296 // callPositionPc inclusive.
2297 uword sample3[] = {controlFlowPc, // doWork.
2298 callPositionPc, // main.
2299 0};
2300
2303 InsertFakeSample(&sample3[0]);
2304
2305 // Generate source report for main.
2306 JSONStream js;
2307 {
2309 sourceReport.PrintJSON(&js, script, do_work.token_pos(),
2310 main.end_token_pos());
2311 }
2312
2313 // Verify positions in do_work.
2314 EXPECT_SUBSTRING("\"positions\":[\"ControlFlow\",19]", js.ToCString());
2315 // Verify exclusive ticks in do_work.
2316 EXPECT_SUBSTRING("\"exclusiveTicks\":[1,2]", js.ToCString());
2317 // Verify inclusive ticks in do_work.
2318 EXPECT_SUBSTRING("\"inclusiveTicks\":[1,2]", js.ToCString());
2319
2320 // Verify positions in main.
2321 EXPECT_SUBSTRING("\"positions\":[95]", js.ToCString());
2322 // Verify exclusive ticks in main.
2323 EXPECT_SUBSTRING("\"exclusiveTicks\":[0]", js.ToCString());
2324 // Verify inclusive ticks in main.
2325 EXPECT_SUBSTRING("\"inclusiveTicks\":[2]", js.ToCString());
2326}
2327
2328ISOLATE_UNIT_TEST_CASE(Profiler_ProfileCodeTableTest) {
2329 Zone* Z = Thread::Current()->zone();
2330
2332 EXPECT_EQ(table->length(), 0);
2333 EXPECT_EQ(table->FindCodeForPC(42), static_cast<ProfileCode*>(nullptr));
2334
2335 int64_t timestamp = 0;
2336 const AbstractCode null_code(Code::null());
2337
2338 ProfileCode* code1 = new (Z)
2339 ProfileCode(ProfileCode::kNativeCode, 50, 60, timestamp, null_code);
2340 EXPECT_EQ(table->InsertCode(code1), 0);
2341 EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(nullptr));
2342 EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(nullptr));
2343 EXPECT_EQ(table->FindCodeForPC(50), code1);
2344 EXPECT_EQ(table->FindCodeForPC(55), code1);
2345 EXPECT_EQ(table->FindCodeForPC(59), code1);
2346 EXPECT_EQ(table->FindCodeForPC(60), static_cast<ProfileCode*>(nullptr));
2347
2348 // Insert below all.
2349 ProfileCode* code2 = new (Z)
2350 ProfileCode(ProfileCode::kNativeCode, 10, 20, timestamp, null_code);
2351 EXPECT_EQ(table->InsertCode(code2), 0);
2352 EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(nullptr));
2353 EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(nullptr));
2354 EXPECT_EQ(table->FindCodeForPC(50), code1);
2355 EXPECT_EQ(table->FindCodeForPC(10), code2);
2356 EXPECT_EQ(table->FindCodeForPC(19), code2);
2357 EXPECT_EQ(table->FindCodeForPC(20), static_cast<ProfileCode*>(nullptr));
2358
2359 // Insert above all.
2360 ProfileCode* code3 = new (Z)
2361 ProfileCode(ProfileCode::kNativeCode, 80, 90, timestamp, null_code);
2362 EXPECT_EQ(table->InsertCode(code3), 2);
2363 EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(nullptr));
2364 EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(nullptr));
2365 EXPECT_EQ(table->FindCodeForPC(50), code1);
2366 EXPECT_EQ(table->FindCodeForPC(10), code2);
2367 EXPECT_EQ(table->FindCodeForPC(80), code3);
2368 EXPECT_EQ(table->FindCodeForPC(89), code3);
2369 EXPECT_EQ(table->FindCodeForPC(90), static_cast<ProfileCode*>(nullptr));
2370
2371 // Insert between.
2372 ProfileCode* code4 = new (Z)
2373 ProfileCode(ProfileCode::kNativeCode, 65, 75, timestamp, null_code);
2374 EXPECT_EQ(table->InsertCode(code4), 2);
2375 EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(nullptr));
2376 EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(nullptr));
2377 EXPECT_EQ(table->FindCodeForPC(50), code1);
2378 EXPECT_EQ(table->FindCodeForPC(10), code2);
2379 EXPECT_EQ(table->FindCodeForPC(80), code3);
2380 EXPECT_EQ(table->FindCodeForPC(65), code4);
2381 EXPECT_EQ(table->FindCodeForPC(74), code4);
2382 EXPECT_EQ(table->FindCodeForPC(75), static_cast<ProfileCode*>(nullptr));
2383
2384 // Insert overlapping left.
2385 ProfileCode* code5 = new (Z)
2386 ProfileCode(ProfileCode::kNativeCode, 15, 25, timestamp, null_code);
2387 EXPECT_EQ(table->InsertCode(code5), 0);
2388 EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(nullptr));
2389 EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(nullptr));
2390 EXPECT_EQ(table->FindCodeForPC(50), code1);
2391 EXPECT_EQ(table->FindCodeForPC(10), code2);
2392 EXPECT_EQ(table->FindCodeForPC(80), code3);
2393 EXPECT_EQ(table->FindCodeForPC(65), code4);
2394 EXPECT_EQ(table->FindCodeForPC(15), code2); // Merged left.
2395 EXPECT_EQ(table->FindCodeForPC(24), code2); // Merged left.
2396 EXPECT_EQ(table->FindCodeForPC(25), static_cast<ProfileCode*>(nullptr));
2397
2398 // Insert overlapping right.
2399 ProfileCode* code6 = new (Z)
2400 ProfileCode(ProfileCode::kNativeCode, 45, 55, timestamp, null_code);
2401 EXPECT_EQ(table->InsertCode(code6), 1);
2402 EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(nullptr));
2403 EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(nullptr));
2404 EXPECT_EQ(table->FindCodeForPC(50), code1);
2405 EXPECT_EQ(table->FindCodeForPC(10), code2);
2406 EXPECT_EQ(table->FindCodeForPC(80), code3);
2407 EXPECT_EQ(table->FindCodeForPC(65), code4);
2408 EXPECT_EQ(table->FindCodeForPC(15), code2); // Merged left.
2409 EXPECT_EQ(table->FindCodeForPC(24), code2); // Merged left.
2410 EXPECT_EQ(table->FindCodeForPC(45), code1); // Merged right.
2411 EXPECT_EQ(table->FindCodeForPC(54), code1); // Merged right.
2412 EXPECT_EQ(table->FindCodeForPC(55), code1);
2413
2414 // Insert overlapping both.
2415 ProfileCode* code7 = new (Z)
2416 ProfileCode(ProfileCode::kNativeCode, 20, 50, timestamp, null_code);
2417 EXPECT_EQ(table->InsertCode(code7), 0);
2418 EXPECT_EQ(table->FindCodeForPC(0), static_cast<ProfileCode*>(nullptr));
2419 EXPECT_EQ(table->FindCodeForPC(100), static_cast<ProfileCode*>(nullptr));
2420 EXPECT_EQ(table->FindCodeForPC(50), code1);
2421 EXPECT_EQ(table->FindCodeForPC(10), code2);
2422 EXPECT_EQ(table->FindCodeForPC(80), code3);
2423 EXPECT_EQ(table->FindCodeForPC(65), code4);
2424 EXPECT_EQ(table->FindCodeForPC(15), code2); // Merged left.
2425 EXPECT_EQ(table->FindCodeForPC(24), code2); // Merged left.
2426 EXPECT_EQ(table->FindCodeForPC(45), code1); // Merged right.
2427 EXPECT_EQ(table->FindCodeForPC(54), code1); // Merged right.
2428 EXPECT_EQ(table->FindCodeForPC(20), code2); // Merged left.
2429 EXPECT_EQ(table->FindCodeForPC(49), code1); // Truncated.
2430 EXPECT_EQ(table->FindCodeForPC(50), code1);
2431}
2432
2433// Try to hit any races in related to setting TLS and Isolate::mutator_thread_.
2434// https://github.com/flutter/flutter/issues/134548
2435ISOLATE_UNIT_TEST_CASE(Profiler_EnterExitIsolate) {
2437 Profiler::SetSamplePeriod(50); // Microseconds.
2438
2439 const char* kScript = "main() => null;\n";
2440 const Library& root_library = Library::Handle(LoadTestScript(kScript));
2441
2442 Isolate* isolate = Isolate::Current();
2443 for (intptr_t i = 0; i < 100000; i++) {
2445 Thread::EnterIsolate(isolate);
2446 Invoke(root_library, "main");
2447 }
2448}
2449
2450#endif // !PRODUCT
2451
2452} // namespace dart
static void sample1(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
Definition: SkSwizzler.cpp:40
static void sample2(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
Definition: SkSwizzler.cpp:50
static void do_work(int howMuchWork)
Definition: TracingTest.cpp:85
SI F table(const skcms_Curve *curve, F v)
#define EXPECT(type, expectedAlignment, expectedSize)
#define Z
bool FilterSample(Sample *sample)
void set_enable_vm_ticks(bool enable)
AllocationFilter(Dart_Port port, intptr_t cid, int64_t time_origin_micros=-1, int64_t time_extent_micros=-1)
static Dart_Handle NewHandle(Thread *thread, ObjectPtr raw)
static ObjectPtr UnwrapHandle(Dart_Handle object)
intptr_t id() const
Definition: object.h:1233
void SetTraceAllocation(bool trace_allocation) const
Definition: object.cc:4443
void DumpSourcePositions(bool relative_addresses=false) const
Definition: object.cc:18489
bool HasOptimizedCode() const
Definition: object.cc:10974
ObjectStore * object_store() const
Definition: isolate.h:510
static IsolateGroup * Current()
Definition: isolate.h:539
static Isolate * Current()
Definition: isolate.h:986
void set_current_sample_block(SampleBlock *block)
Definition: isolate.h:1112
IsolateGroup * group() const
Definition: isolate.h:1037
void set_current_allocation_sample_block(SampleBlock *block)
Definition: isolate.h:1121
Dart_Port main_port() const
Definition: isolate.h:1048
MaxProfileDepthScope(intptr_t new_max_depth)
static OSThread * Current()
Definition: os_thread.h:179
static int64_t GetCurrentMonotonicMicros()
static ObjectPtr null()
Definition: object.h:433
ObjectPtr ptr() const
Definition: object.h:332
static ClassPtr context_class()
Definition: object.h:549
bool IsNull() const
Definition: object.h:363
static Object & Handle()
Definition: object.h:407
static Object & ZoneHandle()
Definition: object.h:419
uword At(intptr_t index) const
Definition: profiler.h:812
int64_t timestamp() const
Definition: profiler.h:819
uword vm_tag() const
Definition: profiler.h:826
void Get(uword pc, const Code &code, ProcessedSample *sample, intptr_t frame_index, GrowableArray< const Function * > **inlined_functions, GrowableArray< TokenPosition > **inlined_token_positions, TokenPosition *token_position)
const Function * function() const
intptr_t inclusive_ticks() const
bool GetSinglePosition(ProfileFunctionSourcePosition *pfsp)
intptr_t exclusive_ticks() const
const char * Name() const
ProfileSampleBufferTestHelper(Dart_Port port)
const char * CurrentName()
const char * CurrentToken()
ProfileStackWalker(Profile *profile, bool as_func=false)
ProfileCode * GetCodeFromPC(uword pc, int64_t timestamp)
ProfileFunction * FindFunction(const Function &function)
static void SetSampleDepth(intptr_t depth)
Definition: profiler.cc:625
static void set_sample_block_buffer(SampleBlockBuffer *buffer)
Definition: profiler.h:70
static void Init()
Definition: profiler.cc:573
static SampleBlockBuffer * sample_block_buffer()
Definition: profiler.h:67
static void SetSamplePeriod(intptr_t period)
Definition: profiler.cc:656
SampleBlockBufferOverrideScope(SampleBlockBuffer *buffer)
void VisitSamples(SampleVisitor *visitor)
Definition: profiler.h:742
Sample * ReserveAllocationSample(Isolate *isolate)
Definition: profiler.cc:821
Sample * ReserveCPUSample(Isolate *isolate)
Definition: profiler.cc:817
Dart_Port port() const
Definition: profiler.h:167
intptr_t visited() const
Definition: profiler.h:132
virtual void Reset()
Definition: profiler.h:130
Dart_Port port() const
Definition: profiler.h:136
void set_thread_task(Thread::TaskKind task)
Definition: profiler.h:324
uword At(intptr_t i) const
Definition: profiler.h:237
void set_is_allocation_sample(bool allocation_sample)
Definition: profiler.h:310
void Init(Dart_Port port, int64_t timestamp, ThreadId tid)
Definition: profiler.h:199
bool is_allocation_sample() const
Definition: profiler.h:306
intptr_t allocation_cid() const
Definition: profiler.h:341
uword vm_tag() const
Definition: profiler.h:265
void SetAt(intptr_t i, uword pc)
Definition: profiler.h:244
void set_metadata(intptr_t metadata)
Definition: profiler.h:353
void PrintJSON(JSONStream *js, const Script &script, TokenPosition start_pos=TokenPosition::kMinSource, TokenPosition end_pos=TokenPosition::kMaxSource)
static StringPtr New(Thread *thread, const char *cstr)
Definition: symbols.h:723
static Dart_Handle LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri=RESOLVED_USER_TEST_URI, bool finalize=true, bool allow_compile_errors=false)
Definition: unit_test.cc:436
Zone * zone() const
Definition: thread_state.h:37
@ kMutatorTask
Definition: thread.h:347
static Thread * Current()
Definition: thread.h:362
Isolate * isolate() const
Definition: thread.h:534
static void EnterIsolate(Isolate *isolate)
Definition: thread.cc:371
static void ExitIsolate(bool isolate_shutdown=false)
Definition: thread.cc:428
static TokenPosition Deserialize(int32_t value)
static const char * TagName(uword id)
Definition: tags.cc:19
int64_t Dart_Port
Definition: dart_api.h:1525
struct _Dart_Handle * Dart_Handle
Definition: dart_api.h:258
#define ASSERT(E)
struct MyStruct s
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
size_t length
char ** argv
Definition: library.h:9
Definition: dart_vm.cc:33
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
const char *const name
DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, Dart_Handle name, int number_of_arguments, Dart_Handle *arguments)
DART_EXPORT Dart_Handle Dart_NewDouble(double value)
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
uintptr_t uword
Definition: globals.h:501
static void InsertFakeSample(uword *pc_offsets)
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
const int64_t kValidTimeStamp
static void VisitSamples(SampleBlockBuffer *buffer, SampleVisitor *visitor)
const uword kValidPc
static void EnableProfiler()
ClassPtr GetClass(const Library &lib, const char *name)
const intptr_t cid
TEST_CASE(DirectoryCurrent)
Dart_Handle NewString(const char *str)
static uword FindPCForTokenPosition(const Code &code, TokenPosition tp)
DART_EXPORT int64_t Dart_TimelineGetMicros()
DECLARE_FLAG(bool, show_invisible_frames)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
Definition: main.py:1
#define EXPECT_VALID(handle)
Definition: unit_test.h:643