OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/profiler/stack_sampling_profiler.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/callback.h" |
| 11 #include "base/memory/singleton.h" |
| 12 #include "base/synchronization/lock.h" |
| 13 #include "base/synchronization/waitable_event.h" |
| 14 #include "base/timer/elapsed_timer.h" |
| 15 |
| 16 template <typename T> struct DefaultSingletonTraits; |
| 17 |
| 18 namespace base { |
| 19 |
| 20 namespace { |
| 21 // Time to delay before first samples are taken. |
| 22 const int64 kInitialDelayInMicroseconds = 0; |
| 23 // Number of sampling bursts to perform. |
| 24 const int kNumberOfBursts = 1; |
| 25 // Interval between sampling bursts. This is the desired duration from the start |
| 26 // of one burst to the start of the next burst. |
| 27 const int64 kBurstIntervalInMicroseconds = 10000000; // 10 sec |
| 28 // Number of samples to record per burst. |
| 29 const int kNumberOfSamples = 300; |
| 30 // Interval between samples during a sampling burst. This is the desired |
| 31 // duration from the start of one burst to the start of the next burst. |
| 32 const int64 kSamplingIntervalInMicroseconds = 100000; // 100 ms |
| 33 |
| 34 // Thread-safe singleton class that stores collected profiles waiting to be |
| 35 // processed. |
| 36 class PendingProfiles { |
| 37 public: |
| 38 PendingProfiles(); |
| 39 ~PendingProfiles(); |
| 40 |
| 41 static PendingProfiles* GetInstance(); |
| 42 |
| 43 // Appends |profiles|. This function is thread safe. |
| 44 void PutProfiles(const std::vector<StackSamplingProfiler::Profile>& profiles); |
| 45 // Gets the pending profiles into *|profiles|. This function is thread safe. |
| 46 void GetProfiles(std::vector<StackSamplingProfiler::Profile>* profiles); |
| 47 |
| 48 private: |
| 49 base::Lock profiles_lock_; |
| 50 std::vector<StackSamplingProfiler::Profile> profiles_; |
| 51 |
| 52 DISALLOW_COPY_AND_ASSIGN(PendingProfiles); |
| 53 }; |
| 54 |
| 55 PendingProfiles::PendingProfiles() {} |
| 56 |
| 57 PendingProfiles::~PendingProfiles() {} |
| 58 |
| 59 // static |
| 60 PendingProfiles* PendingProfiles::GetInstance() { |
| 61 return Singleton<PendingProfiles>::get(); |
| 62 } |
| 63 |
| 64 void PendingProfiles::PutProfiles( |
| 65 const std::vector<StackSamplingProfiler::Profile>& profiles) { |
| 66 base::AutoLock scoped_lock(profiles_lock_); |
| 67 profiles_.insert(profiles_.end(), profiles.begin(), profiles.end()); |
| 68 } |
| 69 |
| 70 void PendingProfiles::GetProfiles( |
| 71 std::vector<StackSamplingProfiler::Profile>* profiles) { |
| 72 profiles->clear(); |
| 73 |
| 74 base::AutoLock scoped_lock(profiles_lock_); |
| 75 profiles_.swap(*profiles); |
| 76 } |
| 77 } // namespace |
| 78 |
| 79 StackSamplingProfiler::Profile::Profile() {} |
| 80 |
| 81 StackSamplingProfiler::Profile::~Profile() {} |
| 82 |
| 83 class StackSamplingProfiler::SamplingThread : public PlatformThread::Delegate { |
| 84 public: |
| 85 // Samples stacks using |native_sampler|. When complete, invokes |
| 86 // |profiles_callback| with the collected profiles. |profiles_callback| must |
| 87 // be thread-safe and may consume the contents of the vector. |
| 88 SamplingThread( |
| 89 scoped_ptr<NativeStackSampler> native_sampler, |
| 90 base::Callback<void(const std::vector<Profile>&)> profiles_callback); |
| 91 ~SamplingThread() override; |
| 92 |
| 93 // Implementation of PlatformThread::Delegate: |
| 94 void ThreadMain() override; |
| 95 |
| 96 void Stop(); |
| 97 |
| 98 private: |
| 99 // Collects a profile from a single burst. Returns true if the profile was |
| 100 // collected, or false if collection was stopped before it completed. |
| 101 bool CollectProfile(Profile* profile, TimeDelta* elapsed_time); |
| 102 // Collects profiles from all bursts, or until the sampling is stopped. If |
| 103 // stopped before complete, |profiles| will contains only full bursts. |
| 104 void CollectProfiles(std::vector<Profile>* profiles); |
| 105 |
| 106 scoped_ptr<NativeStackSampler> native_sampler_; |
| 107 |
| 108 base::WaitableEvent stop_event_; |
| 109 |
| 110 base::Callback<void(const std::vector<Profile>&)> profiles_callback_; |
| 111 |
| 112 DISALLOW_COPY_AND_ASSIGN(SamplingThread); |
| 113 }; |
| 114 |
| 115 StackSamplingProfiler::SamplingThread::SamplingThread( |
| 116 scoped_ptr<NativeStackSampler> native_sampler, |
| 117 base::Callback<void(const std::vector<Profile>&)> profiles_callback) |
| 118 : native_sampler_(native_sampler.Pass()), |
| 119 stop_event_(false, false), |
| 120 profiles_callback_(profiles_callback) { |
| 121 } |
| 122 |
| 123 StackSamplingProfiler::SamplingThread::~SamplingThread() {} |
| 124 |
| 125 void StackSamplingProfiler::SamplingThread::ThreadMain() { |
| 126 PlatformThread::SetName("Chrome_SamplingProfilerThread"); |
| 127 |
| 128 std::vector<Profile> profiles; |
| 129 CollectProfiles(&profiles); |
| 130 profiles_callback_.Run(profiles); |
| 131 } |
| 132 |
| 133 bool StackSamplingProfiler::SamplingThread::CollectProfile( |
| 134 Profile* profile, TimeDelta* elapsed_time) { |
| 135 base::ElapsedTimer profile_timer; |
| 136 Profile current_profile; |
| 137 native_sampler_->ProfileRecordingStarting(¤t_profile); |
| 138 const TimeDelta sampling_interval = |
| 139 TimeDelta::FromMicroseconds(kSamplingIntervalInMicroseconds); |
| 140 current_profile.sampling_period = sampling_interval; |
| 141 bool stopped_early = false; |
| 142 for (int j = 0; j < kNumberOfSamples; ++j) { |
| 143 base::ElapsedTimer sample_timer; |
| 144 current_profile.samples.push_back(Sample()); |
| 145 native_sampler_->RecordStackSample(¤t_profile.samples.back()); |
| 146 base::TimeDelta elapsed_sample_time = sample_timer.Elapsed(); |
| 147 if (j != kNumberOfSamples - 1) { |
| 148 if (stop_event_.TimedWait( |
| 149 std::max(sampling_interval - elapsed_sample_time, TimeDelta()))) { |
| 150 stopped_early = true; |
| 151 break; |
| 152 } |
| 153 } |
| 154 } |
| 155 |
| 156 *elapsed_time = profile_timer.Elapsed(); |
| 157 current_profile.profile_duration = *elapsed_time; |
| 158 native_sampler_->ProfileRecordingStopped(); |
| 159 |
| 160 if (!stopped_early) |
| 161 *profile = current_profile; |
| 162 |
| 163 return !stopped_early; |
| 164 } |
| 165 |
| 166 void StackSamplingProfiler::SamplingThread::CollectProfiles( |
| 167 std::vector<Profile>* profiles) { |
| 168 if (stop_event_.TimedWait(TimeDelta::FromMicroseconds( |
| 169 kInitialDelayInMicroseconds))) |
| 170 return; |
| 171 |
| 172 const TimeDelta burst_interval = |
| 173 TimeDelta::FromMicroseconds(kBurstIntervalInMicroseconds); |
| 174 for (int i = 0; i < kNumberOfBursts; ++i) { |
| 175 Profile profile; |
| 176 base::TimeDelta elapsed_profile_time; |
| 177 if (CollectProfile(&profile, &elapsed_profile_time)) |
| 178 profiles->push_back(profile); |
| 179 else |
| 180 return; |
| 181 |
| 182 if (stop_event_.TimedWait( |
| 183 std::max(burst_interval - elapsed_profile_time, TimeDelta()))) |
| 184 return; |
| 185 } |
| 186 } |
| 187 |
| 188 void StackSamplingProfiler::SamplingThread::Stop() { |
| 189 stop_event_.Signal(); |
| 190 } |
| 191 |
| 192 void StackSamplingProfiler::SamplingThreadDeleter::operator()( |
| 193 SamplingThread* thread) const { |
| 194 delete thread; |
| 195 } |
| 196 |
| 197 StackSamplingProfiler::NativeStackSampler::NativeStackSampler() {} |
| 198 |
| 199 StackSamplingProfiler::NativeStackSampler::~NativeStackSampler() {} |
| 200 |
| 201 StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id) |
| 202 : thread_id_(thread_id) {} |
| 203 |
| 204 StackSamplingProfiler::~StackSamplingProfiler() {} |
| 205 |
| 206 void StackSamplingProfiler::Start() { |
| 207 native_sampler_ = NativeStackSampler::Create(thread_id_); |
| 208 if (!native_sampler_) |
| 209 return; |
| 210 |
| 211 sampling_thread_.reset( |
| 212 new SamplingThread( |
| 213 native_sampler_.Pass(), |
| 214 base::Bind(&PendingProfiles::PutProfiles, |
| 215 base::Unretained(PendingProfiles::GetInstance())))); |
| 216 if (!PlatformThread::CreateNonJoinable(0, sampling_thread_.get())) |
| 217 LOG(ERROR) << "failed to create thread"; |
| 218 } |
| 219 |
| 220 void StackSamplingProfiler::Stop() { |
| 221 if (sampling_thread_) |
| 222 sampling_thread_->Stop(); |
| 223 } |
| 224 |
| 225 // static |
| 226 void StackSamplingProfiler::GetPendingProfiles(std::vector<Profile>* profiles) { |
| 227 PendingProfiles::GetInstance()->GetProfiles(profiles); |
| 228 } |
| 229 |
| 230 bool operator==(const StackSamplingProfiler::Frame &a, |
| 231 const StackSamplingProfiler::Frame &b) { |
| 232 return a.ip_offset == b.ip_offset && a.module_index == b.module_index; |
| 233 } |
| 234 |
| 235 } // namespace base |
OLD | NEW |