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 3 // found in the LICENSE file. 4 5 #include "base/metrics/persistent_histogram_allocator.h" 6 7 #include <memory> 8 9 #include "base/atomicops.h" 10 #include "base/files/file_path.h" 11 #include "base/files/file_util.h" 12 #include "base/files/important_file_writer.h" 13 #include "base/files/memory_mapped_file.h" 14 #include "base/lazy_instance.h" 15 #include "base/logging.h" 16 #include "base/memory/ptr_util.h" 17 #include "base/metrics/histogram.h" 18 #include "base/metrics/histogram_base.h" 19 #include "base/metrics/histogram_samples.h" 20 #include "base/metrics/persistent_sample_map.h" 21 #include "base/metrics/sparse_histogram.h" 22 #include "base/metrics/statistics_recorder.h" 23 #include "base/pickle.h" 24 #include "base/synchronization/lock.h" 25 26 namespace base { 27 28 namespace { 29 30 // Name of histogram for storing results of local operations. 31 const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result"; 32 33 // Type identifiers used when storing in persistent memory so they can be 34 // identified during extraction; the first 4 bytes of the SHA1 of the name 35 // is used as a unique integer. A "version number" is added to the base 36 // so that, if the structure of that object changes, stored older versions 37 // will be safely ignored. 38 enum : uint32_t { 39 kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1 40 kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 41 }; 42 43 // The current globally-active persistent allocator for all new histograms. 44 // The object held here will obviously not be destructed at process exit 45 // but that's best since PersistentMemoryAllocator objects (that underlie 46 // GlobalHistogramAllocator objects) are explicitly forbidden from doing 47 // anything essential at exit anyway due to the fact that they depend on data 48 // managed elsewhere and which could be destructed first. An AtomicWord is 49 // used instead of std::atomic because the latter can create global ctors 50 // and dtors. 51 subtle::AtomicWord g_allocator = 0; 52 53 // Take an array of range boundaries and create a proper BucketRanges object 54 // which is returned to the caller. A return of nullptr indicates that the 55 // passed boundaries are invalid. 56 std::unique_ptr<BucketRanges> CreateRangesFromData( 57 HistogramBase::Sample* ranges_data, 58 uint32_t ranges_checksum, 59 size_t count) { 60 // To avoid racy destruction at shutdown, the following may be leaked. 61 std::unique_ptr<BucketRanges> ranges(new BucketRanges(count)); 62 DCHECK_EQ(count, ranges->size()); 63 for (size_t i = 0; i < count; ++i) { 64 if (i > 0 && ranges_data[i] <= ranges_data[i - 1]) 65 return nullptr; 66 ranges->set_range(i, ranges_data[i]); 67 } 68 69 ranges->ResetChecksum(); 70 if (ranges->checksum() != ranges_checksum) 71 return nullptr; 72 73 return ranges; 74 } 75 76 // Calculate the number of bytes required to store all of a histogram's 77 // "counts". This will return zero (0) if |bucket_count| is not valid. 78 size_t CalculateRequiredCountsBytes(size_t bucket_count) { 79 // 2 because each "sample count" also requires a backup "logged count" 80 // used for calculating the delta during snapshot operations. 81 const size_t kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); 82 83 // If the |bucket_count| is such that it would overflow the return type, 84 // perhaps as the result of a malicious actor, then return zero to 85 // indicate the problem to the caller. 86 if (bucket_count > std::numeric_limits<size_t>::max() / kBytesPerBucket) 87 return 0; 88 89 return bucket_count * kBytesPerBucket; 90 } 91 92 } // namespace 93 94 const Feature kPersistentHistogramsFeature{ 95 "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT 96 }; 97 98 99 PersistentSparseHistogramDataManager::PersistentSparseHistogramDataManager( 100 PersistentMemoryAllocator* allocator) 101 : allocator_(allocator), record_iterator_(allocator) {} 102 103 PersistentSparseHistogramDataManager::~PersistentSparseHistogramDataManager() {} 104 105 PersistentSampleMapRecords* 106 PersistentSparseHistogramDataManager::UseSampleMapRecords(uint64_t id, 107 const void* user) { 108 base::AutoLock auto_lock(lock_); 109 return GetSampleMapRecordsWhileLocked(id)->Acquire(user); 110 } 111 112 PersistentSampleMapRecords* 113 PersistentSparseHistogramDataManager::GetSampleMapRecordsWhileLocked( 114 uint64_t id) { 115 lock_.AssertAcquired(); 116 117 auto found = sample_records_.find(id); 118 if (found != sample_records_.end()) 119 return found->second.get(); 120 121 std::unique_ptr<PersistentSampleMapRecords>& samples = sample_records_[id]; 122 samples = MakeUnique<PersistentSampleMapRecords>(this, id); 123 return samples.get(); 124 } 125 126 bool PersistentSparseHistogramDataManager::LoadRecords( 127 PersistentSampleMapRecords* sample_map_records) { 128 // DataManager must be locked in order to access the found_ field of any 129 // PersistentSampleMapRecords object. 130 base::AutoLock auto_lock(lock_); 131 bool found = false; 132 133 // If there are already "found" entries for the passed object, move them. 134 if (!sample_map_records->found_.empty()) { 135 sample_map_records->records_.reserve(sample_map_records->records_.size() + 136 sample_map_records->found_.size()); 137 sample_map_records->records_.insert(sample_map_records->records_.end(), 138 sample_map_records->found_.begin(), 139 sample_map_records->found_.end()); 140 sample_map_records->found_.clear(); 141 found = true; 142 } 143 144 // Acquiring a lock is a semi-expensive operation so load some records with 145 // each call. More than this number may be loaded if it takes longer to 146 // find at least one matching record for the passed object. 147 const int kMinimumNumberToLoad = 10; 148 const uint64_t match_id = sample_map_records->sample_map_id_; 149 150 // Loop while no enty is found OR we haven't yet loaded the minimum number. 151 // This will continue reading even after a match is found. 152 for (int count = 0; !found || count < kMinimumNumberToLoad; ++count) { 153 // Get the next sample-record. The iterator will always resume from where 154 // it left off even if it previously had nothing further to return. 155 uint64_t found_id; 156 PersistentMemoryAllocator::Reference ref = 157 PersistentSampleMap::GetNextPersistentRecord(record_iterator_, 158 &found_id); 159 160 // Stop immediately if there are none. 161 if (!ref) 162 break; 163 164 // The sample-record could be for any sparse histogram. Add the reference 165 // to the appropriate collection for later use. 166 if (found_id == match_id) { 167 sample_map_records->records_.push_back(ref); 168 found = true; 169 } else { 170 PersistentSampleMapRecords* samples = 171 GetSampleMapRecordsWhileLocked(found_id); 172 DCHECK(samples); 173 samples->found_.push_back(ref); 174 } 175 } 176 177 return found; 178 } 179 180 181 PersistentSampleMapRecords::PersistentSampleMapRecords( 182 PersistentSparseHistogramDataManager* data_manager, 183 uint64_t sample_map_id) 184 : data_manager_(data_manager), sample_map_id_(sample_map_id) {} 185 186 PersistentSampleMapRecords::~PersistentSampleMapRecords() {} 187 188 PersistentSampleMapRecords* PersistentSampleMapRecords::Acquire( 189 const void* user) { 190 DCHECK(!user_); 191 user_ = user; 192 seen_ = 0; 193 return this; 194 } 195 196 void PersistentSampleMapRecords::Release(const void* user) { 197 DCHECK_EQ(user_, user); 198 user_ = nullptr; 199 } 200 201 PersistentMemoryAllocator::Reference PersistentSampleMapRecords::GetNext() { 202 DCHECK(user_); 203 204 // If there are no unseen records, lock and swap in all the found ones. 205 if (records_.size() == seen_) { 206 if (!data_manager_->LoadRecords(this)) 207 return false; 208 } 209 210 // Return the next record. Records *must* be returned in the same order 211 // they are found in the persistent memory in order to ensure that all 212 // objects using this data always have the same state. Race conditions 213 // can cause duplicate records so using the "first found" is the only 214 // guarantee that all objects always access the same one. 215 DCHECK_LT(seen_, records_.size()); 216 return records_[seen_++]; 217 } 218 219 PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew( 220 HistogramBase::Sample value) { 221 return PersistentSampleMap::CreatePersistentRecord(data_manager_->allocator_, 222 sample_map_id_, value); 223 } 224 225 226 // This data will be held in persistent memory in order for processes to 227 // locate and use histograms created elsewhere. 228 struct PersistentHistogramAllocator::PersistentHistogramData { 229 // SHA1(Histogram): Increment this if structure changes! 230 static constexpr uint32_t kPersistentTypeId = 0xF1645910 + 3; 231 232 // Expected size for 32/64-bit check. 233 static constexpr size_t kExpectedInstanceSize = 234 40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize; 235 236 int32_t histogram_type; 237 int32_t flags; 238 int32_t minimum; 239 int32_t maximum; 240 uint32_t bucket_count; 241 PersistentMemoryAllocator::Reference ranges_ref; 242 uint32_t ranges_checksum; 243 PersistentMemoryAllocator::Reference counts_ref; 244 HistogramSamples::Metadata samples_metadata; 245 HistogramSamples::Metadata logged_metadata; 246 247 // Space for the histogram name will be added during the actual allocation 248 // request. This must be the last field of the structure. A zero-size array 249 // or a "flexible" array would be preferred but is not (yet) valid C++. 250 char name[sizeof(uint64_t)]; // Force 64-bit alignment on 32-bit builds. 251 }; 252 253 PersistentHistogramAllocator::Iterator::Iterator( 254 PersistentHistogramAllocator* allocator) 255 : allocator_(allocator), memory_iter_(allocator->memory_allocator()) {} 256 257 std::unique_ptr<HistogramBase> 258 PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) { 259 PersistentMemoryAllocator::Reference ref; 260 while ((ref = memory_iter_.GetNextOfType<PersistentHistogramData>()) != 0) { 261 if (ref != ignore) 262 return allocator_->GetHistogram(ref); 263 } 264 return nullptr; 265 } 266 267 268 PersistentHistogramAllocator::PersistentHistogramAllocator( 269 std::unique_ptr<PersistentMemoryAllocator> memory) 270 : memory_allocator_(std::move(memory)), 271 sparse_histogram_data_manager_(memory_allocator_.get()) {} 272 273 PersistentHistogramAllocator::~PersistentHistogramAllocator() {} 274 275 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::GetHistogram( 276 Reference ref) { 277 // Unfortunately, the histogram "pickle" methods cannot be used as part of 278 // the persistance because the deserialization methods always create local 279 // count data (while these must reference the persistent counts) and always 280 // add it to the local list of known histograms (while these may be simple 281 // references to histograms in other processes). 282 PersistentHistogramData* histogram_data = 283 memory_allocator_->GetAsObject<PersistentHistogramData>(ref); 284 size_t length = memory_allocator_->GetAllocSize(ref); 285 286 // Check that metadata is reasonable: name is NUL terminated and non-empty, 287 // ID fields have been loaded with a hash of the name (0 is considered 288 // unset/invalid). 289 if (!histogram_data || 290 reinterpret_cast<char*>(histogram_data)[length - 1] != '\0' || 291 histogram_data->name[0] == '\0' || 292 histogram_data->samples_metadata.id == 0 || 293 histogram_data->logged_metadata.id == 0) { 294 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA); 295 NOTREACHED(); 296 return nullptr; 297 } 298 return CreateHistogram(histogram_data); 299 } 300 301 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram( 302 HistogramType histogram_type, 303 const std::string& name, 304 int minimum, 305 int maximum, 306 const BucketRanges* bucket_ranges, 307 int32_t flags, 308 Reference* ref_ptr) { 309 // If the allocator is corrupt, don't waste time trying anything else. 310 // This also allows differentiating on the dashboard between allocations 311 // failed due to a corrupt allocator and the number of process instances 312 // with one, the latter being idicated by "newly corrupt", below. 313 if (memory_allocator_->IsCorrupt()) { 314 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_CORRUPT); 315 return nullptr; 316 } 317 318 // Create the metadata necessary for a persistent sparse histogram. This 319 // is done first because it is a small subset of what is required for 320 // other histograms. The type is "under construction" so that a crash 321 // during the datafill doesn't leave a bad record around that could cause 322 // confusion by another process trying to read it. It will be corrected 323 // once histogram construction is complete. 324 PersistentHistogramData* histogram_data = 325 memory_allocator_->New<PersistentHistogramData>( 326 offsetof(PersistentHistogramData, name) + name.length() + 1); 327 if (histogram_data) { 328 memcpy(histogram_data->name, name.c_str(), name.size() + 1); 329 histogram_data->histogram_type = histogram_type; 330 histogram_data->flags = flags | HistogramBase::kIsPersistent; 331 } 332 333 // Create the remaining metadata necessary for regular histograms. 334 if (histogram_type != SPARSE_HISTOGRAM) { 335 size_t bucket_count = bucket_ranges->bucket_count(); 336 size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); 337 if (counts_bytes == 0) { 338 // |bucket_count| was out-of-range. 339 NOTREACHED(); 340 return nullptr; 341 } 342 343 size_t ranges_count = bucket_count + 1; 344 size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample); 345 PersistentMemoryAllocator::Reference counts_ref = 346 memory_allocator_->Allocate(counts_bytes, kTypeIdCountsArray); 347 PersistentMemoryAllocator::Reference ranges_ref = 348 memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray); 349 HistogramBase::Sample* ranges_data = 350 memory_allocator_->GetAsArray<HistogramBase::Sample>( 351 ranges_ref, kTypeIdRangesArray, ranges_count); 352 353 // Only continue here if all allocations were successful. If they weren't, 354 // there is no way to free the space but that's not really a problem since 355 // the allocations only fail because the space is full or corrupt and so 356 // any future attempts will also fail. 357 if (counts_ref && ranges_data && histogram_data) { 358 for (size_t i = 0; i < bucket_ranges->size(); ++i) 359 ranges_data[i] = bucket_ranges->range(i); 360 361 histogram_data->minimum = minimum; 362 histogram_data->maximum = maximum; 363 // |bucket_count| must fit within 32-bits or the allocation of the counts 364 // array would have failed for being too large; the allocator supports 365 // less than 4GB total size. 366 histogram_data->bucket_count = static_cast<uint32_t>(bucket_count); 367 histogram_data->ranges_ref = ranges_ref; 368 histogram_data->ranges_checksum = bucket_ranges->checksum(); 369 histogram_data->counts_ref = counts_ref; 370 } else { 371 histogram_data = nullptr; // Clear this for proper handling below. 372 } 373 } 374 375 if (histogram_data) { 376 // Create the histogram using resources in persistent memory. This ends up 377 // resolving the "ref" values stored in histogram_data instad of just 378 // using what is already known above but avoids duplicating the switch 379 // statement here and serves as a double-check that everything is 380 // correct before commiting the new histogram to persistent space. 381 std::unique_ptr<HistogramBase> histogram = CreateHistogram(histogram_data); 382 DCHECK(histogram); 383 DCHECK_NE(0U, histogram_data->samples_metadata.id); 384 DCHECK_NE(0U, histogram_data->logged_metadata.id); 385 386 PersistentMemoryAllocator::Reference histogram_ref = 387 memory_allocator_->GetAsReference(histogram_data); 388 if (ref_ptr != nullptr) 389 *ref_ptr = histogram_ref; 390 391 // By storing the reference within the allocator to this histogram, the 392 // next import (which will happen before the next histogram creation) 393 // will know to skip it. 394 // See also the comment in ImportHistogramsToStatisticsRecorder(). 395 subtle::NoBarrier_Store(&last_created_, histogram_ref); 396 return histogram; 397 } 398 399 CreateHistogramResultType result; 400 if (memory_allocator_->IsCorrupt()) { 401 RecordCreateHistogramResult(CREATE_HISTOGRAM_ALLOCATOR_NEWLY_CORRUPT); 402 result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT; 403 } else if (memory_allocator_->IsFull()) { 404 result = CREATE_HISTOGRAM_ALLOCATOR_FULL; 405 } else { 406 result = CREATE_HISTOGRAM_ALLOCATOR_ERROR; 407 } 408 RecordCreateHistogramResult(result); 409 410 // Crash for failures caused by internal bugs but not "full" which is 411 // dependent on outside code. 412 if (result != CREATE_HISTOGRAM_ALLOCATOR_FULL) 413 NOTREACHED() << memory_allocator_->Name() << ", error=" << result; 414 415 return nullptr; 416 } 417 418 void PersistentHistogramAllocator::FinalizeHistogram(Reference ref, 419 bool registered) { 420 if (registered) { 421 // If the created persistent histogram was registered then it needs to 422 // be marked as "iterable" in order to be found by other processes. This 423 // happens only after the histogram is fully formed so it's impossible for 424 // code iterating through the allocator to read a partially created record. 425 memory_allocator_->MakeIterable(ref); 426 } else { 427 // If it wasn't registered then a race condition must have caused two to 428 // be created. The allocator does not support releasing the acquired memory 429 // so just change the type to be empty. 430 memory_allocator_->ChangeType(ref, 0, 431 PersistentHistogramData::kPersistentTypeId, 432 /*clear=*/false); 433 } 434 } 435 436 void PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder( 437 HistogramBase* histogram) { 438 DCHECK(histogram); 439 440 HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram); 441 if (!existing) { 442 // The above should never fail but if it does, no real harm is done. 443 // The data won't be merged but it also won't be recorded as merged 444 // so a future try, if successful, will get what was missed. If it 445 // continues to fail, some metric data will be lost but that is better 446 // than crashing. 447 NOTREACHED(); 448 return; 449 } 450 451 // Merge the delta from the passed object to the one in the SR. 452 existing->AddSamples(*histogram->SnapshotDelta()); 453 } 454 455 void PersistentHistogramAllocator::MergeHistogramFinalDeltaToStatisticsRecorder( 456 const HistogramBase* histogram) { 457 DCHECK(histogram); 458 459 HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram); 460 if (!existing) { 461 // The above should never fail but if it does, no real harm is done. 462 // Some metric data will be lost but that is better than crashing. 463 NOTREACHED(); 464 return; 465 } 466 467 // Merge the delta from the passed object to the one in the SR. 468 existing->AddSamples(*histogram->SnapshotFinalDelta()); 469 } 470 471 PersistentSampleMapRecords* PersistentHistogramAllocator::UseSampleMapRecords( 472 uint64_t id, 473 const void* user) { 474 return sparse_histogram_data_manager_.UseSampleMapRecords(id, user); 475 } 476 477 void PersistentHistogramAllocator::CreateTrackingHistograms(StringPiece name) { 478 memory_allocator_->CreateTrackingHistograms(name); 479 } 480 481 void PersistentHistogramAllocator::UpdateTrackingHistograms() { 482 memory_allocator_->UpdateTrackingHistograms(); 483 } 484 485 void PersistentHistogramAllocator::ClearLastCreatedReferenceForTesting() { 486 subtle::NoBarrier_Store(&last_created_, 0); 487 } 488 489 // static 490 HistogramBase* 491 PersistentHistogramAllocator::GetCreateHistogramResultHistogram() { 492 // Get the histogram in which create-results are stored. This is copied 493 // almost exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with 494 // added code to prevent recursion (a likely occurance because the creation 495 // of a new a histogram can end up calling this.) 496 static base::subtle::AtomicWord atomic_histogram_pointer = 0; 497 HistogramBase* histogram_pointer = 498 reinterpret_cast<HistogramBase*>( 499 base::subtle::Acquire_Load(&atomic_histogram_pointer)); 500 if (!histogram_pointer) { 501 // It's possible for multiple threads to make it here in parallel but 502 // they'll always return the same result as there is a mutex in the Get. 503 // The purpose of the "initialized" variable is just to ensure that 504 // the same thread doesn't recurse which is also why it doesn't have 505 // to be atomic. 506 static bool initialized = false; 507 if (!initialized) { 508 initialized = true; 509 if (GlobalHistogramAllocator::Get()) { 510 DVLOG(1) << "Creating the results-histogram inside persistent" 511 << " memory can cause future allocations to crash if" 512 << " that memory is ever released (for testing)."; 513 } 514 515 histogram_pointer = LinearHistogram::FactoryGet( 516 kResultHistogram, 1, CREATE_HISTOGRAM_MAX, CREATE_HISTOGRAM_MAX + 1, 517 HistogramBase::kUmaTargetedHistogramFlag); 518 base::subtle::Release_Store( 519 &atomic_histogram_pointer, 520 reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); 521 } 522 } 523 return histogram_pointer; 524 } 525 526 std::unique_ptr<HistogramBase> PersistentHistogramAllocator::CreateHistogram( 527 PersistentHistogramData* histogram_data_ptr) { 528 if (!histogram_data_ptr) { 529 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER); 530 NOTREACHED(); 531 return nullptr; 532 } 533 534 // Sparse histograms are quite different so handle them as a special case. 535 if (histogram_data_ptr->histogram_type == SPARSE_HISTOGRAM) { 536 std::unique_ptr<HistogramBase> histogram = 537 SparseHistogram::PersistentCreate(this, histogram_data_ptr->name, 538 &histogram_data_ptr->samples_metadata, 539 &histogram_data_ptr->logged_metadata); 540 DCHECK(histogram); 541 histogram->SetFlags(histogram_data_ptr->flags); 542 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); 543 return histogram; 544 } 545 546 // Copy the histogram_data to local storage because anything in persistent 547 // memory cannot be trusted as it could be changed at any moment by a 548 // malicious actor that shares access. The contents of histogram_data are 549 // validated below; the local copy is to ensure that the contents cannot 550 // be externally changed between validation and use. 551 PersistentHistogramData histogram_data = *histogram_data_ptr; 552 553 HistogramBase::Sample* ranges_data = 554 memory_allocator_->GetAsArray<HistogramBase::Sample>( 555 histogram_data.ranges_ref, kTypeIdRangesArray, 556 PersistentMemoryAllocator::kSizeAny); 557 558 const uint32_t max_buckets = 559 std::numeric_limits<uint32_t>::max() / sizeof(HistogramBase::Sample); 560 size_t required_bytes = 561 (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample); 562 size_t allocated_bytes = 563 memory_allocator_->GetAllocSize(histogram_data.ranges_ref); 564 if (!ranges_data || histogram_data.bucket_count < 2 || 565 histogram_data.bucket_count >= max_buckets || 566 allocated_bytes < required_bytes) { 567 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); 568 NOTREACHED(); 569 return nullptr; 570 } 571 572 std::unique_ptr<const BucketRanges> created_ranges = 573 CreateRangesFromData(ranges_data, histogram_data.ranges_checksum, 574 histogram_data.bucket_count + 1); 575 if (!created_ranges) { 576 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY); 577 NOTREACHED(); 578 return nullptr; 579 } 580 const BucketRanges* ranges = 581 StatisticsRecorder::RegisterOrDeleteDuplicateRanges( 582 created_ranges.release()); 583 584 HistogramBase::AtomicCount* counts_data = 585 memory_allocator_->GetAsArray<HistogramBase::AtomicCount>( 586 histogram_data.counts_ref, kTypeIdCountsArray, 587 PersistentMemoryAllocator::kSizeAny); 588 size_t counts_bytes = 589 CalculateRequiredCountsBytes(histogram_data.bucket_count); 590 if (!counts_data || counts_bytes == 0 || 591 memory_allocator_->GetAllocSize(histogram_data.counts_ref) < 592 counts_bytes) { 593 RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); 594 NOTREACHED(); 595 return nullptr; 596 } 597 598 // After the main "counts" array is a second array using for storing what 599 // was previously logged. This is used to calculate the "delta" during 600 // snapshot operations. 601 HistogramBase::AtomicCount* logged_data = 602 counts_data + histogram_data.bucket_count; 603 604 std::string name(histogram_data_ptr->name); 605 std::unique_ptr<HistogramBase> histogram; 606 switch (histogram_data.histogram_type) { 607 case HISTOGRAM: 608 histogram = Histogram::PersistentCreate( 609 name, histogram_data.minimum, histogram_data.maximum, ranges, 610 counts_data, logged_data, histogram_data.bucket_count, 611 &histogram_data_ptr->samples_metadata, 612 &histogram_data_ptr->logged_metadata); 613 DCHECK(histogram); 614 break; 615 case LINEAR_HISTOGRAM: 616 histogram = LinearHistogram::PersistentCreate( 617 name, histogram_data.minimum, histogram_data.maximum, ranges, 618 counts_data, logged_data, histogram_data.bucket_count, 619 &histogram_data_ptr->samples_metadata, 620 &histogram_data_ptr->logged_metadata); 621 DCHECK(histogram); 622 break; 623 case BOOLEAN_HISTOGRAM: 624 histogram = BooleanHistogram::PersistentCreate( 625 name, ranges, counts_data, logged_data, 626 &histogram_data_ptr->samples_metadata, 627 &histogram_data_ptr->logged_metadata); 628 DCHECK(histogram); 629 break; 630 case CUSTOM_HISTOGRAM: 631 histogram = CustomHistogram::PersistentCreate( 632 name, ranges, counts_data, logged_data, histogram_data.bucket_count, 633 &histogram_data_ptr->samples_metadata, 634 &histogram_data_ptr->logged_metadata); 635 DCHECK(histogram); 636 break; 637 default: 638 NOTREACHED(); 639 } 640 641 if (histogram) { 642 DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType()); 643 histogram->SetFlags(histogram_data.flags); 644 RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS); 645 } else { 646 RecordCreateHistogramResult(CREATE_HISTOGRAM_UNKNOWN_TYPE); 647 } 648 649 return histogram; 650 } 651 652 HistogramBase* 653 PersistentHistogramAllocator::GetOrCreateStatisticsRecorderHistogram( 654 const HistogramBase* histogram) { 655 // This should never be called on the global histogram allocator as objects 656 // created there are already within the global statistics recorder. 657 DCHECK_NE(GlobalHistogramAllocator::Get(), this); 658 DCHECK(histogram); 659 660 HistogramBase* existing = 661 StatisticsRecorder::FindHistogram(histogram->histogram_name()); 662 if (existing) 663 return existing; 664 665 // Adding the passed histogram to the SR would cause a problem if the 666 // allocator that holds it eventually goes away. Instead, create a new 667 // one from a serialized version. Deserialization calls the appropriate 668 // FactoryGet() which will create the histogram in the global persistent- 669 // histogram allocator if such is set. 670 base::Pickle pickle; 671 if (!histogram->SerializeInfo(&pickle)) 672 return nullptr; 673 PickleIterator iter(pickle); 674 existing = DeserializeHistogramInfo(&iter); 675 if (!existing) 676 return nullptr; 677 678 // Make sure there is no "serialization" flag set. 679 DCHECK_EQ(0, existing->flags() & HistogramBase::kIPCSerializationSourceFlag); 680 // Record the newly created histogram in the SR. 681 return StatisticsRecorder::RegisterOrDeleteDuplicate(existing); 682 } 683 684 // static 685 void PersistentHistogramAllocator::RecordCreateHistogramResult( 686 CreateHistogramResultType result) { 687 HistogramBase* result_histogram = GetCreateHistogramResultHistogram(); 688 if (result_histogram) 689 result_histogram->Add(result); 690 } 691 692 GlobalHistogramAllocator::~GlobalHistogramAllocator() {} 693 694 // static 695 void GlobalHistogramAllocator::CreateWithPersistentMemory( 696 void* base, 697 size_t size, 698 size_t page_size, 699 uint64_t id, 700 StringPiece name) { 701 Set(WrapUnique( 702 new GlobalHistogramAllocator(MakeUnique<PersistentMemoryAllocator>( 703 base, size, page_size, id, name, false)))); 704 } 705 706 // static 707 void GlobalHistogramAllocator::CreateWithLocalMemory( 708 size_t size, 709 uint64_t id, 710 StringPiece name) { 711 Set(WrapUnique(new GlobalHistogramAllocator( 712 MakeUnique<LocalPersistentMemoryAllocator>(size, id, name)))); 713 } 714 715 #if !defined(OS_NACL) 716 // static 717 bool GlobalHistogramAllocator::CreateWithFile( 718 const FilePath& file_path, 719 size_t size, 720 uint64_t id, 721 StringPiece name) { 722 bool exists = PathExists(file_path); 723 File file( 724 file_path, File::FLAG_OPEN_ALWAYS | File::FLAG_SHARE_DELETE | 725 File::FLAG_READ | File::FLAG_WRITE); 726 727 std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile()); 728 if (exists) { 729 mmfile->Initialize(std::move(file), MemoryMappedFile::READ_WRITE); 730 } else { 731 mmfile->Initialize(std::move(file), {0, static_cast<int64_t>(size)}, 732 MemoryMappedFile::READ_WRITE_EXTEND); 733 } 734 if (!mmfile->IsValid() || 735 !FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) { 736 NOTREACHED(); 737 return false; 738 } 739 740 Set(WrapUnique( 741 new GlobalHistogramAllocator(MakeUnique<FilePersistentMemoryAllocator>( 742 std::move(mmfile), size, id, name, false)))); 743 Get()->SetPersistentLocation(file_path); 744 return true; 745 } 746 747 // static 748 bool GlobalHistogramAllocator::CreateWithActiveFile(const FilePath& base_path, 749 const FilePath& active_path, 750 size_t size, 751 uint64_t id, 752 StringPiece name) { 753 if (!base::ReplaceFile(active_path, base_path, nullptr)) 754 base::DeleteFile(base_path, /*recursive=*/false); 755 756 return base::GlobalHistogramAllocator::CreateWithFile(active_path, size, id, 757 name); 758 } 759 760 // static 761 bool GlobalHistogramAllocator::CreateWithActiveFileInDir(const FilePath& dir, 762 size_t size, 763 uint64_t id, 764 StringPiece name) { 765 FilePath base_path, active_path; 766 ConstructFilePaths(dir, name, &base_path, &active_path); 767 return CreateWithActiveFile(base_path, active_path, size, id, name); 768 } 769 770 // static 771 void GlobalHistogramAllocator::ConstructFilePaths(const FilePath& dir, 772 StringPiece name, 773 FilePath* out_base_path, 774 FilePath* out_active_path) { 775 if (out_base_path) { 776 *out_base_path = dir.AppendASCII(name).AddExtension( 777 PersistentMemoryAllocator::kFileExtension); 778 } 779 if (out_active_path) { 780 *out_active_path = 781 dir.AppendASCII(name.as_string() + std::string("-active")) 782 .AddExtension(PersistentMemoryAllocator::kFileExtension); 783 } 784 } 785 #endif // !defined(OS_NACL) 786 787 // static 788 void GlobalHistogramAllocator::CreateWithSharedMemoryHandle( 789 const SharedMemoryHandle& handle, 790 size_t size) { 791 std::unique_ptr<SharedMemory> shm( 792 new SharedMemory(handle, /*readonly=*/false)); 793 if (!shm->Map(size) || 794 !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) { 795 NOTREACHED(); 796 return; 797 } 798 799 Set(WrapUnique( 800 new GlobalHistogramAllocator(MakeUnique<SharedPersistentMemoryAllocator>( 801 std::move(shm), 0, StringPiece(), /*readonly=*/false)))); 802 } 803 804 // static 805 void GlobalHistogramAllocator::Set( 806 std::unique_ptr<GlobalHistogramAllocator> allocator) { 807 // Releasing or changing an allocator is extremely dangerous because it 808 // likely has histograms stored within it. If the backing memory is also 809 // also released, future accesses to those histograms will seg-fault. 810 CHECK(!subtle::NoBarrier_Load(&g_allocator)); 811 subtle::Release_Store(&g_allocator, 812 reinterpret_cast<uintptr_t>(allocator.release())); 813 size_t existing = StatisticsRecorder::GetHistogramCount(); 814 815 DVLOG_IF(1, existing) 816 << existing << " histograms were created before persistence was enabled."; 817 } 818 819 // static 820 GlobalHistogramAllocator* GlobalHistogramAllocator::Get() { 821 return reinterpret_cast<GlobalHistogramAllocator*>( 822 subtle::Acquire_Load(&g_allocator)); 823 } 824 825 // static 826 std::unique_ptr<GlobalHistogramAllocator> 827 GlobalHistogramAllocator::ReleaseForTesting() { 828 GlobalHistogramAllocator* histogram_allocator = Get(); 829 if (!histogram_allocator) 830 return nullptr; 831 PersistentMemoryAllocator* memory_allocator = 832 histogram_allocator->memory_allocator(); 833 834 // Before releasing the memory, it's necessary to have the Statistics- 835 // Recorder forget about the histograms contained therein; otherwise, 836 // some operations will try to access them and the released memory. 837 PersistentMemoryAllocator::Iterator iter(memory_allocator); 838 const PersistentHistogramData* data; 839 while ((data = iter.GetNextOfObject<PersistentHistogramData>()) != nullptr) { 840 StatisticsRecorder::ForgetHistogramForTesting(data->name); 841 842 // If a test breaks here then a memory region containing a histogram 843 // actively used by this code is being released back to the test. 844 // If that memory segment were to be deleted, future calls to create 845 // persistent histograms would crash. To avoid this, have the test call 846 // the method GetCreateHistogramResultHistogram() *before* setting 847 // the (temporary) memory allocator via SetGlobalAllocator() so that 848 // histogram is instead allocated from the process heap. 849 DCHECK_NE(kResultHistogram, data->name); 850 } 851 852 subtle::Release_Store(&g_allocator, 0); 853 return WrapUnique(histogram_allocator); 854 }; 855 856 void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) { 857 persistent_location_ = location; 858 } 859 860 const FilePath& GlobalHistogramAllocator::GetPersistentLocation() const { 861 return persistent_location_; 862 } 863 864 bool GlobalHistogramAllocator::WriteToPersistentLocation() { 865 #if defined(OS_NACL) 866 // NACL doesn't support file operations, including ImportantFileWriter. 867 NOTREACHED(); 868 return false; 869 #else 870 // Stop if no destination is set. 871 if (persistent_location_.empty()) { 872 NOTREACHED() << "Could not write \"" << Name() << "\" persistent histograms" 873 << " to file because no location was set."; 874 return false; 875 } 876 877 StringPiece contents(static_cast<const char*>(data()), used()); 878 if (!ImportantFileWriter::WriteFileAtomically(persistent_location_, 879 contents)) { 880 LOG(ERROR) << "Could not write \"" << Name() << "\" persistent histograms" 881 << " to file: " << persistent_location_.value(); 882 return false; 883 } 884 885 return true; 886 #endif 887 } 888 889 void GlobalHistogramAllocator::DeletePersistentLocation() { 890 memory_allocator()->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED); 891 892 #if defined(OS_NACL) 893 NOTREACHED(); 894 #else 895 if (persistent_location_.empty()) 896 return; 897 898 // Open (with delete) and then immediately close the file by going out of 899 // scope. This is the only cross-platform safe way to delete a file that may 900 // be open elsewhere. Open handles will continue to operate normally but 901 // new opens will not be possible. 902 File file(persistent_location_, 903 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_DELETE_ON_CLOSE); 904 #endif 905 } 906 907 GlobalHistogramAllocator::GlobalHistogramAllocator( 908 std::unique_ptr<PersistentMemoryAllocator> memory) 909 : PersistentHistogramAllocator(std::move(memory)), 910 import_iterator_(this) { 911 // Make sure the StatisticsRecorder is initialized to prevent duplicate 912 // histograms from being created. It's safe to call this multiple times. 913 StatisticsRecorder::Initialize(); 914 } 915 916 void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() { 917 // Skip the import if it's the histogram that was last created. Should a 918 // race condition cause the "last created" to be overwritten before it 919 // is recognized here then the histogram will be created and be ignored 920 // when it is detected as a duplicate by the statistics-recorder. This 921 // simple check reduces the time of creating persistent histograms by 922 // about 40%. 923 Reference record_to_ignore = last_created(); 924 925 // There is no lock on this because the iterator is lock-free while still 926 // guaranteed to only return each entry only once. The StatisticsRecorder 927 // has its own lock so the Register operation is safe. 928 while (true) { 929 std::unique_ptr<HistogramBase> histogram = 930 import_iterator_.GetNextWithIgnore(record_to_ignore); 931 if (!histogram) 932 break; 933 StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); 934 } 935 } 936 937 } // namespace base 938