| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/metrics/statistics_recorder.h" | 5 #include "base/metrics/statistics_recorder.h" |
| 6 | 6 |
| 7 #include "base/at_exit.h" | 7 #include "base/at_exit.h" |
| 8 #include "base/debug/leak_annotations.h" | 8 #include "base/debug/leak_annotations.h" |
| 9 #include "base/json/string_escape.h" | 9 #include "base/json/string_escape.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 13 #include "base/stl_util.h" |
| 13 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
| 14 #include "base/synchronization/lock.h" | 15 #include "base/synchronization/lock.h" |
| 15 #include "base/values.h" | 16 #include "base/values.h" |
| 16 | 17 |
| 17 namespace { | 18 namespace { |
| 18 // Initialize histogram statistics gathering system. | 19 // Initialize histogram statistics gathering system. |
| 19 base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ = | 20 base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ = |
| 20 LAZY_INSTANCE_INITIALIZER; | 21 LAZY_INSTANCE_INITIALIZER; |
| 21 } // namespace | 22 } // namespace |
| 22 | 23 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 50 } | 51 } |
| 51 | 52 |
| 52 HistogramBase* histogram_to_delete = NULL; | 53 HistogramBase* histogram_to_delete = NULL; |
| 53 HistogramBase* histogram_to_return = NULL; | 54 HistogramBase* histogram_to_return = NULL; |
| 54 { | 55 { |
| 55 base::AutoLock auto_lock(*lock_); | 56 base::AutoLock auto_lock(*lock_); |
| 56 if (histograms_ == NULL) { | 57 if (histograms_ == NULL) { |
| 57 histogram_to_return = histogram; | 58 histogram_to_return = histogram; |
| 58 } else { | 59 } else { |
| 59 const std::string& name = histogram->histogram_name(); | 60 const std::string& name = histogram->histogram_name(); |
| 60 HistogramMap::iterator it = histograms_->find(name); | 61 HistogramMap::iterator it = histograms_->find(HistogramNameRef(name)); |
| 61 if (histograms_->end() == it) { | 62 if (histograms_->end() == it) { |
| 62 (*histograms_)[name] = histogram; | 63 (*histograms_)[HistogramNameRef(name)] = histogram; |
| 63 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 | 64 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 |
| 65 // If there are callbacks for this histogram, we set the kCallbackExists |
| 66 // flag. |
| 67 auto callback_iterator = callbacks_->find(name); |
| 68 if (callback_iterator != callbacks_->end()) { |
| 69 if (!callback_iterator->second.is_null()) |
| 70 histogram->SetFlags(HistogramBase::kCallbackExists); |
| 71 else |
| 72 histogram->ClearFlags(HistogramBase::kCallbackExists); |
| 73 } |
| 64 histogram_to_return = histogram; | 74 histogram_to_return = histogram; |
| 65 } else if (histogram == it->second) { | 75 } else if (histogram == it->second) { |
| 66 // The histogram was registered before. | 76 // The histogram was registered before. |
| 67 histogram_to_return = histogram; | 77 histogram_to_return = histogram; |
| 68 } else { | 78 } else { |
| 69 // We already have one histogram with this name. | 79 // We already have one histogram with this name. |
| 70 histogram_to_return = it->second; | 80 histogram_to_return = it->second; |
| 71 histogram_to_delete = histogram; | 81 histogram_to_delete = histogram; |
| 72 } | 82 } |
| 73 } | 83 } |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 | 193 |
| 184 // static | 194 // static |
| 185 void StatisticsRecorder::GetHistograms(Histograms* output) { | 195 void StatisticsRecorder::GetHistograms(Histograms* output) { |
| 186 if (lock_ == NULL) | 196 if (lock_ == NULL) |
| 187 return; | 197 return; |
| 188 base::AutoLock auto_lock(*lock_); | 198 base::AutoLock auto_lock(*lock_); |
| 189 if (histograms_ == NULL) | 199 if (histograms_ == NULL) |
| 190 return; | 200 return; |
| 191 | 201 |
| 192 for (const auto& entry : *histograms_) { | 202 for (const auto& entry : *histograms_) { |
| 193 DCHECK_EQ(entry.first, entry.second->histogram_name()); | 203 DCHECK_EQ(entry.first.name_, entry.second->histogram_name()); |
| 194 output->push_back(entry.second); | 204 output->push_back(entry.second); |
| 195 } | 205 } |
| 196 } | 206 } |
| 197 | 207 |
| 198 // static | 208 // static |
| 199 void StatisticsRecorder::GetBucketRanges( | 209 void StatisticsRecorder::GetBucketRanges( |
| 200 std::vector<const BucketRanges*>* output) { | 210 std::vector<const BucketRanges*>* output) { |
| 201 if (lock_ == NULL) | 211 if (lock_ == NULL) |
| 202 return; | 212 return; |
| 203 base::AutoLock auto_lock(*lock_); | 213 base::AutoLock auto_lock(*lock_); |
| 204 if (ranges_ == NULL) | 214 if (ranges_ == NULL) |
| 205 return; | 215 return; |
| 206 | 216 |
| 207 for (const auto& entry : *ranges_) { | 217 for (const auto& entry : *ranges_) { |
| 208 for (const auto& range_entry : *entry.second) { | 218 for (const auto& range_entry : *entry.second) { |
| 209 output->push_back(range_entry); | 219 output->push_back(range_entry); |
| 210 } | 220 } |
| 211 } | 221 } |
| 212 } | 222 } |
| 213 | 223 |
| 214 // static | 224 // static |
| 215 HistogramBase* StatisticsRecorder::FindHistogram(const std::string& name) { | 225 HistogramBase* StatisticsRecorder::FindHistogram(const std::string& name) { |
| 216 if (lock_ == NULL) | 226 if (lock_ == NULL) |
| 217 return NULL; | 227 return NULL; |
| 218 base::AutoLock auto_lock(*lock_); | 228 base::AutoLock auto_lock(*lock_); |
| 219 if (histograms_ == NULL) | 229 if (histograms_ == NULL) |
| 220 return NULL; | 230 return NULL; |
| 221 | 231 |
| 222 HistogramMap::iterator it = histograms_->find(name); | 232 HistogramMap::iterator it = histograms_->find(HistogramNameRef(name)); |
| 223 if (histograms_->end() == it) | 233 if (histograms_->end() == it) |
| 224 return NULL; | 234 return NULL; |
| 225 return it->second; | 235 return it->second; |
| 226 } | 236 } |
| 227 | 237 |
| 238 // static |
| 239 bool StatisticsRecorder::SetCallback( |
| 240 const std::string& name, |
| 241 const StatisticsRecorder::OnSampleCallback& cb) { |
| 242 DCHECK(!cb.is_null()); |
| 243 if (lock_ == NULL) |
| 244 return false; |
| 245 base::AutoLock auto_lock(*lock_); |
| 246 if (histograms_ == NULL) |
| 247 return false; |
| 248 |
| 249 if (ContainsKey(*callbacks_, name)) |
| 250 return false; |
| 251 callbacks_->insert(std::make_pair(name, cb)); |
| 252 |
| 253 auto histogram_iterator = histograms_->find(HistogramNameRef(name)); |
| 254 if (histogram_iterator != histograms_->end()) |
| 255 histogram_iterator->second->SetFlags(HistogramBase::kCallbackExists); |
| 256 |
| 257 return true; |
| 258 } |
| 259 |
| 260 // static |
| 261 void StatisticsRecorder::ClearCallback(const std::string& name) { |
| 262 if (lock_ == NULL) |
| 263 return; |
| 264 base::AutoLock auto_lock(*lock_); |
| 265 if (histograms_ == NULL) |
| 266 return; |
| 267 |
| 268 callbacks_->erase(name); |
| 269 |
| 270 // We also clear the flag from the histogram (if it exists). |
| 271 auto histogram_iterator = histograms_->find(HistogramNameRef(name)); |
| 272 if (histogram_iterator != histograms_->end()) |
| 273 histogram_iterator->second->ClearFlags(HistogramBase::kCallbackExists); |
| 274 } |
| 275 |
| 276 // static |
| 277 StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( |
| 278 const std::string& name) { |
| 279 if (lock_ == NULL) |
| 280 return OnSampleCallback(); |
| 281 base::AutoLock auto_lock(*lock_); |
| 282 if (histograms_ == NULL) |
| 283 return OnSampleCallback(); |
| 284 |
| 285 auto callback_iterator = callbacks_->find(name); |
| 286 return callback_iterator != callbacks_->end() ? callback_iterator->second |
| 287 : OnSampleCallback(); |
| 288 } |
| 289 |
| 228 // private static | 290 // private static |
| 229 void StatisticsRecorder::GetSnapshot(const std::string& query, | 291 void StatisticsRecorder::GetSnapshot(const std::string& query, |
| 230 Histograms* snapshot) { | 292 Histograms* snapshot) { |
| 231 if (lock_ == NULL) | 293 if (lock_ == NULL) |
| 232 return; | 294 return; |
| 233 base::AutoLock auto_lock(*lock_); | 295 base::AutoLock auto_lock(*lock_); |
| 234 if (histograms_ == NULL) | 296 if (histograms_ == NULL) |
| 235 return; | 297 return; |
| 236 | 298 |
| 237 for (const auto& entry : *histograms_) { | 299 for (const auto& entry : *histograms_) { |
| 238 if (entry.first.find(query) != std::string::npos) | 300 if (entry.first.name_.find(query) != std::string::npos) |
| 239 snapshot->push_back(entry.second); | 301 snapshot->push_back(entry.second); |
| 240 } | 302 } |
| 241 } | 303 } |
| 242 | 304 |
| 243 // This singleton instance should be started during the single threaded portion | 305 // This singleton instance should be started during the single threaded portion |
| 244 // of main(), and hence it is not thread safe. It initializes globals to | 306 // of main(), and hence it is not thread safe. It initializes globals to |
| 245 // provide support for all future calls. | 307 // provide support for all future calls. |
| 246 StatisticsRecorder::StatisticsRecorder() { | 308 StatisticsRecorder::StatisticsRecorder() { |
| 247 DCHECK(!histograms_); | 309 DCHECK(!histograms_); |
| 248 if (lock_ == NULL) { | 310 if (lock_ == NULL) { |
| 249 // This will leak on purpose. It's the only way to make sure we won't race | 311 // This will leak on purpose. It's the only way to make sure we won't race |
| 250 // against the static uninitialization of the module while one of our | 312 // against the static uninitialization of the module while one of our |
| 251 // static methods relying on the lock get called at an inappropriate time | 313 // static methods relying on the lock get called at an inappropriate time |
| 252 // during the termination phase. Since it's a static data member, we will | 314 // during the termination phase. Since it's a static data member, we will |
| 253 // leak one per process, which would be similar to the instance allocated | 315 // leak one per process, which would be similar to the instance allocated |
| 254 // during static initialization and released only on process termination. | 316 // during static initialization and released only on process termination. |
| 255 lock_ = new base::Lock; | 317 lock_ = new base::Lock; |
| 256 } | 318 } |
| 257 base::AutoLock auto_lock(*lock_); | 319 base::AutoLock auto_lock(*lock_); |
| 258 histograms_ = new HistogramMap; | 320 histograms_ = new HistogramMap; |
| 321 callbacks_ = new CallbackMap; |
| 259 ranges_ = new RangesMap; | 322 ranges_ = new RangesMap; |
| 260 | 323 |
| 261 if (VLOG_IS_ON(1)) | 324 if (VLOG_IS_ON(1)) |
| 262 AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this); | 325 AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this); |
| 263 } | 326 } |
| 264 | 327 |
| 265 // static | 328 // static |
| 266 void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { | 329 void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { |
| 267 std::string output; | 330 std::string output; |
| 268 StatisticsRecorder::WriteGraph(std::string(), &output); | 331 StatisticsRecorder::WriteGraph(std::string(), &output); |
| 269 VLOG(1) << output; | 332 VLOG(1) << output; |
| 270 } | 333 } |
| 271 | 334 |
| 272 StatisticsRecorder::~StatisticsRecorder() { | 335 StatisticsRecorder::~StatisticsRecorder() { |
| 273 DCHECK(histograms_ && ranges_ && lock_); | 336 DCHECK(histograms_ && ranges_ && lock_); |
| 274 | 337 |
| 275 // Clean up. | 338 // Clean up. |
| 276 scoped_ptr<HistogramMap> histograms_deleter; | 339 scoped_ptr<HistogramMap> histograms_deleter; |
| 340 scoped_ptr<CallbackMap> callbacks_deleter; |
| 277 scoped_ptr<RangesMap> ranges_deleter; | 341 scoped_ptr<RangesMap> ranges_deleter; |
| 278 // We don't delete lock_ on purpose to avoid having to properly protect | 342 // We don't delete lock_ on purpose to avoid having to properly protect |
| 279 // against it going away after we checked for NULL in the static methods. | 343 // against it going away after we checked for NULL in the static methods. |
| 280 { | 344 { |
| 281 base::AutoLock auto_lock(*lock_); | 345 base::AutoLock auto_lock(*lock_); |
| 282 histograms_deleter.reset(histograms_); | 346 histograms_deleter.reset(histograms_); |
| 347 callbacks_deleter.reset(callbacks_); |
| 283 ranges_deleter.reset(ranges_); | 348 ranges_deleter.reset(ranges_); |
| 284 histograms_ = NULL; | 349 histograms_ = NULL; |
| 350 callbacks_ = NULL; |
| 285 ranges_ = NULL; | 351 ranges_ = NULL; |
| 286 } | 352 } |
| 287 // We are going to leak the histograms and the ranges. | 353 // We are going to leak the histograms and the ranges. |
| 288 } | 354 } |
| 289 | 355 |
| 290 | 356 |
| 291 // static | 357 // static |
| 292 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; | 358 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; |
| 293 // static | 359 // static |
| 360 StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL; |
| 361 // static |
| 294 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL; | 362 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL; |
| 295 // static | 363 // static |
| 296 base::Lock* StatisticsRecorder::lock_ = NULL; | 364 base::Lock* StatisticsRecorder::lock_ = NULL; |
| 297 | 365 |
| 298 } // namespace base | 366 } // namespace base |
| OLD | NEW |