OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "base/metrics/statistics_recorder.h" |
| 6 |
| 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" |
| 12 |
| 13 namespace base { |
| 14 |
| 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; |
| 21 |
| 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() { |
| 26 DCHECK(!histograms_); |
| 27 if (lock_ == NULL) { |
| 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; |
| 35 } |
| 36 base::AutoLock auto_lock(*lock_); |
| 37 histograms_ = new HistogramMap; |
| 38 ranges_ = new RangesMap; |
| 39 } |
| 40 |
| 41 StatisticsRecorder::~StatisticsRecorder() { |
| 42 DCHECK(histograms_ && lock_); |
| 43 |
| 44 if (dump_on_exit_) { |
| 45 std::string output; |
| 46 WriteGraph("", &output); |
| 47 DLOG(INFO) << output; |
| 48 } |
| 49 // Clean up. |
| 50 HistogramMap* histograms = NULL; |
| 51 { |
| 52 base::AutoLock auto_lock(*lock_); |
| 53 histograms = histograms_; |
| 54 histograms_ = NULL; |
| 55 } |
| 56 RangesMap* ranges = NULL; |
| 57 { |
| 58 base::AutoLock auto_lock(*lock_); |
| 59 ranges = ranges_; |
| 60 ranges_ = NULL; |
| 61 } |
| 62 // We are going to leak the histograms and the ranges. |
| 63 delete histograms; |
| 64 delete 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. |
| 67 } |
| 68 |
| 69 // static |
| 70 bool StatisticsRecorder::IsActive() { |
| 71 if (lock_ == NULL) |
| 72 return false; |
| 73 base::AutoLock auto_lock(*lock_); |
| 74 return NULL != histograms_; |
| 75 } |
| 76 |
| 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()); |
| 84 if (lock_ == NULL) { |
| 85 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 |
| 86 return histogram; |
| 87 } |
| 88 base::AutoLock auto_lock(*lock_); |
| 89 if (!histograms_) { |
| 90 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 |
| 91 return histogram; |
| 92 } |
| 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_; |
| 101 } else { |
| 102 delete histogram; // We already have one by this name. |
| 103 histogram = it->second; |
| 104 } |
| 105 return histogram; |
| 106 } |
| 107 |
| 108 // static |
| 109 void StatisticsRecorder::RegisterOrDeleteDuplicateRanges(Histogram* histogram) { |
| 110 DCHECK(histogram); |
| 111 CachedRanges* histogram_ranges = histogram->cached_ranges(); |
| 112 DCHECK(histogram_ranges); |
| 113 uint32 checksum = histogram->range_checksum(); |
| 114 histogram_ranges->SetRangeChecksum(checksum); |
| 115 |
| 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; |
| 123 return; |
| 124 } |
| 125 |
| 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; |
| 140 return; |
| 141 } |
| 142 } |
| 143 |
| 144 // We haven't found a CachedRanges which has the same ranges. Register the |
| 145 // new CachedRanges. |
| 146 DCHECK(checksum_matching_list_it == checksum_matching_list->end()); |
| 147 checksum_matching_list->push_front(histogram_ranges); |
| 148 } |
| 149 |
| 150 // static |
| 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; |
| 167 return; |
| 168 } |
| 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; |
| 182 return; |
| 183 } |
| 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_)); |
| 193 } |
| 194 |
| 195 // static |
| 196 void StatisticsRecorder::WriteHTMLGraph(const std::string& query, |
| 197 std::string* output) { |
| 198 if (!IsActive()) |
| 199 return; |
| 200 |
| 201 Histograms snapshot; |
| 202 GetSnapshot(query, &snapshot); |
| 203 for (Histograms::iterator it = snapshot.begin(); |
| 204 it != snapshot.end(); |
| 205 ++it) { |
| 206 (*it)->WriteHTMLGraph(output); |
| 207 output->append("<br><hr><br>"); |
| 208 } |
| 209 } |
| 210 |
| 211 // static |
| 212 void StatisticsRecorder::WriteGraph(const std::string& query, |
| 213 std::string* output) { |
| 214 if (!IsActive()) |
| 215 return; |
| 216 if (query.length()) |
| 217 StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); |
| 218 else |
| 219 output->append("Collections of all histograms\n"); |
| 220 |
| 221 Histograms snapshot; |
| 222 GetSnapshot(query, &snapshot); |
| 223 for (Histograms::iterator it = snapshot.begin(); |
| 224 it != snapshot.end(); |
| 225 ++it) { |
| 226 (*it)->WriteAscii(true, "\n", output); |
| 227 output->append("\n"); |
| 228 } |
| 229 } |
| 230 |
| 231 // static |
| 232 void StatisticsRecorder::GetHistograms(Histograms* output) { |
| 233 if (lock_ == NULL) |
| 234 return; |
| 235 base::AutoLock auto_lock(*lock_); |
| 236 if (!histograms_) |
| 237 return; |
| 238 for (HistogramMap::iterator it = histograms_->begin(); |
| 239 histograms_->end() != it; |
| 240 ++it) { |
| 241 DCHECK_EQ(it->first, it->second->histogram_name()); |
| 242 output->push_back(it->second); |
| 243 } |
| 244 } |
| 245 |
| 246 bool StatisticsRecorder::FindHistogram(const std::string& name, |
| 247 Histogram** histogram) { |
| 248 if (lock_ == NULL) |
| 249 return false; |
| 250 base::AutoLock auto_lock(*lock_); |
| 251 if (!histograms_) |
| 252 return false; |
| 253 HistogramMap::iterator it = histograms_->find(name); |
| 254 if (histograms_->end() == it) |
| 255 return false; |
| 256 *histogram = it->second; |
| 257 return true; |
| 258 } |
| 259 |
| 260 // private static |
| 261 void StatisticsRecorder::GetSnapshot(const std::string& query, |
| 262 Histograms* snapshot) { |
| 263 if (lock_ == NULL) |
| 264 return; |
| 265 base::AutoLock auto_lock(*lock_); |
| 266 if (!histograms_) |
| 267 return; |
| 268 for (HistogramMap::iterator it = histograms_->begin(); |
| 269 histograms_->end() != it; |
| 270 ++it) { |
| 271 if (it->first.find(query) != std::string::npos) |
| 272 snapshot->push_back(it->second); |
| 273 } |
| 274 } |
| 275 |
| 276 // static |
| 277 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; |
| 278 // static |
| 279 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL; |
| 280 // static |
| 281 base::Lock* StatisticsRecorder::lock_ = NULL; |
| 282 // static |
| 283 bool StatisticsRecorder::dump_on_exit_ = false; |
| 284 |
| 285 } // namespace base |
OLD | NEW |