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.
5 #include "base/metrics/statistics_recorder.h"
7 #include "base/at_exit.h"
8 #include "base/debug/leak_annotations.h"
9 #include "base/json/string_escape.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/synchronization/lock.h"
16 #include "base/values.h"
19 // Initialize histogram statistics gathering system.
20 base::LazyInstance
<base::StatisticsRecorder
>::Leaky g_statistics_recorder_
=
21 LAZY_INSTANCE_INITIALIZER
;
27 void StatisticsRecorder::Initialize() {
28 // Ensure that an instance of the StatisticsRecorder object is created.
29 g_statistics_recorder_
.Get();
33 bool StatisticsRecorder::IsActive() {
36 base::AutoLock
auto_lock(*lock_
);
37 return NULL
!= histograms_
;
41 HistogramBase
* StatisticsRecorder::RegisterOrDeleteDuplicate(
42 HistogramBase
* histogram
) {
43 // As per crbug.com/79322 the histograms are intentionally leaked, so we need
44 // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once
45 // for an object, the duplicates should not be annotated.
46 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
47 // twice if (lock_ == NULL) || (!histograms_).
49 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
53 HistogramBase
* histogram_to_delete
= NULL
;
54 HistogramBase
* histogram_to_return
= NULL
;
56 base::AutoLock
auto_lock(*lock_
);
57 if (histograms_
== NULL
) {
58 histogram_to_return
= histogram
;
60 const std::string
& name
= histogram
->histogram_name();
61 HistogramMap::iterator it
= histograms_
->find(HistogramNameRef(name
));
62 if (histograms_
->end() == it
) {
63 (*histograms_
)[HistogramNameRef(name
)] = histogram
;
64 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
65 // If there are callbacks for this histogram, we set the kCallbackExists
67 auto callback_iterator
= callbacks_
->find(name
);
68 if (callback_iterator
!= callbacks_
->end()) {
69 if (!callback_iterator
->second
.is_null())
70 histogram
->SetFlags(HistogramBase::kCallbackExists
);
72 histogram
->ClearFlags(HistogramBase::kCallbackExists
);
74 histogram_to_return
= histogram
;
75 } else if (histogram
== it
->second
) {
76 // The histogram was registered before.
77 histogram_to_return
= histogram
;
79 // We already have one histogram with this name.
80 histogram_to_return
= it
->second
;
81 histogram_to_delete
= histogram
;
85 delete histogram_to_delete
;
86 return histogram_to_return
;
90 const BucketRanges
* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
91 const BucketRanges
* ranges
) {
92 DCHECK(ranges
->HasValidChecksum());
93 scoped_ptr
<const BucketRanges
> ranges_deleter
;
96 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
100 base::AutoLock
auto_lock(*lock_
);
101 if (ranges_
== NULL
) {
102 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
106 std::list
<const BucketRanges
*>* checksum_matching_list
;
107 RangesMap::iterator ranges_it
= ranges_
->find(ranges
->checksum());
108 if (ranges_
->end() == ranges_it
) {
109 // Add a new matching list to map.
110 checksum_matching_list
= new std::list
<const BucketRanges
*>();
111 ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list
);
112 (*ranges_
)[ranges
->checksum()] = checksum_matching_list
;
114 checksum_matching_list
= ranges_it
->second
;
117 for (const BucketRanges
* existing_ranges
: *checksum_matching_list
) {
118 if (existing_ranges
->Equals(ranges
)) {
119 if (existing_ranges
== ranges
) {
122 ranges_deleter
.reset(ranges
);
123 return existing_ranges
;
127 // We haven't found a BucketRanges which has the same ranges. Register the
129 checksum_matching_list
->push_front(ranges
);
134 void StatisticsRecorder::WriteHTMLGraph(const std::string
& query
,
135 std::string
* output
) {
140 GetSnapshot(query
, &snapshot
);
141 for (const HistogramBase
* histogram
: snapshot
) {
142 histogram
->WriteHTMLGraph(output
);
143 output
->append("<br><hr><br>");
148 void StatisticsRecorder::WriteGraph(const std::string
& query
,
149 std::string
* output
) {
153 StringAppendF(output
, "Collections of histograms for %s\n", query
.c_str());
155 output
->append("Collections of all histograms\n");
158 GetSnapshot(query
, &snapshot
);
159 for (const HistogramBase
* histogram
: snapshot
) {
160 histogram
->WriteAscii(output
);
161 output
->append("\n");
166 std::string
StatisticsRecorder::ToJSON(const std::string
& query
) {
168 return std::string();
170 std::string
output("{");
171 if (!query
.empty()) {
172 output
+= "\"query\":";
173 EscapeJSONString(query
, true, &output
);
178 GetSnapshot(query
, &snapshot
);
179 output
+= "\"histograms\":[";
180 bool first_histogram
= true;
181 for (const HistogramBase
* histogram
: snapshot
) {
183 first_histogram
= false;
187 histogram
->WriteJSON(&json
);
195 void StatisticsRecorder::GetHistograms(Histograms
* output
) {
198 base::AutoLock
auto_lock(*lock_
);
199 if (histograms_
== NULL
)
202 for (const auto& entry
: *histograms_
) {
203 DCHECK_EQ(entry
.first
.name_
, entry
.second
->histogram_name());
204 output
->push_back(entry
.second
);
209 void StatisticsRecorder::GetBucketRanges(
210 std::vector
<const BucketRanges
*>* output
) {
213 base::AutoLock
auto_lock(*lock_
);
217 for (const auto& entry
: *ranges_
) {
218 for (const auto& range_entry
: *entry
.second
) {
219 output
->push_back(range_entry
);
225 HistogramBase
* StatisticsRecorder::FindHistogram(const std::string
& name
) {
228 base::AutoLock
auto_lock(*lock_
);
229 if (histograms_
== NULL
)
232 HistogramMap::iterator it
= histograms_
->find(HistogramNameRef(name
));
233 if (histograms_
->end() == it
)
239 bool StatisticsRecorder::SetCallback(
240 const std::string
& name
,
241 const StatisticsRecorder::OnSampleCallback
& cb
) {
242 DCHECK(!cb
.is_null());
245 base::AutoLock
auto_lock(*lock_
);
246 if (histograms_
== NULL
)
249 if (ContainsKey(*callbacks_
, name
))
251 callbacks_
->insert(std::make_pair(name
, cb
));
253 auto histogram_iterator
= histograms_
->find(HistogramNameRef(name
));
254 if (histogram_iterator
!= histograms_
->end())
255 histogram_iterator
->second
->SetFlags(HistogramBase::kCallbackExists
);
261 void StatisticsRecorder::ClearCallback(const std::string
& name
) {
264 base::AutoLock
auto_lock(*lock_
);
265 if (histograms_
== NULL
)
268 callbacks_
->erase(name
);
270 // We also clear the flag from the histogram (if it exists).
271 auto histogram_iterator
= histograms_
->find(HistogramNameRef(name
));
272 if (histogram_iterator
!= histograms_
->end())
273 histogram_iterator
->second
->ClearFlags(HistogramBase::kCallbackExists
);
277 StatisticsRecorder::OnSampleCallback
StatisticsRecorder::FindCallback(
278 const std::string
& name
) {
280 return OnSampleCallback();
281 base::AutoLock
auto_lock(*lock_
);
282 if (histograms_
== NULL
)
283 return OnSampleCallback();
285 auto callback_iterator
= callbacks_
->find(name
);
286 return callback_iterator
!= callbacks_
->end() ? callback_iterator
->second
287 : OnSampleCallback();
291 void StatisticsRecorder::GetSnapshot(const std::string
& query
,
292 Histograms
* snapshot
) {
295 base::AutoLock
auto_lock(*lock_
);
296 if (histograms_
== NULL
)
299 for (const auto& entry
: *histograms_
) {
300 if (entry
.first
.name_
.find(query
) != std::string::npos
)
301 snapshot
->push_back(entry
.second
);
305 // This singleton instance should be started during the single threaded portion
306 // of main(), and hence it is not thread safe. It initializes globals to
307 // provide support for all future calls.
308 StatisticsRecorder::StatisticsRecorder() {
309 DCHECK(!histograms_
);
311 // This will leak on purpose. It's the only way to make sure we won't race
312 // against the static uninitialization of the module while one of our
313 // static methods relying on the lock get called at an inappropriate time
314 // during the termination phase. Since it's a static data member, we will
315 // leak one per process, which would be similar to the instance allocated
316 // during static initialization and released only on process termination.
317 lock_
= new base::Lock
;
319 base::AutoLock
auto_lock(*lock_
);
320 histograms_
= new HistogramMap
;
321 callbacks_
= new CallbackMap
;
322 ranges_
= new RangesMap
;
325 AtExitManager::RegisterCallback(&DumpHistogramsToVlog
, this);
329 void StatisticsRecorder::DumpHistogramsToVlog(void* instance
) {
331 StatisticsRecorder::WriteGraph(std::string(), &output
);
335 StatisticsRecorder::~StatisticsRecorder() {
336 DCHECK(histograms_
&& ranges_
&& lock_
);
339 scoped_ptr
<HistogramMap
> histograms_deleter
;
340 scoped_ptr
<CallbackMap
> callbacks_deleter
;
341 scoped_ptr
<RangesMap
> ranges_deleter
;
342 // We don't delete lock_ on purpose to avoid having to properly protect
343 // against it going away after we checked for NULL in the static methods.
345 base::AutoLock
auto_lock(*lock_
);
346 histograms_deleter
.reset(histograms_
);
347 callbacks_deleter
.reset(callbacks_
);
348 ranges_deleter
.reset(ranges_
);
353 // We are going to leak the histograms and the ranges.
358 StatisticsRecorder::HistogramMap
* StatisticsRecorder::histograms_
= NULL
;
360 StatisticsRecorder::CallbackMap
* StatisticsRecorder::callbacks_
= NULL
;
362 StatisticsRecorder::RangesMap
* StatisticsRecorder::ranges_
= NULL
;
364 base::Lock
* StatisticsRecorder::lock_
= NULL
;