• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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/containers/contains.h"
11 #include "base/debug/leak_annotations.h"
12 #include "base/json/string_escape.h"
13 #include "base/logging.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/histogram_snapshot_manager.h"
17 #include "base/metrics/metrics_hashes.h"
18 #include "base/metrics/persistent_histogram_allocator.h"
19 #include "base/metrics/record_histogram_checker.h"
20 #include "base/ranges/algorithm.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/values.h"
24 
25 namespace base {
26 namespace {
27 
HistogramNameLesser(const base::HistogramBase * a,const base::HistogramBase * b)28 bool HistogramNameLesser(const base::HistogramBase* a,
29                          const base::HistogramBase* b) {
30   return strcmp(a->histogram_name(), b->histogram_name()) < 0;
31 }
32 
33 }  // namespace
34 
35 // static
36 LazyInstance<Lock>::Leaky StatisticsRecorder::lock_ = LAZY_INSTANCE_INITIALIZER;
37 
38 // static
39 LazyInstance<base::Lock>::Leaky StatisticsRecorder::snapshot_lock_ =
40     LAZY_INSTANCE_INITIALIZER;
41 
42 // static
43 StatisticsRecorder::SnapshotTransactionId
44     StatisticsRecorder::last_snapshot_transaction_id_ = 0;
45 
46 // static
47 StatisticsRecorder* StatisticsRecorder::top_ = nullptr;
48 
49 // static
50 bool StatisticsRecorder::is_vlog_initialized_ = false;
51 
52 // static
53 std::atomic<bool> StatisticsRecorder::have_active_callbacks_{false};
54 
55 // static
56 std::atomic<StatisticsRecorder::GlobalSampleCallback>
57     StatisticsRecorder::global_sample_callback_{nullptr};
58 
59 StatisticsRecorder::ScopedHistogramSampleObserver::
ScopedHistogramSampleObserver(const std::string & name,OnSampleCallback callback)60     ScopedHistogramSampleObserver(const std::string& name,
61                                   OnSampleCallback callback)
62     : histogram_name_(name), callback_(callback) {
63   StatisticsRecorder::AddHistogramSampleObserver(histogram_name_, this);
64 }
65 
66 StatisticsRecorder::ScopedHistogramSampleObserver::
~ScopedHistogramSampleObserver()67     ~ScopedHistogramSampleObserver() {
68   StatisticsRecorder::RemoveHistogramSampleObserver(histogram_name_, this);
69 }
70 
RunCallback(const char * histogram_name,uint64_t name_hash,HistogramBase::Sample sample)71 void StatisticsRecorder::ScopedHistogramSampleObserver::RunCallback(
72     const char* histogram_name,
73     uint64_t name_hash,
74     HistogramBase::Sample sample) {
75   callback_.Run(histogram_name, name_hash, sample);
76 }
77 
~StatisticsRecorder()78 StatisticsRecorder::~StatisticsRecorder() {
79   const AutoLock auto_lock(lock_.Get());
80   DCHECK_EQ(this, top_);
81   top_ = previous_;
82 }
83 
84 // static
EnsureGlobalRecorderWhileLocked()85 void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() {
86   lock_.Get().AssertAcquired();
87   if (top_)
88     return;
89 
90   const StatisticsRecorder* const p = new StatisticsRecorder;
91   // The global recorder is never deleted.
92   ANNOTATE_LEAKING_OBJECT_PTR(p);
93   DCHECK_EQ(p, top_);
94 }
95 
96 // static
RegisterHistogramProvider(const WeakPtr<HistogramProvider> & provider)97 void StatisticsRecorder::RegisterHistogramProvider(
98     const WeakPtr<HistogramProvider>& provider) {
99   const AutoLock auto_lock(lock_.Get());
100   EnsureGlobalRecorderWhileLocked();
101   top_->providers_.push_back(provider);
102 }
103 
104 // static
RegisterOrDeleteDuplicate(HistogramBase * histogram)105 HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
106     HistogramBase* histogram) {
107   // Declared before |auto_lock| to ensure correct destruction order.
108   std::unique_ptr<HistogramBase> histogram_deleter;
109   const AutoLock auto_lock(lock_.Get());
110   EnsureGlobalRecorderWhileLocked();
111 
112   const char* const name = histogram->histogram_name();
113   HistogramBase*& registered = top_->histograms_[name];
114 
115   if (!registered) {
116     // |name| is guaranteed to never change or be deallocated so long
117     // as the histogram is alive (which is forever).
118     registered = histogram;
119     ANNOTATE_LEAKING_OBJECT_PTR(histogram);  // see crbug.com/79322
120     // If there are callbacks for this histogram, we set the kCallbackExists
121     // flag.
122     if (base::Contains(top_->observers_, name))
123       histogram->SetFlags(HistogramBase::kCallbackExists);
124 
125     return histogram;
126   }
127 
128   if (histogram == registered) {
129     // The histogram was registered before.
130     return histogram;
131   }
132 
133   // We already have one histogram with this name.
134   histogram_deleter.reset(histogram);
135   return registered;
136 }
137 
138 // static
RegisterOrDeleteDuplicateRanges(const BucketRanges * ranges)139 const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
140     const BucketRanges* ranges) {
141   const AutoLock auto_lock(lock_.Get());
142   EnsureGlobalRecorderWhileLocked();
143 
144   const BucketRanges* const registered =
145       top_->ranges_manager_.RegisterOrDeleteDuplicateRanges(ranges);
146 
147   if (registered == ranges)
148     ANNOTATE_LEAKING_OBJECT_PTR(ranges);
149 
150   return registered;
151 }
152 
153 // static
WriteGraph(const std::string & query,std::string * output)154 void StatisticsRecorder::WriteGraph(const std::string& query,
155                                     std::string* output) {
156   if (query.length())
157     StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
158   else
159     output->append("Collections of all histograms\n");
160 
161   for (const HistogramBase* const histogram :
162        Sort(WithName(GetHistograms(), query))) {
163     histogram->WriteAscii(output);
164     output->append("\n");
165   }
166 }
167 
168 // static
ToJSON(JSONVerbosityLevel verbosity_level)169 std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) {
170   std::string output = "{\"histograms\":[";
171   const char* sep = "";
172   for (const HistogramBase* const histogram : Sort(GetHistograms())) {
173     output += sep;
174     sep = ",";
175     std::string json;
176     histogram->WriteJSON(&json, verbosity_level);
177     output += json;
178   }
179   output += "]}";
180   return output;
181 }
182 
183 // static
GetBucketRanges()184 std::vector<const BucketRanges*> StatisticsRecorder::GetBucketRanges() {
185   const AutoLock auto_lock(lock_.Get());
186   EnsureGlobalRecorderWhileLocked();
187 
188   return top_->ranges_manager_.GetBucketRanges();
189 }
190 
191 // static
FindHistogram(base::StringPiece name)192 HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
193   // This must be called *before* the lock is acquired below because it will
194   // call back into this object to register histograms. Those called methods
195   // will acquire the lock at that time.
196   ImportGlobalPersistentHistograms();
197 
198   const AutoLock auto_lock(lock_.Get());
199   EnsureGlobalRecorderWhileLocked();
200 
201   const HistogramMap::const_iterator it = top_->histograms_.find(name);
202   return it != top_->histograms_.end() ? it->second : nullptr;
203 }
204 
205 // static
206 StatisticsRecorder::HistogramProviders
GetHistogramProviders()207 StatisticsRecorder::GetHistogramProviders() {
208   const AutoLock auto_lock(lock_.Get());
209   EnsureGlobalRecorderWhileLocked();
210   return top_->providers_;
211 }
212 
213 // static
ImportProvidedHistograms()214 void StatisticsRecorder::ImportProvidedHistograms() {
215   // Merge histogram data from each provider in turn.
216   for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) {
217     // Weak-pointer may be invalid if the provider was destructed, though they
218     // generally never are.
219     if (provider)
220       provider->MergeHistogramDeltas();
221   }
222 }
223 
224 // static
PrepareDeltas(bool include_persistent,HistogramBase::Flags flags_to_set,HistogramBase::Flags required_flags,HistogramSnapshotManager * snapshot_manager)225 StatisticsRecorder::SnapshotTransactionId StatisticsRecorder::PrepareDeltas(
226     bool include_persistent,
227     HistogramBase::Flags flags_to_set,
228     HistogramBase::Flags required_flags,
229     HistogramSnapshotManager* snapshot_manager) {
230   Histograms histograms = Sort(GetHistograms(include_persistent));
231   base::AutoLock lock(snapshot_lock_.Get());
232   snapshot_manager->PrepareDeltas(std::move(histograms), flags_to_set,
233                                   required_flags);
234   return ++last_snapshot_transaction_id_;
235 }
236 
237 // static
238 StatisticsRecorder::SnapshotTransactionId
SnapshotUnloggedSamples(HistogramBase::Flags required_flags,HistogramSnapshotManager * snapshot_manager)239 StatisticsRecorder::SnapshotUnloggedSamples(
240     HistogramBase::Flags required_flags,
241     HistogramSnapshotManager* snapshot_manager) {
242   Histograms histograms = Sort(GetHistograms());
243   base::AutoLock lock(snapshot_lock_.Get());
244   snapshot_manager->SnapshotUnloggedSamples(std::move(histograms),
245                                             required_flags);
246   return ++last_snapshot_transaction_id_;
247 }
248 
249 // static
250 StatisticsRecorder::SnapshotTransactionId
GetLastSnapshotTransactionId()251 StatisticsRecorder::GetLastSnapshotTransactionId() {
252   base::AutoLock lock(snapshot_lock_.Get());
253   return last_snapshot_transaction_id_;
254 }
255 
256 // static
InitLogOnShutdown()257 void StatisticsRecorder::InitLogOnShutdown() {
258   const AutoLock auto_lock(lock_.Get());
259   InitLogOnShutdownWhileLocked();
260 }
261 
262 // static
AddHistogramSampleObserver(const std::string & name,StatisticsRecorder::ScopedHistogramSampleObserver * observer)263 void StatisticsRecorder::AddHistogramSampleObserver(
264     const std::string& name,
265     StatisticsRecorder::ScopedHistogramSampleObserver* observer) {
266   DCHECK(observer);
267   const AutoLock auto_lock(lock_.Get());
268   EnsureGlobalRecorderWhileLocked();
269 
270   auto iter = top_->observers_.find(name);
271   if (iter == top_->observers_.end()) {
272     top_->observers_.insert(
273         {name, base::MakeRefCounted<HistogramSampleObserverList>()});
274   }
275 
276   top_->observers_[name]->AddObserver(observer);
277 
278   const HistogramMap::const_iterator it = top_->histograms_.find(name);
279   if (it != top_->histograms_.end())
280     it->second->SetFlags(HistogramBase::kCallbackExists);
281 
282   have_active_callbacks_.store(
283       global_sample_callback() || !top_->observers_.empty(),
284       std::memory_order_relaxed);
285 }
286 
287 // static
RemoveHistogramSampleObserver(const std::string & name,StatisticsRecorder::ScopedHistogramSampleObserver * observer)288 void StatisticsRecorder::RemoveHistogramSampleObserver(
289     const std::string& name,
290     StatisticsRecorder::ScopedHistogramSampleObserver* observer) {
291   const AutoLock auto_lock(lock_.Get());
292   EnsureGlobalRecorderWhileLocked();
293 
294   auto iter = top_->observers_.find(name);
295   DCHECK(iter != top_->observers_.end());
296 
297   auto result = iter->second->RemoveObserver(observer);
298   if (result ==
299       HistogramSampleObserverList::RemoveObserverResult::kWasOrBecameEmpty) {
300     top_->observers_.erase(name);
301 
302     // We also clear the flag from the histogram (if it exists).
303     const HistogramMap::const_iterator it = top_->histograms_.find(name);
304     if (it != top_->histograms_.end())
305       it->second->ClearFlags(HistogramBase::kCallbackExists);
306   }
307 
308   have_active_callbacks_.store(
309       global_sample_callback() || !top_->observers_.empty(),
310       std::memory_order_relaxed);
311 }
312 
313 // static
FindAndRunHistogramCallbacks(base::PassKey<HistogramBase>,const char * histogram_name,uint64_t name_hash,HistogramBase::Sample sample)314 void StatisticsRecorder::FindAndRunHistogramCallbacks(
315     base::PassKey<HistogramBase>,
316     const char* histogram_name,
317     uint64_t name_hash,
318     HistogramBase::Sample sample) {
319   const AutoLock auto_lock(lock_.Get());
320   EnsureGlobalRecorderWhileLocked();
321 
322   auto it = top_->observers_.find(histogram_name);
323 
324   // Ensure that this observer is still registered, as it might have been
325   // unregistered before we acquired the lock.
326   if (it == top_->observers_.end())
327     return;
328 
329   it->second->Notify(FROM_HERE, &ScopedHistogramSampleObserver::RunCallback,
330                      histogram_name, name_hash, sample);
331 }
332 
333 // static
SetGlobalSampleCallback(const GlobalSampleCallback & new_global_sample_callback)334 void StatisticsRecorder::SetGlobalSampleCallback(
335     const GlobalSampleCallback& new_global_sample_callback) {
336   const AutoLock auto_lock(lock_.Get());
337   EnsureGlobalRecorderWhileLocked();
338 
339   DCHECK(!global_sample_callback() || !new_global_sample_callback);
340   global_sample_callback_.store(new_global_sample_callback);
341 
342   have_active_callbacks_.store(
343       new_global_sample_callback || !top_->observers_.empty(),
344       std::memory_order_relaxed);
345 }
346 
347 // static
GetHistogramCount()348 size_t StatisticsRecorder::GetHistogramCount() {
349   const AutoLock auto_lock(lock_.Get());
350   EnsureGlobalRecorderWhileLocked();
351   return top_->histograms_.size();
352 }
353 
354 // static
ForgetHistogramForTesting(base::StringPiece name)355 void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
356   const AutoLock auto_lock(lock_.Get());
357   EnsureGlobalRecorderWhileLocked();
358 
359   const HistogramMap::iterator found = top_->histograms_.find(name);
360   if (found == top_->histograms_.end())
361     return;
362 
363   HistogramBase* const base = found->second;
364   if (base->GetHistogramType() != SPARSE_HISTOGRAM) {
365     // When forgetting a histogram, it's likely that other information is
366     // also becoming invalid. Clear the persistent reference that may no
367     // longer be valid. There's no danger in this as, at worst, duplicates
368     // will be created in persistent memory.
369     static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0);
370   }
371 
372   top_->histograms_.erase(found);
373 }
374 
375 // static
376 std::unique_ptr<StatisticsRecorder>
CreateTemporaryForTesting()377 StatisticsRecorder::CreateTemporaryForTesting() {
378   const AutoLock auto_lock(lock_.Get());
379   std::unique_ptr<StatisticsRecorder> temporary_recorder =
380       WrapUnique(new StatisticsRecorder());
381   temporary_recorder->ranges_manager_
382       .DoNotReleaseRangesOnDestroyForTesting();  // IN-TEST
383   return temporary_recorder;
384 }
385 
386 // static
SetRecordChecker(std::unique_ptr<RecordHistogramChecker> record_checker)387 void StatisticsRecorder::SetRecordChecker(
388     std::unique_ptr<RecordHistogramChecker> record_checker) {
389   const AutoLock auto_lock(lock_.Get());
390   EnsureGlobalRecorderWhileLocked();
391   top_->record_checker_ = std::move(record_checker);
392 }
393 
394 // static
ShouldRecordHistogram(uint32_t histogram_hash)395 bool StatisticsRecorder::ShouldRecordHistogram(uint32_t histogram_hash) {
396   const AutoLock auto_lock(lock_.Get());
397   EnsureGlobalRecorderWhileLocked();
398   return !top_->record_checker_ ||
399          top_->record_checker_->ShouldRecord(histogram_hash);
400 }
401 
402 // static
GetHistograms(bool include_persistent)403 StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms(
404     bool include_persistent) {
405   // This must be called *before* the lock is acquired below because it will
406   // call back into this object to register histograms. Those called methods
407   // will acquire the lock at that time.
408   ImportGlobalPersistentHistograms();
409 
410   Histograms out;
411 
412   const AutoLock auto_lock(lock_.Get());
413   EnsureGlobalRecorderWhileLocked();
414 
415   out.reserve(top_->histograms_.size());
416   for (const auto& entry : top_->histograms_) {
417     bool is_persistent = entry.second->HasFlags(HistogramBase::kIsPersistent);
418     if (!include_persistent && is_persistent)
419       continue;
420     out.push_back(entry.second);
421   }
422 
423   return out;
424 }
425 
426 // static
Sort(Histograms histograms)427 StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) {
428   ranges::sort(histograms, &HistogramNameLesser);
429   return histograms;
430 }
431 
432 // static
WithName(Histograms histograms,const std::string & query,bool case_sensitive)433 StatisticsRecorder::Histograms StatisticsRecorder::WithName(
434     Histograms histograms,
435     const std::string& query,
436     bool case_sensitive) {
437   // Need a C-string query for comparisons against C-string histogram name.
438   std::string lowercase_query;
439   const char* query_string;
440   if (case_sensitive) {
441     query_string = query.c_str();
442   } else {
443     lowercase_query = base::ToLowerASCII(query);
444     query_string = lowercase_query.c_str();
445   }
446 
447   histograms.erase(
448       ranges::remove_if(
449           histograms,
450           [query_string, case_sensitive](const HistogramBase* const h) {
451             return !strstr(
452                 case_sensitive
453                     ? h->histogram_name()
454                     : base::ToLowerASCII(h->histogram_name()).c_str(),
455                 query_string);
456           }),
457       histograms.end());
458   return histograms;
459 }
460 
461 // static
ImportGlobalPersistentHistograms()462 void StatisticsRecorder::ImportGlobalPersistentHistograms() {
463   // Import histograms from known persistent storage. Histograms could have been
464   // added by other processes and they must be fetched and recognized locally.
465   // If the persistent memory segment is not shared between processes, this call
466   // does nothing.
467   if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get())
468     allocator->ImportHistogramsToStatisticsRecorder();
469 }
470 
StatisticsRecorder()471 StatisticsRecorder::StatisticsRecorder() {
472   lock_.Get().AssertAcquired();
473   previous_ = top_;
474   top_ = this;
475   InitLogOnShutdownWhileLocked();
476 }
477 
478 // static
InitLogOnShutdownWhileLocked()479 void StatisticsRecorder::InitLogOnShutdownWhileLocked() {
480   lock_.Get().AssertAcquired();
481   if (!is_vlog_initialized_ && VLOG_IS_ON(1)) {
482     is_vlog_initialized_ = true;
483     const auto dump_to_vlog = [](void*) {
484       std::string output;
485       WriteGraph("", &output);
486       VLOG(1) << output;
487     };
488     AtExitManager::RegisterCallback(dump_to_vlog, nullptr);
489   }
490 }
491 
492 }  // namespace base
493