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/strings/stringprintf.h"
14 #include "base/synchronization/lock.h"
15 #include "base/values.h"
18 // Initialize histogram statistics gathering system.
19 base::LazyInstance
<base::StatisticsRecorder
>::Leaky g_statistics_recorder_
=
20 LAZY_INSTANCE_INITIALIZER
;
26 void StatisticsRecorder::Initialize() {
27 // Ensure that an instance of the StatisticsRecorder object is created.
28 g_statistics_recorder_
.Get();
32 bool StatisticsRecorder::IsActive() {
35 base::AutoLock
auto_lock(*lock_
);
36 return NULL
!= histograms_
;
40 HistogramBase
* StatisticsRecorder::RegisterOrDeleteDuplicate(
41 HistogramBase
* histogram
) {
42 // As per crbug.com/79322 the histograms are intentionally leaked, so we need
43 // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once
44 // for an object, the duplicates should not be annotated.
45 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
46 // twice if (lock_ == NULL) || (!histograms_).
48 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
52 HistogramBase
* histogram_to_delete
= NULL
;
53 HistogramBase
* histogram_to_return
= NULL
;
55 base::AutoLock
auto_lock(*lock_
);
56 if (histograms_
== NULL
) {
57 histogram_to_return
= histogram
;
59 const std::string
& name
= histogram
->histogram_name();
60 HistogramMap::iterator it
= histograms_
->find(name
);
61 if (histograms_
->end() == it
) {
62 (*histograms_
)[name
] = histogram
;
63 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
64 histogram_to_return
= histogram
;
65 } else if (histogram
== it
->second
) {
66 // The histogram was registered before.
67 histogram_to_return
= histogram
;
69 // We already have one histogram with this name.
70 histogram_to_return
= it
->second
;
71 histogram_to_delete
= histogram
;
75 delete histogram_to_delete
;
76 return histogram_to_return
;
80 const BucketRanges
* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
81 const BucketRanges
* ranges
) {
82 DCHECK(ranges
->HasValidChecksum());
83 scoped_ptr
<const BucketRanges
> ranges_deleter
;
86 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
90 base::AutoLock
auto_lock(*lock_
);
91 if (ranges_
== NULL
) {
92 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
96 std::list
<const BucketRanges
*>* checksum_matching_list
;
97 RangesMap::iterator ranges_it
= ranges_
->find(ranges
->checksum());
98 if (ranges_
->end() == ranges_it
) {
99 // Add a new matching list to map.
100 checksum_matching_list
= new std::list
<const BucketRanges
*>();
101 ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list
);
102 (*ranges_
)[ranges
->checksum()] = checksum_matching_list
;
104 checksum_matching_list
= ranges_it
->second
;
107 for (const BucketRanges
* existing_ranges
: *checksum_matching_list
) {
108 if (existing_ranges
->Equals(ranges
)) {
109 if (existing_ranges
== ranges
) {
112 ranges_deleter
.reset(ranges
);
113 return existing_ranges
;
117 // We haven't found a BucketRanges which has the same ranges. Register the
119 checksum_matching_list
->push_front(ranges
);
124 void StatisticsRecorder::WriteHTMLGraph(const std::string
& query
,
125 std::string
* output
) {
130 GetSnapshot(query
, &snapshot
);
131 for (const HistogramBase
* histogram
: snapshot
) {
132 histogram
->WriteHTMLGraph(output
);
133 output
->append("<br><hr><br>");
138 void StatisticsRecorder::WriteGraph(const std::string
& query
,
139 std::string
* output
) {
143 StringAppendF(output
, "Collections of histograms for %s\n", query
.c_str());
145 output
->append("Collections of all histograms\n");
148 GetSnapshot(query
, &snapshot
);
149 for (const HistogramBase
* histogram
: snapshot
) {
150 histogram
->WriteAscii(output
);
151 output
->append("\n");
156 std::string
StatisticsRecorder::ToJSON(const std::string
& query
) {
158 return std::string();
160 std::string
output("{");
161 if (!query
.empty()) {
162 output
+= "\"query\":";
163 EscapeJSONString(query
, true, &output
);
168 GetSnapshot(query
, &snapshot
);
169 output
+= "\"histograms\":[";
170 bool first_histogram
= true;
171 for (const HistogramBase
* histogram
: snapshot
) {
173 first_histogram
= false;
177 histogram
->WriteJSON(&json
);
185 void StatisticsRecorder::GetHistograms(Histograms
* output
) {
188 base::AutoLock
auto_lock(*lock_
);
189 if (histograms_
== NULL
)
192 for (const auto& entry
: *histograms_
) {
193 DCHECK_EQ(entry
.first
, entry
.second
->histogram_name());
194 output
->push_back(entry
.second
);
199 void StatisticsRecorder::GetBucketRanges(
200 std::vector
<const BucketRanges
*>* output
) {
203 base::AutoLock
auto_lock(*lock_
);
207 for (const auto& entry
: *ranges_
) {
208 for (const auto& range_entry
: *entry
.second
) {
209 output
->push_back(range_entry
);
215 HistogramBase
* StatisticsRecorder::FindHistogram(const std::string
& name
) {
218 base::AutoLock
auto_lock(*lock_
);
219 if (histograms_
== NULL
)
222 HistogramMap::iterator it
= histograms_
->find(name
);
223 if (histograms_
->end() == it
)
229 void StatisticsRecorder::GetSnapshot(const std::string
& query
,
230 Histograms
* snapshot
) {
233 base::AutoLock
auto_lock(*lock_
);
234 if (histograms_
== NULL
)
237 for (const auto& entry
: *histograms_
) {
238 if (entry
.first
.find(query
) != std::string::npos
)
239 snapshot
->push_back(entry
.second
);
243 // This singleton instance should be started during the single threaded portion
244 // of main(), and hence it is not thread safe. It initializes globals to
245 // provide support for all future calls.
246 StatisticsRecorder::StatisticsRecorder() {
247 DCHECK(!histograms_
);
249 // This will leak on purpose. It's the only way to make sure we won't race
250 // against the static uninitialization of the module while one of our
251 // static methods relying on the lock get called at an inappropriate time
252 // during the termination phase. Since it's a static data member, we will
253 // leak one per process, which would be similar to the instance allocated
254 // during static initialization and released only on process termination.
255 lock_
= new base::Lock
;
257 base::AutoLock
auto_lock(*lock_
);
258 histograms_
= new HistogramMap
;
259 ranges_
= new RangesMap
;
262 AtExitManager::RegisterCallback(&DumpHistogramsToVlog
, this);
266 void StatisticsRecorder::DumpHistogramsToVlog(void* instance
) {
268 StatisticsRecorder::WriteGraph(std::string(), &output
);
272 StatisticsRecorder::~StatisticsRecorder() {
273 DCHECK(histograms_
&& ranges_
&& lock_
);
276 scoped_ptr
<HistogramMap
> histograms_deleter
;
277 scoped_ptr
<RangesMap
> ranges_deleter
;
278 // We don't delete lock_ on purpose to avoid having to properly protect
279 // against it going away after we checked for NULL in the static methods.
281 base::AutoLock
auto_lock(*lock_
);
282 histograms_deleter
.reset(histograms_
);
283 ranges_deleter
.reset(ranges_
);
287 // We are going to leak the histograms and the ranges.
292 StatisticsRecorder::HistogramMap
* StatisticsRecorder::histograms_
= NULL
;
294 StatisticsRecorder::RangesMap
* StatisticsRecorder::ranges_
= NULL
;
296 base::Lock
* StatisticsRecorder::lock_
= NULL
;