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:
John McCutchan 2015-08-24 11:29:44 -07:00
parent 2bde865138
commit d61c0ebc04
4 changed files with 363 additions and 123 deletions

View file

@ -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),

View file

@ -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;

View file

@ -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()));
{

View file

@ -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