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/debug/leak_annotations.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/stringprintf.h"
11 #include "base/synchronization/lock.h"
15 // Collect the number of histograms created.
16 static uint32 number_of_histograms_
= 0;
17 // Collect the number of vectors saved because of caching ranges.
18 static uint32 number_of_vectors_saved_
= 0;
19 // Collect the number of ranges_ elements saved because of caching ranges.
20 static size_t saved_ranges_size_
= 0;
22 // This singleton instance should be started during the single threaded portion
23 // of main(), and hence it is not thread safe. It initializes globals to
24 // provide support for all future calls.
25 StatisticsRecorder::StatisticsRecorder() {
28 // This will leak on purpose. It's the only way to make sure we won't race
29 // against the static uninitialization of the module while one of our
30 // static methods relying on the lock get called at an inappropriate time
31 // during the termination phase. Since it's a static data member, we will
32 // leak one per process, which would be similar to the instance allocated
33 // during static initialization and released only on process termination.
34 lock_
= new base::Lock
;
36 base::AutoLock
auto_lock(*lock_
);
37 histograms_
= new HistogramMap
;
38 ranges_
= new RangesMap
;
41 StatisticsRecorder::~StatisticsRecorder() {
42 DCHECK(histograms_
&& lock_
);
46 WriteGraph("", &output
);
50 HistogramMap
* histograms
= NULL
;
52 base::AutoLock
auto_lock(*lock_
);
53 histograms
= histograms_
;
56 RangesMap
* ranges
= NULL
;
58 base::AutoLock
auto_lock(*lock_
);
62 // We are going to leak the histograms and the ranges.
65 // We don't delete lock_ on purpose to avoid having to properly protect
66 // against it going away after we checked for NULL in the static methods.
70 bool StatisticsRecorder::IsActive() {
73 base::AutoLock
auto_lock(*lock_
);
74 return NULL
!= histograms_
;
77 Histogram
* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram
* histogram
) {
78 // As per crbug.com/79322 the histograms are intentionally leaked, so we need
79 // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once
80 // for an object, the duplicates should not be annotated.
81 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
82 // twice if (lock_ == NULL) || (!histograms_).
83 DCHECK(histogram
->HasValidRangeChecksum());
85 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
88 base::AutoLock
auto_lock(*lock_
);
90 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
93 const std::string name
= histogram
->histogram_name();
94 HistogramMap::iterator it
= histograms_
->find(name
);
95 // Avoid overwriting a previous registration.
96 if (histograms_
->end() == it
) {
97 (*histograms_
)[name
] = histogram
;
98 ANNOTATE_LEAKING_OBJECT_PTR(histogram
); // see crbug.com/79322
99 RegisterOrDeleteDuplicateRanges(histogram
);
100 ++number_of_histograms_
;
102 delete histogram
; // We already have one by this name.
103 histogram
= it
->second
;
109 void StatisticsRecorder::RegisterOrDeleteDuplicateRanges(Histogram
* histogram
) {
111 CachedRanges
* histogram_ranges
= histogram
->cached_ranges();
112 DCHECK(histogram_ranges
);
113 uint32 checksum
= histogram
->range_checksum();
114 histogram_ranges
->SetRangeChecksum(checksum
);
116 RangesMap::iterator ranges_it
= ranges_
->find(checksum
);
117 if (ranges_
->end() == ranges_it
) {
118 // Register the new CachedRanges.
119 std::list
<CachedRanges
*>* checksum_matching_list(
120 new std::list
<CachedRanges
*>());
121 checksum_matching_list
->push_front(histogram_ranges
);
122 (*ranges_
)[checksum
] = checksum_matching_list
;
126 // Use the registered CachedRanges if the registered CachedRanges has same
127 // ranges_ as |histogram|'s CachedRanges.
128 std::list
<CachedRanges
*>* checksum_matching_list
= ranges_it
->second
;
129 std::list
<CachedRanges
*>::iterator checksum_matching_list_it
;
130 for (checksum_matching_list_it
= checksum_matching_list
->begin();
131 checksum_matching_list_it
!= checksum_matching_list
->end();
132 ++checksum_matching_list_it
) {
133 CachedRanges
* existing_histogram_ranges
= *checksum_matching_list_it
;
134 DCHECK(existing_histogram_ranges
);
135 if (existing_histogram_ranges
->Equals(histogram_ranges
)) {
136 histogram
->set_cached_ranges(existing_histogram_ranges
);
137 ++number_of_vectors_saved_
;
138 saved_ranges_size_
+= histogram_ranges
->size();
139 delete histogram_ranges
;
144 // We haven't found a CachedRanges which has the same ranges. Register the
146 DCHECK(checksum_matching_list_it
== checksum_matching_list
->end());
147 checksum_matching_list
->push_front(histogram_ranges
);
151 void StatisticsRecorder::CollectHistogramStats(const std::string
& suffix
) {
152 static int uma_upload_attempt
= 0;
153 ++uma_upload_attempt
;
154 if (uma_upload_attempt
== 1) {
155 UMA_HISTOGRAM_COUNTS_10000(
156 "Histogram.SharedRange.Count.FirstUpload." + suffix
,
157 number_of_histograms_
);
158 UMA_HISTOGRAM_COUNTS_10000(
159 "Histogram.SharedRange.RangesSaved.FirstUpload." + suffix
,
160 number_of_vectors_saved_
);
161 UMA_HISTOGRAM_COUNTS(
162 "Histogram.SharedRange.ElementsSaved.FirstUpload." + suffix
,
163 static_cast<int>(saved_ranges_size_
));
164 number_of_histograms_
= 0;
165 number_of_vectors_saved_
= 0;
166 saved_ranges_size_
= 0;
169 if (uma_upload_attempt
== 2) {
170 UMA_HISTOGRAM_COUNTS_10000(
171 "Histogram.SharedRange.Count.SecondUpload." + suffix
,
172 number_of_histograms_
);
173 UMA_HISTOGRAM_COUNTS_10000(
174 "Histogram.SharedRange.RangesSaved.SecondUpload." + suffix
,
175 number_of_vectors_saved_
);
176 UMA_HISTOGRAM_COUNTS(
177 "Histogram.SharedRange.ElementsSaved.SecondUpload." + suffix
,
178 static_cast<int>(saved_ranges_size_
));
179 number_of_histograms_
= 0;
180 number_of_vectors_saved_
= 0;
181 saved_ranges_size_
= 0;
184 UMA_HISTOGRAM_COUNTS_10000(
185 "Histogram.SharedRange.Count.RestOfUploads." + suffix
,
186 number_of_histograms_
);
187 UMA_HISTOGRAM_COUNTS_10000(
188 "Histogram.SharedRange.RangesSaved.RestOfUploads." + suffix
,
189 number_of_vectors_saved_
);
190 UMA_HISTOGRAM_COUNTS(
191 "Histogram.SharedRange.ElementsSaved.RestOfUploads." + suffix
,
192 static_cast<int>(saved_ranges_size_
));
196 void StatisticsRecorder::WriteHTMLGraph(const std::string
& query
,
197 std::string
* output
) {
202 GetSnapshot(query
, &snapshot
);
203 for (Histograms::iterator it
= snapshot
.begin();
204 it
!= snapshot
.end();
206 (*it
)->WriteHTMLGraph(output
);
207 output
->append("<br><hr><br>");
212 void StatisticsRecorder::WriteGraph(const std::string
& query
,
213 std::string
* output
) {
217 StringAppendF(output
, "Collections of histograms for %s\n", query
.c_str());
219 output
->append("Collections of all histograms\n");
222 GetSnapshot(query
, &snapshot
);
223 for (Histograms::iterator it
= snapshot
.begin();
224 it
!= snapshot
.end();
226 (*it
)->WriteAscii(true, "\n", output
);
227 output
->append("\n");
232 void StatisticsRecorder::GetHistograms(Histograms
* output
) {
235 base::AutoLock
auto_lock(*lock_
);
238 for (HistogramMap::iterator it
= histograms_
->begin();
239 histograms_
->end() != it
;
241 DCHECK_EQ(it
->first
, it
->second
->histogram_name());
242 output
->push_back(it
->second
);
246 bool StatisticsRecorder::FindHistogram(const std::string
& name
,
247 Histogram
** histogram
) {
250 base::AutoLock
auto_lock(*lock_
);
253 HistogramMap::iterator it
= histograms_
->find(name
);
254 if (histograms_
->end() == it
)
256 *histogram
= it
->second
;
261 void StatisticsRecorder::GetSnapshot(const std::string
& query
,
262 Histograms
* snapshot
) {
265 base::AutoLock
auto_lock(*lock_
);
268 for (HistogramMap::iterator it
= histograms_
->begin();
269 histograms_
->end() != it
;
271 if (it
->first
.find(query
) != std::string::npos
)
272 snapshot
->push_back(it
->second
);
277 StatisticsRecorder::HistogramMap
* StatisticsRecorder::histograms_
= NULL
;
279 StatisticsRecorder::RangesMap
* StatisticsRecorder::ranges_
= NULL
;
281 base::Lock
* StatisticsRecorder::lock_
= NULL
;
283 bool StatisticsRecorder::dump_on_exit_
= false;