mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 21:51:18 +00:00
Chain samples together to collect long stack traces in profiler
- Add continuation_index field to profiler sample. - Add bit to tell if a sample is the head of a chain. - Add bit to tell if a sample has a chained sample. - Each sample holds 8 frames. - Collect up to 8 frames (1 sample) by default. - Refactor stack walker code to support chaining during stack walking. - Add unit test collecting 20 stack frames. R=asiva@google.com Review URL: https://codereview.chromium.org//1270003002 .
This commit is contained in:
parent
2bde865138
commit
d61c0ebc04
|
@ -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) {
|
|||
}
|
||||
|
||||
|
||||
// Given an exit frame, walk the Dart stack.
|
||||
class ProfilerDartExitStackWalker : public ValueObject {
|
||||
class ProfilerStackWalker : public ValueObject {
|
||||
public:
|
||||
ProfilerDartExitStackWalker(Isolate* isolate, Sample* sample)
|
||||
: sample_(sample),
|
||||
frame_iterator_(isolate) {
|
||||
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 ProfilerStackWalker {
|
||||
public:
|
||||
ProfilerDartExitStackWalker(Isolate* isolate,
|
||||
Sample* sample,
|
||||
SampleBuffer* sample_buffer)
|
||||
: ProfilerStackWalker(isolate, sample, sample_buffer),
|
||||
frame_iterator_(isolate) {
|
||||
}
|
||||
|
||||
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),
|
||||
|
|
|
@ -155,6 +155,11 @@ class Sample {
|
|||
return isolate_;
|
||||
}
|
||||
|
||||
// Thread sample was taken on.
|
||||
ThreadId tid() const {
|
||||
return tid_;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
isolate_ = NULL;
|
||||
pc_marker_ = 0;
|
||||
|
@ -166,10 +171,12 @@ class Sample {
|
|||
lr_ = 0;
|
||||
metadata_ = 0;
|
||||
state_ = 0;
|
||||
continuation_index_ = -1;
|
||||
uword* pcs = GetPCArray();
|
||||
for (intptr_t i = 0; i < pcs_length_; i++) {
|
||||
pcs[i] = 0;
|
||||
}
|
||||
set_head_sample(true);
|
||||
}
|
||||
|
||||
// Timestamp sample was taken at.
|
||||
|
@ -229,30 +236,6 @@ class Sample {
|
|||
lr_ = link_register;
|
||||
}
|
||||
|
||||
void InsertCallerForTopFrame(uword pc) {
|
||||
if (pcs_length_ == 1) {
|
||||
// Only sampling top frame.
|
||||
return;
|
||||
}
|
||||
uword* pcs = GetPCArray();
|
||||
// The caller for the top frame is store at index 1.
|
||||
// Shift all entries down by one.
|
||||
for (intptr_t i = pcs_length_ - 1; i >= 2; i--) {
|
||||
pcs[i] = pcs[i - 1];
|
||||
}
|
||||
// Insert caller for top frame.
|
||||
pcs[1] = pc;
|
||||
set_missing_frame_inserted(true);
|
||||
}
|
||||
|
||||
bool processed() const {
|
||||
return ProcessedBit::decode(state_);
|
||||
}
|
||||
|
||||
void set_processed(bool processed) {
|
||||
state_ = ProcessedBit::update(processed, state_);
|
||||
}
|
||||
|
||||
bool leaf_frame_is_dart() const {
|
||||
return LeafFrameIsDart::decode(state_);
|
||||
}
|
||||
|
@ -301,11 +284,36 @@ class Sample {
|
|||
state_ = ClassAllocationSampleBit::update(allocation_sample, state_);
|
||||
}
|
||||
|
||||
bool is_continuation_sample() const {
|
||||
return ContinuationSampleBit::decode(state_);
|
||||
}
|
||||
|
||||
void SetContinuationIndex(intptr_t index) {
|
||||
ASSERT(!is_continuation_sample());
|
||||
ASSERT(continuation_index_ == -1);
|
||||
state_ = ContinuationSampleBit::update(true, state_);
|
||||
continuation_index_ = index;
|
||||
ASSERT(is_continuation_sample());
|
||||
}
|
||||
|
||||
intptr_t continuation_index() const {
|
||||
ASSERT(is_continuation_sample());
|
||||
return continuation_index_;
|
||||
}
|
||||
|
||||
intptr_t allocation_cid() const {
|
||||
ASSERT(is_allocation_sample());
|
||||
return metadata_;
|
||||
}
|
||||
|
||||
void set_head_sample(bool head_sample) {
|
||||
state_ = HeadSampleBit::update(head_sample, state_);
|
||||
}
|
||||
|
||||
bool head_sample() const {
|
||||
return HeadSampleBit::decode(state_);
|
||||
}
|
||||
|
||||
void set_metadata(intptr_t metadata) {
|
||||
metadata_ = metadata;
|
||||
}
|
||||
|
@ -332,23 +340,26 @@ class Sample {
|
|||
static intptr_t instance_size_;
|
||||
static intptr_t pcs_length_;
|
||||
enum StateBits {
|
||||
kProcessedBit = 0,
|
||||
kHeadSampleBit = 0,
|
||||
kLeafFrameIsDartBit = 1,
|
||||
kIgnoreBit = 2,
|
||||
kExitFrameBit = 3,
|
||||
kMissingFrameInsertedBit = 4,
|
||||
kTruncatedTrace = 5,
|
||||
kClassAllocationSample = 6,
|
||||
kTruncatedTraceBit = 5,
|
||||
kClassAllocationSampleBit = 6,
|
||||
kContinuationSampleBit = 7,
|
||||
};
|
||||
class ProcessedBit : public BitField<bool, kProcessedBit, 1> {};
|
||||
class HeadSampleBit : public BitField<bool, kHeadSampleBit, 1> {};
|
||||
class LeafFrameIsDart : public BitField<bool, kLeafFrameIsDartBit, 1> {};
|
||||
class IgnoreBit : public BitField<bool, kIgnoreBit, 1> {};
|
||||
class ExitFrameBit : public BitField<bool, kExitFrameBit, 1> {};
|
||||
class MissingFrameInsertedBit
|
||||
: public BitField<bool, kMissingFrameInsertedBit, 1> {};
|
||||
class TruncatedTraceBit : public BitField<bool, kTruncatedTrace, 1> {};
|
||||
: public BitField<bool, kMissingFrameInsertedBit, 1> {};
|
||||
class TruncatedTraceBit : public BitField<bool, kTruncatedTraceBit, 1> {};
|
||||
class ClassAllocationSampleBit
|
||||
: public BitField<bool, kClassAllocationSample, 1> {};
|
||||
: public BitField<bool, kClassAllocationSampleBit, 1> {};
|
||||
class ContinuationSampleBit
|
||||
: public BitField<bool, kContinuationSampleBit, 1> {};
|
||||
|
||||
int64_t timestamp_;
|
||||
ThreadId tid_;
|
||||
|
@ -360,6 +371,7 @@ class Sample {
|
|||
uword metadata_;
|
||||
uword lr_;
|
||||
uword state_;
|
||||
intptr_t continuation_index_;
|
||||
|
||||
/* There are a variable number of words that follow, the words hold the
|
||||
* sampled pc values. Access via GetPCArray() */
|
||||
|
@ -387,13 +399,19 @@ class SampleBuffer {
|
|||
intptr_t capacity() const { return capacity_; }
|
||||
|
||||
Sample* At(intptr_t idx) const;
|
||||
intptr_t ReserveSampleSlot();
|
||||
Sample* ReserveSample();
|
||||
Sample* ReserveSampleAndLink(Sample* previous);
|
||||
|
||||
void VisitSamples(SampleVisitor* visitor) {
|
||||
ASSERT(visitor != NULL);
|
||||
const intptr_t length = capacity();
|
||||
for (intptr_t i = 0; i < length; i++) {
|
||||
Sample* sample = At(i);
|
||||
if (!sample->head_sample()) {
|
||||
// An inner sample in a chain of samples.
|
||||
continue;
|
||||
}
|
||||
if (sample->ignore_sample()) {
|
||||
// Bad sample.
|
||||
continue;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
DECLARE_FLAG(int, profile_depth);
|
||||
DECLARE_FLAG(int, max_profile_depth);
|
||||
DECLARE_FLAG(int, profile_period);
|
||||
|
||||
DEFINE_FLAG(bool, trace_profiler, false, "Trace profiler.");
|
||||
|
@ -2043,7 +2043,7 @@ void Profile::PrintJSON(JSONStream* stream) {
|
|||
obj.AddProperty("samplePeriod",
|
||||
static_cast<intptr_t>(FLAG_profile_period));
|
||||
obj.AddProperty("stackDepth",
|
||||
static_cast<intptr_t>(FLAG_profile_depth));
|
||||
static_cast<intptr_t>(FLAG_max_profile_depth));
|
||||
obj.AddProperty("sampleCount", sample_count());
|
||||
obj.AddProperty("timeSpan", MicrosecondsToSeconds(GetTimeSpan()));
|
||||
{
|
||||
|
|
|
@ -14,9 +14,10 @@
|
|||
namespace dart {
|
||||
|
||||
DECLARE_FLAG(bool, profile_vm);
|
||||
DECLARE_FLAG(int, max_profile_depth);
|
||||
|
||||
// Some tests are written assuming native stack trace profiling is disabled.
|
||||
class DisableNativeProfileScope {
|
||||
class DisableNativeProfileScope : public ValueObject {
|
||||
public:
|
||||
DisableNativeProfileScope()
|
||||
: FLAG_profile_vm_(FLAG_profile_vm) {
|
||||
|
@ -32,6 +33,23 @@ class DisableNativeProfileScope {
|
|||
};
|
||||
|
||||
|
||||
// Temporarily adjust the maximum profile depth.
|
||||
class MaxProfileDepthScope : public ValueObject {
|
||||
public:
|
||||
explicit MaxProfileDepthScope(intptr_t new_max_depth)
|
||||
: FLAG_max_profile_depth_(FLAG_max_profile_depth) {
|
||||
Profiler::SetSampleDepth(new_max_depth);
|
||||
}
|
||||
|
||||
~MaxProfileDepthScope() {
|
||||
Profiler::SetSampleDepth(FLAG_max_profile_depth_);
|
||||
}
|
||||
|
||||
private:
|
||||
const intptr_t FLAG_max_profile_depth_;
|
||||
};
|
||||
|
||||
|
||||
class ProfileSampleBufferTestHelper {
|
||||
public:
|
||||
static intptr_t IterateCount(const Isolate* isolate,
|
||||
|
@ -186,9 +204,10 @@ TEST_CASE(Profiler_TrivialRecordAllocation) {
|
|||
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -264,9 +283,10 @@ TEST_CASE(Profiler_ToggleRecordAllocation) {
|
|||
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -281,9 +301,10 @@ TEST_CASE(Profiler_ToggleRecordAllocation) {
|
|||
EXPECT_VALID(result);
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -335,9 +356,10 @@ TEST_CASE(Profiler_ToggleRecordAllocation) {
|
|||
EXPECT_VALID(result);
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -375,9 +397,10 @@ TEST_CASE(Profiler_CodeTicks) {
|
|||
EXPECT_VALID(result);
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -397,9 +420,10 @@ TEST_CASE(Profiler_CodeTicks) {
|
|||
EXPECT_VALID(result);
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -468,9 +492,10 @@ TEST_CASE(Profiler_FunctionTicks) {
|
|||
EXPECT_VALID(result);
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -490,9 +515,10 @@ TEST_CASE(Profiler_FunctionTicks) {
|
|||
EXPECT_VALID(result);
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -1141,9 +1167,10 @@ TEST_CASE(Profiler_FunctionInline) {
|
|||
// At this point B.boo should be optimized and inlined B.foo and B.choo.
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -1159,9 +1186,10 @@ TEST_CASE(Profiler_FunctionInline) {
|
|||
EXPECT_VALID(result);
|
||||
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
|
@ -1260,9 +1288,10 @@ TEST_CASE(Profiler_FunctionInline) {
|
|||
|
||||
// Test code transition tags.
|
||||
{
|
||||
Isolate* isolate = Isolate::Current();
|
||||
StackZone zone(isolate);
|
||||
HANDLESCOPE(isolate);
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter,
|
||||
|
@ -1338,5 +1367,113 @@ TEST_CASE(Profiler_FunctionInline) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE(Profiler_ChainedSamples) {
|
||||
MaxProfileDepthScope mpds(32);
|
||||
// Each sample holds 8 stack frames.
|
||||
// This chain is 20 stack frames deep.
|
||||
const char* kScript =
|
||||
"class A {\n"
|
||||
" var a;\n"
|
||||
" var b;\n"
|
||||
"}\n"
|
||||
"class B {\n"
|
||||
" static boo() {\n"
|
||||
" return new A();\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"go() => init();\n"
|
||||
"init() => secondInit();\n"
|
||||
"secondInit() => apple();\n"
|
||||
"apple() => banana();\n"
|
||||
"banana() => cantaloupe();\n"
|
||||
"cantaloupe() => dog();\n"
|
||||
"dog() => elephant();\n"
|
||||
"elephant() => fred();\n"
|
||||
"fred() => granola();\n"
|
||||
"granola() => haystack();\n"
|
||||
"haystack() => ice();\n"
|
||||
"ice() => jeep();\n"
|
||||
"jeep() => kindle();\n"
|
||||
"kindle() => lemon();\n"
|
||||
"lemon() => mayo();\n"
|
||||
"mayo() => napkin();\n"
|
||||
"napkin() => orange();\n"
|
||||
"orange() => B.boo();\n"
|
||||
"main() {\n"
|
||||
" return go();\n"
|
||||
"}\n";
|
||||
|
||||
Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL);
|
||||
EXPECT_VALID(lib);
|
||||
Library& root_library = Library::Handle();
|
||||
root_library ^= Api::UnwrapHandle(lib);
|
||||
|
||||
const Class& class_a = Class::Handle(GetClass(root_library, "A"));
|
||||
EXPECT(!class_a.IsNull());
|
||||
class_a.SetTraceAllocation(true);
|
||||
|
||||
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
||||
EXPECT_VALID(result);
|
||||
|
||||
|
||||
{
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
AllocationFilter filter(isolate, class_a.id());
|
||||
profile.Build(&filter, Profile::kNoTags);
|
||||
// We should have 1 allocation sample.
|
||||
EXPECT_EQ(1, profile.sample_count());
|
||||
ProfileTrieWalker walker(&profile);
|
||||
|
||||
walker.Reset(Profile::kExclusiveCode);
|
||||
// Move down from the root.
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("B.boo", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("orange", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("napkin", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("mayo", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("lemon", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("kindle", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("jeep", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("ice", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("haystack", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("granola", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("fred", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("elephant", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("dog", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("cantaloupe", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("banana", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("apple", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("secondInit", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("init", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("go", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_STREQ("main", walker.CurrentName());
|
||||
EXPECT(!walker.Down());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
|
|
Loading…
Reference in a new issue