OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/persistent_histogram_allocator.h" | 5 #include "base/metrics/persistent_histogram_allocator.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 | 8 |
9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
(...skipping 17 matching lines...) Expand all Loading... | |
28 | 28 |
29 // Name of histogram for storing results of local operations. | 29 // Name of histogram for storing results of local operations. |
30 const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result"; | 30 const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result"; |
31 | 31 |
32 // Type identifiers used when storing in persistent memory so they can be | 32 // Type identifiers used when storing in persistent memory so they can be |
33 // identified during extraction; the first 4 bytes of the SHA1 of the name | 33 // identified during extraction; the first 4 bytes of the SHA1 of the name |
34 // is used as a unique integer. A "version number" is added to the base | 34 // is used as a unique integer. A "version number" is added to the base |
35 // so that, if the structure of that object changes, stored older versions | 35 // so that, if the structure of that object changes, stored older versions |
36 // will be safely ignored. | 36 // will be safely ignored. |
37 enum : uint32_t { | 37 enum : uint32_t { |
38 kTypeIdHistogram = 0xF1645910 + 3, // SHA1(Histogram) v3 | |
39 kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1 | 38 kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1 |
40 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 | 39 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 |
41 | |
42 kTypeIdHistogramUnderConstruction = ~kTypeIdHistogram, | |
43 }; | 40 }; |
44 | 41 |
45 // The current globally-active persistent allocator for all new histograms. | 42 // The current globally-active persistent allocator for all new histograms. |
46 // The object held here will obviously not be destructed at process exit | 43 // The object held here will obviously not be destructed at process exit |
47 // but that's best since PersistentMemoryAllocator objects (that underlie | 44 // but that's best since PersistentMemoryAllocator objects (that underlie |
48 // GlobalHistogramAllocator objects) are explicitly forbidden from doing | 45 // GlobalHistogramAllocator objects) are explicitly forbidden from doing |
49 // anything essential at exit anyway due to the fact that they depend on data | 46 // anything essential at exit anyway due to the fact that they depend on data |
50 // managed elsewhere and which could be destructed first. | 47 // managed elsewhere and which could be destructed first. |
51 GlobalHistogramAllocator* g_allocator = nullptr; | 48 GlobalHistogramAllocator* g_allocator = nullptr; |
52 | 49 |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
219 PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew( | 216 PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew( |
220 HistogramBase::Sample value) { | 217 HistogramBase::Sample value) { |
221 return PersistentSampleMap::CreatePersistentRecord(data_manager_->allocator_, | 218 return PersistentSampleMap::CreatePersistentRecord(data_manager_->allocator_, |
222 sample_map_id_, value); | 219 sample_map_id_, value); |
223 } | 220 } |
224 | 221 |
225 | 222 |
226 // This data will be held in persistent memory in order for processes to | 223 // This data will be held in persistent memory in order for processes to |
227 // locate and use histograms created elsewhere. | 224 // locate and use histograms created elsewhere. |
228 struct PersistentHistogramAllocator::PersistentHistogramData { | 225 struct PersistentHistogramAllocator::PersistentHistogramData { |
226 // SHA1(Histogram): Increment this if structure changes! | |
227 static constexpr uint32_t kPersistentTypeId = 0xF1645910 + 3; | |
228 | |
229 // Expected size for 32/64-bit check. | 229 // Expected size for 32/64-bit check. |
230 static constexpr size_t kExpectedInstanceSize = | 230 static constexpr size_t kExpectedInstanceSize = |
231 40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize; | 231 40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize; |
232 | 232 |
233 int32_t histogram_type; | 233 int32_t histogram_type; |
234 int32_t flags; | 234 int32_t flags; |
235 int32_t minimum; | 235 int32_t minimum; |
236 int32_t maximum; | 236 int32_t maximum; |
237 uint32_t bucket_count; | 237 uint32_t bucket_count; |
238 PersistentMemoryAllocator::Reference ranges_ref; | 238 PersistentMemoryAllocator::Reference ranges_ref; |
239 uint32_t ranges_checksum; | 239 uint32_t ranges_checksum; |
240 PersistentMemoryAllocator::Reference counts_ref; | 240 PersistentMemoryAllocator::Reference counts_ref; |
241 HistogramSamples::Metadata samples_metadata; | 241 HistogramSamples::Metadata samples_metadata; |
242 HistogramSamples::Metadata logged_metadata; | 242 HistogramSamples::Metadata logged_metadata; |
243 | 243 |
244 // Space for the histogram name will be added during the actual allocation | 244 // Space for the histogram name will be added during the actual allocation |
245 // request. This must be the last field of the structure. A zero-size array | 245 // request. This must be the last field of the structure. A zero-size array |
246 // or a "flexible" array would be preferred but is not (yet) valid C++. | 246 // or a "flexible" array would be preferred but is not (yet) valid C++. |
247 char name[sizeof(uint64_t)]; // Force 64-bit alignment on 32-bit builds. | 247 char name[sizeof(uint64_t)]; // Force 64-bit alignment on 32-bit builds. |
248 }; | 248 }; |
249 | 249 |
250 PersistentHistogramAllocator::Iterator::Iterator( | 250 PersistentHistogramAllocator::Iterator::Iterator( |
251 PersistentHistogramAllocator* allocator) | 251 PersistentHistogramAllocator* allocator) |
252 : allocator_(allocator), memory_iter_(allocator->memory_allocator()) {} | 252 : allocator_(allocator), memory_iter_(allocator->memory_allocator()) {} |
253 | 253 |
254 std::unique_ptr<HistogramBase> | 254 std::unique_ptr<HistogramBase> |
255 PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) { | 255 PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) { |
256 PersistentMemoryAllocator::Reference ref; | 256 PersistentMemoryAllocator::Reference ref; |
257 while ((ref = memory_iter_.GetNextOfType(kTypeIdHistogram)) != 0) { | 257 while ((ref = memory_iter_.GetNextOfType<PersistentHistogramData>()) != 0) { |
258 if (ref != ignore) | 258 if (ref != ignore) |
259 return allocator_->GetHistogram(ref); | 259 return allocator_->GetHistogram(ref); |
260 } | 260 } |
261 return nullptr; | 261 return nullptr; |
262 } | 262 } |
263 | 263 |
264 | 264 |
265 PersistentHistogramAllocator::PersistentHistogramAllocator( | 265 PersistentHistogramAllocator::PersistentHistogramAllocator( |
266 std::unique_ptr<PersistentMemoryAllocator> memory) | 266 std::unique_ptr<PersistentMemoryAllocator> memory) |
267 : memory_allocator_(std::move(memory)), | 267 : memory_allocator_(std::move(memory)), |
268 sparse_histogram_data_manager_(memory_allocator_.get()) {} | 268 sparse_histogram_data_manager_(memory_allocator_.get()) {} |
269 | 269 |
270 PersistentHistogramAllocator::~PersistentHistogramAllocator() {} | 270 PersistentHistogramAllocator::~PersistentHistogramAllocator() {} |
271 | 271 |
272 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::GetHistogram( | 272 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::GetHistogram( |
273 Reference ref) { | 273 Reference ref) { |
274 // Unfortunately, the histogram "pickle" methods cannot be used as part of | 274 // Unfortunately, the histogram "pickle" methods cannot be used as part of |
275 // the persistance because the deserialization methods always create local | 275 // the persistance because the deserialization methods always create local |
276 // count data (while these must reference the persistent counts) and always | 276 // count data (while these must reference the persistent counts) and always |
277 // add it to the local list of known histograms (while these may be simple | 277 // add it to the local list of known histograms (while these may be simple |
278 // references to histograms in other processes). | 278 // references to histograms in other processes). |
279 PersistentHistogramData* histogram_data = | 279 PersistentHistogramData* histogram_data = |
280 memory_allocator_->GetAsObject<PersistentHistogramData>( | 280 memory_allocator_->GetAsObject<PersistentHistogramData>(ref); |
281 ref, kTypeIdHistogram); | |
282 size_t length = memory_allocator_->GetAllocSize(ref); | 281 size_t length = memory_allocator_->GetAllocSize(ref); |
283 | 282 |
284 // Check that metadata is reasonable: name is NUL terminated and non-empty, | 283 // Check that metadata is reasonable: name is NUL terminated and non-empty, |
285 // ID fields have been loaded with a hash of the name (0 is considered | 284 // ID fields have been loaded with a hash of the name (0 is considered |
286 // unset/invalid). | 285 // unset/invalid). |
287 if (!histogram_data || | 286 if (!histogram_data || |
288 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0' || | 287 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0' || |
289 histogram_data->name[0] == '\0' || | 288 histogram_data->name[0] == '\0' || |
290 histogram_data->samples_metadata.id == 0 || | 289 histogram_data->samples_metadata.id == 0 || |
291 histogram_data->logged_metadata.id == 0) { | 290 histogram_data->logged_metadata.id == 0) { |
(...skipping 20 matching lines...) Expand all Loading... | |
312 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); | 311 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); |
313 return nullptr; | 312 return nullptr; |
314 } | 313 } |
315 | 314 |
316 // Create the metadata necessary for a persistent sparse histogram. This | 315 // Create the metadata necessary for a persistent sparse histogram. This |
317 // is done first because it is a small subset of what is required for | 316 // is done first because it is a small subset of what is required for |
318 // other histograms. The type is "under construction" so that a crash | 317 // other histograms. The type is "under construction" so that a crash |
319 // during the datafill doesn't leave a bad record around that could cause | 318 // during the datafill doesn't leave a bad record around that could cause |
320 // confusion by another process trying to read it. It will be corrected | 319 // confusion by another process trying to read it. It will be corrected |
321 // once histogram construction is complete. | 320 // once histogram construction is complete. |
322 PersistentMemoryAllocator::Reference histogram_ref = | |
323 memory_allocator_->Allocate( | |
324 offsetof(PersistentHistogramData, name) + name.length() + 1, | |
325 kTypeIdHistogramUnderConstruction); | |
326 PersistentHistogramData* histogram_data = | 321 PersistentHistogramData* histogram_data = |
327 memory_allocator_->GetAsObject<PersistentHistogramData>( | 322 memory_allocator_->AllocateObject<PersistentHistogramData>( |
328 histogram_ref, kTypeIdHistogramUnderConstruction); | 323 offsetof(PersistentHistogramData, name) + name.length() + 1); |
329 if (histogram_data) { | 324 if (histogram_data) { |
330 memcpy(histogram_data->name, name.c_str(), name.size() + 1); | 325 memcpy(histogram_data->name, name.c_str(), name.size() + 1); |
331 histogram_data->histogram_type = histogram_type; | 326 histogram_data->histogram_type = histogram_type; |
332 histogram_data->flags = flags | HistogramBase::kIsPersistent; | 327 histogram_data->flags = flags | HistogramBase::kIsPersistent; |
333 } | 328 } |
334 | 329 |
335 // Create the remaining metadata necessary for regular histograms. | 330 // Create the remaining metadata necessary for regular histograms. |
336 if (histogram_type != SPARSE_HISTOGRAM) { | 331 if (histogram_type != SPARSE_HISTOGRAM) { |
337 size_t bucket_count = bucket_ranges->bucket_count(); | 332 size_t bucket_count = bucket_ranges->bucket_count(); |
338 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); | 333 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
377 if (histogram_data) { | 372 if (histogram_data) { |
378 // Create the histogram using resources in persistent memory. This ends up | 373 // Create the histogram using resources in persistent memory. This ends up |
379 // resolving the "ref" values stored in histogram_data instad of just | 374 // resolving the "ref" values stored in histogram_data instad of just |
380 // using what is already known above but avoids duplicating the switch | 375 // using what is already known above but avoids duplicating the switch |
381 // statement here and serves as a double-check that everything is | 376 // statement here and serves as a double-check that everything is |
382 // correct before commiting the new histogram to persistent space. | 377 // correct before commiting the new histogram to persistent space. |
383 std::unique_ptr<HistogramBase> histogram = CreateHistogram(histogram_data); | 378 std::unique_ptr<HistogramBase> histogram = CreateHistogram(histogram_data); |
384 DCHECK(histogram); | 379 DCHECK(histogram); |
385 DCHECK_NE(0U, histogram_data->samples_metadata.id); | 380 DCHECK_NE(0U, histogram_data->samples_metadata.id); |
386 DCHECK_NE(0U, histogram_data->logged_metadata.id); | 381 DCHECK_NE(0U, histogram_data->logged_metadata.id); |
387 memory_allocator_->ChangeType(histogram_ref, kTypeIdHistogram, | |
388 kTypeIdHistogramUnderConstruction); | |
389 | 382 |
383 PersistentMemoryAllocator::Reference histogram_ref = | |
384 memory_allocator_->GetAsReference(histogram_data); | |
390 if (ref_ptr != nullptr) | 385 if (ref_ptr != nullptr) |
391 *ref_ptr = histogram_ref; | 386 *ref_ptr = histogram_ref; |
392 | 387 |
393 // By storing the reference within the allocator to this histogram, the | 388 // By storing the reference within the allocator to this histogram, the |
394 // next import (which will happen before the next histogram creation) | 389 // next import (which will happen before the next histogram creation) |
395 // will know to skip it. | 390 // will know to skip it. |
396 // See also the comment in ImportHistogramsToStatisticsRecorder(). | 391 // See also the comment in ImportHistogramsToStatisticsRecorder(). |
397 subtle::NoBarrier_Store(&last_created_, histogram_ref); | 392 subtle::NoBarrier_Store(&last_created_, histogram_ref); |
398 return histogram; | 393 return histogram; |
399 } | 394 } |
400 | 395 |
401 CreateHistogramResultType result; | 396 CreateHistogramResultType result; |
402 if (memory_allocator_->IsCorrupt()) { | 397 if (memory_allocator_->IsCorrupt()) { |
403 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT); | 398 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT); |
404 result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT; | 399 result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT; |
405 } else if (memory_allocator_->IsFull()) { | 400 } else if (memory_allocator_->IsFull()) { |
406 result = CREATE_HISTOGRAM_ALLOCATOR_FULL; | 401 result = CREATE_HISTOGRAM_ALLOCATOR_FULL; |
407 } else { | 402 } else { |
408 result = CREATE_HISTOGRAM_ALLOCATOR_ERROR; | 403 result = CREATE_HISTOGRAM_ALLOCATOR_ERROR; |
409 } | 404 } |
410 RecordCreateHistogramResult(result); | 405 RecordCreateHistogramResult(result); |
411 NOTREACHED() << "error=" << result; | 406 NOTREACHED() << "error=" << result; |
412 | 407 |
413 return nullptr; | 408 return nullptr; |
414 } | 409 } |
415 | 410 |
416 void PersistentHistogramAllocator::FinalizeHistogram(Reference ref, | 411 void PersistentHistogramAllocator::FinalizeHistogram(Reference ref, |
417 bool registered) { | 412 bool registered) { |
418 // If the created persistent histogram was registered then it needs to | 413 if (registered) { |
419 // be marked as "iterable" in order to be found by other processes. | 414 // If the created persistent histogram was registered then it needs to |
420 if (registered) | 415 // be marked as "iterable" in order to be found by other processes. This |
416 // happens only after the histogram is fully formed so it's impossible for | |
417 // code iterating through the allocator to read a partially created record. | |
421 memory_allocator_->MakeIterable(ref); | 418 memory_allocator_->MakeIterable(ref); |
422 // If it wasn't registered then a race condition must have caused | 419 } else { |
423 // two to be created. The allocator does not support releasing the | 420 // If it wasn't registered then a race condition must have caused two to |
424 // acquired memory so just change the type to be empty. | 421 // be created. The allocator does not support releasing the acquired memory |
425 else | 422 // so just change the type to be empty. |
426 memory_allocator_->ChangeType(ref, 0, kTypeIdHistogram); | 423 memory_allocator_->ChangeType(ref, 0, |
424 PersistentHistogramData::kPersistentTypeId); | |
425 } | |
427 } | 426 } |
428 | 427 |
429 void PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder( | 428 void PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder( |
430 HistogramBase* histogram) { | 429 HistogramBase* histogram) { |
431 DCHECK(histogram); | 430 DCHECK(histogram); |
432 | 431 |
433 HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram); | 432 HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram); |
434 if (!existing) { | 433 if (!existing) { |
435 // The above should never fail but if it does, no real harm is done. | 434 // The above should never fail but if it does, no real harm is done. |
436 // The data won't be merged but it also won't be recorded as merged | 435 // The data won't be merged but it also won't be recorded as merged |
(...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
835 GlobalHistogramAllocator* histogram_allocator = g_allocator; | 834 GlobalHistogramAllocator* histogram_allocator = g_allocator; |
836 if (!histogram_allocator) | 835 if (!histogram_allocator) |
837 return nullptr; | 836 return nullptr; |
838 PersistentMemoryAllocator* memory_allocator = | 837 PersistentMemoryAllocator* memory_allocator = |
839 histogram_allocator->memory_allocator(); | 838 histogram_allocator->memory_allocator(); |
840 | 839 |
841 // Before releasing the memory, it's necessary to have the Statistics- | 840 // Before releasing the memory, it's necessary to have the Statistics- |
842 // Recorder forget about the histograms contained therein; otherwise, | 841 // Recorder forget about the histograms contained therein; otherwise, |
843 // some operations will try to access them and the released memory. | 842 // some operations will try to access them and the released memory. |
844 PersistentMemoryAllocator::Iterator iter(memory_allocator); | 843 PersistentMemoryAllocator::Iterator iter(memory_allocator); |
845 PersistentMemoryAllocator::Reference ref; | 844 const PersistentHistogramData* data; |
846 while ((ref = iter.GetNextOfType(kTypeIdHistogram)) != 0) { | 845 while ((data = iter.GetNextOfObject<PersistentHistogramData>()) != nullptr) { |
847 PersistentHistogramData* histogram_data = | 846 DCHECK(data); |
Alexei Svitkine (slow)
2017/01/10 18:01:04
Remove this since the outer while guarantees this.
bcwhite
2017/01/10 18:30:18
Done.
| |
848 memory_allocator->GetAsObject<PersistentHistogramData>( | 847 StatisticsRecorder::ForgetHistogramForTesting(data->name); |
849 ref, kTypeIdHistogram); | |
850 DCHECK(histogram_data); | |
851 StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name); | |
852 | 848 |
853 // If a test breaks here then a memory region containing a histogram | 849 // If a test breaks here then a memory region containing a histogram |
854 // actively used by this code is being released back to the test. | 850 // actively used by this code is being released back to the test. |
855 // If that memory segment were to be deleted, future calls to create | 851 // If that memory segment were to be deleted, future calls to create |
856 // persistent histograms would crash. To avoid this, have the test call | 852 // persistent histograms would crash. To avoid this, have the test call |
857 // the method GetCreateHistogramResultHistogram() *before* setting | 853 // the method GetCreateHistogramResultHistogram() *before* setting |
858 // the (temporary) memory allocator via SetGlobalAllocator() so that | 854 // the (temporary) memory allocator via SetGlobalAllocator() so that |
859 // histogram is instead allocated from the process heap. | 855 // histogram is instead allocated from the process heap. |
860 DCHECK_NE(kResultHistogram, histogram_data->name); | 856 DCHECK_NE(kResultHistogram, data->name); |
861 } | 857 } |
862 | 858 |
863 g_allocator = nullptr; | 859 g_allocator = nullptr; |
864 return WrapUnique(histogram_allocator); | 860 return WrapUnique(histogram_allocator); |
865 }; | 861 }; |
866 | 862 |
867 void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) { | 863 void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) { |
868 persistent_location_ = location; | 864 persistent_location_ = location; |
869 } | 865 } |
870 | 866 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
937 while (true) { | 933 while (true) { |
938 std::unique_ptr<HistogramBase> histogram = | 934 std::unique_ptr<HistogramBase> histogram = |
939 import_iterator_.GetNextWithIgnore(record_to_ignore); | 935 import_iterator_.GetNextWithIgnore(record_to_ignore); |
940 if (!histogram) | 936 if (!histogram) |
941 break; | 937 break; |
942 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); | 938 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); |
943 } | 939 } |
944 } | 940 } |
945 | 941 |
946 } // namespace base | 942 } // namespace base |
OLD | NEW |