Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: base/metrics/persistent_histogram_allocator.cc

Issue 2578323002: Improved support for objects inside persistent memory. (Closed)
Patch Set: addressed review comments by asvitkine Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698