Chromium Code Reviews| Index: runtime/vm/profiler.cc |
| diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..892835ee1c509e3872740096a5975e434df65123 |
| --- /dev/null |
| +++ b/runtime/vm/profiler.cc |
| @@ -0,0 +1,652 @@ |
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +#include <cstdio> |
| + |
| +#include "platform/utils.h" |
| + |
| +#include "vm/isolate.h" |
| +#include "vm/json_stream.h" |
| +#include "vm/native_symbol.h" |
| +#include "vm/object.h" |
| +#include "vm/os.h" |
| +#include "vm/profiler.h" |
| +#include "vm/signal_handler.h" |
| + |
| +namespace dart { |
| + |
| +#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS) || \ |
| + defined(TARGET_OS_ANDROID) |
| +#define PROFILER_USES_SIGNALS |
| +#endif |
| + |
| +DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); |
| + |
| +#if defined(PROFILER_USES_SIGNALS) |
| +static void ProfileSignalAction(int signal, siginfo_t* info, void* context_); |
| +#endif |
| + |
| +class ProfilerSampleStackWalker { |
| + public: |
| + ProfilerSampleStackWalker(Sample* sample, uintptr_t stack_lower, |
| + uintptr_t stack_upper) : sample_(sample), |
| + stack_lower_(stack_lower), |
| + stack_upper_(stack_upper) { |
| + } |
| + |
| + int walk(uintptr_t pc_, uintptr_t fp_) { |
|
siva
2013/10/28 05:19:21
why underscrore for these param names?
Cutch
2013/11/04 20:36:05
Cleaned up.
|
| + original_pc_ = pc_; |
| + original_fp_ = fp_; |
| + uword* pc = reinterpret_cast<uword*>(pc_); |
| + uword* fp = reinterpret_cast<uword*>(fp_); |
| + int i = 0; |
| + for (; i < Sample::kNumStackFrames; i++) { |
| + sample_->pcs[i] = reinterpret_cast<uintptr_t>(pc); |
| + if (!ValidInstructionPointer(pc) || !ValidFramePointer(fp, i)) { |
| + break; |
| + } |
| + pc = parent_pc(fp); |
| + fp = parent_fp(fp); |
| + } |
| + return i; |
| + } |
| + |
| + private: |
| + uword* parent_pc(uword* fp) { |
| + ASSERT(fp != NULL); |
| + return reinterpret_cast<uword*>(*(fp+1)); |
| + } |
| + |
| + uword* parent_fp(uword* fp) { |
| + ASSERT(fp != NULL); |
| + return reinterpret_cast<uword*>(*fp); |
| + } |
| + |
| + bool ValidInstructionPointer(uword* pc) { |
| + uintptr_t cursor = reinterpret_cast<uintptr_t>(pc); |
| + return cursor != 0; |
| + } |
| + |
| + bool ValidFramePointer(uword* fp, int i) { |
| + uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); |
| + cursor += sizeof(fp); |
| + bool r = cursor >= stack_lower_ && cursor <= stack_upper_; |
| + return r; |
| + } |
| + |
| + Sample* sample_; |
| + uintptr_t original_fp_; |
| + uintptr_t original_pc_; |
| + uintptr_t stack_lower_; |
| + uintptr_t stack_upper_; |
|
siva
2013/10/28 05:19:21
DISALLOW stuff
Cutch
2013/11/04 20:36:05
Done.
|
| +}; |
| + |
| + |
| +bool ProfilerManager::initialized_ = false; |
| +bool ProfilerManager::shutdown_ = false; |
| +Monitor* ProfilerManager::monitor_ = NULL; |
| +Isolate** ProfilerManager::isolates_ = NULL; |
| +intptr_t ProfilerManager::isolates_capacity_ = 0; |
| +intptr_t ProfilerManager::isolates_size_ = 0; |
| + |
| + |
| +void ProfilerManager::InitOnce() { |
| + if (!FLAG_profile) { |
| + return; |
| + } |
| + NativeSymbolResolver::InitOnce(); |
| + ASSERT(!initialized_); |
| + monitor_ = new Monitor(); |
| + initialized_ = true; |
| + ResizeIsolates(16); |
| +#if defined(PROFILER_USES_SIGNALS) |
| + SignalHandler::Install(ProfileSignalAction); |
| +#endif |
| + Thread::Start(ThreadMain, 0); |
| +} |
| + |
| + |
| +void ProfilerManager::Shutdown() { |
| + if (!FLAG_profile) { |
| + return; |
| + } |
| + ScopedMonitorLock lock(monitor_); |
| + shutdown_ = true; |
| + for (intptr_t i = 0; i < isolates_size_; i++) { |
| + Isolate* isolate = isolates_[i]; |
|
siva
2013/10/28 05:19:21
ASSERT(isolate != NULL);
Cutch
2013/11/04 20:36:05
Done.
|
| + ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); |
| + FreeIsolateProfilingData(isolate); |
| + } |
| + isolates_size_ = 0; |
| + lock.Notify(); |
| + NativeSymbolResolver::ShutdownOnce(); |
| +} |
| + |
| + |
| +void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) { |
| + if (!FLAG_profile) { |
| + return; |
| + } |
| + ASSERT(isolate != NULL); |
| + ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); |
| + SampleBuffer* sample_buffer = new SampleBuffer(); |
| + IsolateProfilerData* profiler_data = |
| + new IsolateProfilerData(isolate, sample_buffer); |
| + profiler_data->set_sample_interval(1000); |
| + isolate->set_profiler_data(profiler_data); |
| +} |
| + |
| + |
| +void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) { |
|
siva
2013/10/28 05:19:21
why not move the lock here
ScopedMutexLock profile
Cutch
2013/11/04 20:36:05
I've added a TODO. I need to think about this agai
|
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + // Already freed. |
| + return; |
| + } |
| + isolate->set_profiler_data(NULL); |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + ASSERT(sample_buffer != NULL); |
| + delete sample_buffer; |
| + delete profiler_data; |
| +} |
| + |
| + |
| +void ProfilerManager::ShutdownIsolate(Isolate* isolate) { |
| + ASSERT(isolate != NULL); |
| + if (!FLAG_profile) { |
| + return; |
| + } |
| + ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); |
| + FreeIsolateProfilingData(isolate); |
| +} |
| + |
| + |
| +static void CollectSample(IsolateProfilerData* profiler_data, |
| + uintptr_t pc, |
| + uintptr_t fp, |
| + uintptr_t stack_lower, |
| + uintptr_t stack_upper) { |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + Sample* sample = sample_buffer->ReserveSample(); |
| + ASSERT(sample != NULL); |
| + sample->timestamp = OS::GetCurrentTimeMicros(); |
| + // TODO(johnmccutchan): Make real use of vm_tags and runtime_tags. |
|
siva
2013/10/28 05:19:21
open an issue and use the issue number in the TODO
Cutch
2013/11/04 20:36:05
Done.
|
| + sample->vm_tags = Sample::kExecuting; |
| + sample->runtime_tags = 0; |
| + int64_t cpu_usage; |
| + Thread::GetThreadCPUUsage(&cpu_usage); |
| + sample->cpu_usage = profiler_data->set_and_delta_cpu_usage(cpu_usage); |
| + ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper); |
| + stackWalker.walk(pc, fp); |
| +} |
| + |
| + |
| +#if defined(PROFILER_USES_SIGNALS) |
| +static void ProfileSignalAction(int signal, siginfo_t* info, void* context_) { |
| + if (signal != SIGPROF) { |
| + return; |
| + } |
| + ucontext_t* context = reinterpret_cast<ucontext_t*>(context_); |
| + mcontext_t mcontext = context->uc_mcontext; |
| + Isolate* isolate = Isolate::Current(); |
| + if (isolate == NULL) { |
| + return; |
| + } |
| + { |
| + ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| + } |
| + |
| + uintptr_t stack_lower = isolate->stack_limit(); |
| + uintptr_t stack_upper = stack_lower + isolate->GetSpecifiedStackSize(); |
|
siva
2013/10/28 05:19:21
There is an implicit assumption here that the stac
Cutch
2013/11/04 20:36:05
Yes. When would this not be true?
|
| + if (stack_lower == static_cast<uintptr_t>(~0)) { |
| + stack_lower = isolate->saved_stack_limit(); |
| + stack_upper = stack_lower + isolate->GetSpecifiedStackSize(); |
| + } |
| + if (stack_lower == static_cast<uintptr_t>(~0)) { |
| + stack_lower = 0; |
| + stack_upper = 0; |
| + } |
|
siva
2013/10/28 05:19:21
Not sure I understand the logic here.
Cutch
2013/11/04 20:36:05
I'm trying to get the address space bounds for the
|
| + uintptr_t PC = SignalHandler::GetProgramCounter(mcontext); |
| + uintptr_t FP = SignalHandler::GetFramePointer(mcontext); |
| + stack_lower = SignalHandler::GetStackPointer(mcontext); |
| + int64_t sample_time = OS::GetCurrentTimeMicros(); |
| + profiler_data->SampledAt(sample_time); |
| + CollectSample(profiler_data, PC, FP, stack_lower, stack_upper); |
| + } |
| + ProfilerManager::ScheduleIsolate(isolate); |
| +} |
| +#endif |
| + |
| + |
| +void ProfilerManager::ScheduleIsolate(Isolate* isolate) { |
| + if (!FLAG_profile) { |
| + return; |
| + } |
| + ASSERT(initialized_); |
| + ASSERT(isolate != NULL); |
| + ScopedMonitorLock lock(monitor_); |
| + ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| + } |
| + profiler_data->Scheduled(OS::GetCurrentTimeMicros(), |
| + Thread::GetCurrentThreadId()); |
| + AddIsolate(isolate); |
| + lock.Notify(); |
| +} |
| + |
| + |
| +void ProfilerManager::DescheduleIsolate(Isolate* isolate) { |
| + if (!FLAG_profile) { |
| + return; |
| + } |
| + ASSERT(initialized_); |
| + ASSERT(isolate != NULL); |
| + ScopedMonitorLock lock(monitor_); |
| + intptr_t i = FindIsolate(isolate); |
| + if (i < 0) { |
| + // Not scheduled. |
| + return; |
| + } |
| + { |
| + ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + ASSERT(profiler_data != NULL); |
| + profiler_data->Descheduled(); |
| + } |
| + RemoveIsolate(i); |
| + lock.Notify(); |
| +} |
| + |
| + |
| +void PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
| + ASSERT(isolate == Isolate::Current()); |
| + { |
| + // We can't get signals here. |
| + } |
| + UNIMPLEMENTED(); |
| +} |
| + |
| + |
| +void ProfilerManager::ResizeIsolates(intptr_t new_capacity) { |
| + ASSERT(new_capacity > isolates_capacity_); |
| + Isolate* isolate = NULL; |
| + isolates_ = reinterpret_cast<Isolate**>( |
| + realloc(isolates_, sizeof(isolate) * new_capacity)); |
| + isolates_capacity_ = new_capacity; |
| +} |
| + |
| + |
| +void ProfilerManager::AddIsolate(Isolate* isolate) { |
| + if (isolates_size_ == isolates_capacity_) { |
| + ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2); |
| + } |
| + isolates_[isolates_size_] = isolate; |
| + isolates_size_++; |
| +} |
| + |
| + |
| +intptr_t ProfilerManager::FindIsolate(Isolate* isolate) { |
| + for (intptr_t i = 0; i < isolates_size_; i++) { |
| + if (isolates_[i] == isolate) { |
| + return i; |
| + } |
| + } |
| + return -1; |
| +} |
| + |
| + |
| +void ProfilerManager::RemoveIsolate(intptr_t i) { |
| + ASSERT(i < isolates_size_); |
| + intptr_t last = isolates_size_ - 1; |
| + if (i != last) { |
| + // Swap. |
| + Isolate* temp = isolates_[last]; |
| + isolates_[last] = isolates_[i]; |
| + isolates_[i] = temp; |
|
siva
2013/10/28 05:19:21
why do they have to be swapped why not just
isolat
Cutch
2013/11/04 20:36:05
Done.
|
| + } |
| + // Mark as NULL. |
| + isolates_[last] = NULL; |
| + // Pop. |
| + isolates_size_--; |
| +} |
| + |
| + |
| +#if defined(PROFILER_USES_SIGNALS) |
| +int64_t ProfilerManager::SampleAndRescheduleIsolates(int64_t current_time) { |
| + if (isolates_size_ == 0) { |
| + return 0; |
| + } |
| + static const int64_t max_time = 0x7fffffffffffffffLL; |
| + int64_t lowest = max_time; |
| + for (intptr_t i = 0; i < isolates_size_; i++) { |
| + Isolate* isolate = isolates_[i]; |
| + ScopedMutexLock isolate_lock(isolate->profiler_data_mutex()); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + ASSERT(profiler_data != NULL); |
| + if (profiler_data->ShouldSample(current_time)) { |
| + pthread_kill(profiler_data->thread_id(), SIGPROF); |
| + RemoveIsolate(i); |
| + i--; // Remove moves the last element into i, this decrement cancels |
| + // the increment in the for loop. |
|
siva
2013/10/28 05:19:21
This is tricky, why not replace the for loop with
Cutch
2013/11/04 20:36:05
Done.
|
| + continue; |
| + } |
| + if (profiler_data->CanExpire()) { |
| + int64_t isolate_time_left = |
| + profiler_data->TimeUntilExpiration(current_time); |
| + if (isolate_time_left < 0) { |
| + continue; |
| + } |
| + if (isolate_time_left < lowest) { |
| + lowest = isolate_time_left; |
| + } |
| + } |
| + } |
| + if (isolates_size_ == 0) { |
| + return 0; |
| + } |
| + if (lowest == max_time) { |
| + return 0; |
| + } |
| + ASSERT(lowest != max_time); |
| + ASSERT(lowest > 0); |
| + return lowest; |
| +} |
| +#else |
| +int64_t ProfilerManager::SampleAndRescheduleIsolates(int64_t current_time) { |
| + if (isolates_size_ == 0) { |
| + return 0; |
| + } |
| + static const int64_t max_time = 0x7fffffffffffffffLL; |
| + int64_t lowest = max_time; |
| + // TODO(johnmccutchan): Implement sampling loop on Windows. |
| + if (isolates_size_ == 0) { |
| + return 0; |
| + } |
| + if (lowest == max_time) { |
| + return 0; |
| + } |
| + ASSERT(lowest != max_time); |
| + ASSERT(lowest > 0); |
| + return lowest; |
| +} |
| +#endif |
|
siva
2013/10/28 05:19:21
I would prefer if we moved this code to indiviual
Cutch
2013/11/04 20:36:05
Agreed and Done.
|
| + |
| + |
| +static const char* FindSymbolName(uintptr_t pc, bool* native_symbol) { |
| + // TODO(johnmccutchan): Differentiate between symbols which can't be found |
| + // and symbols which were GCed. (Heap::CodeContains). |
| + ASSERT(native_symbol != NULL); |
| + const char* symbol_name = "Unknown"; |
| + *native_symbol = false; |
| + const Code& code = Code::Handle(Code::LookupCode(pc)); |
| + if (code.IsNull()) { |
| + // Possibly a native symbol. |
| + const char* native_name = |
| + NativeSymbolResolver::LookupSymbolName(pc); |
| + if (native_name != NULL) { |
| + symbol_name = native_name; |
| + *native_symbol = true; |
| + } |
| + } else { |
| + const Function& function = Function::Handle(code.function()); |
| + if (!function.IsNull()) { |
| + const String& name = String::Handle(function.QualifiedUserVisibleName()); |
| + if (!name.IsNull()) { |
| + symbol_name = name.ToCString(); |
| + } |
| + } |
| + } |
| + return symbol_name; |
| +} |
| + |
| + |
| +void ProfilerManager::WriteTracing(Isolate* isolate, const char* name, |
| + Dart_Port port) { |
| + ASSERT(isolate == Isolate::Current()); |
| + ScopedMutexLock profiler_data_lock(isolate->profiler_data_mutex()); |
| + IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| + if (profiler_data == NULL) { |
| + return; |
| + } |
| + SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| + ASSERT(sample_buffer != NULL); |
| + JSONStream stream(10 * MB); |
| + intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer); |
| + intptr_t pid = 1; |
| + { |
| + JSONArray events(&stream); |
| + { |
| + JSONObject thread_name(&events); |
| + thread_name.AddProperty("name", "thread_name"); |
| + thread_name.AddProperty("ph", "M"); |
| + thread_name.AddProperty("tid", tid); |
| + thread_name.AddProperty("pid", pid); |
| + { |
| + JSONObject args(&thread_name, "args"); |
| + args.AddProperty("name", name); |
| + } |
| + } |
| + { |
| + JSONObject process_name(&events); |
| + process_name.AddProperty("name", "process_name"); |
| + process_name.AddProperty("ph", "M"); |
| + process_name.AddProperty("tid", tid); |
| + process_name.AddProperty("pid", pid); |
| + { |
| + JSONObject args(&process_name, "args"); |
| + args.AddProperty("name", "Dart VM"); |
| + } |
| + } |
| + uint64_t last_time = 0; |
| + for (Sample* i = sample_buffer->FirstSample(); |
| + i != sample_buffer->LastSample(); |
| + i = sample_buffer->NextSample(i)) { |
| + if (last_time == 0) { |
| + last_time = i->timestamp; |
| + } |
| + intptr_t delta = i->timestamp - last_time; |
| + { |
| + double percentage = static_cast<double>(i->cpu_usage) / |
| + static_cast<double>(delta) * 100.0; |
| + if (percentage != percentage) { |
| + percentage = 0.0; |
| + } |
| + percentage = percentage < 0.0 ? 0.0 : percentage; |
| + percentage = percentage > 100.0 ? 100.0 : percentage; |
| + { |
| + JSONObject cpu_usage(&events); |
| + cpu_usage.AddProperty("name", "CPU Usage"); |
| + cpu_usage.AddProperty("ph", "C"); |
| + cpu_usage.AddProperty("tid", tid); |
| + cpu_usage.AddProperty("pid", pid); |
| + cpu_usage.AddProperty("ts", static_cast<double>(last_time)); |
| + { |
| + JSONObject args(&cpu_usage, "args"); |
| + args.AddProperty("CPU", percentage); |
| + } |
| + } |
| + { |
| + JSONObject cpu_usage(&events); |
| + cpu_usage.AddProperty("name", "CPU Usage"); |
| + cpu_usage.AddProperty("ph", "C"); |
| + cpu_usage.AddProperty("tid", tid); |
| + cpu_usage.AddProperty("pid", pid); |
| + cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp)); |
| + { |
| + JSONObject args(&cpu_usage, "args"); |
| + args.AddProperty("CPU", percentage); |
| + } |
| + } |
| + } |
| + for (int j = 0; j < Sample::kNumStackFrames; j++) { |
| + if (i->pcs[j] == 0) { |
| + continue; |
| + } |
| + bool native_symbol = false; |
| + const char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); |
| + { |
| + JSONObject begin(&events); |
| + begin.AddProperty("ph", "B"); |
| + begin.AddProperty("tid", tid); |
| + begin.AddProperty("pid", pid); |
| + begin.AddProperty("name", symbol_name); |
| + begin.AddProperty("ts", static_cast<double>(last_time)); |
| + } |
| + if (native_symbol) { |
| + NativeSymbolResolver::FreeSymbolName(symbol_name); |
| + } |
| + } |
| + for (int j = Sample::kNumStackFrames-1; j >= 0; j--) { |
| + if (i->pcs[j] == 0) { |
| + continue; |
| + } |
| + bool native_symbol = false; |
| + const char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); |
| + { |
| + JSONObject end(&events); |
| + end.AddProperty("ph", "E"); |
| + end.AddProperty("tid", tid); |
| + end.AddProperty("pid", pid); |
| + end.AddProperty("name", symbol_name); |
| + end.AddProperty("ts", static_cast<double>(i->timestamp)); |
| + } |
| + if (native_symbol) { |
| + NativeSymbolResolver::FreeSymbolName(symbol_name); |
| + } |
| + } |
| + last_time = i->timestamp; |
| + } |
| + } |
| + char fname[1024]; |
| + snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof", |
| + static_cast<int>(port)); |
| + FILE* f = fopen(fname, "wb"); |
| + ASSERT(f != NULL); |
| + fputs(stream.ToCString(), f); |
| + fclose(f); |
| +} |
| + |
| + |
| +void ProfilerManager::ThreadMain(uword parameters) { |
| + ASSERT(initialized_); |
| + ASSERT(FLAG_profile); |
| + ScopedMonitorLock lock(monitor_); |
| + while (!shutdown_) { |
| + int64_t current_time = OS::GetCurrentTimeMicros(); |
| + int64_t next_sample = SampleAndRescheduleIsolates(current_time); |
| + lock.WaitMicros(next_sample); |
| + } |
| +} |
| + |
| + |
| +IsolateProfilerData::IsolateProfilerData(Isolate* isolate, |
| + SampleBuffer* sample_buffer) { |
| + isolate_ = isolate; |
| + sample_buffer_ = sample_buffer; |
| + timer_expiration_micros_ = kNoExpirationTime; |
| + last_sampled_micros_ = 0; |
| + thread_id_ = 0; |
| +} |
| + |
| + |
| +IsolateProfilerData::~IsolateProfilerData() { |
| +} |
| + |
| + |
| +void IsolateProfilerData::SampledAt(int64_t current_time) { |
| + last_sampled_micros_ = current_time; |
| +} |
| + |
| + |
| +void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) { |
| + timer_expiration_micros_ = current_time + sample_interval_micros_; |
| + Thread::GetThreadCPUUsage(&cpu_usage_); |
| + thread_id_ = thread_id; |
| +} |
| + |
| + |
| +void IsolateProfilerData::Descheduled() { |
| + cpu_usage_ = kDescheduledCpuUsage; |
| + timer_expiration_micros_ = kNoExpirationTime; |
| + thread_id_ = 0; |
| + Sample* sample = sample_buffer_->ReserveSample(); |
| + ASSERT(sample != NULL); |
| + sample->timestamp = OS::GetCurrentTimeMicros(); |
| + sample->cpu_usage = 0; |
| + sample->vm_tags = Sample::kIdle; |
| +} |
| + |
| + |
| +const char* Sample::kLookupSymbol = "Symbol Not Looked Up"; |
| +const char* Sample::kNoSymbol = "No Symbol Found"; |
| + |
| +Sample::Sample() { |
| + timestamp = 0; |
| + cpu_usage = 0; |
| + for (int i = 0; i < kNumStackFrames; i++) { |
| + pcs[i] = 0; |
| + } |
| + vm_tags = kIdle; |
| + runtime_tags = 0; |
| +} |
| + |
| + |
| +SampleBuffer::SampleBuffer(intptr_t capacity) { |
| + start_ = 0; |
| + end_ = 0; |
| + capacity_ = capacity; |
| + samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample))); |
| +} |
| + |
| + |
| +SampleBuffer::~SampleBuffer() { |
| + if (samples_ != NULL) { |
| + free(samples_); |
| + samples_ = NULL; |
| + } |
| +} |
| + |
| + |
| +Sample* SampleBuffer::ReserveSample() { |
| + intptr_t index = end_; |
| + end_ = WrapIncrement(end_); |
| + if (end_ == start_) { |
| + start_ = WrapIncrement(start_); |
| + } |
| + // Reset. |
| + samples_[index] = Sample(); |
| + return &samples_[index]; |
| +} |
| + |
| + |
| +Sample* SampleBuffer::FirstSample() const { |
| + return &samples_[start_]; |
| +} |
| + |
| + |
| +Sample* SampleBuffer::NextSample(Sample* sample) const { |
| + ASSERT(sample >= &samples_[0]); |
| + ASSERT(sample < &samples_[capacity_]); |
| + intptr_t index = sample - samples_; |
| + index = WrapIncrement(index); |
| + return &samples_[index]; |
| +} |
| + |
| + |
| +Sample* SampleBuffer::LastSample() const { |
| + return &samples_[end_]; |
| +} |
| + |
| + |
| +intptr_t SampleBuffer::WrapIncrement(intptr_t i) const { |
| + return (i + 1) % capacity_; |
| +} |
| + |
| + |
| +} // namespace dart |