| // 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. |
| |
| #ifndef VM_PROFILER_H_ |
| #define VM_PROFILER_H_ |
| |
| #include "platform/hashmap.h" |
| #include "platform/thread.h" |
| #include "vm/allocation.h" |
| #include "vm/code_observers.h" |
| #include "vm/globals.h" |
| |
| namespace dart { |
| |
| // Forward declarations. |
| class JSONStream; |
| |
| // Profiler manager. |
| class ProfilerManager : public AllStatic { |
| public: |
| static void InitOnce(); |
| static void Shutdown(); |
| |
| static void SetupIsolateForProfiling(Isolate* isolate); |
| static void ShutdownIsolateForProfiling(Isolate* isolate); |
| static void ScheduleIsolate(Isolate* isolate, bool inside_signal = false); |
| static void DescheduleIsolate(Isolate* isolate); |
| |
| static void PrintToJSONStream(Isolate* isolate, JSONStream* stream); |
| |
| static void WriteTracing(Isolate* isolate, const char* name, Dart_Port port); |
| |
| private: |
| static const intptr_t kMaxProfiledIsolates = 4096; |
| static bool initialized_; |
| static bool shutdown_; |
| static bool thread_running_; |
| static Monitor* monitor_; |
| static Monitor* start_stop_monitor_; |
| |
| static Isolate** isolates_; |
| static intptr_t isolates_capacity_; |
| static intptr_t isolates_size_; |
| |
| static void ScheduleIsolateHelper(Isolate* isolate); |
| static void ResizeIsolates(intptr_t new_capacity); |
| static void AddIsolate(Isolate* isolate); |
| static intptr_t FindIsolate(Isolate* isolate); |
| static void RemoveIsolate(intptr_t i); |
| |
| // Returns the microseconds until the next live timer fires. |
| static int64_t SampleAndRescheduleIsolates(int64_t current_time); |
| static void FreeIsolateProfilingData(Isolate* isolate); |
| static void ThreadMain(uword parameters); |
| }; |
| |
| |
| class IsolateProfilerData { |
| public: |
| static const int64_t kDescheduledCpuUsage = -1; |
| static const int64_t kNoExpirationTime = -2; |
| |
| IsolateProfilerData(Isolate* isolate, SampleBuffer* sample_buffer); |
| ~IsolateProfilerData(); |
| |
| int64_t sample_interval_micros() const { return sample_interval_micros_; } |
| |
| void set_sample_interval_micros(int64_t sample_interval) { |
| sample_interval_micros_ = sample_interval; |
| } |
| |
| bool CanExpire() const { |
| return timer_expiration_micros_ != kNoExpirationTime; |
| } |
| |
| bool ShouldSample(int64_t current_time) const { |
| return CanExpire() && TimeUntilExpiration(current_time) <= 0; |
| } |
| |
| int64_t TimeUntilExpiration(int64_t current_time_micros) const { |
| ASSERT(CanExpire()); |
| return timer_expiration_micros_ - current_time_micros; |
| } |
| |
| void set_cpu_usage(int64_t cpu_usage) { |
| cpu_usage_ = cpu_usage; |
| } |
| |
| void SampledAt(int64_t current_time); |
| |
| void Scheduled(int64_t current_time, ThreadId thread); |
| |
| void Descheduled(); |
| |
| int64_t cpu_usage() const { return cpu_usage_; } |
| |
| int64_t ComputeDeltaAndSetCpuUsage(int64_t cpu_usage) { |
| int64_t delta = 0; |
| if (cpu_usage_ != kDescheduledCpuUsage) { |
| // Only compute the real delta if we are being sampled regularly. |
| delta = cpu_usage - cpu_usage_; |
| } |
| set_cpu_usage(cpu_usage); |
| return delta; |
| } |
| |
| ThreadId thread_id() const { return thread_id_; } |
| |
| Isolate* isolate() const { return isolate_; } |
| |
| SampleBuffer* sample_buffer() const { return sample_buffer_; } |
| |
| void set_sample_buffer(SampleBuffer* sample_buffer) { |
| sample_buffer_ = sample_buffer; |
| } |
| |
| private: |
| int64_t last_sampled_micros_; |
| int64_t timer_expiration_micros_; |
| int64_t sample_interval_micros_; |
| int64_t cpu_usage_; |
| ThreadId thread_id_; |
| Isolate* isolate_; |
| SampleBuffer* sample_buffer_; |
| DISALLOW_COPY_AND_ASSIGN(IsolateProfilerData); |
| }; |
| |
| |
| // Profile sample. |
| struct Sample { |
| static const char* kLookupSymbol; |
| static const char* kNoSymbol; |
| static const intptr_t kNumStackFrames = 4; |
| enum SampleState { |
| kIdle = 0, |
| kExecuting = 1, |
| kNumSampleStates |
| }; |
| int64_t timestamp; |
| int64_t cpu_usage; |
| uintptr_t pcs[kNumStackFrames]; |
| uint16_t vm_tags; |
| uint16_t runtime_tags; |
| Sample(); |
| }; |
| |
| |
| // Ring buffer of samples. One per isolate. |
| class SampleBuffer { |
| public: |
| static const intptr_t kDefaultBufferCapacity = 120000; // 2 minutes @ 1000hz. |
| |
| explicit SampleBuffer(intptr_t capacity = kDefaultBufferCapacity); |
| ~SampleBuffer(); |
| |
| intptr_t capacity() const { return capacity_; } |
| |
| Sample* ReserveSample(); |
| |
| Sample* FirstSample() const; |
| Sample* NextSample(Sample* sample) const; |
| Sample* LastSample() const; |
| private: |
| Sample* samples_; |
| intptr_t capacity_; |
| intptr_t start_; |
| intptr_t end_; |
| |
| intptr_t WrapIncrement(intptr_t i) const; |
| DISALLOW_COPY_AND_ASSIGN(SampleBuffer); |
| }; |
| |
| |
| class ProfilerSampleStackWalker : public ValueObject { |
| public: |
| ProfilerSampleStackWalker(Sample* sample, |
| uintptr_t stack_lower, |
| uintptr_t stack_upper, |
| uintptr_t pc, |
| uintptr_t fp, |
| uintptr_t sp); |
| |
| int walk(); |
| |
| private: |
| uword* CallerPC(uword* fp); |
| uword* CallerFP(uword* fp); |
| |
| bool ValidInstructionPointer(uword* pc); |
| |
| bool ValidFramePointer(uword* fp); |
| |
| Sample* sample_; |
| const uintptr_t stack_lower_; |
| const uintptr_t stack_upper_; |
| const uintptr_t original_pc_; |
| const uintptr_t original_fp_; |
| const uintptr_t original_sp_; |
| uintptr_t lower_bound_; |
| }; |
| |
| |
| } // namespace dart |
| |
| #endif // VM_PROFILER_H_ |