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