// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/metrics/persistent_histogram_storage.h" #include #include #include "base/files/file_util.h" #include "base/files/important_file_writer.h" #include "base/logging.h" #include "base/metrics/persistent_histogram_allocator.h" #include "base/metrics/persistent_memory_allocator.h" #include "base/process/memory.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "build/build_config.h" #if BUILDFLAG(IS_WIN) #include // Must be after #include #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include #endif namespace { constexpr size_t kAllocSize = 1 << 20; // 1 MiB void* AllocateLocalMemory(size_t size) { void* address; #if BUILDFLAG(IS_WIN) address = ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (address) return address; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac. // MAP_SHARED is not available on Linux <2.4 but required on Mac. address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (address != MAP_FAILED) return address; #else #error This architecture is not (yet) supported. #endif // As a last resort, just allocate the memory from the heap. This will // achieve the same basic result but the acquired memory has to be // explicitly zeroed and thus realized immediately (i.e. all pages are // added to the process now instead of only when first accessed). if (!base::UncheckedMalloc(size, &address)) return nullptr; DCHECK(address); memset(address, 0, size); return address; } } // namespace namespace base { PersistentHistogramStorage::PersistentHistogramStorage( std::string_view allocator_name, StorageDirManagement storage_dir_management) : storage_dir_management_(storage_dir_management) { DCHECK(!allocator_name.empty()); DCHECK(IsStringASCII(allocator_name)); // This code may be executed before crash handling and/or OOM handling has // been initialized for the process. Silently ignore a failed allocation // (no metric persistence) rather that generating a crash that won't be // caught/reported. void* memory = AllocateLocalMemory(kAllocSize); if (!memory) return; GlobalHistogramAllocator::CreateWithPersistentMemory(memory, kAllocSize, 0, 0, // No identifier. allocator_name); GlobalHistogramAllocator::Get()->CreateTrackingHistograms(allocator_name); } PersistentHistogramStorage::~PersistentHistogramStorage() { PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); if (!allocator) return; allocator->UpdateTrackingHistograms(); if (disabled_) return; // Stop if the storage base directory has not been properly set. if (storage_base_dir_.empty()) { LOG(ERROR) << "Could not write \"" << allocator->Name() << "\" persistent histograms to file as the storage base directory " "is not properly set."; return; } FilePath storage_dir = storage_base_dir_.AppendASCII(allocator->Name()); switch (storage_dir_management_) { case StorageDirManagement::kCreate: if (!CreateDirectory(storage_dir)) { LOG(ERROR) << "Could not write \"" << allocator->Name() << "\" persistent histograms to file as the storage directory " "cannot be created."; return; } break; case StorageDirManagement::kUseExisting: if (!DirectoryExists(storage_dir)) { // When the consumer of this class decides to use an existing storage // directory, it should ensure the directory's existence if it's // essential. LOG(ERROR) << "Could not write \"" << allocator->Name() << "\" persistent histograms to file as the storage directory " "does not exist."; return; } break; } // Save data using the process ID and microseconds since Windows Epoch for the // filename with the correct extension. Using this format prevents collisions // between multiple processes using the same provider name. const FilePath file_path = storage_dir .AppendASCII(StringPrintf( "%" CrPRIdPid "_%" PRId64, GetCurrentProcId(), Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds())) .AddExtension(PersistentMemoryAllocator::kFileExtension); std::string_view contents(static_cast(allocator->data()), allocator->used()); if (!ImportantFileWriter::WriteFileAtomically(file_path, contents)) { LOG(ERROR) << "Persistent histograms fail to write to file: " << file_path.value(); } } } // namespace base