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