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