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();
36 bool StatisticsRecorder::IsActive() {
39 base::AutoLock
auto_lock(*lock_
);
40 return NULL
!= histograms_
;
44 HistogramBase
* StatisticsRecorder::RegisterOrDeleteDuplicate(
45 HistogramBase
* histogram
) {
46 // As per crbug.com/79322 the histograms are intentionally leaked, so we need
47 // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once
48 // for an object, the duplicates should not be annotated.
49 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
50 // twice if (lock_ == NULL) || (!histograms_).
52 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
56 HistogramBase
* histogram_to_delete
= NULL
;
57 HistogramBase
* histogram_to_return
= NULL
;
59 base::AutoLock
auto_lock(*lock_
);
60 if (histograms_
== NULL
) {
61 histogram_to_return
= histogram
;
63 const string
& name
= histogram
->histogram_name();
64 HistogramMap::iterator it
= histograms_
->find(name
);
65 if (histograms_
->end() == it
) {
66 (*histograms_
)[name
] = histogram
;
67 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
68 histogram_to_return
= histogram
;
69 } else if (histogram
== it
->second
) {
70 // The histogram was registered before.
71 histogram_to_return
= histogram
;
73 // We already have one histogram with this name.
74 histogram_to_return
= it
->second
;
75 histogram_to_delete
= histogram
;
79 delete histogram_to_delete
;
80 return histogram_to_return
;
84 const BucketRanges
* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
85 const BucketRanges
* ranges
) {
86 DCHECK(ranges
->HasValidChecksum());
87 scoped_ptr
<const BucketRanges
> ranges_deleter
;
90 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
94 base::AutoLock
auto_lock(*lock_
);
95 if (ranges_
== NULL
) {
96 ANNOTATE_LEAKING_OBJECT_PTR(ranges
);
100 list
<const BucketRanges
*>* checksum_matching_list
;
101 RangesMap::iterator ranges_it
= ranges_
->find(ranges
->checksum());
102 if (ranges_
->end() == ranges_it
) {
103 // Add a new matching list to map.
104 checksum_matching_list
= new list
<const BucketRanges
*>();
105 ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list
);
106 (*ranges_
)[ranges
->checksum()] = checksum_matching_list
;
108 checksum_matching_list
= ranges_it
->second
;
111 list
<const BucketRanges
*>::iterator checksum_matching_list_it
;
112 for (checksum_matching_list_it
= checksum_matching_list
->begin();
113 checksum_matching_list_it
!= checksum_matching_list
->end();
114 ++checksum_matching_list_it
) {
115 const BucketRanges
* existing_ranges
= *checksum_matching_list_it
;
116 if (existing_ranges
->Equals(ranges
)) {
117 if (existing_ranges
== ranges
) {
120 ranges_deleter
.reset(ranges
);
121 return existing_ranges
;
125 // We haven't found a BucketRanges which has the same ranges. Register the
127 checksum_matching_list
->push_front(ranges
);
132 void StatisticsRecorder::WriteHTMLGraph(const std::string
& query
,
133 std::string
* output
) {
138 GetSnapshot(query
, &snapshot
);
139 for (Histograms::iterator it
= snapshot
.begin();
140 it
!= snapshot
.end();
142 (*it
)->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 (Histograms::iterator it
= snapshot
.begin();
160 it
!= snapshot
.end();
162 (*it
)->WriteAscii(output
);
163 output
->append("\n");
168 std::string
StatisticsRecorder::ToJSON(const std::string
& query
) {
170 return std::string();
172 std::string
output("{");
173 if (!query
.empty()) {
174 output
+= "\"query\":";
175 EscapeJSONString(query
, true, &output
);
180 GetSnapshot(query
, &snapshot
);
181 output
+= "\"histograms\":[";
182 bool first_histogram
= true;
183 for (Histograms::const_iterator it
= snapshot
.begin(); it
!= snapshot
.end();
186 first_histogram
= false;
190 (*it
)->WriteJSON(&json
);
198 void StatisticsRecorder::GetHistograms(Histograms
* output
) {
201 base::AutoLock
auto_lock(*lock_
);
202 if (histograms_
== NULL
)
205 for (HistogramMap::iterator it
= histograms_
->begin();
206 histograms_
->end() != it
;
208 DCHECK_EQ(it
->first
, it
->second
->histogram_name());
209 output
->push_back(it
->second
);
214 void StatisticsRecorder::GetBucketRanges(
215 std::vector
<const BucketRanges
*>* output
) {
218 base::AutoLock
auto_lock(*lock_
);
222 for (RangesMap::iterator it
= ranges_
->begin();
223 ranges_
->end() != it
;
225 list
<const BucketRanges
*>* ranges_list
= it
->second
;
226 list
<const BucketRanges
*>::iterator ranges_list_it
;
227 for (ranges_list_it
= ranges_list
->begin();
228 ranges_list_it
!= ranges_list
->end();
230 output
->push_back(*ranges_list_it
);
236 HistogramBase
* StatisticsRecorder::FindHistogram(const std::string
& name
) {
239 base::AutoLock
auto_lock(*lock_
);
240 if (histograms_
== NULL
)
243 HistogramMap::iterator it
= histograms_
->find(name
);
244 if (histograms_
->end() == it
)
250 void StatisticsRecorder::GetSnapshot(const std::string
& query
,
251 Histograms
* snapshot
) {
254 base::AutoLock
auto_lock(*lock_
);
255 if (histograms_
== NULL
)
258 for (HistogramMap::iterator it
= histograms_
->begin();
259 histograms_
->end() != it
;
261 if (it
->first
.find(query
) != std::string::npos
)
262 snapshot
->push_back(it
->second
);
266 // This singleton instance should be started during the single threaded portion
267 // of main(), and hence it is not thread safe. It initializes globals to
268 // provide support for all future calls.
269 StatisticsRecorder::StatisticsRecorder() {
270 DCHECK(!histograms_
);
272 // This will leak on purpose. It's the only way to make sure we won't race
273 // against the static uninitialization of the module while one of our
274 // static methods relying on the lock get called at an inappropriate time
275 // during the termination phase. Since it's a static data member, we will
276 // leak one per process, which would be similar to the instance allocated
277 // during static initialization and released only on process termination.
278 lock_
= new base::Lock
;
280 base::AutoLock
auto_lock(*lock_
);
281 histograms_
= new HistogramMap
;
282 ranges_
= new RangesMap
;
285 AtExitManager::RegisterCallback(&DumpHistogramsToVlog
, this);
289 void StatisticsRecorder::DumpHistogramsToVlog(void* instance
) {
290 DCHECK(VLOG_IS_ON(1));
292 StatisticsRecorder
* me
= reinterpret_cast<StatisticsRecorder
*>(instance
);
294 me
->WriteGraph(std::string(), &output
);
298 StatisticsRecorder::~StatisticsRecorder() {
299 DCHECK(histograms_
&& ranges_
&& lock_
);
302 scoped_ptr
<HistogramMap
> histograms_deleter
;
303 scoped_ptr
<RangesMap
> ranges_deleter
;
304 // We don't delete lock_ on purpose to avoid having to properly protect
305 // against it going away after we checked for NULL in the static methods.
307 base::AutoLock
auto_lock(*lock_
);
308 histograms_deleter
.reset(histograms_
);
309 ranges_deleter
.reset(ranges_
);
313 // We are going to leak the histograms and the ranges.
318 StatisticsRecorder::HistogramMap
* StatisticsRecorder::histograms_
= NULL
;
320 StatisticsRecorder::RangesMap
* StatisticsRecorder::ranges_
= NULL
;
322 base::Lock
* StatisticsRecorder::lock_
= NULL
;