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 
HistogramNameLesser(const base::HistogramBase * a,const base::HistogramBase * b)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 
operator ()(const BucketRanges * const a) const42 size_t StatisticsRecorder::BucketRangesHash::operator()(
43     const BucketRanges* const a) const {
44   return a->checksum();
45 }
46 
operator ()(const BucketRanges * const a,const BucketRanges * const b) const47 bool StatisticsRecorder::BucketRangesEqual::operator()(
48     const BucketRanges* const a,
49     const BucketRanges* const b) const {
50   return a->Equals(b);
51 }
52 
~StatisticsRecorder()53 StatisticsRecorder::~StatisticsRecorder() {
54   const AutoLock auto_lock(lock_.Get());
55   DCHECK_EQ(this, top_);
56   top_ = previous_;
57 }
58 
59 // static
EnsureGlobalRecorderWhileLocked()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
RegisterHistogramProvider(const WeakPtr<HistogramProvider> & provider)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
RegisterOrDeleteDuplicate(HistogramBase * histogram)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
RegisterOrDeleteDuplicateRanges(const BucketRanges * ranges)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
WriteHTMLGraph(const std::string & query,std::string * output)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
WriteGraph(const std::string & query,std::string * output)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
ToJSON(JSONVerbosityLevel verbosity_level)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
GetBucketRanges()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
FindHistogram(base::StringPiece name)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
GetHistogramProviders()203 StatisticsRecorder::GetHistogramProviders() {
204   const AutoLock auto_lock(lock_.Get());
205   EnsureGlobalRecorderWhileLocked();
206   return top_->providers_;
207 }
208 
209 // static
ImportProvidedHistograms()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
PrepareDeltas(bool include_persistent,HistogramBase::Flags flags_to_set,HistogramBase::Flags required_flags,HistogramSnapshotManager * snapshot_manager)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
InitLogOnShutdown()234 void StatisticsRecorder::InitLogOnShutdown() {
235   const AutoLock auto_lock(lock_.Get());
236   InitLogOnShutdownWhileLocked();
237 }
238 
239 // static
SetCallback(const std::string & name,const StatisticsRecorder::OnSampleCallback & cb)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
ClearCallback(const std::string & name)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
FindCallback(const std::string & name)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
GetHistogramCount()280 size_t StatisticsRecorder::GetHistogramCount() {
281   const AutoLock auto_lock(lock_.Get());
282   EnsureGlobalRecorderWhileLocked();
283   return top_->histograms_.size();
284 }
285 
286 // static
ForgetHistogramForTesting(base::StringPiece name)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>
CreateTemporaryForTesting()309 StatisticsRecorder::CreateTemporaryForTesting() {
310   const AutoLock auto_lock(lock_.Get());
311   return WrapUnique(new StatisticsRecorder());
312 }
313 
314 // static
SetRecordChecker(std::unique_ptr<RecordHistogramChecker> record_checker)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
ShouldRecordHistogram(uint64_t histogram_hash)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
GetHistograms()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
Sort(Histograms histograms)350 StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) {
351   std::sort(histograms.begin(), histograms.end(), &HistogramNameLesser);
352   return histograms;
353 }
354 
355 // static
WithName(Histograms histograms,const std::string & query)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
NonPersistent(Histograms histograms)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
ImportGlobalPersistentHistograms()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.
StatisticsRecorder()395 StatisticsRecorder::StatisticsRecorder() {
396   lock_.Get().AssertAcquired();
397   previous_ = top_;
398   top_ = this;
399   InitLogOnShutdownWhileLocked();
400 }
401 
402 // static
InitLogOnShutdownWhileLocked()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