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/at_exit.h" 8 #include "base/json/string_escape.h" 9 #include "base/logging.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/metrics/histogram.h" 12 #include "base/metrics/metrics_hashes.h" 13 #include "base/stl_util.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/synchronization/lock.h" 16 #include "base/values.h" 17 18 namespace { 19 // Initialize histogram statistics gathering system. 20 base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ = 21 LAZY_INSTANCE_INITIALIZER; 22 } // namespace 23 24 namespace base { 25 26 // static 27 void StatisticsRecorder::Initialize() { 28 // Ensure that an instance of the StatisticsRecorder object is created. 29 g_statistics_recorder_.Get(); 30 } 31 32 // static 33 bool StatisticsRecorder::IsActive() { 34 if (lock_ == NULL) 35 return false; 36 base::AutoLock auto_lock(*lock_); 37 return NULL != histograms_; 38 } 39 40 // static 41 HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( 42 HistogramBase* histogram) { 43 // As per crbug.com/79322 the histograms are intentionally leaked. 44 if (lock_ == NULL) { 45 return histogram; 46 } 47 48 HistogramBase* histogram_to_delete = NULL; 49 HistogramBase* histogram_to_return = NULL; 50 { 51 base::AutoLock auto_lock(*lock_); 52 if (histograms_ == NULL) { 53 histogram_to_return = histogram; 54 } else { 55 const std::string& name = histogram->histogram_name(); 56 uint64_t name_hash = histogram->name_hash(); 57 HistogramMap::iterator it = histograms_->find(name_hash); 58 if (histograms_->end() == it) { 59 (*histograms_)[name_hash] = histogram; 60 // If there are callbacks for this histogram, we set the kCallbackExists 61 // flag. 62 auto callback_iterator = callbacks_->find(name); 63 if (callback_iterator != callbacks_->end()) { 64 if (!callback_iterator->second.is_null()) 65 histogram->SetFlags(HistogramBase::kCallbackExists); 66 else 67 histogram->ClearFlags(HistogramBase::kCallbackExists); 68 } 69 histogram_to_return = histogram; 70 } else if (histogram == it->second) { 71 // The histogram was registered before. 72 histogram_to_return = histogram; 73 } else { 74 // We already have one histogram with this name. 75 DCHECK_EQ(histogram->histogram_name(), 76 it->second->histogram_name()) << "hash collision"; 77 histogram_to_return = it->second; 78 histogram_to_delete = histogram; 79 } 80 } 81 } 82 delete histogram_to_delete; 83 return histogram_to_return; 84 } 85 86 // static 87 const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( 88 const BucketRanges* ranges) { 89 DCHECK(ranges->HasValidChecksum()); 90 scoped_ptr<const BucketRanges> ranges_deleter; 91 92 if (lock_ == NULL) { 93 return ranges; 94 } 95 96 base::AutoLock auto_lock(*lock_); 97 if (ranges_ == NULL) { 98 return ranges; 99 } 100 101 std::list<const BucketRanges*>* checksum_matching_list; 102 RangesMap::iterator ranges_it = ranges_->find(ranges->checksum()); 103 if (ranges_->end() == ranges_it) { 104 // Add a new matching list to map. 105 checksum_matching_list = new std::list<const BucketRanges*>(); 106 (*ranges_)[ranges->checksum()] = checksum_matching_list; 107 } else { 108 checksum_matching_list = ranges_it->second; 109 } 110 111 for (const BucketRanges* existing_ranges : *checksum_matching_list) { 112 if (existing_ranges->Equals(ranges)) { 113 if (existing_ranges == ranges) { 114 return ranges; 115 } else { 116 ranges_deleter.reset(ranges); 117 return existing_ranges; 118 } 119 } 120 } 121 // We haven't found a BucketRanges which has the same ranges. Register the 122 // new BucketRanges. 123 checksum_matching_list->push_front(ranges); 124 return ranges; 125 } 126 127 // static 128 void StatisticsRecorder::WriteHTMLGraph(const std::string& query, 129 std::string* output) { 130 if (!IsActive()) 131 return; 132 133 Histograms snapshot; 134 GetSnapshot(query, &snapshot); 135 for (const HistogramBase* histogram : snapshot) { 136 histogram->WriteHTMLGraph(output); 137 output->append("<br><hr><br>"); 138 } 139 } 140 141 // static 142 void StatisticsRecorder::WriteGraph(const std::string& query, 143 std::string* output) { 144 if (!IsActive()) 145 return; 146 if (query.length()) 147 StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); 148 else 149 output->append("Collections of all histograms\n"); 150 151 Histograms snapshot; 152 GetSnapshot(query, &snapshot); 153 for (const HistogramBase* histogram : snapshot) { 154 histogram->WriteAscii(output); 155 output->append("\n"); 156 } 157 } 158 159 // static 160 std::string StatisticsRecorder::ToJSON(const std::string& query) { 161 if (!IsActive()) 162 return std::string(); 163 164 std::string output("{"); 165 if (!query.empty()) { 166 output += "\"query\":"; 167 EscapeJSONString(query, true, &output); 168 output += ","; 169 } 170 171 Histograms snapshot; 172 GetSnapshot(query, &snapshot); 173 output += "\"histograms\":["; 174 bool first_histogram = true; 175 for (const HistogramBase* histogram : snapshot) { 176 if (first_histogram) 177 first_histogram = false; 178 else 179 output += ","; 180 std::string json; 181 histogram->WriteJSON(&json); 182 output += json; 183 } 184 output += "]}"; 185 return output; 186 } 187 188 // static 189 void StatisticsRecorder::GetHistograms(Histograms* output) { 190 if (lock_ == NULL) 191 return; 192 base::AutoLock auto_lock(*lock_); 193 if (histograms_ == NULL) 194 return; 195 196 for (const auto& entry : *histograms_) { 197 DCHECK_EQ(entry.first, entry.second->name_hash()); 198 output->push_back(entry.second); 199 } 200 } 201 202 // static 203 void StatisticsRecorder::GetBucketRanges( 204 std::vector<const BucketRanges*>* output) { 205 if (lock_ == NULL) 206 return; 207 base::AutoLock auto_lock(*lock_); 208 if (ranges_ == NULL) 209 return; 210 211 for (const auto& entry : *ranges_) { 212 for (const auto& range_entry : *entry.second) { 213 output->push_back(range_entry); 214 } 215 } 216 } 217 218 // static 219 HistogramBase* StatisticsRecorder::FindHistogram(const std::string& name) { 220 if (lock_ == NULL) 221 return NULL; 222 base::AutoLock auto_lock(*lock_); 223 if (histograms_ == NULL) 224 return NULL; 225 226 HistogramMap::iterator it = histograms_->find(HashMetricName(name)); 227 if (histograms_->end() == it) 228 return NULL; 229 DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; 230 return it->second; 231 } 232 233 // static 234 bool StatisticsRecorder::SetCallback( 235 const std::string& name, 236 const StatisticsRecorder::OnSampleCallback& cb) { 237 DCHECK(!cb.is_null()); 238 if (lock_ == NULL) 239 return false; 240 base::AutoLock auto_lock(*lock_); 241 if (histograms_ == NULL) 242 return false; 243 244 if (ContainsKey(*callbacks_, name)) 245 return false; 246 callbacks_->insert(std::make_pair(name, cb)); 247 248 HistogramMap::iterator it = histograms_->find(HashMetricName(name)); 249 if (it != histograms_->end()) { 250 DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; 251 it->second->SetFlags(HistogramBase::kCallbackExists); 252 } 253 254 return true; 255 } 256 257 // static 258 void StatisticsRecorder::ClearCallback(const std::string& name) { 259 if (lock_ == NULL) 260 return; 261 base::AutoLock auto_lock(*lock_); 262 if (histograms_ == NULL) 263 return; 264 265 callbacks_->erase(name); 266 267 // We also clear the flag from the histogram (if it exists). 268 HistogramMap::iterator it = histograms_->find(HashMetricName(name)); 269 if (it != histograms_->end()) { 270 DCHECK_EQ(name, it->second->histogram_name()) << "hash collision"; 271 it->second->ClearFlags(HistogramBase::kCallbackExists); 272 } 273 } 274 275 // static 276 StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( 277 const std::string& name) { 278 if (lock_ == NULL) 279 return OnSampleCallback(); 280 base::AutoLock auto_lock(*lock_); 281 if (histograms_ == NULL) 282 return OnSampleCallback(); 283 284 auto callback_iterator = callbacks_->find(name); 285 return callback_iterator != callbacks_->end() ? callback_iterator->second 286 : OnSampleCallback(); 287 } 288 289 // private static 290 void StatisticsRecorder::GetSnapshot(const std::string& query, 291 Histograms* snapshot) { 292 if (lock_ == NULL) 293 return; 294 base::AutoLock auto_lock(*lock_); 295 if (histograms_ == NULL) 296 return; 297 298 for (const auto& entry : *histograms_) { 299 if (entry.second->histogram_name().find(query) != std::string::npos) 300 snapshot->push_back(entry.second); 301 } 302 } 303 304 // This singleton instance should be started during the single threaded portion 305 // of main(), and hence it is not thread safe. It initializes globals to 306 // provide support for all future calls. 307 StatisticsRecorder::StatisticsRecorder() { 308 DCHECK(!histograms_); 309 if (lock_ == NULL) { 310 // This will leak on purpose. It's the only way to make sure we won't race 311 // against the static uninitialization of the module while one of our 312 // static methods relying on the lock get called at an inappropriate time 313 // during the termination phase. Since it's a static data member, we will 314 // leak one per process, which would be similar to the instance allocated 315 // during static initialization and released only on process termination. 316 lock_ = new base::Lock; 317 } 318 base::AutoLock auto_lock(*lock_); 319 histograms_ = new HistogramMap; 320 callbacks_ = new CallbackMap; 321 ranges_ = new RangesMap; 322 323 if (VLOG_IS_ON(1)) 324 AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this); 325 } 326 327 // static 328 void StatisticsRecorder::DumpHistogramsToVlog(void* /* instance */) { 329 std::string output; 330 StatisticsRecorder::WriteGraph(std::string(), &output); 331 VLOG(1) << output; 332 } 333 334 StatisticsRecorder::~StatisticsRecorder() { 335 DCHECK(histograms_ && ranges_ && lock_); 336 337 // Clean up. 338 scoped_ptr<HistogramMap> histograms_deleter; 339 scoped_ptr<CallbackMap> callbacks_deleter; 340 scoped_ptr<RangesMap> ranges_deleter; 341 // We don't delete lock_ on purpose to avoid having to properly protect 342 // against it going away after we checked for NULL in the static methods. 343 { 344 base::AutoLock auto_lock(*lock_); 345 histograms_deleter.reset(histograms_); 346 callbacks_deleter.reset(callbacks_); 347 ranges_deleter.reset(ranges_); 348 histograms_ = NULL; 349 callbacks_ = NULL; 350 ranges_ = NULL; 351 } 352 // We are going to leak the histograms and the ranges. 353 } 354 355 356 // static 357 StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; 358 // static 359 StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL; 360 // static 361 StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL; 362 // static 363 base::Lock* StatisticsRecorder::lock_ = NULL; 364 365 } // namespace base 366