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/address_sanitizer.h"
6#include "platform/memory_sanitizer.h"
7#include "platform/utils.h"
8
9#include "platform/atomic.h"
10#include "vm/allocation.h"
11#include "vm/code_patcher.h"
12#include "vm/debugger.h"
13#include "vm/instructions.h"
14#include "vm/isolate.h"
15#include "vm/json_stream.h"
16#include "vm/lockers.h"
17#include "vm/message_handler.h"
18#include "vm/native_symbol.h"
19#include "vm/object.h"
20#include "vm/os.h"
21#include "vm/profiler.h"
22#include "vm/reusable_handles.h"
23#include "vm/signal_handler.h"
24#include "vm/simulator.h"
25#include "vm/stack_frame.h"
26#include "vm/version.h"
27
28namespace dart {
29
30static const intptr_t kSampleSize = 8;
31static const intptr_t kMaxSamplesPerTick = 16;
32
33DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates.");
34
35DEFINE_FLAG(int,
36 profile_period,
37 1000,
38 "Time between profiler samples in microseconds. Minimum 50.");
39DEFINE_FLAG(int,
40 max_profile_depth,
41 kSampleSize* kMaxSamplesPerTick,
42 "Maximum number stack frames walked. Minimum 1. Maximum 255.");
43#if defined(USING_SIMULATOR)
44DEFINE_FLAG(bool, profile_vm, true, "Always collect native stack traces.");
45#else
46DEFINE_FLAG(bool, profile_vm, false, "Always collect native stack traces.");
47#endif
48DEFINE_FLAG(bool,
49 profile_vm_allocation,
50 false,
51 "Collect native stack traces when tracing Dart allocations.");
52
53DEFINE_FLAG(
54 int,
55 sample_buffer_duration,
56 0,
57 "Defines the size of the profiler sample buffer to contain at least "
58 "N seconds of samples at a given sample rate. If not provided, the "
59 "default is ~4 seconds. Large values will greatly increase memory "
60 "consumption.");
61
62#ifndef PRODUCT
63
64RelaxedAtomic<bool> Profiler::initialized_ = false;
65SampleBuffer* Profiler::sample_buffer_ = NULL;
66AllocationSampleBuffer* Profiler::allocation_sample_buffer_ = NULL;
67ProfilerCounters Profiler::counters_ = {};
68
69void Profiler::Init() {
70 // Place some sane restrictions on user controlled flags.
71 SetSampleDepth(FLAG_max_profile_depth);
72 Sample::Init();
73 if (!FLAG_profiler) {
74 return;
75 }
76 ASSERT(!initialized_);
77 SetSamplePeriod(FLAG_profile_period);
78 // The profiler may have been shutdown previously, in which case the sample
79 // buffer will have already been initialized.
80 if (sample_buffer_ == NULL) {
81 intptr_t capacity = CalculateSampleBufferCapacity();
82 sample_buffer_ = new SampleBuffer(capacity);
83 Profiler::InitAllocationSampleBuffer();
84 }
85 ThreadInterrupter::Init();
86 ThreadInterrupter::Startup();
87 initialized_ = true;
88}
89
90void Profiler::InitAllocationSampleBuffer() {
91 ASSERT(Profiler::allocation_sample_buffer_ == NULL);
92 if (FLAG_profiler_native_memory) {
93 Profiler::allocation_sample_buffer_ = new AllocationSampleBuffer();
94 }
95}
96
97void Profiler::Cleanup() {
98 if (!FLAG_profiler) {
99 return;
100 }
101 ASSERT(initialized_);
102 ThreadInterrupter::Cleanup();
103 delete sample_buffer_;
104 sample_buffer_ = NULL;
105 initialized_ = false;
106}
107
108void Profiler::UpdateRunningState() {
109 if (!FLAG_profiler && initialized_) {
110 Cleanup();
111 } else if (FLAG_profiler && !initialized_) {
112 Init();
113 }
114}
115
116void Profiler::SetSampleDepth(intptr_t depth) {
117 const int kMinimumDepth = 2;
118 const int kMaximumDepth = 255;
119 if (depth < kMinimumDepth) {
120 FLAG_max_profile_depth = kMinimumDepth;
121 } else if (depth > kMaximumDepth) {
122 FLAG_max_profile_depth = kMaximumDepth;
123 } else {
124 FLAG_max_profile_depth = depth;
125 }
126}
127
128static intptr_t SamplesPerSecond() {
129 const intptr_t kMicrosPerSec = 1000000;
130 return kMicrosPerSec / FLAG_profile_period;
131}
132
133intptr_t Profiler::CalculateSampleBufferCapacity() {
134 if (FLAG_sample_buffer_duration <= 0) {
135 return SampleBuffer::kDefaultBufferCapacity;
136 }
137 // Deeper stacks require more than a single Sample object to be represented
138 // correctly. These samples are chained, so we need to determine the worst
139 // case sample chain length for a single stack.
140 const intptr_t max_sample_chain_length =
141 FLAG_max_profile_depth / kMaxSamplesPerTick;
142 const intptr_t buffer_size = FLAG_sample_buffer_duration *
143 SamplesPerSecond() * max_sample_chain_length;
144 return buffer_size;
145}
146
147void Profiler::SetSamplePeriod(intptr_t period) {
148 const int kMinimumProfilePeriod = 50;
149 if (period < kMinimumProfilePeriod) {
150 FLAG_profile_period = kMinimumProfilePeriod;
151 } else {
152 FLAG_profile_period = period;
153 }
154 ThreadInterrupter::SetInterruptPeriod(FLAG_profile_period);
155}
156
157void Profiler::UpdateSamplePeriod() {
158 SetSamplePeriod(FLAG_profile_period);
159}
160
161intptr_t Sample::pcs_length_ = 0;
162intptr_t Sample::instance_size_ = 0;
163
164void Sample::Init() {
165 pcs_length_ = kSampleSize;
166 instance_size_ = sizeof(Sample) + (sizeof(uword) * pcs_length_); // NOLINT.
167}
168
169uword* Sample::GetPCArray() const {
170 return reinterpret_cast<uword*>(reinterpret_cast<uintptr_t>(this) +
171 sizeof(*this));
172}
173
174SampleBuffer::SampleBuffer(intptr_t capacity) {
175 ASSERT(Sample::instance_size() > 0);
176
177 const intptr_t size = Utils::RoundUp(capacity * Sample::instance_size(),
178 VirtualMemory::PageSize());
179 const bool kNotExecutable = false;
180 memory_ = VirtualMemory::Allocate(size, kNotExecutable, "dart-profiler");
181 if (memory_ == NULL) {
182 OUT_OF_MEMORY();
183 }
184
185 samples_ = reinterpret_cast<Sample*>(memory_->address());
186 capacity_ = capacity;
187 cursor_ = 0;
188
189 if (FLAG_trace_profiler) {
190 OS::PrintErr("Profiler holds %" Pd " samples\n", capacity);
191 OS::PrintErr("Profiler sample is %" Pd " bytes\n", Sample::instance_size());
192 OS::PrintErr("Profiler memory usage = %" Pd " bytes\n", size);
193 }
194 if (FLAG_sample_buffer_duration != 0) {
195 OS::PrintErr(
196 "** WARNING ** Custom sample buffer size provided via "
197 "--sample-buffer-duration\n");
198 OS::PrintErr(
199 "The sample buffer can hold at least %ds worth of "
200 "samples with stacks depths of up to %d, collected at "
201 "a sample rate of %" Pd "Hz.\n",
202 FLAG_sample_buffer_duration, FLAG_max_profile_depth,
203 SamplesPerSecond());
204 OS::PrintErr("The resulting sample buffer size is %" Pd " bytes.\n", size);
205 }
206}
207
208AllocationSampleBuffer::AllocationSampleBuffer(intptr_t capacity)
209 : SampleBuffer(capacity), mutex_(), free_sample_list_(NULL) {}
210
211SampleBuffer::~SampleBuffer() {
212 delete memory_;
213}
214
215AllocationSampleBuffer::~AllocationSampleBuffer() {
216}
217
218Sample* SampleBuffer::At(intptr_t idx) const {
219 ASSERT(idx >= 0);
220 ASSERT(idx < capacity_);
221 intptr_t offset = idx * Sample::instance_size();
222 uint8_t* samples = reinterpret_cast<uint8_t*>(samples_);
223 return reinterpret_cast<Sample*>(samples + offset);
224}
225
226intptr_t SampleBuffer::ReserveSampleSlot() {
227 ASSERT(samples_ != NULL);
228 uintptr_t cursor = cursor_.fetch_add(1u);
229 // Map back into sample buffer range.
230 cursor = cursor % capacity_;
231 return cursor;
232}
233
234Sample* SampleBuffer::ReserveSample() {
235 return At(ReserveSampleSlot());
236}
237
238Sample* SampleBuffer::ReserveSampleAndLink(Sample* previous) {
239 ASSERT(previous != NULL);
240 intptr_t next_index = ReserveSampleSlot();
241 Sample* next = At(next_index);
242 next->Init(previous->port(), previous->timestamp(), previous->tid());
243 next->set_head_sample(false);
244 // Mark that previous continues at next.
245 previous->SetContinuationIndex(next_index);
246 return next;
247}
248
249void AllocationSampleBuffer::FreeAllocationSample(Sample* sample) {
250 MutexLocker ml(&mutex_);
251 while (sample != NULL) {
252 intptr_t continuation_index = -1;
253 if (sample->is_continuation_sample()) {
254 continuation_index = sample->continuation_index();
255 }
256 sample->Clear();
257 sample->set_next_free(free_sample_list_);
258 free_sample_list_ = sample;
259
260 if (continuation_index != -1) {
261 sample = At(continuation_index);
262 } else {
263 sample = NULL;
264 }
265 }
266}
267
268intptr_t AllocationSampleBuffer::ReserveSampleSlotLocked() {
269 if (free_sample_list_ != NULL) {
270 Sample* free_sample = free_sample_list_;
271 free_sample_list_ = free_sample->next_free();
272 free_sample->set_next_free(NULL);
273 uint8_t* samples_array_ptr = reinterpret_cast<uint8_t*>(samples_);
274 uint8_t* free_sample_ptr = reinterpret_cast<uint8_t*>(free_sample);
275 return static_cast<intptr_t>((free_sample_ptr - samples_array_ptr) /
276 Sample::instance_size());
277 } else if (cursor_ < static_cast<uintptr_t>(capacity_ - 1)) {
278 return cursor_ += 1;
279 } else {
280 return -1;
281 }
282}
283
284Sample* AllocationSampleBuffer::ReserveSampleAndLink(Sample* previous) {
285 MutexLocker ml(&mutex_);
286 ASSERT(previous != NULL);
287 intptr_t next_index = ReserveSampleSlotLocked();
288 if (next_index < 0) {
289 // Could not find a free sample.
290 return NULL;
291 }
292 Sample* next = At(next_index);
293 next->Init(previous->port(), previous->timestamp(), previous->tid());
294 next->set_native_allocation_address(previous->native_allocation_address());
295 next->set_native_allocation_size_bytes(
296 previous->native_allocation_size_bytes());
297 next->set_head_sample(false);
298 // Mark that previous continues at next.
299 previous->SetContinuationIndex(next_index);
300 return next;
301}
302
303Sample* AllocationSampleBuffer::ReserveSample() {
304 MutexLocker ml(&mutex_);
305 intptr_t index = ReserveSampleSlotLocked();
306 if (index < 0) {
307 return NULL;
308 }
309 return At(index);
310}
311
312// Attempts to find the true return address when a Dart frame is being setup
313// or torn down.
314// NOTE: Architecture specific implementations below.
315class ReturnAddressLocator : public ValueObject {
316 public:
317 ReturnAddressLocator(Sample* sample, const Code& code)
318 : stack_buffer_(sample->GetStackBuffer()),
319 pc_(sample->pc()),
320 code_(Code::ZoneHandle(code.raw())) {
321 ASSERT(!code_.IsNull());
322 ASSERT(code_.ContainsInstructionAt(pc()));
323 }
324
325 ReturnAddressLocator(uword pc, uword* stack_buffer, const Code& code)
326 : stack_buffer_(stack_buffer),
327 pc_(pc),
328 code_(Code::ZoneHandle(code.raw())) {
329 ASSERT(!code_.IsNull());
330 ASSERT(code_.ContainsInstructionAt(pc_));
331 }
332
333 uword pc() { return pc_; }
334
335 // Returns false on failure.
336 bool LocateReturnAddress(uword* return_address);
337
338 // Returns offset into code object.
339 intptr_t RelativePC() {
340 ASSERT(pc() >= code_.PayloadStart());
341 return static_cast<intptr_t>(pc() - code_.PayloadStart());
342 }
343
344 uint8_t* CodePointer(intptr_t offset) {
345 const intptr_t size = code_.Size();
346 ASSERT(offset < size);
347 uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code_.PayloadStart());
348 code_pointer += offset;
349 return code_pointer;
350 }
351
352 uword StackAt(intptr_t i) {
353 ASSERT(i >= 0);
354 ASSERT(i < Sample::kStackBufferSizeInWords);
355 return stack_buffer_[i];
356 }
357
358 private:
359 uword* stack_buffer_;
360 uword pc_;
361 const Code& code_;
362};
363
364#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
365bool ReturnAddressLocator::LocateReturnAddress(uword* return_address) {
366 ASSERT(return_address != NULL);
367 const intptr_t offset = RelativePC();
368 ASSERT(offset >= 0);
369 const intptr_t size = code_.Size();
370 ASSERT(offset < size);
371 const intptr_t prologue_offset = code_.GetPrologueOffset();
372 if (offset < prologue_offset) {
373 // Before the prologue, return address is at the top of the stack.
374 // TODO(johnmccutchan): Some intrinsics and stubs do not conform to the
375 // expected stack layout. Use a more robust solution for those code objects.
376 *return_address = StackAt(0);
377 return true;
378 }
379 // Detect if we are:
380 // push ebp <--- here
381 // mov ebp, esp
382 // on X64 the register names are different but the sequence is the same.
383 ProloguePattern pp(pc());
384 if (pp.IsValid()) {
385 // Stack layout:
386 // 0 RETURN ADDRESS.
387 *return_address = StackAt(0);
388 return true;
389 }
390 // Detect if we are:
391 // push ebp
392 // mov ebp, esp <--- here
393 // on X64 the register names are different but the sequence is the same.
394 SetFramePointerPattern sfpp(pc());
395 if (sfpp.IsValid()) {
396 // Stack layout:
397 // 0 CALLER FRAME POINTER
398 // 1 RETURN ADDRESS
399 *return_address = StackAt(1);
400 return true;
401 }
402 // Detect if we are:
403 // ret <--- here
404 ReturnPattern rp(pc());
405 if (rp.IsValid()) {
406 // Stack layout:
407 // 0 RETURN ADDRESS.
408 *return_address = StackAt(0);
409 return true;
410 }
411 return false;
412}
413#elif defined(TARGET_ARCH_ARM)
414bool ReturnAddressLocator::LocateReturnAddress(uword* return_address) {
415 ASSERT(return_address != NULL);
416 return false;
417}
418#elif defined(TARGET_ARCH_ARM64)
419bool ReturnAddressLocator::LocateReturnAddress(uword* return_address) {
420 ASSERT(return_address != NULL);
421 return false;
422}
423#else
424#error ReturnAddressLocator implementation missing for this architecture.
425#endif
426
427bool SampleFilter::TimeFilterSample(Sample* sample) {
428 if ((time_origin_micros_ == -1) || (time_extent_micros_ == -1)) {
429 // No time filter passed in, always pass.
430 return true;
431 }
432 const int64_t timestamp = sample->timestamp();
433 int64_t delta = timestamp - time_origin_micros_;
434 return (delta >= 0) && (delta <= time_extent_micros_);
435}
436
437bool SampleFilter::TaskFilterSample(Sample* sample) {
438 const intptr_t task = static_cast<intptr_t>(sample->thread_task());
439 if (thread_task_mask_ == kNoTaskFilter) {
440 return true;
441 }
442 return (task & thread_task_mask_) != 0;
443}
444
445ClearProfileVisitor::ClearProfileVisitor(Isolate* isolate)
446 : SampleVisitor(isolate->main_port()) {}
447
448void ClearProfileVisitor::VisitSample(Sample* sample) {
449 sample->Clear();
450}
451
452static void DumpStackFrame(intptr_t frame_index, uword pc, uword fp) {
453 uword start = 0;
454 if (auto const name = NativeSymbolResolver::LookupSymbolName(pc, &start)) {
455 uword offset = pc - start;
456 OS::PrintErr(" pc 0x%" Pp " fp 0x%" Pp " %s+0x%" Px "\n", pc, fp, name,
457 offset);
458 NativeSymbolResolver::FreeSymbolName(name);
459 return;
460 }
461
462 char* dso_name;
463 uword dso_base;
464 if (NativeSymbolResolver::LookupSharedObject(pc, &dso_base, &dso_name)) {
465 uword dso_offset = pc - dso_base;
466 OS::PrintErr(" pc 0x%" Pp " fp 0x%" Pp " %s+0x%" Px "\n", pc, fp, dso_name,
467 dso_offset);
468 NativeSymbolResolver::FreeSymbolName(dso_name);
469 return;
470 }
471
472 OS::PrintErr(" pc 0x%" Pp " fp 0x%" Pp " Unknown symbol\n", pc, fp);
473}
474
475class ProfilerStackWalker : public ValueObject {
476 public:
477 ProfilerStackWalker(Dart_Port port_id,
478 Sample* head_sample,
479 SampleBuffer* sample_buffer,
480 intptr_t skip_count = 0)
481 : port_id_(port_id),
482 sample_(head_sample),
483 sample_buffer_(sample_buffer),
484 skip_count_(skip_count),
485 frames_skipped_(0),
486 frame_index_(0),
487 total_frames_(0) {
488 if (sample_ == NULL) {
489 ASSERT(sample_buffer_ == NULL);
490 } else {
491 ASSERT(sample_buffer_ != NULL);
492 ASSERT(sample_->head_sample());
493 }
494 }
495
496 bool Append(uword pc, uword fp) {
497 if (frames_skipped_ < skip_count_) {
498 frames_skipped_++;
499 return true;
500 }
501
502 if (sample_ == NULL) {
503 DumpStackFrame(frame_index_, pc, fp);
504 frame_index_++;
505 total_frames_++;
506 return true;
507 }
508 if (total_frames_ >= FLAG_max_profile_depth) {
509 sample_->set_truncated_trace(true);
510 return false;
511 }
512 ASSERT(sample_ != NULL);
513 if (frame_index_ == kSampleSize) {
514 Sample* new_sample = sample_buffer_->ReserveSampleAndLink(sample_);
515 if (new_sample == NULL) {
516 // Could not reserve new sample- mark this as truncated.
517 sample_->set_truncated_trace(true);
518 return false;
519 }
520 frame_index_ = 0;
521 sample_ = new_sample;
522 }
523 ASSERT(frame_index_ < kSampleSize);
524 sample_->SetAt(frame_index_, pc);
525 frame_index_++;
526 total_frames_++;
527 return true;
528 }
529
530 protected:
531 Dart_Port port_id_;
532 Sample* sample_;
533 SampleBuffer* sample_buffer_;
534 intptr_t skip_count_;
535 intptr_t frames_skipped_;
536 intptr_t frame_index_;
537 intptr_t total_frames_;
538};
539
540// Executing Dart code, walk the stack.
541class ProfilerDartStackWalker : public ProfilerStackWalker {
542 public:
543 ProfilerDartStackWalker(Thread* thread,
544 Sample* sample,
545 SampleBuffer* sample_buffer,
546 uword pc,
547 uword fp,
548 bool allocation_sample,
549 intptr_t skip_count = 0)
550 : ProfilerStackWalker((thread->isolate() != NULL)
551 ? thread->isolate()->main_port()
552 : ILLEGAL_PORT,
553 sample,
554 sample_buffer,
555 skip_count),
556 thread_(thread),
557 pc_(reinterpret_cast<uword*>(pc)),
558 fp_(reinterpret_cast<uword*>(fp)) {}
559
560 bool IsInterpretedFrame(uword* fp) {
561#if defined(DART_PRECOMPILED_RUNTIME)
562 return false;
563#else
564 Interpreter* interpreter = thread_->interpreter();
565 if (interpreter == nullptr) return false;
566 return interpreter->HasFrame(reinterpret_cast<uword>(fp));
567#endif
568 }
569
570 void walk() {
571 RELEASE_ASSERT(StubCode::HasBeenInitialized());
572 if (thread_->isolate()->IsDeoptimizing()) {
573 sample_->set_ignore_sample(true);
574 return;
575 }
576
577 uword* exit_fp = reinterpret_cast<uword*>(thread_->top_exit_frame_info());
578 bool in_interpreted_frame;
579 bool has_exit_frame = exit_fp != 0;
580 if (has_exit_frame) {
581 if (IsInterpretedFrame(exit_fp)) {
582 // Exited from interpreter.
583 pc_ = 0;
584 fp_ = exit_fp;
585 in_interpreted_frame = true;
586 RELEASE_ASSERT(IsInterpretedFrame(fp_));
587 } else {
588 // Exited from compiled code.
589 pc_ = 0;
590 fp_ = exit_fp;
591 in_interpreted_frame = false;
592 }
593
594 // Skip exit frame.
595 pc_ = CallerPC(in_interpreted_frame);
596 fp_ = CallerFP(in_interpreted_frame);
597
598 // Can only move between interpreted and compiled frames after an exit
599 // frame.
600 RELEASE_ASSERT(IsInterpretedFrame(fp_) == in_interpreted_frame);
601 } else {
602 if (thread_->vm_tag() == VMTag::kDartCompiledTagId) {
603 // Running compiled code.
604 // Use the FP and PC from the thread interrupt or simulator; already set
605 // in the constructor.
606 in_interpreted_frame = false;
607 } else if (thread_->vm_tag() == VMTag::kDartInterpretedTagId) {
608 // Running interpreter.
609#if defined(DART_PRECOMPILED_RUNTIME)
610 UNREACHABLE();
611#else
612 pc_ = reinterpret_cast<uword*>(thread_->interpreter()->get_pc());
613 fp_ = reinterpret_cast<uword*>(thread_->interpreter()->get_fp());
614#endif
615 in_interpreted_frame = true;
616 RELEASE_ASSERT(IsInterpretedFrame(fp_));
617 } else {
618 // No Dart on the stack; caller shouldn't use this walker.
619 UNREACHABLE();
620 }
621 }
622
623 sample_->set_exit_frame_sample(has_exit_frame);
624
625 if (!has_exit_frame && !in_interpreted_frame &&
626 (CallerPC(in_interpreted_frame) == EntryMarker(in_interpreted_frame))) {
627 // During the prologue of a function, CallerPC will return the caller's
628 // caller. For most frames, the missing PC will be added during profile
629 // processing. However, during this stack walk, it can cause us to fail
630 // to identify the entry frame and lead the stack walk into the weeds.
631 // Do not continue the stalk walk since this might be a false positive
632 // from a Smi or unboxed value.
633 RELEASE_ASSERT(!has_exit_frame);
634 sample_->set_ignore_sample(true);
635 return;
636 }
637
638 for (;;) {
639 // Skip entry frame.
640 if (StubCode::InInvocationStub(reinterpret_cast<uword>(pc_),
641 in_interpreted_frame)) {
642 pc_ = 0;
643 fp_ = ExitLink(in_interpreted_frame);
644 if (fp_ == 0) {
645 break; // End of Dart stack.
646 }
647 in_interpreted_frame = IsInterpretedFrame(fp_);
648
649 // Skip exit frame.
650 pc_ = CallerPC(in_interpreted_frame);
651 fp_ = CallerFP(in_interpreted_frame);
652
653 // At least one frame between exit and next entry frame.
654 RELEASE_ASSERT(!StubCode::InInvocationStub(reinterpret_cast<uword>(pc_),
655 in_interpreted_frame));
656 }
657
658 if (!Append(reinterpret_cast<uword>(pc_), reinterpret_cast<uword>(fp_))) {
659 break; // Sample is full.
660 }
661
662 pc_ = CallerPC(in_interpreted_frame);
663 fp_ = CallerFP(in_interpreted_frame);
664
665 // Can only move between interpreted and compiled frames after an exit
666 // frame.
667 RELEASE_ASSERT(IsInterpretedFrame(fp_) == in_interpreted_frame);
668 }
669 }
670
671 private:
672 uword* CallerPC(bool interp) const {
673 ASSERT(fp_ != NULL);
674 uword* caller_pc_ptr =
675 fp_ + (interp ? kKBCSavedCallerPcSlotFromFp : kSavedCallerPcSlotFromFp);
676 // MSan/ASan are unaware of frames initialized by generated code.
677 MSAN_UNPOISON(caller_pc_ptr, kWordSize);
678 ASAN_UNPOISON(caller_pc_ptr, kWordSize);
679 return reinterpret_cast<uword*>(*caller_pc_ptr);
680 }
681
682 uword* CallerFP(bool interp) const {
683 ASSERT(fp_ != NULL);
684 uword* caller_fp_ptr =
685 fp_ + (interp ? kKBCSavedCallerFpSlotFromFp : kSavedCallerFpSlotFromFp);
686 // MSan/ASan are unaware of frames initialized by generated code.
687 MSAN_UNPOISON(caller_fp_ptr, kWordSize);
688 ASAN_UNPOISON(caller_fp_ptr, kWordSize);
689 return reinterpret_cast<uword*>(*caller_fp_ptr);
690 }
691
692 uword* ExitLink(bool interp) const {
693 ASSERT(fp_ != NULL);
694 uword* exit_link_ptr =
695 fp_ + (interp ? kKBCExitLinkSlotFromEntryFp : kExitLinkSlotFromEntryFp);
696 // MSan/ASan are unaware of frames initialized by generated code.
697 MSAN_UNPOISON(exit_link_ptr, kWordSize);
698 ASAN_UNPOISON(exit_link_ptr, kWordSize);
699 return reinterpret_cast<uword*>(*exit_link_ptr);
700 }
701
702 // Note because of stack guards, it is important that this marker lives
703 // above FP.
704 uword* EntryMarker(bool interp) const {
705 ASSERT(!interp);
706 ASSERT(fp_ != NULL);
707 uword* entry_marker_ptr = fp_ + kSavedCallerPcSlotFromFp + 1;
708 // MSan/ASan are unaware of frames initialized by generated code.
709 MSAN_UNPOISON(entry_marker_ptr, kWordSize);
710 ASAN_UNPOISON(entry_marker_ptr, kWordSize);
711 return reinterpret_cast<uword*>(*entry_marker_ptr);
712 }
713
714 Thread* const thread_;
715 uword* pc_;
716 uword* fp_;
717};
718
719// If the VM is compiled without frame pointers (which is the default on
720// recent GCC versions with optimizing enabled) the stack walking code may
721// fail.
722//
723class ProfilerNativeStackWalker : public ProfilerStackWalker {
724 public:
725 ProfilerNativeStackWalker(ProfilerCounters* counters,
726 Dart_Port port_id,
727 Sample* sample,
728 SampleBuffer* sample_buffer,
729 uword stack_lower,
730 uword stack_upper,
731 uword pc,
732 uword fp,
733 uword sp,
734 intptr_t skip_count = 0)
735 : ProfilerStackWalker(port_id, sample, sample_buffer, skip_count),
736 counters_(counters),
737 stack_upper_(stack_upper),
738 original_pc_(pc),
739 original_fp_(fp),
740 original_sp_(sp),
741 lower_bound_(stack_lower) {}
742
743 void walk() {
744 const uword kMaxStep = VirtualMemory::PageSize();
745
746 Append(original_pc_, original_fp_);
747
748 uword* pc = reinterpret_cast<uword*>(original_pc_);
749 uword* fp = reinterpret_cast<uword*>(original_fp_);
750 uword* previous_fp = fp;
751
752 uword gap = original_fp_ - original_sp_;
753 if (gap >= kMaxStep) {
754 // Gap between frame pointer and stack pointer is
755 // too large.
756 counters_->incomplete_sample_fp_step.fetch_add(1);
757 return;
758 }
759
760 if (!ValidFramePointer(fp)) {
761 counters_->incomplete_sample_fp_bounds.fetch_add(1);
762 return;
763 }
764
765 while (true) {
766 pc = CallerPC(fp);
767 previous_fp = fp;
768 fp = CallerFP(fp);
769
770 if (fp == NULL) {
771 return;
772 }
773
774 if (fp <= previous_fp) {
775 // Frame pointer did not move to a higher address.
776 counters_->incomplete_sample_fp_step.fetch_add(1);
777 return;
778 }
779
780 gap = fp - previous_fp;
781 if (gap >= kMaxStep) {
782 // Frame pointer step is too large.
783 counters_->incomplete_sample_fp_step.fetch_add(1);
784 return;
785 }
786
787 if (!ValidFramePointer(fp)) {
788 // Frame pointer is outside of isolate stack boundary.
789 counters_->incomplete_sample_fp_bounds.fetch_add(1);
790 return;
791 }
792
793 const uword pc_value = reinterpret_cast<uword>(pc);
794 if ((pc_value + 1) < pc_value) {
795 // It is not uncommon to encounter an invalid pc as we
796 // traverse a stack frame. Most of these we can tolerate. If
797 // the pc is so large that adding one to it will cause an
798 // overflow it is invalid and it will cause headaches later
799 // while we are building the profile. Discard it.
800 counters_->incomplete_sample_bad_pc.fetch_add(1);
801 return;
802 }
803
804 // Move the lower bound up.
805 lower_bound_ = reinterpret_cast<uword>(fp);
806
807 if (!Append(pc_value, reinterpret_cast<uword>(fp))) {
808 return;
809 }
810 }
811 }
812
813 private:
814 uword* CallerPC(uword* fp) const {
815 ASSERT(fp != NULL);
816 uword* caller_pc_ptr = fp + kSavedCallerPcSlotFromFp;
817 // This may actually be uninitialized, by design (see class comment above).
818 MSAN_UNPOISON(caller_pc_ptr, kWordSize);
819 ASAN_UNPOISON(caller_pc_ptr, kWordSize);
820 return reinterpret_cast<uword*>(*caller_pc_ptr);
821 }
822
823 uword* CallerFP(uword* fp) const {
824 ASSERT(fp != NULL);
825 uword* caller_fp_ptr = fp + kSavedCallerFpSlotFromFp;
826 // This may actually be uninitialized, by design (see class comment above).
827 MSAN_UNPOISON(caller_fp_ptr, kWordSize);
828 ASAN_UNPOISON(caller_fp_ptr, kWordSize);
829 return reinterpret_cast<uword*>(*caller_fp_ptr);
830 }
831
832 bool ValidFramePointer(uword* fp) const {
833 if (fp == NULL) {
834 return false;
835 }
836 uword cursor = reinterpret_cast<uword>(fp);
837 cursor += sizeof(fp);
838 bool r = (cursor >= lower_bound_) && (cursor < stack_upper_);
839 return r;
840 }
841
842 ProfilerCounters* const counters_;
843 const uword stack_upper_;
844 const uword original_pc_;
845 const uword original_fp_;
846 const uword original_sp_;
847 uword lower_bound_;
848};
849
850static void CopyStackBuffer(Sample* sample, uword sp_addr) {
851 ASSERT(sample != NULL);
852 uword* sp = reinterpret_cast<uword*>(sp_addr);
853 uword* buffer = sample->GetStackBuffer();
854 if (sp != NULL) {
855 for (intptr_t i = 0; i < Sample::kStackBufferSizeInWords; i++) {
856 MSAN_UNPOISON(sp, kWordSize);
857 ASAN_UNPOISON(sp, kWordSize);
858 buffer[i] = *sp;
859 sp++;
860 }
861 }
862}
863
864#if defined(HOST_OS_WINDOWS)
865// On Windows this code is synchronously executed from the thread interrupter
866// thread. This means we can safely have a static fault_address.
867static uword fault_address = 0;
868static LONG GuardPageExceptionFilter(EXCEPTION_POINTERS* ep) {
869 fault_address = 0;
870 if (ep->ExceptionRecord->ExceptionCode != STATUS_GUARD_PAGE_VIOLATION) {
871 return EXCEPTION_CONTINUE_SEARCH;
872 }
873 // https://goo.gl/p5Fe10
874 fault_address = ep->ExceptionRecord->ExceptionInformation[1];
875 // Read access.
876 ASSERT(ep->ExceptionRecord->ExceptionInformation[0] == 0);
877 return EXCEPTION_EXECUTE_HANDLER;
878}
879#endif
880
881// All memory access done to collect the sample is performed in CollectSample.
882static void CollectSample(Isolate* isolate,
883 bool exited_dart_code,
884 bool in_dart_code,
885 Sample* sample,
886 ProfilerNativeStackWalker* native_stack_walker,
887 ProfilerDartStackWalker* dart_stack_walker,
888 uword pc,
889 uword fp,
890 uword sp,
891 ProfilerCounters* counters) {
892 ASSERT(counters != NULL);
893#if defined(HOST_OS_WINDOWS)
894 // Use structured exception handling to trap guard page access on Windows.
895 __try {
896#endif
897
898 if (in_dart_code) {
899 // We can only trust the stack pointer if we are executing Dart code.
900 // See http://dartbug.com/20421 for details.
901 CopyStackBuffer(sample, sp);
902 }
903
904 if (FLAG_profile_vm) {
905 // Always walk the native stack collecting both native and Dart frames.
906 counters->stack_walker_native.fetch_add(1);
907 native_stack_walker->walk();
908 } else if (StubCode::HasBeenInitialized() && exited_dart_code) {
909 counters->stack_walker_dart_exit.fetch_add(1);
910 // We have a valid exit frame info, use the Dart stack walker.
911 dart_stack_walker->walk();
912 } else if (StubCode::HasBeenInitialized() && in_dart_code) {
913 counters->stack_walker_dart.fetch_add(1);
914 // We are executing Dart code. We have frame pointers.
915 dart_stack_walker->walk();
916 } else {
917 counters->stack_walker_none.fetch_add(1);
918 sample->SetAt(0, pc);
919 }
920
921#if defined(HOST_OS_WINDOWS)
922 // Use structured exception handling to trap guard page access.
923 } __except (GuardPageExceptionFilter(GetExceptionInformation())) { // NOLINT
924 // Sample collection triggered a guard page fault:
925 // 1) discard entire sample.
926 sample->set_ignore_sample(true);
927
928 // 2) Reenable guard bit on page that triggered the fault.
929 // https://goo.gl/5mCsXW
930 DWORD new_protect = PAGE_READWRITE | PAGE_GUARD;
931 DWORD old_protect = 0;
932 BOOL success =
933 VirtualProtect(reinterpret_cast<void*>(fault_address),
934 sizeof(fault_address), new_protect, &old_protect);
935 USE(success);
936 ASSERT(success);
937 ASSERT(old_protect == PAGE_READWRITE);
938 }
939#endif
940}
941
942static bool ValidateThreadStackBounds(uintptr_t fp,
943 uintptr_t sp,
944 uword stack_lower,
945 uword stack_upper) {
946 if (stack_lower >= stack_upper) {
947 // Stack boundary is invalid.
948 return false;
949 }
950
951 if ((sp < stack_lower) || (sp >= stack_upper)) {
952 // Stack pointer is outside thread's stack boundary.
953 return false;
954 }
955
956 if ((fp < stack_lower) || (fp >= stack_upper)) {
957 // Frame pointer is outside threads's stack boundary.
958 return false;
959 }
960
961 return true;
962}
963
964// Get |thread|'s stack boundary and verify that |sp| and |fp| are within
965// it. Return |false| if anything looks suspicious.
966static bool GetAndValidateThreadStackBounds(OSThread* os_thread,
967 Thread* thread,
968 uintptr_t fp,
969 uintptr_t sp,
970 uword* stack_lower,
971 uword* stack_upper) {
972 ASSERT(os_thread != NULL);
973 ASSERT(stack_lower != NULL);
974 ASSERT(stack_upper != NULL);
975
976#if defined(USING_SIMULATOR)
977 const bool use_simulator_stack_bounds =
978 thread != NULL && thread->IsExecutingDartCode();
979 if (use_simulator_stack_bounds) {
980 Isolate* isolate = thread->isolate();
981 ASSERT(isolate != NULL);
982 Simulator* simulator = isolate->simulator();
983 *stack_lower = simulator->stack_limit();
984 *stack_upper = simulator->stack_base();
985 }
986#else
987 const bool use_simulator_stack_bounds = false;
988#endif // defined(USING_SIMULATOR)
989
990 if (!use_simulator_stack_bounds) {
991 *stack_lower = os_thread->stack_limit();
992 *stack_upper = os_thread->stack_base();
993 }
994
995 if ((*stack_lower == 0) || (*stack_upper == 0)) {
996 return false;
997 }
998
999 if (!use_simulator_stack_bounds && (sp > *stack_lower)) {
1000 // The stack pointer gives us a tighter lower bound.
1001 *stack_lower = sp;
1002 }
1003
1004 return ValidateThreadStackBounds(fp, sp, *stack_lower, *stack_upper);
1005}
1006
1007// Some simple sanity checking of |pc|, |fp|, and |sp|.
1008static bool InitialRegisterCheck(uintptr_t pc, uintptr_t fp, uintptr_t sp) {
1009 if ((sp == 0) || (fp == 0) || (pc == 0)) {
1010 // None of these registers should be zero.
1011 return false;
1012 }
1013
1014 if (sp > fp) {
1015 // Assuming the stack grows down, we should never have a stack pointer above
1016 // the frame pointer.
1017 return false;
1018 }
1019
1020 return true;
1021}
1022
1023static Sample* SetupSample(Thread* thread,
1024 SampleBuffer* sample_buffer,
1025 ThreadId tid) {
1026 ASSERT(thread != NULL);
1027 Isolate* isolate = thread->isolate();
1028 ASSERT(sample_buffer != NULL);
1029 Sample* sample = sample_buffer->ReserveSample();
1030 sample->Init(isolate->main_port(), OS::GetCurrentMonotonicMicros(), tid);
1031 uword vm_tag = thread->vm_tag();
1032#if defined(USING_SIMULATOR)
1033 // When running in the simulator, the runtime entry function address
1034 // (stored as the vm tag) is the address of a redirect function.
1035 // Attempt to find the real runtime entry function address and use that.
1036 uword redirect_vm_tag = Simulator::FunctionForRedirect(vm_tag);
1037 if (redirect_vm_tag != 0) {
1038 vm_tag = redirect_vm_tag;
1039 }
1040#endif
1041 sample->set_vm_tag(vm_tag);
1042 sample->set_user_tag(isolate->user_tag());
1043 sample->set_thread_task(thread->task_kind());
1044 return sample;
1045}
1046
1047static Sample* SetupSampleNative(SampleBuffer* sample_buffer, ThreadId tid) {
1048 Sample* sample = sample_buffer->ReserveSample();
1049 if (sample == NULL) {
1050 return NULL;
1051 }
1052 sample->Init(ILLEGAL_PORT, OS::GetCurrentMonotonicMicros(), tid);
1053 Thread* thread = Thread::Current();
1054
1055 // Note: setting thread task in order to be consistent with other samples. The
1056 // task kind is not used by NativeAllocationSampleFilter for filtering
1057 // purposes as some samples may be collected when no thread exists.
1058 if (thread != NULL) {
1059 sample->set_thread_task(thread->task_kind());
1060 sample->set_vm_tag(thread->vm_tag());
1061 } else {
1062 sample->set_vm_tag(VMTag::kEmbedderTagId);
1063 }
1064 return sample;
1065}
1066
1067static bool CheckIsolate(Isolate* isolate) {
1068 if ((isolate == NULL) || (Dart::vm_isolate() == NULL)) {
1069 // No isolate.
1070 return false;
1071 }
1072 return isolate != Dart::vm_isolate();
1073}
1074
1075void Profiler::DumpStackTrace(void* context) {
1076 if (context == NULL) {
1077 DumpStackTrace(/*for_crash=*/true);
1078 return;
1079 }
1080#if defined(HOST_OS_LINUX) || defined(HOST_OS_MACOS) || defined(HOST_OS_ANDROID)
1081 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
1082 mcontext_t mcontext = ucontext->uc_mcontext;
1083 uword pc = SignalHandler::GetProgramCounter(mcontext);
1084 uword fp = SignalHandler::GetFramePointer(mcontext);
1085 uword sp = SignalHandler::GetCStackPointer(mcontext);
1086 DumpStackTrace(sp, fp, pc, /*for_crash=*/true);
1087#elif defined(HOST_OS_WINDOWS)
1088 CONTEXT* ctx = reinterpret_cast<CONTEXT*>(context);
1089#if defined(HOST_ARCH_IA32)
1090 uword pc = static_cast<uword>(ctx->Eip);
1091 uword fp = static_cast<uword>(ctx->Ebp);
1092 uword sp = static_cast<uword>(ctx->Esp);
1093#elif defined(HOST_ARCH_X64)
1094 uword pc = static_cast<uword>(ctx->Rip);
1095 uword fp = static_cast<uword>(ctx->Rbp);
1096 uword sp = static_cast<uword>(ctx->Rsp);
1097#else
1098#error Unsupported architecture.
1099#endif
1100 DumpStackTrace(sp, fp, pc, /*for_crash=*/true);
1101#else
1102// TODO(fschneider): Add support for more platforms.
1103// Do nothing on unsupported platforms.
1104#endif
1105}
1106
1107void Profiler::DumpStackTrace(bool for_crash) {
1108 uintptr_t sp = OSThread::GetCurrentStackPointer();
1109 uintptr_t fp = 0;
1110 uintptr_t pc = OS::GetProgramCounter();
1111
1112 COPY_FP_REGISTER(fp);
1113
1114 DumpStackTrace(sp, fp, pc, for_crash);
1115}
1116
1117void Profiler::DumpStackTrace(uword sp, uword fp, uword pc, bool for_crash) {
1118 if (for_crash) {
1119 // Allow only one stack trace to prevent recursively printing stack traces
1120 // if we hit an assert while printing the stack.
1121 static RelaxedAtomic<uintptr_t> started_dump = 0;
1122 if (started_dump.fetch_add(1u) != 0) {
1123 OS::PrintErr("Aborting re-entrant request for stack trace.\n");
1124 return;
1125 }
1126 }
1127
1128 auto os_thread = OSThread::Current();
1129 ASSERT(os_thread != nullptr);
1130 auto thread = Thread::Current(); // NULL if no current isolate.
1131 auto isolate = thread == nullptr ? nullptr : thread->isolate();
1132 auto isolate_group = thread == nullptr ? nullptr : thread->isolate_group();
1133 auto source = isolate_group == nullptr ? nullptr : isolate_group->source();
1134 auto vm_source =
1135 Dart::vm_isolate() == nullptr ? nullptr : Dart::vm_isolate()->source();
1136 const char* isolate_group_name =
1137 isolate_group == nullptr ? "(nil)" : isolate_group->source()->name;
1138 const char* isolate_name = isolate == nullptr ? "(nil)" : isolate->name();
1139
1140 OS::PrintErr("version=%s\n", Version::String());
1141 OS::PrintErr("pid=%" Pd ", thread=%" Pd
1142 ", isolate_group=%s(%p), isolate=%s(%p)\n",
1143 static_cast<intptr_t>(OS::ProcessId()),
1144 OSThread::ThreadIdToIntPtr(os_thread->trace_id()),
1145 isolate_group_name, isolate_group, isolate_name, isolate);
1146 OS::PrintErr("isolate_instructions=%" Px ", vm_instructions=%" Px "\n",
1147 source == nullptr
1148 ? 0
1149 : reinterpret_cast<uword>(source->snapshot_instructions),
1150 vm_source == nullptr
1151 ? 0
1152 : reinterpret_cast<uword>(vm_source->snapshot_instructions));
1153
1154 if (!InitialRegisterCheck(pc, fp, sp)) {
1155 OS::PrintErr("Stack dump aborted because InitialRegisterCheck failed.\n");
1156 return;
1157 }
1158
1159 uword stack_lower = 0;
1160 uword stack_upper = 0;
1161 if (!GetAndValidateThreadStackBounds(os_thread, thread, fp, sp, &stack_lower,
1162 &stack_upper)) {
1163 OS::PrintErr(
1164 "Stack dump aborted because GetAndValidateThreadStackBounds failed.\n");
1165 return;
1166 }
1167
1168 ProfilerNativeStackWalker native_stack_walker(&counters_, ILLEGAL_PORT, NULL,
1169 NULL, stack_lower, stack_upper,
1170 pc, fp, sp,
1171 /*skip_count=*/0);
1172 native_stack_walker.walk();
1173 OS::PrintErr("-- End of DumpStackTrace\n");
1174
1175 if (thread != nullptr) {
1176 if (thread->execution_state() == Thread::kThreadInNative) {
1177 TransitionNativeToVM transition(thread);
1178 StackFrame::DumpCurrentTrace();
1179 } else if (thread->execution_state() == Thread::kThreadInVM) {
1180 StackFrame::DumpCurrentTrace();
1181 }
1182 }
1183}
1184
1185void Profiler::SampleAllocation(Thread* thread, intptr_t cid) {
1186 ASSERT(thread != NULL);
1187 OSThread* os_thread = thread->os_thread();
1188 ASSERT(os_thread != NULL);
1189 Isolate* isolate = thread->isolate();
1190 if (!CheckIsolate(isolate)) {
1191 return;
1192 }
1193
1194 const bool exited_dart_code = thread->HasExitedDartCode();
1195
1196 SampleBuffer* sample_buffer = Profiler::sample_buffer();
1197 if (sample_buffer == NULL) {
1198 // Profiler not initialized.
1199 return;
1200 }
1201
1202 uintptr_t sp = OSThread::GetCurrentStackPointer();
1203 uintptr_t fp = 0;
1204 uintptr_t pc = OS::GetProgramCounter();
1205
1206 COPY_FP_REGISTER(fp);
1207
1208 uword stack_lower = 0;
1209 uword stack_upper = 0;
1210
1211 if (!InitialRegisterCheck(pc, fp, sp)) {
1212 return;
1213 }
1214
1215 if (!GetAndValidateThreadStackBounds(os_thread, thread, fp, sp, &stack_lower,
1216 &stack_upper)) {
1217 // Could not get stack boundary.
1218 return;
1219 }
1220
1221 Sample* sample = SetupSample(thread, sample_buffer, os_thread->trace_id());
1222 sample->SetAllocationCid(cid);
1223
1224 if (FLAG_profile_vm_allocation) {
1225 ProfilerNativeStackWalker native_stack_walker(
1226 &counters_, (isolate != NULL) ? isolate->main_port() : ILLEGAL_PORT,
1227 sample, sample_buffer, stack_lower, stack_upper, pc, fp, sp);
1228 native_stack_walker.walk();
1229 } else if (exited_dart_code) {
1230 ProfilerDartStackWalker dart_exit_stack_walker(
1231 thread, sample, sample_buffer, pc, fp, /* allocation_sample*/ true);
1232 dart_exit_stack_walker.walk();
1233 } else {
1234 // Fall back.
1235 uintptr_t pc = OS::GetProgramCounter();
1236 Sample* sample = SetupSample(thread, sample_buffer, os_thread->trace_id());
1237 sample->SetAllocationCid(cid);
1238 sample->SetAt(0, pc);
1239 }
1240}
1241
1242Sample* Profiler::SampleNativeAllocation(intptr_t skip_count,
1243 uword address,
1244 uintptr_t allocation_size) {
1245 AllocationSampleBuffer* sample_buffer = Profiler::allocation_sample_buffer();
1246 if (sample_buffer == NULL) {
1247 return NULL;
1248 }
1249
1250 uintptr_t sp = OSThread::GetCurrentStackPointer();
1251 uintptr_t fp = 0;
1252 uintptr_t pc = OS::GetProgramCounter();
1253
1254 COPY_FP_REGISTER(fp);
1255
1256 uword stack_lower = 0;
1257 uword stack_upper = 0;
1258 if (!InitialRegisterCheck(pc, fp, sp)) {
1259 counters_.failure_native_allocation_sample.fetch_add(1);
1260 return NULL;
1261 }
1262
1263 if (!(OSThread::GetCurrentStackBounds(&stack_lower, &stack_upper) &&
1264 ValidateThreadStackBounds(fp, sp, stack_lower, stack_upper))) {
1265 // Could not get stack boundary.
1266 counters_.failure_native_allocation_sample.fetch_add(1);
1267 return NULL;
1268 }
1269
1270 OSThread* os_thread = OSThread::Current();
1271 Sample* sample = SetupSampleNative(sample_buffer, os_thread->trace_id());
1272 if (sample == NULL) {
1273 OS::PrintErr(
1274 "Native memory profile sample buffer is full because there are more "
1275 "than %" Pd
1276 " outstanding allocations. Not recording allocation "
1277 "0x%" Px " with size: %" Pu " bytes.\n",
1278 sample_buffer->capacity(), address, allocation_size);
1279 return NULL;
1280 }
1281
1282 sample->set_native_allocation_address(address);
1283 sample->set_native_allocation_size_bytes(allocation_size);
1284
1285 ProfilerNativeStackWalker native_stack_walker(
1286 &counters_, ILLEGAL_PORT, sample, sample_buffer, stack_lower, stack_upper,
1287 pc, fp, sp, skip_count);
1288
1289 native_stack_walker.walk();
1290
1291 return sample;
1292}
1293
1294void Profiler::SampleThreadSingleFrame(Thread* thread, uintptr_t pc) {
1295 ASSERT(thread != NULL);
1296 OSThread* os_thread = thread->os_thread();
1297 ASSERT(os_thread != NULL);
1298 Isolate* isolate = thread->isolate();
1299
1300 SampleBuffer* sample_buffer = Profiler::sample_buffer();
1301 if (sample_buffer == NULL) {
1302 // Profiler not initialized.
1303 return;
1304 }
1305
1306 // Setup sample.
1307 Sample* sample = SetupSample(thread, sample_buffer, os_thread->trace_id());
1308 // Increment counter for vm tag.
1309 VMTagCounters* counters = isolate->vm_tag_counters();
1310 ASSERT(counters != NULL);
1311 if (thread->IsMutatorThread()) {
1312 counters->Increment(sample->vm_tag());
1313 }
1314
1315 // Write the single pc value.
1316 sample->SetAt(0, pc);
1317}
1318
1319void Profiler::SampleThread(Thread* thread,
1320 const InterruptedThreadState& state) {
1321 ASSERT(thread != NULL);
1322 OSThread* os_thread = thread->os_thread();
1323 ASSERT(os_thread != NULL);
1324 Isolate* isolate = thread->isolate();
1325
1326 // Thread is not doing VM work.
1327 if (thread->task_kind() == Thread::kUnknownTask) {
1328 counters_.bail_out_unknown_task.fetch_add(1);
1329 return;
1330 }
1331
1332 if (StubCode::HasBeenInitialized() && StubCode::InJumpToFrameStub(state.pc)) {
1333 // The JumpToFrame stub manually adjusts the stack pointer, frame
1334 // pointer, and some isolate state. It is not safe to walk the
1335 // stack when executing this stub.
1336 counters_.bail_out_jump_to_exception_handler.fetch_add(1);
1337 return;
1338 }
1339
1340 const bool in_dart_code = thread->IsExecutingDartCode();
1341
1342 uintptr_t sp = 0;
1343 uintptr_t fp = state.fp;
1344 uintptr_t pc = state.pc;
1345#if defined(USING_SIMULATOR)
1346 Simulator* simulator = NULL;
1347#endif
1348
1349 if (in_dart_code) {
1350// If we're in Dart code, use the Dart stack pointer.
1351#if defined(USING_SIMULATOR)
1352 simulator = isolate->simulator();
1353 sp = simulator->get_register(SPREG);
1354 fp = simulator->get_register(FPREG);
1355 pc = simulator->get_pc();
1356#else
1357 sp = state.dsp;
1358#endif
1359 } else {
1360 // If we're in runtime code, use the C stack pointer.
1361 sp = state.csp;
1362 }
1363
1364 if (!CheckIsolate(isolate)) {
1365 counters_.bail_out_check_isolate.fetch_add(1);
1366 return;
1367 }
1368
1369 if (thread->IsMutatorThread()) {
1370 if (isolate->IsDeoptimizing()) {
1371 counters_.single_frame_sample_deoptimizing.fetch_add(1);
1372 SampleThreadSingleFrame(thread, pc);
1373 return;
1374 }
1375 if (isolate->group()->compaction_in_progress()) {
1376 // The Dart stack isn't fully walkable.
1377 SampleThreadSingleFrame(thread, pc);
1378 return;
1379 }
1380 }
1381
1382 if (!InitialRegisterCheck(pc, fp, sp)) {
1383 counters_.single_frame_sample_register_check.fetch_add(1);
1384 SampleThreadSingleFrame(thread, pc);
1385 return;
1386 }
1387
1388 uword stack_lower = 0;
1389 uword stack_upper = 0;
1390 if (!GetAndValidateThreadStackBounds(os_thread, thread, fp, sp, &stack_lower,
1391 &stack_upper)) {
1392 counters_.single_frame_sample_get_and_validate_stack_bounds.fetch_add(1);
1393 // Could not get stack boundary.
1394 SampleThreadSingleFrame(thread, pc);
1395 return;
1396 }
1397
1398 // At this point we have a valid stack boundary for this isolate and
1399 // know that our initial stack and frame pointers are within the boundary.
1400 SampleBuffer* sample_buffer = Profiler::sample_buffer();
1401 if (sample_buffer == NULL) {
1402 // Profiler not initialized.
1403 return;
1404 }
1405
1406 // Setup sample.
1407 Sample* sample = SetupSample(thread, sample_buffer, os_thread->trace_id());
1408 // Increment counter for vm tag.
1409 VMTagCounters* counters = isolate->vm_tag_counters();
1410 ASSERT(counters != NULL);
1411 if (thread->IsMutatorThread()) {
1412 counters->Increment(sample->vm_tag());
1413 }
1414
1415 ProfilerNativeStackWalker native_stack_walker(
1416 &counters_, (isolate != NULL) ? isolate->main_port() : ILLEGAL_PORT,
1417 sample, sample_buffer, stack_lower, stack_upper, pc, fp, sp);
1418 const bool exited_dart_code = thread->HasExitedDartCode();
1419 ProfilerDartStackWalker dart_stack_walker(thread, sample, sample_buffer, pc,
1420 fp, /* allocation_sample*/ false);
1421
1422 // All memory access is done inside CollectSample.
1423 CollectSample(isolate, exited_dart_code, in_dart_code, sample,
1424 &native_stack_walker, &dart_stack_walker, pc, fp, sp,
1425 &counters_);
1426}
1427
1428CodeDescriptor::CodeDescriptor(const AbstractCode code) : code_(code) {}
1429
1430uword CodeDescriptor::Start() const {
1431 return code_.PayloadStart();
1432}
1433
1434uword CodeDescriptor::Size() const {
1435 return code_.Size();
1436}
1437
1438int64_t CodeDescriptor::CompileTimestamp() const {
1439 return code_.compile_timestamp();
1440}
1441
1442CodeLookupTable::CodeLookupTable(Thread* thread) {
1443 Build(thread);
1444}
1445
1446class CodeLookupTableBuilder : public ObjectVisitor {
1447 public:
1448 explicit CodeLookupTableBuilder(CodeLookupTable* table) : table_(table) {
1449 ASSERT(table_ != NULL);
1450 }
1451
1452 ~CodeLookupTableBuilder() {}
1453
1454 void VisitObject(ObjectPtr raw_obj) {
1455 if (raw_obj->IsCode()) {
1456 table_->Add(Code::Handle(Code::RawCast(raw_obj)));
1457 } else if (raw_obj->IsBytecode()) {
1458 table_->Add(Bytecode::Handle(Bytecode::RawCast(raw_obj)));
1459 }
1460 }
1461
1462 private:
1463 CodeLookupTable* table_;
1464};
1465
1466void CodeLookupTable::Build(Thread* thread) {
1467 ASSERT(thread != NULL);
1468 Isolate* isolate = thread->isolate();
1469 ASSERT(isolate != NULL);
1470 Isolate* vm_isolate = Dart::vm_isolate();
1471 ASSERT(vm_isolate != NULL);
1472
1473 // Clear.
1474 code_objects_.Clear();
1475
1476 // Add all found Code objects.
1477 {
1478 HeapIterationScope iteration(thread);
1479 CodeLookupTableBuilder cltb(this);
1480 iteration.IterateVMIsolateObjects(&cltb);
1481 iteration.IterateOldObjects(&cltb);
1482 }
1483
1484 // Sort by entry.
1485 code_objects_.Sort(CodeDescriptor::Compare);
1486
1487#if defined(DEBUG)
1488 if (length() <= 1) {
1489 return;
1490 }
1491 ASSERT(FindCode(0) == NULL);
1492 ASSERT(FindCode(~0) == NULL);
1493 // Sanity check that we don't have duplicate entries and that the entries
1494 // are sorted.
1495 for (intptr_t i = 0; i < length() - 1; i++) {
1496 const CodeDescriptor* a = At(i);
1497 const CodeDescriptor* b = At(i + 1);
1498 ASSERT(a->Start() < b->Start());
1499 ASSERT(FindCode(a->Start()) == a);
1500 ASSERT(FindCode(b->Start()) == b);
1501 ASSERT(FindCode(a->Start() + a->Size() - 1) == a);
1502 ASSERT(FindCode(b->Start() + b->Size() - 1) == b);
1503 }
1504#endif
1505}
1506
1507void CodeLookupTable::Add(const Object& code) {
1508 ASSERT(!code.IsNull());
1509 ASSERT(code.IsCode() || code.IsBytecode());
1510 CodeDescriptor* cd = new CodeDescriptor(AbstractCode(code.raw()));
1511 code_objects_.Add(cd);
1512}
1513
1514const CodeDescriptor* CodeLookupTable::FindCode(uword pc) const {
1515 intptr_t first = 0;
1516 intptr_t count = length();
1517 while (count > 0) {
1518 intptr_t current = first;
1519 intptr_t step = count / 2;
1520 current += step;
1521 const CodeDescriptor* cd = At(current);
1522 if (pc >= cd->Start()) {
1523 first = ++current;
1524 count -= step + 1;
1525 } else {
1526 count = step;
1527 }
1528 }
1529 // First points to the first code object whose entry is greater than PC.
1530 // That means the code object we need to check is first - 1.
1531 if (first == 0) {
1532 return NULL;
1533 }
1534 first--;
1535 ASSERT(first >= 0);
1536 ASSERT(first < length());
1537 const CodeDescriptor* cd = At(first);
1538 if (cd->Contains(pc)) {
1539 return cd;
1540 }
1541 return NULL;
1542}
1543
1544ProcessedSampleBuffer* SampleBuffer::BuildProcessedSampleBuffer(
1545 SampleFilter* filter) {
1546 ASSERT(filter != NULL);
1547 Thread* thread = Thread::Current();
1548 Zone* zone = thread->zone();
1549
1550 ProcessedSampleBuffer* buffer = new (zone) ProcessedSampleBuffer();
1551
1552 const intptr_t length = capacity();
1553 for (intptr_t i = 0; i < length; i++) {
1554 Sample* sample = At(i);
1555 if (sample->ignore_sample()) {
1556 // Bad sample.
1557 continue;
1558 }
1559 if (!sample->head_sample()) {
1560 // An inner sample in a chain of samples.
1561 continue;
1562 }
1563 // If we're requesting all the native allocation samples, we don't care
1564 // whether or not we're in the same isolate as the sample.
1565 if (sample->port() != filter->port()) {
1566 // Another isolate.
1567 continue;
1568 }
1569 if (sample->timestamp() == 0) {
1570 // Empty.
1571 continue;
1572 }
1573 if (sample->At(0) == 0) {
1574 // No frames.
1575 continue;
1576 }
1577 if (!filter->TimeFilterSample(sample)) {
1578 // Did not pass time filter.
1579 continue;
1580 }
1581 if (!filter->TaskFilterSample(sample)) {
1582 // Did not pass task filter.
1583 continue;
1584 }
1585 if (!filter->FilterSample(sample)) {
1586 // Did not pass filter.
1587 continue;
1588 }
1589 buffer->Add(BuildProcessedSample(sample, buffer->code_lookup_table()));
1590 }
1591 return buffer;
1592}
1593
1594ProcessedSample* SampleBuffer::BuildProcessedSample(
1595 Sample* sample,
1596 const CodeLookupTable& clt) {
1597 Thread* thread = Thread::Current();
1598 Zone* zone = thread->zone();
1599
1600 ProcessedSample* processed_sample = new (zone) ProcessedSample();
1601
1602 // Copy state bits from sample.
1603 processed_sample->set_native_allocation_size_bytes(
1604 sample->native_allocation_size_bytes());
1605 processed_sample->set_timestamp(sample->timestamp());
1606 processed_sample->set_tid(sample->tid());
1607 processed_sample->set_vm_tag(sample->vm_tag());
1608 processed_sample->set_user_tag(sample->user_tag());
1609 if (sample->is_allocation_sample()) {
1610 processed_sample->set_allocation_cid(sample->allocation_cid());
1611 }
1612 processed_sample->set_first_frame_executing(!sample->exit_frame_sample());
1613
1614 // Copy stack trace from sample(s).
1615 bool truncated = false;
1616 Sample* current = sample;
1617 while (current != NULL) {
1618 for (intptr_t i = 0; i < kSampleSize; i++) {
1619 if (current->At(i) == 0) {
1620 break;
1621 }
1622 processed_sample->Add(current->At(i));
1623 }
1624
1625 truncated = truncated || current->truncated_trace();
1626 current = Next(current);
1627 }
1628
1629 if (!sample->exit_frame_sample()) {
1630 processed_sample->FixupCaller(clt, sample->pc_marker(),
1631 sample->GetStackBuffer());
1632 }
1633
1634 processed_sample->set_truncated(truncated);
1635 return processed_sample;
1636}
1637
1638Sample* SampleBuffer::Next(Sample* sample) {
1639 if (!sample->is_continuation_sample()) return NULL;
1640 Sample* next_sample = At(sample->continuation_index());
1641 // Sanity check.
1642 ASSERT(sample != next_sample);
1643 // Detect invalid chaining.
1644 if (sample->port() != next_sample->port()) {
1645 return NULL;
1646 }
1647 if (sample->timestamp() != next_sample->timestamp()) {
1648 return NULL;
1649 }
1650 if (sample->tid() != next_sample->tid()) {
1651 return NULL;
1652 }
1653 return next_sample;
1654}
1655
1656ProcessedSample::ProcessedSample()
1657 : pcs_(kSampleSize),
1658 timestamp_(0),
1659 vm_tag_(0),
1660 user_tag_(0),
1661 allocation_cid_(-1),
1662 truncated_(false),
1663 timeline_code_trie_(nullptr),
1664 timeline_function_trie_(nullptr) {}
1665
1666void ProcessedSample::FixupCaller(const CodeLookupTable& clt,
1667 uword pc_marker,
1668 uword* stack_buffer) {
1669 const CodeDescriptor* cd = clt.FindCode(At(0));
1670 if (cd == NULL) {
1671 // No Dart code.
1672 return;
1673 }
1674 if (cd->CompileTimestamp() > timestamp()) {
1675 // Code compiled after sample. Ignore.
1676 return;
1677 }
1678 CheckForMissingDartFrame(clt, cd, pc_marker, stack_buffer);
1679}
1680
1681void ProcessedSample::CheckForMissingDartFrame(const CodeLookupTable& clt,
1682 const CodeDescriptor* cd,
1683 uword pc_marker,
1684 uword* stack_buffer) {
1685 ASSERT(cd != NULL);
1686 if (cd->code().IsBytecode()) {
1687 // Bytecode frame build is atomic from the profiler's perspective: no
1688 // missing frame.
1689 return;
1690 }
1691 const Code& code = Code::Handle(Code::RawCast(cd->code().raw()));
1692 ASSERT(!code.IsNull());
1693 // Some stubs (and intrinsics) do not push a frame onto the stack leaving
1694 // the frame pointer in the caller.
1695 //
1696 // PC -> STUB
1697 // FP -> DART3 <-+
1698 // DART2 <-| <- TOP FRAME RETURN ADDRESS.
1699 // DART1 <-|
1700 // .....
1701 //
1702 // In this case, traversing the linked stack frames will not collect a PC
1703 // inside DART3. The stack will incorrectly be: STUB, DART2, DART1.
1704 // In Dart code, after pushing the FP onto the stack, an IP in the current
1705 // function is pushed onto the stack as well. This stack slot is called
1706 // the PC marker. We can use the PC marker to insert DART3 into the stack
1707 // so that it will correctly be: STUB, DART3, DART2, DART1. Note the
1708 // inserted PC may not accurately reflect the true return address into DART3.
1709
1710 // The pc marker is our current best guess of a return address.
1711 uword return_address = pc_marker;
1712
1713 // Attempt to find a better return address.
1714 ReturnAddressLocator ral(At(0), stack_buffer, code);
1715
1716 if (!ral.LocateReturnAddress(&return_address)) {
1717 ASSERT(return_address == pc_marker);
1718 if (code.GetPrologueOffset() == 0) {
1719 // Code has the prologue at offset 0. The frame is already setup and
1720 // can be trusted.
1721 return;
1722 }
1723 // Could not find a better return address than the pc_marker.
1724 if (code.ContainsInstructionAt(return_address)) {
1725 // PC marker is in the same code as pc, no missing frame.
1726 return;
1727 }
1728 }
1729
1730 if (clt.FindCode(return_address) == NULL) {
1731 // Return address is not from a Dart code object. Do not insert.
1732 return;
1733 }
1734
1735 if (return_address != 0) {
1736 InsertAt(1, return_address);
1737 }
1738}
1739
1740ProcessedSampleBuffer::ProcessedSampleBuffer()
1741 : code_lookup_table_(new CodeLookupTable(Thread::Current())) {
1742 ASSERT(code_lookup_table_ != NULL);
1743}
1744
1745#endif // !PRODUCT
1746
1747} // namespace dart
1748