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" |
| 18 #include "base/metrics/statistics_recorder.h" |
19 #include "base/pickle.h" | 19 #include "base/pickle.h" |
20 #include "base/stringprintf.h" | 20 #include "base/stringprintf.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, |
(...skipping 38 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 |