| Index: runtime/vm/profiler.cc
|
| diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
|
| index 339caadf11579ee061ea6b6c6f80c8041b7dd034..c470b6a6b6b4743087c713b70376a35596d95853 100644
|
| --- a/runtime/vm/profiler.cc
|
| +++ b/runtime/vm/profiler.cc
|
| @@ -25,6 +25,8 @@
|
| namespace dart {
|
|
|
|
|
| +static const intptr_t kSampleSize = 8;
|
| +
|
| DECLARE_FLAG(bool, trace_profiler);
|
|
|
| DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler");
|
| @@ -38,7 +40,7 @@ DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates.");
|
| DEFINE_FLAG(int, profile_period, 1000,
|
| "Time between profiler samples in microseconds. Minimum 50.");
|
| #endif
|
| -DEFINE_FLAG(int, profile_depth, 8,
|
| +DEFINE_FLAG(int, max_profile_depth, kSampleSize,
|
| "Maximum number stack frames walked. Minimum 1. Maximum 255.");
|
| #if defined(USING_SIMULATOR)
|
| DEFINE_FLAG(bool, profile_vm, true,
|
| @@ -51,18 +53,11 @@ DEFINE_FLAG(bool, profile_vm, false,
|
| bool Profiler::initialized_ = false;
|
| SampleBuffer* Profiler::sample_buffer_ = NULL;
|
|
|
| -static intptr_t NumberOfFramesToCollect() {
|
| - if (FLAG_profile_depth <= 0) {
|
| - return 0;
|
| - }
|
| - // Subtract to reserve space for the possible missing frame.
|
| - return FLAG_profile_depth - 1;
|
| -}
|
|
|
| void Profiler::InitOnce() {
|
| // Place some sane restrictions on user controlled flags.
|
| SetSamplePeriod(FLAG_profile_period);
|
| - SetSampleDepth(FLAG_profile_depth);
|
| + SetSampleDepth(FLAG_max_profile_depth);
|
| Sample::InitOnce();
|
| if (!FLAG_profile) {
|
| return;
|
| @@ -90,11 +85,11 @@ void Profiler::SetSampleDepth(intptr_t depth) {
|
| const int kMinimumDepth = 2;
|
| const int kMaximumDepth = 255;
|
| if (depth < kMinimumDepth) {
|
| - FLAG_profile_depth = kMinimumDepth;
|
| + FLAG_max_profile_depth = kMinimumDepth;
|
| } else if (depth > kMaximumDepth) {
|
| - FLAG_profile_depth = kMaximumDepth;
|
| + FLAG_max_profile_depth = kMaximumDepth;
|
| } else {
|
| - FLAG_profile_depth = depth;
|
| + FLAG_max_profile_depth = depth;
|
| }
|
| }
|
|
|
| @@ -228,8 +223,7 @@ intptr_t Sample::instance_size_ = 0;
|
|
|
|
|
| void Sample::InitOnce() {
|
| - ASSERT(FLAG_profile_depth >= 2);
|
| - pcs_length_ = FLAG_profile_depth;
|
| + pcs_length_ = kSampleSize;
|
| instance_size_ =
|
| sizeof(Sample) + (sizeof(uword) * pcs_length_); // NOLINT.
|
| }
|
| @@ -265,14 +259,31 @@ Sample* SampleBuffer::At(intptr_t idx) const {
|
| }
|
|
|
|
|
| -Sample* SampleBuffer::ReserveSample() {
|
| +intptr_t SampleBuffer::ReserveSampleSlot() {
|
| ASSERT(samples_ != NULL);
|
| uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_);
|
| // Map back into sample buffer range.
|
| cursor = cursor % capacity_;
|
| - return At(cursor);
|
| + return cursor;
|
| +}
|
| +
|
| +Sample* SampleBuffer::ReserveSample() {
|
| + return At(ReserveSampleSlot());
|
| }
|
|
|
| +
|
| +Sample* SampleBuffer::ReserveSampleAndLink(Sample* previous) {
|
| + ASSERT(previous != NULL);
|
| + intptr_t next_index = ReserveSampleSlot();
|
| + Sample* next = At(next_index);
|
| + next->Init(previous->isolate(), previous->timestamp(), previous->tid());
|
| + next->set_head_sample(false);
|
| + // Mark that previous continues at next.
|
| + previous->SetContinuationIndex(next_index);
|
| + return next;
|
| +}
|
| +
|
| +
|
| // Attempts to find the true return address when a Dart frame is being setup
|
| // or torn down.
|
| // NOTE: Architecture specific implementations below.
|
| @@ -407,50 +418,96 @@ void ClearProfileVisitor::VisitSample(Sample* sample) {
|
| }
|
|
|
|
|
| +class ProfilerStackWalker : public ValueObject {
|
| + public:
|
| + ProfilerStackWalker(Isolate* isolate,
|
| + Sample* head_sample,
|
| + SampleBuffer* sample_buffer)
|
| + : isolate_(isolate),
|
| + sample_(head_sample),
|
| + sample_buffer_(sample_buffer),
|
| + frame_index_(0),
|
| + total_frames_(0) {
|
| + ASSERT(isolate_ != NULL);
|
| + ASSERT(sample_ != NULL);
|
| + ASSERT(sample_buffer_ != NULL);
|
| + ASSERT(sample_->head_sample());
|
| + }
|
| +
|
| + bool Append(uword pc) {
|
| + if (total_frames_ >= FLAG_max_profile_depth) {
|
| + sample_->set_truncated_trace(true);
|
| + return false;
|
| + }
|
| + ASSERT(sample_ != NULL);
|
| + if (frame_index_ == kSampleSize) {
|
| + Sample* new_sample = sample_buffer_->ReserveSampleAndLink(sample_);
|
| + if (new_sample == NULL) {
|
| + // Could not reserve new sample- mark this as truncated.
|
| + sample_->set_truncated_trace(true);
|
| + return false;
|
| + }
|
| + frame_index_ = 0;
|
| + sample_ = new_sample;
|
| + }
|
| + ASSERT(frame_index_ < kSampleSize);
|
| + sample_->SetAt(frame_index_, pc);
|
| + frame_index_++;
|
| + total_frames_++;
|
| + return true;
|
| + }
|
| +
|
| + protected:
|
| + Isolate* isolate_;
|
| + Sample* sample_;
|
| + SampleBuffer* sample_buffer_;
|
| + intptr_t frame_index_;
|
| + intptr_t total_frames_;
|
| +};
|
| +
|
| +
|
| // Given an exit frame, walk the Dart stack.
|
| -class ProfilerDartExitStackWalker : public ValueObject {
|
| +class ProfilerDartExitStackWalker : public ProfilerStackWalker {
|
| public:
|
| - ProfilerDartExitStackWalker(Isolate* isolate, Sample* sample)
|
| - : sample_(sample),
|
| + ProfilerDartExitStackWalker(Isolate* isolate,
|
| + Sample* sample,
|
| + SampleBuffer* sample_buffer)
|
| + : ProfilerStackWalker(isolate, sample, sample_buffer),
|
| frame_iterator_(isolate) {
|
| - ASSERT(sample_ != NULL);
|
| }
|
|
|
| void walk() {
|
| // Mark that this sample was collected from an exit frame.
|
| sample_->set_exit_frame_sample(true);
|
| - intptr_t frame_index = 0;
|
| +
|
| StackFrame* frame = frame_iterator_.NextFrame();
|
| while (frame != NULL) {
|
| - sample_->SetAt(frame_index, frame->pc());
|
| - frame_index++;
|
| - if (frame_index >= NumberOfFramesToCollect()) {
|
| - sample_->set_truncated_trace(true);
|
| - break;
|
| + if (!Append(frame->pc())) {
|
| + return;
|
| }
|
| frame = frame_iterator_.NextFrame();
|
| }
|
| }
|
|
|
| private:
|
| - Sample* sample_;
|
| DartFrameIterator frame_iterator_;
|
| };
|
|
|
|
|
| // Executing Dart code, walk the stack.
|
| -class ProfilerDartStackWalker : public ValueObject {
|
| +class ProfilerDartStackWalker : public ProfilerStackWalker {
|
| public:
|
| - ProfilerDartStackWalker(Sample* sample,
|
| + ProfilerDartStackWalker(Isolate* isolate,
|
| + Sample* sample,
|
| + SampleBuffer* sample_buffer,
|
| uword stack_lower,
|
| uword stack_upper,
|
| uword pc,
|
| uword fp,
|
| uword sp)
|
| - : sample_(sample),
|
| + : ProfilerStackWalker(isolate, sample, sample_buffer),
|
| stack_upper_(stack_upper),
|
| stack_lower_(stack_lower) {
|
| - ASSERT(sample_ != NULL);
|
| pc_ = reinterpret_cast<uword*>(pc);
|
| fp_ = reinterpret_cast<uword*>(fp);
|
| sp_ = reinterpret_cast<uword*>(sp);
|
| @@ -474,13 +531,14 @@ class ProfilerDartStackWalker : public ValueObject {
|
| return;
|
| }
|
| }
|
| - for (int i = 0; i < NumberOfFramesToCollect(); i++) {
|
| - sample_->SetAt(i, reinterpret_cast<uword>(pc_));
|
| + while (true) {
|
| + if (!Append(reinterpret_cast<uword>(pc_))) {
|
| + return;
|
| + }
|
| if (!Next()) {
|
| return;
|
| }
|
| }
|
| - sample_->set_truncated_trace(true);
|
| }
|
|
|
| private:
|
| @@ -583,7 +641,6 @@ class ProfilerDartStackWalker : public ValueObject {
|
| uword* pc_;
|
| uword* fp_;
|
| uword* sp_;
|
| - Sample* sample_;
|
| const uword stack_upper_;
|
| uword stack_lower_;
|
| };
|
| @@ -593,27 +650,28 @@ class ProfilerDartStackWalker : public ValueObject {
|
| // recent GCC versions with optimizing enabled) the stack walking code may
|
| // fail.
|
| //
|
| -class ProfilerNativeStackWalker : public ValueObject {
|
| +class ProfilerNativeStackWalker : public ProfilerStackWalker {
|
| public:
|
| - ProfilerNativeStackWalker(Sample* sample,
|
| + ProfilerNativeStackWalker(Isolate* isolate,
|
| + Sample* sample,
|
| + SampleBuffer* sample_buffer,
|
| uword stack_lower,
|
| uword stack_upper,
|
| uword pc,
|
| uword fp,
|
| uword sp)
|
| - : sample_(sample),
|
| + : ProfilerStackWalker(isolate, sample, sample_buffer),
|
| stack_upper_(stack_upper),
|
| original_pc_(pc),
|
| original_fp_(fp),
|
| original_sp_(sp),
|
| lower_bound_(stack_lower) {
|
| - ASSERT(sample_ != NULL);
|
| }
|
|
|
| void walk() {
|
| const uword kMaxStep = VirtualMemory::PageSize();
|
|
|
| - sample_->SetAt(0, original_pc_);
|
| + Append(original_pc_);
|
|
|
| uword* pc = reinterpret_cast<uword*>(original_pc_);
|
| uword* fp = reinterpret_cast<uword*>(original_fp_);
|
| @@ -630,8 +688,10 @@ class ProfilerNativeStackWalker : public ValueObject {
|
| return;
|
| }
|
|
|
| - for (int i = 0; i < NumberOfFramesToCollect(); i++) {
|
| - sample_->SetAt(i, reinterpret_cast<uword>(pc));
|
| + while (true) {
|
| + if (!Append(reinterpret_cast<uword>(pc))) {
|
| + return;
|
| + }
|
|
|
| pc = CallerPC(fp);
|
| previous_fp = fp;
|
| @@ -660,8 +720,6 @@ class ProfilerNativeStackWalker : public ValueObject {
|
| // Move the lower bound up.
|
| lower_bound_ = reinterpret_cast<uword>(fp);
|
| }
|
| -
|
| - sample_->set_truncated_trace(true);
|
| }
|
|
|
| private:
|
| @@ -693,7 +751,6 @@ class ProfilerNativeStackWalker : public ValueObject {
|
| return r;
|
| }
|
|
|
| - Sample* sample_;
|
| const uword stack_upper_;
|
| const uword original_pc_;
|
| const uword original_fp_;
|
| @@ -1000,7 +1057,9 @@ void Profiler::RecordAllocation(Isolate* isolate, intptr_t cid) {
|
| sample_buffer,
|
| OSThread::GetCurrentThreadId());
|
| sample->SetAllocationCid(cid);
|
| - ProfilerNativeStackWalker native_stack_walker(sample,
|
| + ProfilerNativeStackWalker native_stack_walker(isolate,
|
| + sample,
|
| + sample_buffer,
|
| stack_lower,
|
| stack_upper,
|
| pc,
|
| @@ -1012,7 +1071,9 @@ void Profiler::RecordAllocation(Isolate* isolate, intptr_t cid) {
|
| sample_buffer,
|
| OSThread::GetCurrentThreadId());
|
| sample->SetAllocationCid(cid);
|
| - ProfilerDartExitStackWalker dart_exit_stack_walker(isolate, sample);
|
| + ProfilerDartExitStackWalker dart_exit_stack_walker(isolate,
|
| + sample,
|
| + sample_buffer);
|
| dart_exit_stack_walker.walk();
|
| } else {
|
| // Fall back.
|
| @@ -1102,16 +1163,22 @@ void Profiler::RecordSampleInterruptCallback(
|
| ASSERT(counters != NULL);
|
| counters->Increment(sample->vm_tag());
|
|
|
| - ProfilerNativeStackWalker native_stack_walker(sample,
|
| + ProfilerNativeStackWalker native_stack_walker(isolate,
|
| + sample,
|
| + sample_buffer,
|
| stack_lower,
|
| stack_upper,
|
| pc,
|
| fp,
|
| sp);
|
|
|
| - ProfilerDartExitStackWalker dart_exit_stack_walker(isolate, sample);
|
| + ProfilerDartExitStackWalker dart_exit_stack_walker(isolate,
|
| + sample,
|
| + sample_buffer);
|
|
|
| - ProfilerDartStackWalker dart_stack_walker(sample,
|
| + ProfilerDartStackWalker dart_stack_walker(isolate,
|
| + sample,
|
| + sample_buffer,
|
| stack_lower,
|
| stack_upper,
|
| pc,
|
| @@ -1147,6 +1214,10 @@ ProcessedSampleBuffer* SampleBuffer::BuildProcessedSampleBuffer(
|
| // Bad sample.
|
| continue;
|
| }
|
| + if (!sample->head_sample()) {
|
| + // An inner sample in a chain of samples.
|
| + continue;
|
| + }
|
| if (sample->isolate() != filter->isolate()) {
|
| // Another isolate.
|
| continue;
|
| @@ -1188,7 +1259,7 @@ ProcessedSample* SampleBuffer::BuildProcessedSample(Sample* sample) {
|
| bool truncated = false;
|
| Sample* current = sample;
|
| while (current != NULL) {
|
| - for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
|
| + for (intptr_t i = 0; i < kSampleSize; i++) {
|
| if (current->At(i) == 0) {
|
| break;
|
| }
|
| @@ -1196,7 +1267,7 @@ ProcessedSample* SampleBuffer::BuildProcessedSample(Sample* sample) {
|
| }
|
|
|
| truncated = truncated || current->truncated_trace();
|
| - current = Next(sample);
|
| + current = Next(current);
|
| }
|
|
|
| if (!sample->exit_frame_sample()) {
|
| @@ -1214,13 +1285,27 @@ ProcessedSample* SampleBuffer::BuildProcessedSample(Sample* sample) {
|
|
|
|
|
| Sample* SampleBuffer::Next(Sample* sample) {
|
| - // TODO(johnmccutchan): Support chaining samples for complete stack traces.
|
| - return NULL;
|
| + if (!sample->is_continuation_sample())
|
| + return NULL;
|
| + Sample* next_sample = At(sample->continuation_index());
|
| + // Sanity check.
|
| + ASSERT(sample != next_sample);
|
| + // Detect invalid chaining.
|
| + if (sample->isolate() != next_sample->isolate()) {
|
| + return NULL;
|
| + }
|
| + if (sample->timestamp() != next_sample->timestamp()) {
|
| + return NULL;
|
| + }
|
| + if (sample->tid() != next_sample->tid()) {
|
| + return NULL;
|
| + }
|
| + return next_sample;
|
| }
|
|
|
|
|
| ProcessedSample::ProcessedSample()
|
| - : pcs_(FLAG_profile_depth),
|
| + : pcs_(kSampleSize),
|
| timestamp_(0),
|
| vm_tag_(0),
|
| user_tag_(0),
|
|
|