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 |