Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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:
95
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);
492 Profile profile;
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());
500 ProfileStackWalker walker(&profile);
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);
521 Profile profile;
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);
535 Profile profile;
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);
571 Profile profile;
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);
587 Profile profile;
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());
592 ProfileStackWalker walker(&profile);
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);
616 Profile profile;
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);
653 Profile profile;
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);
672 Profile profile;
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());
677 ProfileStackWalker walker(&profile);
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);
726 Profile profile;
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);
745 Profile profile;
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);
794 Profile profile;
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);
806 Profile profile;
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());
811 ProfileStackWalker walker(&profile);
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);
827 Profile profile;
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);
853 Profile profile;
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);
865 Profile profile;
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());
870 ProfileStackWalker walker(&profile);
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);
886 Profile profile;
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);
907 Profile profile;
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);
936 Profile profile;
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);
948 Profile profile;
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());
953 ProfileStackWalker walker(&profile);
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);
967 Profile profile;
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);
1006 Profile profile;
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);
1029 Profile profile;
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);
1059 Profile profile;
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);
1071 Profile profile;
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);
1092 Profile profile;
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);
1104 Profile profile;
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);
1135 Profile profile;
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);
1147 Profile profile;
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);
1166 Profile profile;
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);
1178 Profile profile;
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);
1209 Profile profile;
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);
1221 Profile profile;
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);
1246 Profile profile;
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);
1258 Profile profile;
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);
1313 Profile profile;
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);
1330 Profile profile;
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);
1458 Profile profile;
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);
1474 Profile profile;
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);
1549 Profile profile;
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);
1643 Profile profile;
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);
1724 Profile profile;
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);
1801 Profile profile;
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);
1909 Profile profile;
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);
2003 Profile profile;
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);
2120 Profile profile;
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[])
static void sample2(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
static void do_work(int howMuchWork)
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:1235
void SetTraceAllocation(bool trace_allocation) const
Definition object.cc:4498
void DumpSourcePositions(bool relative_addresses=false) const
Definition object.cc:18539
bool HasOptimizedCode() const
Definition object.cc:11032
ObjectStore * object_store() const
Definition isolate.h:505
static IsolateGroup * Current()
Definition isolate.h:534
static Isolate * Current()
Definition isolate.h:939
void set_current_sample_block(SampleBlock *block)
Definition isolate.h:1065
IsolateGroup * group() const
Definition isolate.h:990
void set_current_allocation_sample_block(SampleBlock *block)
Definition isolate.h:1074
Dart_Port main_port() const
Definition isolate.h:1001
MaxProfileDepthScope(intptr_t new_max_depth)
static OSThread * Current()
Definition os_thread.h:175
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:819
int64_t timestamp() const
Definition profiler.h:826
uword vm_tag() const
Definition profiler.h:833
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
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:619
static void set_sample_block_buffer(SampleBlockBuffer *buffer)
Definition profiler.h:70
static void Init()
Definition profiler.cc:567
static SampleBlockBuffer * sample_block_buffer()
Definition profiler.h:67
static void SetSamplePeriod(intptr_t period)
Definition profiler.cc:650
SampleBlockBufferOverrideScope(SampleBlockBuffer *buffer)
void VisitSamples(SampleVisitor *visitor)
Definition profiler.h:749
Sample * ReserveAllocationSample(Isolate *isolate)
Definition profiler.cc:815
Sample * ReserveCPUSample(Isolate *isolate)
Definition profiler.cc:811
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:722
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:422
Zone * zone() const
static Thread * Current()
Definition thread.h:361
Isolate * isolate() const
Definition thread.h:533
static void EnterIsolate(Isolate *isolate)
Definition thread.cc:366
static void ExitIsolate(bool isolate_shutdown=false)
Definition thread.cc:423
static TokenPosition Deserialize(int32_t value)
static const char * TagName(uword id)
Definition tags.cc:19
int64_t Dart_Port
Definition dart_api.h:1524
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
#define ASSERT(E)
struct MyStruct s
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const uint8_t buffer[]
GAsyncResult * result
#define DECLARE_FLAG(type, name)
Definition flags.h:14
Dart_NativeFunction function
Definition fuchsia.cc:51
size_t length
char ** argv
Definition library.h:9
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)
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
Dart_Handle NewString(const char *str)
static uword FindPCForTokenPosition(const Code &code, TokenPosition tp)
DART_EXPORT int64_t Dart_TimelineGetMicros()
Definition main.py:1
#define ISOLATE_UNIT_TEST_CASE(name)
Definition unit_test.h:64
#define TEST_CASE(name)
Definition unit_test.h:85
#define EXPECT_VALID(handle)
Definition unit_test.h:650