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 3 // found in the LICENSE file. 4 5 #include "base/metrics/statistics_recorder.h" 6 7 #include <memory> 8 9 #include "base/at_exit.h" 10 #include "base/debug/leak_annotations.h" 11 #include "base/json/string_escape.h" 12 #include "base/logging.h" 13 #include "base/memory/ptr_util.h" 14 #include "base/metrics/histogram.h" 15 #include "base/metrics/metrics_hashes.h" 16 #include "base/metrics/persistent_histogram_allocator.h" 17 #include "base/stl_util.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/values.h" 20 21 namespace { 22 23 // Initialize histogram statistics gathering system. 24 base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ = 25 LAZY_INSTANCE_INITIALIZER; 26 27 bool HistogramNameLesser(const base::HistogramBase* a, 28 const base::HistogramBase* b) { 29 return a->histogram_name() < b->histogram_name(); 30 } 31 32 } // namespace 33 34 namespace base { 35 36 StatisticsRecorder::HistogramIterator::HistogramIterator( 37 const HistogramMap::iterator& iter, bool include_persistent) 38 : iter_(iter), 39 include_persistent_(include_persistent) { 40 // The starting location could point to a persistent histogram when such 41 // is not wanted. If so, skip it. 42 if (!include_persistent_ && iter_ != histograms_->end() && 43 (iter_->second->flags() & HistogramBase::kIsPersistent)) { 44 // This operator will continue to skip until a non-persistent histogram 45 // is found. 46 operator++(); 47 } 48 } 49 50 StatisticsRecorder::HistogramIterator::HistogramIterator( 51 const HistogramIterator& rhs) 52 : iter_(rhs.iter_), 53 include_persistent_(rhs.include_persistent_) { 54 } 55 56 StatisticsRecorder::HistogramIterator::~HistogramIterator() {} 57 58 StatisticsRecorder::HistogramIterator& 59 StatisticsRecorder::HistogramIterator::operator++() { 60 const HistogramMap::iterator histograms_end = histograms_->end(); 61 if (iter_ == histograms_end) 62 return *this; 63 64 base::AutoLock auto_lock(lock_.Get()); 65 66 for (;;) { 67 ++iter_; 68 if (iter_ == histograms_end) 69 break; 70 if (!include_persistent_ && (iter_->second->flags() & 71 HistogramBase::kIsPersistent)) { 72 continue; 73 } 74 break; 75 } 76 77 return *this; 78 } 79 80 StatisticsRecorder::~StatisticsRecorder() { 81 DCHECK(histograms_); 82 DCHECK(ranges_); 83 84 // Clean out what this object created and then restore what existed before. 85 Reset(); 86 base::AutoLock auto_lock(lock_.Get()); 87 histograms_ = existing_histograms_.release(); 88 callbacks_ = existing_callbacks_.release(); 89 ranges_ = existing_ranges_.release(); 90 providers_ = existing_providers_.release(); 91 } 92 93 // static 94 void StatisticsRecorder::Initialize() { 95 // Tests sometimes create local StatisticsRecorders in order to provide a 96 // contained environment of histograms that can be later discarded. If a 97 // true global instance gets created in this environment then it will 98 // eventually get disconnected when the local instance destructs and 99 // restores the previous state, resulting in no StatisticsRecorder at all. 100 // The global lazy instance, however, will remain valid thus ensuring that 101 // another never gets installed via this method. If a |histograms_| map 102 // exists then assume the StatisticsRecorder is already "initialized". 103 if (histograms_) 104 return; 105 106 // Ensure that an instance of the StatisticsRecorder object is created. 107 g_statistics_recorder_.Get(); 108 } 109 110 // static 111 bool StatisticsRecorder::IsActive() { 112 base::AutoLock auto_lock(lock_.Get()); 113 return histograms_ != nullptr; 114 } 115 116 // static 117 void StatisticsRecorder::RegisterHistogramProvider( 118 const WeakPtr<HistogramProvider>& provider) { 119 providers_->push_back(provider); 120 } 121 122 // static 123 HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( 124 HistogramBase* histogram) { 125 HistogramBase* histogram_to_delete = nullptr; 126 HistogramBase* histogram_to_return = nullptr; 127 { 128 base::AutoLock auto_lock(lock_.Get()); 129 if (!histograms_) { 130 histogram_to_return = histogram; 131 132 // As per crbug.com/79322 the histograms are intentionally leaked, so we 133 // need to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used 134 // only once for an object, the duplicates should not be annotated. 135 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr) 136 // twice |if (!histograms_)|. 137 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 138 } else { 139 const std::string& name = histogram->histogram_name(); 140 HistogramMap::iterator it = histograms_->find(name); 141 if (histograms_->end() == it) { 142 // The StringKey references the name within |histogram| rather than 143 // making a copy. 144 (*histograms_)[name] = histogram; 145 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 146 // If there are callbacks for this histogram, we set the kCallbackExists 147 // flag. 148 auto callback_iterator = callbacks_->find(name); 149 if (callback_iterator != callbacks_->end()) { 150 if (!callback_iterator->second.is_null()) 151 histogram->SetFlags(HistogramBase::kCallbackExists); 152 else 153 histogram->ClearFlags(HistogramBase::kCallbackExists); 154 } 155 histogram_to_return = histogram; 156 } else if (histogram == it->second) { 157 // The histogram was registered before. 158 histogram_to_return = histogram; 159 } else { 160 // We already have one histogram with this name. 161 DCHECK_EQ(histogram->histogram_name(), 162 it->second->histogram_name()) << "hash collision"; 163 histogram_to_return = it->second; 164 histogram_to_delete = histogram; 165 } 166 } 167 } 168 delete histogram_to_delete; 169 return histogram_to_return; 170 } 171 172 // static 173 const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( 174 const BucketRanges* ranges) { 175 DCHECK(ranges->HasValidChecksum()); 176 std::unique_ptr<const BucketRanges> ranges_deleter; 177 178 base::AutoLock auto_lock(lock_.Get()); 179 if (!ranges_) { 180 ANNOTATE_LEAKING_OBJECT_PTR(ranges); 181 return ranges; 182 } 183 184 std::list<const BucketRanges*>* checksum_matching_list; 185 RangesMap::iterator ranges_it = ranges_->find(ranges->checksum()); 186 if (ranges_->end() == ranges_it) { 187 // Add a new matching list to map. 188 checksum_matching_list = new std::list<const BucketRanges*>(); 189 ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list); 190 (*ranges_)[ranges->checksum()] = checksum_matching_list; 191 } else { 192 checksum_matching_list = ranges_it->second; 193 } 194 195 for (const BucketRanges* existing_ranges : *checksum_matching_list) { 196 if (existing_ranges->Equals(ranges)) { 197 if (existing_ranges == ranges) { 198 return ranges; 199 } else { 200 ranges_deleter.reset(ranges); 201 return existing_ranges; 202 } 203 } 204 } 205 // We haven't found a BucketRanges which has the same ranges. Register the 206 // new BucketRanges. 207 checksum_matching_list->push_front(ranges); 208 return ranges; 209 } 210 211 // static 212 void StatisticsRecorder::WriteHTMLGraph(const std::string& query, 213 std::string* output) { 214 if (!IsActive()) 215 return; 216 217 Histograms snapshot; 218 GetSnapshot(query, &snapshot); 219 std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); 220 for (const HistogramBase* histogram : snapshot) { 221 histogram->WriteHTMLGraph(output); 222 output->append("<br><hr><br>"); 223 } 224 } 225 226 // static 227 void StatisticsRecorder::WriteGraph(const std::string& query, 228 std::string* output) { 229 if (!IsActive()) 230 return; 231 if (query.length()) 232 StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); 233 else 234 output->append("Collections of all histograms\n"); 235 236 Histograms snapshot; 237 GetSnapshot(query, &snapshot); 238 std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); 239 for (const HistogramBase* histogram : snapshot) { 240 histogram->WriteAscii(output); 241 output->append("\n"); 242 } 243 } 244 245 // static 246 std::string StatisticsRecorder::ToJSON(const std::string& query) { 247 if (!IsActive()) 248 return std::string(); 249 250 std::string output("{"); 251 if (!query.empty()) { 252 output += "\"query\":"; 253 EscapeJSONString(query, true, &output); 254 output += ","; 255 } 256 257 Histograms snapshot; 258 GetSnapshot(query, &snapshot); 259 output += "\"histograms\":["; 260 bool first_histogram = true; 261 for (const HistogramBase* histogram : snapshot) { 262 if (first_histogram) 263 first_histogram = false; 264 else 265 output += ","; 266 std::string json; 267 histogram->WriteJSON(&json); 268 output += json; 269 } 270 output += "]}"; 271 return output; 272 } 273 274 // static 275 void StatisticsRecorder::GetHistograms(Histograms* output) { 276 base::AutoLock auto_lock(lock_.Get()); 277 if (!histograms_) 278 return; 279 280 for (const auto& entry : *histograms_) { 281 output->push_back(entry.second); 282 } 283 } 284 285 // static 286 void StatisticsRecorder::GetBucketRanges( 287 std::vector<const BucketRanges*>* output) { 288 base::AutoLock auto_lock(lock_.Get()); 289 if (!ranges_) 290 return; 291 292 for (const auto& entry : *ranges_) { 293 for (auto* range_entry : *entry.second) { 294 output->push_back(range_entry); 295 } 296 } 297 } 298 299 // static 300 HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { 301 // This must be called *before* the lock is acquired below because it will 302 // call back into this object to register histograms. Those called methods 303 // will acquire the lock at that time. 304 ImportGlobalPersistentHistograms(); 305 306 base::AutoLock auto_lock(lock_.Get()); 307 if (!histograms_) 308 return nullptr; 309 310 HistogramMap::iterator it = histograms_->find(name); 311 if (histograms_->end() == it) 312 return nullptr; 313 return it->second; 314 } 315 316 // static 317 void StatisticsRecorder::ImportProvidedHistograms() { 318 if (!providers_) 319 return; 320 321 // Merge histogram data from each provider in turn. 322 for (const WeakPtr<HistogramProvider>& provider : *providers_) { 323 // Weak-pointer may be invalid if the provider was destructed, though they 324 // generally never are. 325 if (provider) 326 provider->MergeHistogramDeltas(); 327 } 328 } 329 330 // static 331 StatisticsRecorder::HistogramIterator StatisticsRecorder::begin( 332 bool include_persistent) { 333 DCHECK(histograms_); 334 ImportGlobalPersistentHistograms(); 335 336 HistogramMap::iterator iter_begin; 337 { 338 base::AutoLock auto_lock(lock_.Get()); 339 iter_begin = histograms_->begin(); 340 } 341 return HistogramIterator(iter_begin, include_persistent); 342 } 343 344 // static 345 StatisticsRecorder::HistogramIterator StatisticsRecorder::end() { 346 HistogramMap::iterator iter_end; 347 { 348 base::AutoLock auto_lock(lock_.Get()); 349 iter_end = histograms_->end(); 350 } 351 return HistogramIterator(iter_end, true); 352 } 353 354 // static 355 void StatisticsRecorder::InitLogOnShutdown() { 356 if (!histograms_) 357 return; 358 359 base::AutoLock auto_lock(lock_.Get()); 360 g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock(); 361 } 362 363 // static 364 void StatisticsRecorder::GetSnapshot(const std::string& query, 365 Histograms* snapshot) { 366 base::AutoLock auto_lock(lock_.Get()); 367 if (!histograms_) 368 return; 369 370 ImportGlobalPersistentHistograms(); 371 372 for (const auto& entry : *histograms_) { 373 if (entry.second->histogram_name().find(query) != std::string::npos) 374 snapshot->push_back(entry.second); 375 } 376 } 377 378 // static 379 bool StatisticsRecorder::SetCallback( 380 const std::string& name, 381 const StatisticsRecorder::OnSampleCallback& cb) { 382 DCHECK(!cb.is_null()); 383 base::AutoLock auto_lock(lock_.Get()); 384 if (!histograms_) 385 return false; 386 387 if (ContainsKey(*callbacks_, name)) 388 return false; 389 callbacks_->insert(std::make_pair(name, cb)); 390 391 auto it = histograms_->find(name); 392 if (it != histograms_->end()) 393 it->second->SetFlags(HistogramBase::kCallbackExists); 394 395 return true; 396 } 397 398 // static 399 void StatisticsRecorder::ClearCallback(const std::string& name) { 400 base::AutoLock auto_lock(lock_.Get()); 401 if (!histograms_) 402 return; 403 404 callbacks_->erase(name); 405 406 // We also clear the flag from the histogram (if it exists). 407 auto it = histograms_->find(name); 408 if (it != histograms_->end()) 409 it->second->ClearFlags(HistogramBase::kCallbackExists); 410 } 411 412 // static 413 StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( 414 const std::string& name) { 415 base::AutoLock auto_lock(lock_.Get()); 416 if (!histograms_) 417 return OnSampleCallback(); 418 419 auto callback_iterator = callbacks_->find(name); 420 return callback_iterator != callbacks_->end() ? callback_iterator->second 421 : OnSampleCallback(); 422 } 423 424 // static 425 size_t StatisticsRecorder::GetHistogramCount() { 426 base::AutoLock auto_lock(lock_.Get()); 427 if (!histograms_) 428 return 0; 429 return histograms_->size(); 430 } 431 432 // static 433 void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) { 434 if (histograms_) 435 histograms_->erase(name); 436 } 437 438 // static 439 std::unique_ptr<StatisticsRecorder> 440 StatisticsRecorder::CreateTemporaryForTesting() { 441 return WrapUnique(new StatisticsRecorder()); 442 } 443 444 // static 445 void StatisticsRecorder::UninitializeForTesting() { 446 // Stop now if it's never been initialized. 447 if (!histograms_) 448 return; 449 450 // Get the global instance and destruct it. It's held in static memory so 451 // can't "delete" it; call the destructor explicitly. 452 DCHECK(g_statistics_recorder_.private_instance_); 453 g_statistics_recorder_.Get().~StatisticsRecorder(); 454 455 // Now the ugly part. There's no official way to release a LazyInstance once 456 // created so it's necessary to clear out an internal variable which 457 // shouldn't be publicly visible but is for initialization reasons. 458 g_statistics_recorder_.private_instance_ = 0; 459 } 460 461 // static 462 void StatisticsRecorder::ImportGlobalPersistentHistograms() { 463 if (!histograms_) 464 return; 465 466 // Import histograms from known persistent storage. Histograms could have 467 // been added by other processes and they must be fetched and recognized 468 // locally. If the persistent memory segment is not shared between processes, 469 // this call does nothing. 470 GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); 471 if (allocator) 472 allocator->ImportHistogramsToStatisticsRecorder(); 473 } 474 475 // This singleton instance should be started during the single threaded portion 476 // of main(), and hence it is not thread safe. It initializes globals to 477 // provide support for all future calls. 478 StatisticsRecorder::StatisticsRecorder() { 479 base::AutoLock auto_lock(lock_.Get()); 480 481 existing_histograms_.reset(histograms_); 482 existing_callbacks_.reset(callbacks_); 483 existing_ranges_.reset(ranges_); 484 existing_providers_.reset(providers_); 485 486 histograms_ = new HistogramMap; 487 callbacks_ = new CallbackMap; 488 ranges_ = new RangesMap; 489 providers_ = new HistogramProviders; 490 491 InitLogOnShutdownWithoutLock(); 492 } 493 494 void StatisticsRecorder::InitLogOnShutdownWithoutLock() { 495 if (!vlog_initialized_ && VLOG_IS_ON(1)) { 496 vlog_initialized_ = true; 497 AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this); 498 } 499 } 500 501 // static 502 void StatisticsRecorder::Reset() { 503 504 std::unique_ptr<HistogramMap> histograms_deleter; 505 std::unique_ptr<CallbackMap> callbacks_deleter; 506 std::unique_ptr<RangesMap> ranges_deleter; 507 std::unique_ptr<HistogramProviders> providers_deleter; 508 { 509 base::AutoLock auto_lock(lock_.Get()); 510 histograms_deleter.reset(histograms_); 511 callbacks_deleter.reset(callbacks_); 512 ranges_deleter.reset(ranges_); 513 providers_deleter.reset(providers_); 514 histograms_ = nullptr; 515 callbacks_ = nullptr; 516 ranges_ = nullptr; 517 providers_ = nullptr; 518 } 519 // We are going to leak the histograms and the ranges. 520 } 521 522 // static 523 void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { 524 std::string output; 525 StatisticsRecorder::WriteGraph(std::string(), &output); 526 VLOG(1) << output; 527 } 528 529 530 // static 531 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr; 532 // static 533 StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr; 534 // static 535 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr; 536 // static 537 StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_; 538 // static 539 base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ = 540 LAZY_INSTANCE_INITIALIZER; 541 542 } // namespace base 543