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/histogram_snapshot_manager.h" 16 #include "base/metrics/metrics_hashes.h" 17 #include "base/metrics/persistent_histogram_allocator.h" 18 #include "base/metrics/record_histogram_checker.h" 19 #include "base/stl_util.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/values.h" 22 23 namespace base { 24 namespace { 25 26 bool HistogramNameLesser(const base::HistogramBase* a, 27 const base::HistogramBase* b) { 28 return strcmp(a->histogram_name(), b->histogram_name()) < 0; 29 } 30 31 } // namespace 32 33 // static 34 LazyInstance<Lock>::Leaky StatisticsRecorder::lock_; 35 36 // static 37 StatisticsRecorder* StatisticsRecorder::top_ = nullptr; 38 39 // static 40 bool StatisticsRecorder::is_vlog_initialized_ = false; 41 42 size_t StatisticsRecorder::BucketRangesHash::operator()( 43 const BucketRanges* const a) const { 44 return a->checksum(); 45 } 46 47 bool StatisticsRecorder::BucketRangesEqual::operator()( 48 const BucketRanges* const a, 49 const BucketRanges* const b) const { 50 return a->Equals(b); 51 } 52 53 StatisticsRecorder::~StatisticsRecorder() { 54 const AutoLock auto_lock(lock_.Get()); 55 DCHECK_EQ(this, top_); 56 top_ = previous_; 57 } 58 59 // static 60 void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() { 61 lock_.Get().AssertAcquired(); 62 if (top_) 63 return; 64 65 const StatisticsRecorder* const p = new StatisticsRecorder; 66 // The global recorder is never deleted. 67 ANNOTATE_LEAKING_OBJECT_PTR(p); 68 DCHECK_EQ(p, top_); 69 } 70 71 // static 72 void StatisticsRecorder::RegisterHistogramProvider( 73 const WeakPtr<HistogramProvider>& provider) { 74 const AutoLock auto_lock(lock_.Get()); 75 EnsureGlobalRecorderWhileLocked(); 76 top_->providers_.push_back(provider); 77 } 78 79 // static 80 HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( 81 HistogramBase* histogram) { 82 // Declared before |auto_lock| to ensure correct destruction order. 83 std::unique_ptr<HistogramBase> histogram_deleter; 84 const AutoLock auto_lock(lock_.Get()); 85 EnsureGlobalRecorderWhileLocked(); 86 87 const char* const name = histogram->histogram_name(); 88 HistogramBase*& registered = top_->histograms_[name]; 89 90 if (!registered) { 91 // |name| is guaranteed to never change or be deallocated so long 92 // as the histogram is alive (which is forever). 93 registered = histogram; 94 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 95 // If there are callbacks for this histogram, we set the kCallbackExists 96 // flag. 97 const auto callback_iterator = top_->callbacks_.find(name); 98 if (callback_iterator != top_->callbacks_.end()) { 99 if (!callback_iterator->second.is_null()) 100 histogram->SetFlags(HistogramBase::kCallbackExists); 101 else 102 histogram->ClearFlags(HistogramBase::kCallbackExists); 103 } 104 return histogram; 105 } 106 107 if (histogram == registered) { 108 // The histogram was registered before. 109 return histogram; 110 } 111 112 // We already have one histogram with this name. 113 histogram_deleter.reset(histogram); 114 return registered; 115 } 116 117 // static 118 const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( 119 const BucketRanges* ranges) { 120 DCHECK(ranges->HasValidChecksum()); 121 122 // Declared before |auto_lock| to ensure correct destruction order. 123 std::unique_ptr<const BucketRanges> ranges_deleter; 124 const AutoLock auto_lock(lock_.Get()); 125 EnsureGlobalRecorderWhileLocked(); 126 127 const BucketRanges* const registered = *top_->ranges_.insert(ranges).first; 128 if (registered == ranges) { 129 ANNOTATE_LEAKING_OBJECT_PTR(ranges); 130 } else { 131 ranges_deleter.reset(ranges); 132 } 133 134 return registered; 135 } 136 137 // static 138 void StatisticsRecorder::WriteHTMLGraph(const std::string& query, 139 std::string* output) { 140 for (const HistogramBase* const histogram : 141 Sort(WithName(GetHistograms(), query))) { 142 histogram->WriteHTMLGraph(output); 143 *output += "<br><hr><br>"; 144 } 145 } 146 147 // static 148 void StatisticsRecorder::WriteGraph(const std::string& query, 149 std::string* output) { 150 if (query.length()) 151 StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); 152 else 153 output->append("Collections of all histograms\n"); 154 155 for (const HistogramBase* const histogram : 156 Sort(WithName(GetHistograms(), query))) { 157 histogram->WriteAscii(output); 158 output->append("\n"); 159 } 160 } 161 162 // static 163 std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) { 164 std::string output = "{\"histograms\":["; 165 const char* sep = ""; 166 for (const HistogramBase* const histogram : Sort(GetHistograms())) { 167 output += sep; 168 sep = ","; 169 std::string json; 170 histogram->WriteJSON(&json, verbosity_level); 171 output += json; 172 } 173 output += "]}"; 174 return output; 175 } 176 177 // static 178 std::vector<const BucketRanges*> StatisticsRecorder::GetBucketRanges() { 179 std::vector<const BucketRanges*> out; 180 const AutoLock auto_lock(lock_.Get()); 181 EnsureGlobalRecorderWhileLocked(); 182 out.reserve(top_->ranges_.size()); 183 out.assign(top_->ranges_.begin(), top_->ranges_.end()); 184 return out; 185 } 186 187 // static 188 HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { 189 // This must be called *before* the lock is acquired below because it will 190 // call back into this object to register histograms. Those called methods 191 // will acquire the lock at that time. 192 ImportGlobalPersistentHistograms(); 193 194 const AutoLock auto_lock(lock_.Get()); 195 EnsureGlobalRecorderWhileLocked(); 196 197 const HistogramMap::const_iterator it = top_->histograms_.find(name); 198 return it != top_->histograms_.end() ? it->second : nullptr; 199 } 200 201 // static 202 StatisticsRecorder::HistogramProviders 203 StatisticsRecorder::GetHistogramProviders() { 204 const AutoLock auto_lock(lock_.Get()); 205 EnsureGlobalRecorderWhileLocked(); 206 return top_->providers_; 207 } 208 209 // static 210 void StatisticsRecorder::ImportProvidedHistograms() { 211 // Merge histogram data from each provider in turn. 212 for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) { 213 // Weak-pointer may be invalid if the provider was destructed, though they 214 // generally never are. 215 if (provider) 216 provider->MergeHistogramDeltas(); 217 } 218 } 219 220 // static 221 void StatisticsRecorder::PrepareDeltas( 222 bool include_persistent, 223 HistogramBase::Flags flags_to_set, 224 HistogramBase::Flags required_flags, 225 HistogramSnapshotManager* snapshot_manager) { 226 Histograms histograms = GetHistograms(); 227 if (!include_persistent) 228 histograms = NonPersistent(std::move(histograms)); 229 snapshot_manager->PrepareDeltas(Sort(std::move(histograms)), flags_to_set, 230 required_flags); 231 } 232 233 // static 234 void StatisticsRecorder::InitLogOnShutdown() { 235 const AutoLock auto_lock(lock_.Get()); 236 InitLogOnShutdownWhileLocked(); 237 } 238 239 // static 240 bool StatisticsRecorder::SetCallback( 241 const std::string& name, 242 const StatisticsRecorder::OnSampleCallback& cb) { 243 DCHECK(!cb.is_null()); 244 const AutoLock auto_lock(lock_.Get()); 245 EnsureGlobalRecorderWhileLocked(); 246 247 if (!top_->callbacks_.insert({name, cb}).second) 248 return false; 249 250 const HistogramMap::const_iterator it = top_->histograms_.find(name); 251 if (it != top_->histograms_.end()) 252 it->second->SetFlags(HistogramBase::kCallbackExists); 253 254 return true; 255 } 256 257 // static 258 void StatisticsRecorder::ClearCallback(const std::string& name) { 259 const AutoLock auto_lock(lock_.Get()); 260 EnsureGlobalRecorderWhileLocked(); 261 262 top_->callbacks_.erase(name); 263 264 // We also clear the flag from the histogram (if it exists). 265 const HistogramMap::const_iterator it = top_->histograms_.find(name); 266 if (it != top_->histograms_.end()) 267 it->second->ClearFlags(HistogramBase::kCallbackExists); 268 } 269 270 // static 271 StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( 272 const std::string& name) { 273 const AutoLock auto_lock(lock_.Get()); 274 EnsureGlobalRecorderWhileLocked(); 275 const auto it = top_->callbacks_.find(name); 276 return it != top_->callbacks_.end() ? it->second : OnSampleCallback(); 277 } 278 279 // static 280 size_t StatisticsRecorder::GetHistogramCount() { 281 const AutoLock auto_lock(lock_.Get()); 282 EnsureGlobalRecorderWhileLocked(); 283 return top_->histograms_.size(); 284 } 285 286 // static 287 void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) { 288 const AutoLock auto_lock(lock_.Get()); 289 EnsureGlobalRecorderWhileLocked(); 290 291 const HistogramMap::iterator found = top_->histograms_.find(name); 292 if (found == top_->histograms_.end()) 293 return; 294 295 HistogramBase* const base = found->second; 296 if (base->GetHistogramType() != SPARSE_HISTOGRAM) { 297 // When forgetting a histogram, it's likely that other information is 298 // also becoming invalid. Clear the persistent reference that may no 299 // longer be valid. There's no danger in this as, at worst, duplicates 300 // will be created in persistent memory. 301 static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0); 302 } 303 304 top_->histograms_.erase(found); 305 } 306 307 // static 308 std::unique_ptr<StatisticsRecorder> 309 StatisticsRecorder::CreateTemporaryForTesting() { 310 const AutoLock auto_lock(lock_.Get()); 311 return WrapUnique(new StatisticsRecorder()); 312 } 313 314 // static 315 void StatisticsRecorder::SetRecordChecker( 316 std::unique_ptr<RecordHistogramChecker> record_checker) { 317 const AutoLock auto_lock(lock_.Get()); 318 EnsureGlobalRecorderWhileLocked(); 319 top_->record_checker_ = std::move(record_checker); 320 } 321 322 // static 323 bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) { 324 const AutoLock auto_lock(lock_.Get()); 325 EnsureGlobalRecorderWhileLocked(); 326 return !top_->record_checker_ || 327 top_->record_checker_->ShouldRecord(histogram_hash); 328 } 329 330 // static 331 StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms() { 332 // This must be called *before* the lock is acquired below because it will 333 // call back into this object to register histograms. Those called methods 334 // will acquire the lock at that time. 335 ImportGlobalPersistentHistograms(); 336 337 Histograms out; 338 339 const AutoLock auto_lock(lock_.Get()); 340 EnsureGlobalRecorderWhileLocked(); 341 342 out.reserve(top_->histograms_.size()); 343 for (const auto& entry : top_->histograms_) 344 out.push_back(entry.second); 345 346 return out; 347 } 348 349 // static 350 StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) { 351 std::sort(histograms.begin(), histograms.end(), &HistogramNameLesser); 352 return histograms; 353 } 354 355 // static 356 StatisticsRecorder::Histograms StatisticsRecorder::WithName( 357 Histograms histograms, 358 const std::string& query) { 359 // Need a C-string query for comparisons against C-string histogram name. 360 const char* const query_string = query.c_str(); 361 histograms.erase(std::remove_if(histograms.begin(), histograms.end(), 362 [query_string](const HistogramBase* const h) { 363 return !strstr(h->histogram_name(), 364 query_string); 365 }), 366 histograms.end()); 367 return histograms; 368 } 369 370 // static 371 StatisticsRecorder::Histograms StatisticsRecorder::NonPersistent( 372 Histograms histograms) { 373 histograms.erase( 374 std::remove_if(histograms.begin(), histograms.end(), 375 [](const HistogramBase* const h) { 376 return (h->flags() & HistogramBase::kIsPersistent) != 0; 377 }), 378 histograms.end()); 379 return histograms; 380 } 381 382 // static 383 void StatisticsRecorder::ImportGlobalPersistentHistograms() { 384 // Import histograms from known persistent storage. Histograms could have been 385 // added by other processes and they must be fetched and recognized locally. 386 // If the persistent memory segment is not shared between processes, this call 387 // does nothing. 388 if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get()) 389 allocator->ImportHistogramsToStatisticsRecorder(); 390 } 391 392 // This singleton instance should be started during the single threaded portion 393 // of main(), and hence it is not thread safe. It initializes globals to provide 394 // support for all future calls. 395 StatisticsRecorder::StatisticsRecorder() { 396 lock_.Get().AssertAcquired(); 397 previous_ = top_; 398 top_ = this; 399 InitLogOnShutdownWhileLocked(); 400 } 401 402 // static 403 void StatisticsRecorder::InitLogOnShutdownWhileLocked() { 404 lock_.Get().AssertAcquired(); 405 if (!is_vlog_initialized_ && VLOG_IS_ON(1)) { 406 is_vlog_initialized_ = true; 407 const auto dump_to_vlog = [](void*) { 408 std::string output; 409 WriteGraph("", &output); 410 VLOG(1) << output; 411 }; 412 AtExitManager::RegisterCallback(dump_to_vlog, nullptr); 413 } 414 } 415 416 } // namespace base 417