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 #include "base/metrics/histogram_base.h" | 5 #include "base/metrics/histogram_base.h" |
6 | 6 |
7 #include <climits> | 7 #include <climits> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/json/json_string_value_serializer.h" | 10 #include "base/json/json_string_value_serializer.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
14 #include "base/metrics/histogram_samples.h" | 14 #include "base/metrics/histogram_samples.h" |
15 #include "base/metrics/sparse_histogram.h" | 15 #include "base/metrics/sparse_histogram.h" |
16 #include "base/metrics/statistics_recorder.h" | 16 #include "base/metrics/statistics_recorder.h" |
17 #include "base/pickle.h" | 17 #include "base/pickle.h" |
18 #include "base/process/process_handle.h" | 18 #include "base/process/process_handle.h" |
19 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 20 #include "base/synchronization/lock.h" |
20 #include "base/values.h" | 21 #include "base/values.h" |
21 | 22 |
22 namespace base { | 23 namespace base { |
23 | 24 |
| 25 // Type identifiers used when storing in persistent memory so they can be |
| 26 // identified during extraction. A "version number" is added to the base |
| 27 // so that, if the structure of that object changes, stored older versions |
| 28 // will be safely ignored. |
| 29 enum : uint32_t { |
| 30 kTypeIdHistogram = 0xF1645910 + 1, // SHA1(Histogram) v1 |
| 31 kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1 |
| 32 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 |
| 33 kTypeIdNameString = 0x117DF971 + 1, // SHA1(NameString) v1 |
| 34 }; |
| 35 |
| 36 // This data must be held in persistent memory in order for processes to |
| 37 // locate and use histograms created elsewhere. |
| 38 struct HistogramBase::PersistentHistogramData { |
| 39 PersistentMemoryAllocator::Reference name_ref; |
| 40 size_t name_length; |
| 41 int histogram_type; |
| 42 int flags; |
| 43 int minimum; |
| 44 int maximum; |
| 45 size_t bucket_count; |
| 46 PersistentMemoryAllocator::Reference ranges_ref; |
| 47 uint32_t ranges_checksum; |
| 48 PersistentMemoryAllocator::Reference counts_ref; |
| 49 HistogramSamples::Metadata samples_metadata; |
| 50 }; |
| 51 |
24 std::string HistogramTypeToString(HistogramType type) { | 52 std::string HistogramTypeToString(HistogramType type) { |
25 switch (type) { | 53 switch (type) { |
26 case HISTOGRAM: | 54 case HISTOGRAM: |
27 return "HISTOGRAM"; | 55 return "HISTOGRAM"; |
28 case LINEAR_HISTOGRAM: | 56 case LINEAR_HISTOGRAM: |
29 return "LINEAR_HISTOGRAM"; | 57 return "LINEAR_HISTOGRAM"; |
30 case BOOLEAN_HISTOGRAM: | 58 case BOOLEAN_HISTOGRAM: |
31 return "BOOLEAN_HISTOGRAM"; | 59 return "BOOLEAN_HISTOGRAM"; |
32 case CUSTOM_HISTOGRAM: | 60 case CUSTOM_HISTOGRAM: |
33 return "CUSTOM_HISTOGRAM"; | 61 return "CUSTOM_HISTOGRAM"; |
(...skipping 20 matching lines...) Expand all Loading... |
54 case CUSTOM_HISTOGRAM: | 82 case CUSTOM_HISTOGRAM: |
55 return CustomHistogram::DeserializeInfoImpl(iter); | 83 return CustomHistogram::DeserializeInfoImpl(iter); |
56 case SPARSE_HISTOGRAM: | 84 case SPARSE_HISTOGRAM: |
57 return SparseHistogram::DeserializeInfoImpl(iter); | 85 return SparseHistogram::DeserializeInfoImpl(iter); |
58 default: | 86 default: |
59 return NULL; | 87 return NULL; |
60 } | 88 } |
61 } | 89 } |
62 | 90 |
63 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; | 91 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; |
| 92 scoped_ptr<PersistentMemoryAllocator> HistogramBase::allocator_; |
| 93 |
| 94 // Extract a histogram from persistent memory. Unfortunately, the above "pickle" |
| 95 // methods cannot be used as part of the persistance because the deserialization |
| 96 // methods always create local count data (these must referenced the persistent |
| 97 // counts) and always add it to the local list of known histograms (these may |
| 98 // be simple references to histograms in other processes). |
| 99 // static |
| 100 HistogramBase* HistogramBase::GetPersistentHistogram( |
| 101 PersistentMemoryAllocator* allocator, |
| 102 int32_t ref) { |
| 103 PersistentHistogramData* histogram_data = |
| 104 allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram); |
| 105 return CreatePersistentHistogram(allocator, histogram_data); |
| 106 } |
| 107 |
| 108 // static |
| 109 HistogramBase* HistogramBase::GetNextPersistentHistogram( |
| 110 PersistentMemoryAllocator* allocator, |
| 111 PersistentMemoryAllocator::Iterator* iter) { |
| 112 PersistentMemoryAllocator::Reference ref; |
| 113 uint32_t type_id; |
| 114 while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) { |
| 115 if (type_id == kTypeIdHistogram) |
| 116 return GetPersistentHistogram(allocator, ref); |
| 117 } |
| 118 return nullptr; |
| 119 } |
| 120 |
| 121 // static |
| 122 HistogramBase* HistogramBase::CreatePersistentHistogram( |
| 123 PersistentMemoryAllocator* allocator, |
| 124 PersistentHistogramData* histogram_data_ptr) { |
| 125 if (!histogram_data_ptr) { |
| 126 LOG(WARNING) << "Persistent histogram data was not valid; skipped."; |
| 127 NOTREACHED(); |
| 128 return nullptr; |
| 129 } |
| 130 |
| 131 // Copy the histogram_data to local storage because anything in persistent |
| 132 // memory cannot be trusted as it could be changed at any moment by a |
| 133 // malicious actor that shares access. The contents of histogram_data are |
| 134 // validated below; the local copy is to ensure that the contents cannot |
| 135 // be externally changed between validation and use. |
| 136 PersistentHistogramData histogram_data = *histogram_data_ptr; |
| 137 |
| 138 char* name_data = |
| 139 allocator->GetAsObject<char>(histogram_data.name_ref, kTypeIdNameString); |
| 140 size_t name_length = histogram_data.name_length; |
| 141 if (!name_data || |
| 142 name_length >= allocator->GetAllocSize(histogram_data.name_ref) || |
| 143 name_data[name_length] != 0) { |
| 144 LOG(WARNING) << "Persistent histogram referenced invalid name string" |
| 145 << "; skipped."; |
| 146 NOTREACHED(); |
| 147 return nullptr; |
| 148 } |
| 149 std::string name(name_data, name_length); |
| 150 |
| 151 HistogramBase::Sample* ranges_data = |
| 152 allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref, |
| 153 kTypeIdRangesArray); |
| 154 if (!ranges_data || histogram_data.bucket_count < 2 || |
| 155 histogram_data.bucket_count + 1 > |
| 156 std::numeric_limits<size_t>::max() / sizeof(HistogramBase::Sample) || |
| 157 allocator->GetAllocSize(histogram_data.ranges_ref) < |
| 158 (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) { |
| 159 LOG(WARNING) << "Persistent histogram referenced invalid ranges array" |
| 160 << "; skipped."; |
| 161 NOTREACHED(); |
| 162 return nullptr; |
| 163 } |
| 164 // To avoid racy destruction at shutdown, the following will be leaked. |
| 165 BucketRanges* ranges = new BucketRanges(histogram_data.bucket_count + 1); |
| 166 bool bad_ranges = false; |
| 167 for (size_t i = 0; i < ranges->size(); ++i) { |
| 168 if (i > 0 && ranges_data[i] <= ranges_data[i - 1]) |
| 169 bad_ranges = true; |
| 170 ranges->set_range(i, ranges_data[i]); |
| 171 } |
| 172 ranges->ResetChecksum(); |
| 173 if (bad_ranges || ranges->checksum() != histogram_data.ranges_checksum) { |
| 174 LOG(WARNING) << "Persistent histogram referenced invalid ranges array" |
| 175 << "; skipped."; |
| 176 NOTREACHED(); |
| 177 return nullptr; |
| 178 } |
| 179 const BucketRanges* registered_ranges = |
| 180 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); |
| 181 |
| 182 HistogramBase::AtomicCount* counts_data = |
| 183 allocator->GetAsObject<HistogramBase::AtomicCount>( |
| 184 histogram_data.counts_ref, kTypeIdCountsArray); |
| 185 if (!counts_data || |
| 186 allocator->GetAllocSize(histogram_data.counts_ref) < |
| 187 histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) { |
| 188 LOG(WARNING) << "Persistent histogram referenced invalid counts array" |
| 189 << "; skipped."; |
| 190 NOTREACHED(); |
| 191 return nullptr; |
| 192 } |
| 193 |
| 194 HistogramBase* histogram = nullptr; |
| 195 switch (histogram_data.histogram_type) { |
| 196 case HISTOGRAM: |
| 197 histogram = new Histogram( |
| 198 name, |
| 199 histogram_data.minimum, |
| 200 histogram_data.maximum, |
| 201 registered_ranges, |
| 202 counts_data, |
| 203 histogram_data.bucket_count, |
| 204 &histogram_data_ptr->samples_metadata); |
| 205 break; |
| 206 case LINEAR_HISTOGRAM: |
| 207 histogram = new LinearHistogram( |
| 208 name, |
| 209 histogram_data.minimum, |
| 210 histogram_data.maximum, |
| 211 registered_ranges, |
| 212 counts_data, |
| 213 histogram_data.bucket_count, |
| 214 &histogram_data_ptr->samples_metadata); |
| 215 break; |
| 216 case BOOLEAN_HISTOGRAM: |
| 217 histogram = new BooleanHistogram( |
| 218 name, |
| 219 registered_ranges, |
| 220 counts_data, |
| 221 &histogram_data_ptr->samples_metadata); |
| 222 break; |
| 223 case CUSTOM_HISTOGRAM: |
| 224 histogram = new CustomHistogram( |
| 225 name, |
| 226 registered_ranges, |
| 227 counts_data, |
| 228 histogram_data.bucket_count, |
| 229 &histogram_data_ptr->samples_metadata); |
| 230 break; |
| 231 } |
| 232 |
| 233 if (histogram) { |
| 234 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); |
| 235 histogram->SetFlags(histogram_data.flags); |
| 236 } |
| 237 |
| 238 return histogram; |
| 239 } |
| 240 |
| 241 // static |
| 242 HistogramBase* HistogramBase::AllocatePersistentHistogram( |
| 243 PersistentMemoryAllocator* allocator, |
| 244 HistogramType histogram_type, |
| 245 const std::string& name, |
| 246 int minimum, |
| 247 int maximum, |
| 248 const BucketRanges* bucket_ranges, |
| 249 int32 flags, |
| 250 PersistentMemoryAllocator::Reference* ref_ptr) { |
| 251 if (allocator) { |
| 252 size_t bucket_count = bucket_ranges->bucket_count(); |
| 253 CHECK(bucket_count <= std::numeric_limits<int32_t>::max() / |
| 254 sizeof(HistogramBase::AtomicCount)); |
| 255 size_t counts_memory = bucket_count * sizeof(HistogramBase::AtomicCount); |
| 256 size_t ranges_memory = (bucket_count + 1) * sizeof(HistogramBase::Sample); |
| 257 PersistentMemoryAllocator::Reference name_ref = |
| 258 allocator->Allocate(name.size() + 1, kTypeIdNameString); |
| 259 PersistentMemoryAllocator::Reference ranges_ref = |
| 260 allocator->Allocate(ranges_memory, kTypeIdRangesArray); |
| 261 PersistentMemoryAllocator::Reference counts_ref = |
| 262 allocator->Allocate(counts_memory, kTypeIdCountsArray); |
| 263 PersistentMemoryAllocator::Reference histogram_ref = |
| 264 allocator->Allocate(sizeof(PersistentHistogramData), kTypeIdHistogram); |
| 265 char* name_data = allocator->GetAsObject<char>(name_ref, kTypeIdNameString); |
| 266 HistogramBase::Sample* ranges_data = |
| 267 allocator->GetAsObject<HistogramBase::Sample>(ranges_ref, |
| 268 kTypeIdRangesArray); |
| 269 PersistentHistogramData* histogram_data = |
| 270 allocator->GetAsObject<PersistentHistogramData>(histogram_ref, |
| 271 kTypeIdHistogram); |
| 272 |
| 273 // Only continue here if all allocations were successful. |
| 274 if (counts_ref && name_data && ranges_data && histogram_data) { |
| 275 strcpy(name_data, name.c_str()); |
| 276 for (size_t i = 0; i < bucket_ranges->size(); ++i) |
| 277 ranges_data[i] = bucket_ranges->range(i); |
| 278 |
| 279 histogram_data->name_ref = name_ref; |
| 280 histogram_data->name_length = name.size(); |
| 281 histogram_data->histogram_type = histogram_type; |
| 282 histogram_data->flags = flags; |
| 283 histogram_data->minimum = minimum; |
| 284 histogram_data->maximum = maximum; |
| 285 histogram_data->bucket_count = bucket_count; |
| 286 histogram_data->ranges_ref = ranges_ref; |
| 287 histogram_data->ranges_checksum = bucket_ranges->checksum(); |
| 288 histogram_data->counts_ref = counts_ref; |
| 289 |
| 290 // Create the histogram using resources in persistent memory. This ends up |
| 291 // resolving the "ref" values stored in histogram_data instad of just |
| 292 // using what is already known above but avoids duplicating the switch |
| 293 // statement here and serves as a double-check that everything is |
| 294 // correct before commiting the new histogram to persistent space. |
| 295 HistogramBase* histogram = |
| 296 CreatePersistentHistogram(allocator, histogram_data); |
| 297 DCHECK(histogram); |
| 298 if (ref_ptr != nullptr) |
| 299 *ref_ptr = histogram_ref; |
| 300 return histogram; |
| 301 } |
| 302 |
| 303 LOG(WARNING) << "Could not create histogram \"" << name |
| 304 << "\" in persistent memory (full=" << allocator->IsFull() |
| 305 << ", corrupt=" << allocator->IsCorrupt() << ")"; |
| 306 } |
| 307 |
| 308 return nullptr; |
| 309 } |
| 310 |
| 311 // static |
| 312 void HistogramBase::ImportPersistentHistograms() { |
| 313 // Each call resumes from where it last left off so need persistant iterator. |
| 314 // The lock protects against concurrent access to the iterator. |
| 315 static PersistentMemoryAllocator::Iterator iter; |
| 316 static base::Lock lock; |
| 317 |
| 318 if (allocator_) { |
| 319 base::AutoLock auto_lock(lock); |
| 320 if (iter.is_clear()) |
| 321 allocator_->CreateIterator(&iter); |
| 322 |
| 323 for (;;) { |
| 324 HistogramBase* h = GetNextPersistentHistogram(allocator_.get(), &iter); |
| 325 if (!h) |
| 326 break; |
| 327 StatisticsRecorder::RegisterOrDeleteDuplicate(h); |
| 328 } |
| 329 } |
| 330 } |
64 | 331 |
65 HistogramBase::HistogramBase(const std::string& name) | 332 HistogramBase::HistogramBase(const std::string& name) |
66 : histogram_name_(name), | 333 : histogram_name_(name), |
67 flags_(kNoFlags) {} | 334 flags_(kNoFlags) {} |
68 | 335 |
69 HistogramBase::~HistogramBase() {} | 336 HistogramBase::~HistogramBase() {} |
70 | 337 |
71 void HistogramBase::CheckName(const StringPiece& name) const { | 338 void HistogramBase::CheckName(const StringPiece& name) const { |
72 DCHECK_EQ(histogram_name(), name); | 339 DCHECK_EQ(histogram_name(), name); |
73 } | 340 } |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const { | 391 void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const { |
125 if ((flags() & kCallbackExists) == 0) | 392 if ((flags() & kCallbackExists) == 0) |
126 return; | 393 return; |
127 | 394 |
128 StatisticsRecorder::OnSampleCallback cb = | 395 StatisticsRecorder::OnSampleCallback cb = |
129 StatisticsRecorder::FindCallback(histogram_name()); | 396 StatisticsRecorder::FindCallback(histogram_name()); |
130 if (!cb.is_null()) | 397 if (!cb.is_null()) |
131 cb.Run(sample); | 398 cb.Run(sample); |
132 } | 399 } |
133 | 400 |
| 401 // static |
| 402 void HistogramBase::SetDefaultPersistentMemoryAllocator( |
| 403 PersistentMemoryAllocator* allocator) { |
| 404 // Releasing or changing an allocator is extremely dangerous because it |
| 405 // likely has histograms stored within it. If the backing memory is also |
| 406 // also released, future accesses to those histograms will seg-fault. |
| 407 // It's not a fatal CHECK() because tests do this knowing that all |
| 408 // such persistent histograms have already been forgotten. |
| 409 if (allocator_) { |
| 410 LOG(WARNING) << "Active PersistentMemoryAllocator has been released." |
| 411 << " Some existing histogram pointers may be invalid."; |
| 412 } |
| 413 allocator_.reset(allocator); |
| 414 } |
| 415 |
134 void HistogramBase::WriteAsciiBucketGraph(double current_size, | 416 void HistogramBase::WriteAsciiBucketGraph(double current_size, |
135 double max_size, | 417 double max_size, |
136 std::string* output) const { | 418 std::string* output) const { |
137 const int k_line_length = 72; // Maximal horizontal width of graph. | 419 const int k_line_length = 72; // Maximal horizontal width of graph. |
138 int x_count = static_cast<int>(k_line_length * (current_size / max_size) | 420 int x_count = static_cast<int>(k_line_length * (current_size / max_size) |
139 + 0.5); | 421 + 0.5); |
140 int x_remainder = k_line_length - x_count; | 422 int x_remainder = k_line_length - x_count; |
141 | 423 |
142 while (0 < x_count--) | 424 while (0 < x_count--) |
143 output->append("-"); | 425 output->append("-"); |
(...skipping 12 matching lines...) Expand all Loading... |
156 return result; | 438 return result; |
157 } | 439 } |
158 | 440 |
159 void HistogramBase::WriteAsciiBucketValue(Count current, | 441 void HistogramBase::WriteAsciiBucketValue(Count current, |
160 double scaled_sum, | 442 double scaled_sum, |
161 std::string* output) const { | 443 std::string* output) const { |
162 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); | 444 StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); |
163 } | 445 } |
164 | 446 |
165 } // namespace base | 447 } // namespace base |
OLD | NEW |