| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Histogram is an object that aggregates statistics, and can summarize them in | 5 // Histogram is an object that aggregates statistics, and can summarize them in |
| 6 // various forms, including ASCII graphical, HTML, and numerically (as a | 6 // various forms, including ASCII graphical, HTML, and numerically (as a |
| 7 // vector of numbers corresponding to each of the aggregating buckets). | 7 // vector of numbers corresponding to each of the aggregating buckets). |
| 8 // See header file for details and examples. | 8 // See header file for details and examples. |
| 9 | 9 |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 11 | 11 |
| 12 #include <math.h> | 12 #include <math.h> |
| 13 | 13 |
| 14 #include <algorithm> | 14 #include <algorithm> |
| 15 #include <string> | 15 #include <string> |
| 16 | 16 |
| 17 #include "base/debug/leak_annotations.h" | |
| 18 #include "base/logging.h" | 17 #include "base/logging.h" |
| 19 #include "base/pickle.h" | 18 #include "base/pickle.h" |
| 20 #include "base/stringprintf.h" | 19 #include "base/stringprintf.h" |
| 20 #include "base/metrics/statistics_recorder.h" |
| 21 #include "base/synchronization/lock.h" | 21 #include "base/synchronization/lock.h" |
| 22 | 22 |
| 23 namespace base { | 23 namespace base { |
| 24 | 24 |
| 25 // Static table of checksums for all possible 8 bit bytes. | 25 // Static table of checksums for all possible 8 bit bytes. |
| 26 const uint32 Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL, | 26 const uint32 Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL, |
| 27 0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, | 27 0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, |
| 28 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, | 28 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, |
| 29 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, | 29 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, |
| 30 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, | 30 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, | 67 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, |
| 68 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, | 68 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, |
| 69 0x2d02ef8dL, | 69 0x2d02ef8dL, |
| 70 }; | 70 }; |
| 71 | 71 |
| 72 typedef Histogram::Count Count; | 72 typedef Histogram::Count Count; |
| 73 | 73 |
| 74 // static | 74 // static |
| 75 const size_t Histogram::kBucketCount_MAX = 16384u; | 75 const size_t Histogram::kBucketCount_MAX = 16384u; |
| 76 | 76 |
| 77 // Collect the number of histograms created. | |
| 78 static uint32 number_of_histograms_ = 0; | |
| 79 // Collect the number of vectors saved because of caching ranges. | |
| 80 static uint32 number_of_vectors_saved_ = 0; | |
| 81 // Collect the number of ranges_ elements saved because of caching ranges. | |
| 82 static size_t saved_ranges_size_ = 0; | |
| 83 | |
| 84 Histogram* Histogram::FactoryGet(const std::string& name, | 77 Histogram* Histogram::FactoryGet(const std::string& name, |
| 85 Sample minimum, | 78 Sample minimum, |
| 86 Sample maximum, | 79 Sample maximum, |
| 87 size_t bucket_count, | 80 size_t bucket_count, |
| 88 Flags flags) { | 81 Flags flags) { |
| 89 Histogram* histogram(NULL); | 82 Histogram* histogram(NULL); |
| 90 | 83 |
| 91 // Defensive code. | 84 // Defensive code. |
| 92 if (minimum < 1) | 85 if (minimum < 1) |
| 93 minimum = 1; | 86 minimum = 1; |
| (...skipping 936 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1030 DCHECK_LE(custom_ranges.size(), bucket_count()); | 1023 DCHECK_LE(custom_ranges.size(), bucket_count()); |
| 1031 for (size_t index = 0; index < custom_ranges.size(); ++index) | 1024 for (size_t index = 0; index < custom_ranges.size(); ++index) |
| 1032 SetBucketRange(index, custom_ranges[index]); | 1025 SetBucketRange(index, custom_ranges[index]); |
| 1033 ResetRangeChecksum(); | 1026 ResetRangeChecksum(); |
| 1034 } | 1027 } |
| 1035 | 1028 |
| 1036 double CustomHistogram::GetBucketSize(Count current, size_t i) const { | 1029 double CustomHistogram::GetBucketSize(Count current, size_t i) const { |
| 1037 return 1; | 1030 return 1; |
| 1038 } | 1031 } |
| 1039 | 1032 |
| 1040 //------------------------------------------------------------------------------ | |
| 1041 // The next section handles global (central) support for all histograms, as well | |
| 1042 // as startup/teardown of this service. | |
| 1043 //------------------------------------------------------------------------------ | |
| 1044 | |
| 1045 // This singleton instance should be started during the single threaded portion | |
| 1046 // of main(), and hence it is not thread safe. It initializes globals to | |
| 1047 // provide support for all future calls. | |
| 1048 StatisticsRecorder::StatisticsRecorder() { | |
| 1049 DCHECK(!histograms_); | |
| 1050 if (lock_ == NULL) { | |
| 1051 // This will leak on purpose. It's the only way to make sure we won't race | |
| 1052 // against the static uninitialization of the module while one of our | |
| 1053 // static methods relying on the lock get called at an inappropriate time | |
| 1054 // during the termination phase. Since it's a static data member, we will | |
| 1055 // leak one per process, which would be similar to the instance allocated | |
| 1056 // during static initialization and released only on process termination. | |
| 1057 lock_ = new base::Lock; | |
| 1058 } | |
| 1059 base::AutoLock auto_lock(*lock_); | |
| 1060 histograms_ = new HistogramMap; | |
| 1061 ranges_ = new RangesMap; | |
| 1062 } | |
| 1063 | |
| 1064 StatisticsRecorder::~StatisticsRecorder() { | |
| 1065 DCHECK(histograms_ && lock_); | |
| 1066 | |
| 1067 if (dump_on_exit_) { | |
| 1068 std::string output; | |
| 1069 WriteGraph("", &output); | |
| 1070 DLOG(INFO) << output; | |
| 1071 } | |
| 1072 // Clean up. | |
| 1073 HistogramMap* histograms = NULL; | |
| 1074 { | |
| 1075 base::AutoLock auto_lock(*lock_); | |
| 1076 histograms = histograms_; | |
| 1077 histograms_ = NULL; | |
| 1078 } | |
| 1079 RangesMap* ranges = NULL; | |
| 1080 { | |
| 1081 base::AutoLock auto_lock(*lock_); | |
| 1082 ranges = ranges_; | |
| 1083 ranges_ = NULL; | |
| 1084 } | |
| 1085 // We are going to leak the histograms and the ranges. | |
| 1086 delete histograms; | |
| 1087 delete ranges; | |
| 1088 // We don't delete lock_ on purpose to avoid having to properly protect | |
| 1089 // against it going away after we checked for NULL in the static methods. | |
| 1090 } | |
| 1091 | |
| 1092 // static | |
| 1093 bool StatisticsRecorder::IsActive() { | |
| 1094 if (lock_ == NULL) | |
| 1095 return false; | |
| 1096 base::AutoLock auto_lock(*lock_); | |
| 1097 return NULL != histograms_; | |
| 1098 } | |
| 1099 | |
| 1100 Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) { | |
| 1101 // As per crbug.com/79322 the histograms are intentionally leaked, so we need | |
| 1102 // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once | |
| 1103 // for an object, the duplicates should not be annotated. | |
| 1104 // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr) | |
| 1105 // twice if (lock_ == NULL) || (!histograms_). | |
| 1106 DCHECK(histogram->HasValidRangeChecksum()); | |
| 1107 if (lock_ == NULL) { | |
| 1108 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 | |
| 1109 return histogram; | |
| 1110 } | |
| 1111 base::AutoLock auto_lock(*lock_); | |
| 1112 if (!histograms_) { | |
| 1113 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 | |
| 1114 return histogram; | |
| 1115 } | |
| 1116 const std::string name = histogram->histogram_name(); | |
| 1117 HistogramMap::iterator it = histograms_->find(name); | |
| 1118 // Avoid overwriting a previous registration. | |
| 1119 if (histograms_->end() == it) { | |
| 1120 (*histograms_)[name] = histogram; | |
| 1121 ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 | |
| 1122 RegisterOrDeleteDuplicateRanges(histogram); | |
| 1123 ++number_of_histograms_; | |
| 1124 } else { | |
| 1125 delete histogram; // We already have one by this name. | |
| 1126 histogram = it->second; | |
| 1127 } | |
| 1128 return histogram; | |
| 1129 } | |
| 1130 | |
| 1131 // static | |
| 1132 void StatisticsRecorder::RegisterOrDeleteDuplicateRanges(Histogram* histogram) { | |
| 1133 DCHECK(histogram); | |
| 1134 CachedRanges* histogram_ranges = histogram->cached_ranges(); | |
| 1135 DCHECK(histogram_ranges); | |
| 1136 uint32 checksum = histogram->range_checksum(); | |
| 1137 histogram_ranges->SetRangeChecksum(checksum); | |
| 1138 | |
| 1139 RangesMap::iterator ranges_it = ranges_->find(checksum); | |
| 1140 if (ranges_->end() == ranges_it) { | |
| 1141 // Register the new CachedRanges. | |
| 1142 std::list<CachedRanges*>* checksum_matching_list( | |
| 1143 new std::list<CachedRanges*>()); | |
| 1144 checksum_matching_list->push_front(histogram_ranges); | |
| 1145 (*ranges_)[checksum] = checksum_matching_list; | |
| 1146 return; | |
| 1147 } | |
| 1148 | |
| 1149 // Use the registered CachedRanges if the registered CachedRanges has same | |
| 1150 // ranges_ as |histogram|'s CachedRanges. | |
| 1151 std::list<CachedRanges*>* checksum_matching_list = ranges_it->second; | |
| 1152 std::list<CachedRanges*>::iterator checksum_matching_list_it; | |
| 1153 for (checksum_matching_list_it = checksum_matching_list->begin(); | |
| 1154 checksum_matching_list_it != checksum_matching_list->end(); | |
| 1155 ++checksum_matching_list_it) { | |
| 1156 CachedRanges* existing_histogram_ranges = *checksum_matching_list_it; | |
| 1157 DCHECK(existing_histogram_ranges); | |
| 1158 if (existing_histogram_ranges->Equals(histogram_ranges)) { | |
| 1159 histogram->set_cached_ranges(existing_histogram_ranges); | |
| 1160 ++number_of_vectors_saved_; | |
| 1161 saved_ranges_size_ += histogram_ranges->size(); | |
| 1162 delete histogram_ranges; | |
| 1163 return; | |
| 1164 } | |
| 1165 } | |
| 1166 | |
| 1167 // We haven't found a CachedRanges which has the same ranges. Register the | |
| 1168 // new CachedRanges. | |
| 1169 DCHECK(checksum_matching_list_it == checksum_matching_list->end()); | |
| 1170 checksum_matching_list->push_front(histogram_ranges); | |
| 1171 } | |
| 1172 | |
| 1173 // static | |
| 1174 void StatisticsRecorder::CollectHistogramStats(const std::string& suffix) { | |
| 1175 static int uma_upload_attempt = 0; | |
| 1176 ++uma_upload_attempt; | |
| 1177 if (uma_upload_attempt == 1) { | |
| 1178 UMA_HISTOGRAM_COUNTS_10000( | |
| 1179 "Histogram.SharedRange.Count.FirstUpload." + suffix, | |
| 1180 number_of_histograms_); | |
| 1181 UMA_HISTOGRAM_COUNTS_10000( | |
| 1182 "Histogram.SharedRange.RangesSaved.FirstUpload." + suffix, | |
| 1183 number_of_vectors_saved_); | |
| 1184 UMA_HISTOGRAM_COUNTS( | |
| 1185 "Histogram.SharedRange.ElementsSaved.FirstUpload." + suffix, | |
| 1186 static_cast<int>(saved_ranges_size_)); | |
| 1187 number_of_histograms_ = 0; | |
| 1188 number_of_vectors_saved_ = 0; | |
| 1189 saved_ranges_size_ = 0; | |
| 1190 return; | |
| 1191 } | |
| 1192 if (uma_upload_attempt == 2) { | |
| 1193 UMA_HISTOGRAM_COUNTS_10000( | |
| 1194 "Histogram.SharedRange.Count.SecondUpload." + suffix, | |
| 1195 number_of_histograms_); | |
| 1196 UMA_HISTOGRAM_COUNTS_10000( | |
| 1197 "Histogram.SharedRange.RangesSaved.SecondUpload." + suffix, | |
| 1198 number_of_vectors_saved_); | |
| 1199 UMA_HISTOGRAM_COUNTS( | |
| 1200 "Histogram.SharedRange.ElementsSaved.SecondUpload." + suffix, | |
| 1201 static_cast<int>(saved_ranges_size_)); | |
| 1202 number_of_histograms_ = 0; | |
| 1203 number_of_vectors_saved_ = 0; | |
| 1204 saved_ranges_size_ = 0; | |
| 1205 return; | |
| 1206 } | |
| 1207 UMA_HISTOGRAM_COUNTS_10000( | |
| 1208 "Histogram.SharedRange.Count.RestOfUploads." + suffix, | |
| 1209 number_of_histograms_); | |
| 1210 UMA_HISTOGRAM_COUNTS_10000( | |
| 1211 "Histogram.SharedRange.RangesSaved.RestOfUploads." + suffix, | |
| 1212 number_of_vectors_saved_); | |
| 1213 UMA_HISTOGRAM_COUNTS( | |
| 1214 "Histogram.SharedRange.ElementsSaved.RestOfUploads." + suffix, | |
| 1215 static_cast<int>(saved_ranges_size_)); | |
| 1216 } | |
| 1217 | |
| 1218 // static | |
| 1219 void StatisticsRecorder::WriteHTMLGraph(const std::string& query, | |
| 1220 std::string* output) { | |
| 1221 if (!IsActive()) | |
| 1222 return; | |
| 1223 | |
| 1224 Histograms snapshot; | |
| 1225 GetSnapshot(query, &snapshot); | |
| 1226 for (Histograms::iterator it = snapshot.begin(); | |
| 1227 it != snapshot.end(); | |
| 1228 ++it) { | |
| 1229 (*it)->WriteHTMLGraph(output); | |
| 1230 output->append("<br><hr><br>"); | |
| 1231 } | |
| 1232 } | |
| 1233 | |
| 1234 // static | |
| 1235 void StatisticsRecorder::WriteGraph(const std::string& query, | |
| 1236 std::string* output) { | |
| 1237 if (!IsActive()) | |
| 1238 return; | |
| 1239 if (query.length()) | |
| 1240 StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); | |
| 1241 else | |
| 1242 output->append("Collections of all histograms\n"); | |
| 1243 | |
| 1244 Histograms snapshot; | |
| 1245 GetSnapshot(query, &snapshot); | |
| 1246 for (Histograms::iterator it = snapshot.begin(); | |
| 1247 it != snapshot.end(); | |
| 1248 ++it) { | |
| 1249 (*it)->WriteAscii(true, "\n", output); | |
| 1250 output->append("\n"); | |
| 1251 } | |
| 1252 } | |
| 1253 | |
| 1254 // static | |
| 1255 void StatisticsRecorder::GetHistograms(Histograms* output) { | |
| 1256 if (lock_ == NULL) | |
| 1257 return; | |
| 1258 base::AutoLock auto_lock(*lock_); | |
| 1259 if (!histograms_) | |
| 1260 return; | |
| 1261 for (HistogramMap::iterator it = histograms_->begin(); | |
| 1262 histograms_->end() != it; | |
| 1263 ++it) { | |
| 1264 DCHECK_EQ(it->first, it->second->histogram_name()); | |
| 1265 output->push_back(it->second); | |
| 1266 } | |
| 1267 } | |
| 1268 | |
| 1269 bool StatisticsRecorder::FindHistogram(const std::string& name, | |
| 1270 Histogram** histogram) { | |
| 1271 if (lock_ == NULL) | |
| 1272 return false; | |
| 1273 base::AutoLock auto_lock(*lock_); | |
| 1274 if (!histograms_) | |
| 1275 return false; | |
| 1276 HistogramMap::iterator it = histograms_->find(name); | |
| 1277 if (histograms_->end() == it) | |
| 1278 return false; | |
| 1279 *histogram = it->second; | |
| 1280 return true; | |
| 1281 } | |
| 1282 | |
| 1283 // private static | |
| 1284 void StatisticsRecorder::GetSnapshot(const std::string& query, | |
| 1285 Histograms* snapshot) { | |
| 1286 if (lock_ == NULL) | |
| 1287 return; | |
| 1288 base::AutoLock auto_lock(*lock_); | |
| 1289 if (!histograms_) | |
| 1290 return; | |
| 1291 for (HistogramMap::iterator it = histograms_->begin(); | |
| 1292 histograms_->end() != it; | |
| 1293 ++it) { | |
| 1294 if (it->first.find(query) != std::string::npos) | |
| 1295 snapshot->push_back(it->second); | |
| 1296 } | |
| 1297 } | |
| 1298 | |
| 1299 CachedRanges::CachedRanges(size_t bucket_count, int initial_value) | 1033 CachedRanges::CachedRanges(size_t bucket_count, int initial_value) |
| 1300 : ranges_(bucket_count, initial_value), | 1034 : ranges_(bucket_count, initial_value), |
| 1301 range_checksum_(0) { | 1035 range_checksum_(0) { |
| 1302 } | 1036 } |
| 1303 | 1037 |
| 1304 CachedRanges::~CachedRanges() { | 1038 CachedRanges::~CachedRanges() { |
| 1305 } | 1039 } |
| 1306 | 1040 |
| 1307 void CachedRanges::SetBucketRange(size_t i, Histogram::Sample value) { | 1041 void CachedRanges::SetBucketRange(size_t i, Histogram::Sample value) { |
| 1308 DCHECK_LT(i, ranges_.size()); | 1042 DCHECK_LT(i, ranges_.size()); |
| 1309 DCHECK_GE(value, 0); | 1043 DCHECK_GE(value, 0); |
| 1310 ranges_[i] = value; | 1044 ranges_[i] = value; |
| 1311 } | 1045 } |
| 1312 | 1046 |
| 1313 bool CachedRanges::Equals(CachedRanges* other) const { | 1047 bool CachedRanges::Equals(CachedRanges* other) const { |
| 1314 if (range_checksum_ != other->range_checksum_) | 1048 if (range_checksum_ != other->range_checksum_) |
| 1315 return false; | 1049 return false; |
| 1316 if (ranges_.size() != other->ranges_.size()) | 1050 if (ranges_.size() != other->ranges_.size()) |
| 1317 return false; | 1051 return false; |
| 1318 for (size_t index = 0; index < ranges_.size(); ++index) { | 1052 for (size_t index = 0; index < ranges_.size(); ++index) { |
| 1319 if (ranges_[index] != other->ranges_[index]) | 1053 if (ranges_[index] != other->ranges_[index]) |
| 1320 return false; | 1054 return false; |
| 1321 } | 1055 } |
| 1322 return true; | 1056 return true; |
| 1323 } | 1057 } |
| 1324 | 1058 |
| 1325 // static | |
| 1326 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; | |
| 1327 // static | |
| 1328 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL; | |
| 1329 // static | |
| 1330 base::Lock* StatisticsRecorder::lock_ = NULL; | |
| 1331 // static | |
| 1332 bool StatisticsRecorder::dump_on_exit_ = false; | |
| 1333 } // namespace base | 1059 } // namespace base |
| OLD | NEW |