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"
21 // Initialize histogram statistics gathering system.
22 base::LazyInstance
<base::StatisticsRecorder
>::Leaky g_statistics_recorder_
=
23 LAZY_INSTANCE_INITIALIZER
;
29 void StatisticsRecorder::Initialize() {
30 // Ensure that an instance of the StatisticsRecorder object is created.
31 g_statistics_recorder_
.Get();
35 bool StatisticsRecorder::IsActive() {
38 base::AutoLock
auto_lock(*lock_
);
39 return NULL
!= histograms_
;
43 HistogramBase
* StatisticsRecorder::RegisterOrDeleteDuplicate(
44 HistogramBase
* histogram
) {
45 // As per crbug.com/79322 the histograms are intentionally leaked, so we need
46 // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once
47 // for an object, the duplicates should not be annotated.
48 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
49 // twice if (lock_ == NULL) || (!histograms_).
51 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
55 HistogramBase
* histogram_to_delete
= NULL
;
56 HistogramBase
* histogram_to_return
= NULL
;
58 base::AutoLock
auto_lock(*lock_
);
59 if (histograms_
== NULL
) {
60 histogram_to_return
= histogram
;
62 const string
& name
= histogram
->histogram_name();
63 HistogramMap::iterator it
= histograms_
->find(name
);
64 if (histograms_
->end() == it
) {
65 (*histograms_
)[name
] = histogram
;
66 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
67 histogram_to_return
= histogram
;
68 } else if (histogram
== it
->second
) {
69 // The histogram was registered before.
70 histogram_to_return
= histogram
;
72 // We already have one histogram with this name.
73 histogram_to_return
= it
->second
;
74 histogram_to_delete
= histogram
;
78 delete histogram_to_delete
;
79 return histogram_to_return
;
83 const BucketRanges
* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
84 const BucketRanges
* ranges
) {
85 DCHECK(ranges
->HasValidChecksum());
86 scoped_ptr
<const BucketRanges
> ranges_deleter
;
89 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
93 base::AutoLock
auto_lock(*lock_
);
94 if (ranges_
== NULL
) {
95 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
99 list
<const BucketRanges
*>* checksum_matching_list
;
100 RangesMap::iterator ranges_it
= ranges_
->find(ranges
->checksum());
101 if (ranges_
->end() == ranges_it
) {
102 // Add a new matching list to map.
103 checksum_matching_list
= new list
<const BucketRanges
*>();
104 ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list
);
105 (*ranges_
)[ranges
->checksum()] = checksum_matching_list
;
107 checksum_matching_list
= ranges_it
->second
;
110 list
<const BucketRanges
*>::iterator checksum_matching_list_it
;
111 for (checksum_matching_list_it
= checksum_matching_list
->begin();
112 checksum_matching_list_it
!= checksum_matching_list
->end();
113 ++checksum_matching_list_it
) {
114 const BucketRanges
* existing_ranges
= *checksum_matching_list_it
;
115 if (existing_ranges
->Equals(ranges
)) {
116 if (existing_ranges
== ranges
) {
119 ranges_deleter
.reset(ranges
);
120 return existing_ranges
;
124 // We haven't found a BucketRanges which has the same ranges. Register the
126 checksum_matching_list
->push_front(ranges
);
131 void StatisticsRecorder::WriteHTMLGraph(const std::string
& query
,
132 std::string
* output
) {
137 GetSnapshot(query
, &snapshot
);
138 for (Histograms::iterator it
= snapshot
.begin();
139 it
!= snapshot
.end();
141 (*it
)->WriteHTMLGraph(output
);
142 output
->append("<br><hr><br>");
147 void StatisticsRecorder::WriteGraph(const std::string
& query
,
148 std::string
* output
) {
152 StringAppendF(output
, "Collections of histograms for %s\n", query
.c_str());
154 output
->append("Collections of all histograms\n");
157 GetSnapshot(query
, &snapshot
);
158 for (Histograms::iterator it
= snapshot
.begin();
159 it
!= snapshot
.end();
161 (*it
)->WriteAscii(output
);
162 output
->append("\n");
167 std::string
StatisticsRecorder::ToJSON(const std::string
& query
) {
169 return std::string();
171 std::string
output("{");
172 if (!query
.empty()) {
173 output
+= "\"query\":";
174 EscapeJSONString(query
, true, &output
);
179 GetSnapshot(query
, &snapshot
);
180 output
+= "\"histograms\":[";
181 bool first_histogram
= true;
182 for (Histograms::const_iterator it
= snapshot
.begin(); it
!= snapshot
.end();
185 first_histogram
= false;
189 (*it
)->WriteJSON(&json
);
197 void StatisticsRecorder::GetHistograms(Histograms
* output
) {
200 base::AutoLock
auto_lock(*lock_
);
201 if (histograms_
== NULL
)
204 for (HistogramMap::iterator it
= histograms_
->begin();
205 histograms_
->end() != it
;
207 DCHECK_EQ(it
->first
, it
->second
->histogram_name());
208 output
->push_back(it
->second
);
213 void StatisticsRecorder::GetBucketRanges(
214 std::vector
<const BucketRanges
*>* output
) {
217 base::AutoLock
auto_lock(*lock_
);
221 for (RangesMap::iterator it
= ranges_
->begin();
222 ranges_
->end() != it
;
224 list
<const BucketRanges
*>* ranges_list
= it
->second
;
225 list
<const BucketRanges
*>::iterator ranges_list_it
;
226 for (ranges_list_it
= ranges_list
->begin();
227 ranges_list_it
!= ranges_list
->end();
229 output
->push_back(*ranges_list_it
);
235 HistogramBase
* StatisticsRecorder::FindHistogram(const std::string
& name
) {
238 base::AutoLock
auto_lock(*lock_
);
239 if (histograms_
== NULL
)
242 HistogramMap::iterator it
= histograms_
->find(name
);
243 if (histograms_
->end() == it
)
249 void StatisticsRecorder::GetSnapshot(const std::string
& query
,
250 Histograms
* snapshot
) {
253 base::AutoLock
auto_lock(*lock_
);
254 if (histograms_
== NULL
)
257 for (HistogramMap::iterator it
= histograms_
->begin();
258 histograms_
->end() != it
;
260 if (it
->first
.find(query
) != std::string::npos
)
261 snapshot
->push_back(it
->second
);
265 // This singleton instance should be started during the single threaded portion
266 // of main(), and hence it is not thread safe. It initializes globals to
267 // provide support for all future calls.
268 StatisticsRecorder::StatisticsRecorder() {
269 DCHECK(!histograms_
);
271 // This will leak on purpose. It's the only way to make sure we won't race
272 // against the static uninitialization of the module while one of our
273 // static methods relying on the lock get called at an inappropriate time
274 // during the termination phase. Since it's a static data member, we will
275 // leak one per process, which would be similar to the instance allocated
276 // during static initialization and released only on process termination.
277 lock_
= new base::Lock
;
279 base::AutoLock
auto_lock(*lock_
);
280 histograms_
= new HistogramMap
;
281 ranges_
= new RangesMap
;
284 AtExitManager::RegisterCallback(&DumpHistogramsToVlog
, this);
288 void StatisticsRecorder::DumpHistogramsToVlog(void* instance
) {
289 DCHECK(VLOG_IS_ON(1));
292 StatisticsRecorder::WriteGraph(std::string(), &output
);
296 StatisticsRecorder::~StatisticsRecorder() {
297 DCHECK(histograms_
&& ranges_
&& lock_
);
300 scoped_ptr
<HistogramMap
> histograms_deleter
;
301 scoped_ptr
<RangesMap
> ranges_deleter
;
302 // We don't delete lock_ on purpose to avoid having to properly protect
303 // against it going away after we checked for NULL in the static methods.
305 base::AutoLock
auto_lock(*lock_
);
306 histograms_deleter
.reset(histograms_
);
307 ranges_deleter
.reset(ranges_
);
311 // We are going to leak the histograms and the ranges.
316 StatisticsRecorder::HistogramMap
* StatisticsRecorder::histograms_
= NULL
;
318 StatisticsRecorder::RangesMap
* StatisticsRecorder::ranges_
= NULL
;
320 base::Lock
* StatisticsRecorder::lock_
= NULL
;