Remove base/metrics and some related things
Change-Id: I47706be34e135d9941bf849297bb2fc5010c88d0
Reviewed-on: https://gn-review.googlesource.com/1420
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
deleted file mode 100644
index bb1349c..0000000
--- a/base/debug/activity_tracker.cc
+++ /dev/null
@@ -1,1828 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/debug/activity_tracker.h"
-
-#include <algorithm>
-#include <limits>
-#include <utility>
-
-#include "base/atomic_sequence_num.h"
-#include "base/debug/stack_trace.h"
-#include "base/files/file.h"
-#include "base/files/file_path.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/pending_task.h"
-#include "base/pickle.h"
-#include "base/process/process.h"
-#include "base/process/process_handle.h"
-#include "base/stl_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/platform_thread.h"
-#include "build_config.h"
-
-namespace base {
-namespace debug {
-
-namespace {
-
-// The minimum depth a stack should support.
-const int kMinStackDepth = 2;
-
-// The amount of memory set aside for holding arbitrary user data (key/value
-// pairs) globally or associated with ActivityData entries.
-const size_t kUserDataSize = 1 << 10; // 1 KiB
-const size_t kProcessDataSize = 4 << 10; // 4 KiB
-const size_t kMaxUserDataNameLength =
- static_cast<size_t>(std::numeric_limits<uint8_t>::max());
-
-// A constant used to indicate that module information is changing.
-const uint32_t kModuleInformationChanging = 0x80000000;
-
-// The key used to record process information.
-const char kProcessPhaseDataKey[] = "process-phase";
-
-// An atomically incrementing number, used to check for recreations of objects
-// in the same memory space.
-AtomicSequenceNumber g_next_id;
-
-union ThreadRef {
- int64_t as_id;
-#if defined(OS_WIN)
- // On Windows, the handle itself is often a pseudo-handle with a common
- // value meaning "this thread" and so the thread-id is used. The former
- // can be converted to a thread-id with a system call.
- PlatformThreadId as_tid;
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- // On Posix and Fuchsia, the handle is always a unique identifier so no
- // conversion needs to be done. However, its value is officially opaque so
- // there is no one correct way to convert it to a numerical identifier.
- PlatformThreadHandle::Handle as_handle;
-#endif
-};
-
-// Gets the next non-zero identifier. It is only unique within a process.
-uint32_t GetNextDataId() {
- uint32_t id;
- while ((id = g_next_id.GetNext()) == 0)
- ;
- return id;
-}
-
-// Gets the current process-id, either from the GlobalActivityTracker if it
-// exists (where the PID can be defined for testing) or from the system if
-// there isn't such.
-int64_t GetProcessId() {
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- if (global)
- return global->process_id();
- return GetCurrentProcId();
-}
-
-// Finds and reuses a specific allocation or creates a new one.
-PersistentMemoryAllocator::Reference AllocateFrom(
- PersistentMemoryAllocator* allocator,
- uint32_t from_type,
- size_t size,
- uint32_t to_type) {
- PersistentMemoryAllocator::Iterator iter(allocator);
- PersistentMemoryAllocator::Reference ref;
- while ((ref = iter.GetNextOfType(from_type)) != 0) {
- DCHECK_LE(size, allocator->GetAllocSize(ref));
- // This can fail if a another thread has just taken it. It is assumed that
- // the memory is cleared during the "free" operation.
- if (allocator->ChangeType(ref, to_type, from_type, /*clear=*/false))
- return ref;
- }
-
- return allocator->Allocate(size, to_type);
-}
-
-// Determines the previous aligned index.
-size_t RoundDownToAlignment(size_t index, size_t alignment) {
- return index & (0 - alignment);
-}
-
-// Determines the next aligned index.
-size_t RoundUpToAlignment(size_t index, size_t alignment) {
- return (index + (alignment - 1)) & (0 - alignment);
-}
-
-// Converts "tick" timing into wall time.
-Time WallTimeFromTickTime(int64_t ticks_start, int64_t ticks, Time time_start) {
- return time_start + TimeDelta::FromInternalValue(ticks - ticks_start);
-}
-
-} // namespace
-
-OwningProcess::OwningProcess() = default;
-OwningProcess::~OwningProcess() = default;
-
-void OwningProcess::Release_Initialize(int64_t pid) {
- uint32_t old_id = data_id.load(std::memory_order_acquire);
- DCHECK_EQ(0U, old_id);
- process_id = pid != 0 ? pid : GetProcessId();
- create_stamp = Time::Now().ToInternalValue();
- data_id.store(GetNextDataId(), std::memory_order_release);
-}
-
-void OwningProcess::SetOwningProcessIdForTesting(int64_t pid, int64_t stamp) {
- DCHECK_NE(0U, data_id);
- process_id = pid;
- create_stamp = stamp;
-}
-
-// static
-bool OwningProcess::GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp) {
- const OwningProcess* info = reinterpret_cast<const OwningProcess*>(memory);
- uint32_t id = info->data_id.load(std::memory_order_acquire);
- if (id == 0)
- return false;
-
- *out_id = info->process_id;
- *out_stamp = info->create_stamp;
- return id == info->data_id.load(std::memory_order_seq_cst);
-}
-
-// It doesn't matter what is contained in this (though it will be all zeros)
-// as only the address of it is important.
-const ActivityData kNullActivityData = {};
-
-ActivityData ActivityData::ForThread(const PlatformThreadHandle& handle) {
- ThreadRef thread_ref;
- thread_ref.as_id = 0; // Zero the union in case other is smaller.
-#if defined(OS_WIN)
- thread_ref.as_tid = ::GetThreadId(handle.platform_handle());
-#elif defined(OS_POSIX)
- thread_ref.as_handle = handle.platform_handle();
-#endif
- return ForThread(thread_ref.as_id);
-}
-
-ActivityTrackerMemoryAllocator::ActivityTrackerMemoryAllocator(
- PersistentMemoryAllocator* allocator,
- uint32_t object_type,
- uint32_t object_free_type,
- size_t object_size,
- size_t cache_size,
- bool make_iterable)
- : allocator_(allocator),
- object_type_(object_type),
- object_free_type_(object_free_type),
- object_size_(object_size),
- cache_size_(cache_size),
- make_iterable_(make_iterable),
- iterator_(allocator),
- cache_values_(new Reference[cache_size]),
- cache_used_(0) {
- DCHECK(allocator);
-}
-
-ActivityTrackerMemoryAllocator::~ActivityTrackerMemoryAllocator() = default;
-
-ActivityTrackerMemoryAllocator::Reference
-ActivityTrackerMemoryAllocator::GetObjectReference() {
- // First see if there is a cached value that can be returned. This is much
- // faster than searching the memory system for free blocks.
- while (cache_used_ > 0) {
- Reference cached = cache_values_[--cache_used_];
- // Change the type of the cached object to the proper type and return it.
- // If the type-change fails that means another thread has taken this from
- // under us (via the search below) so ignore it and keep trying. Don't
- // clear the memory because that was done when the type was made "free".
- if (allocator_->ChangeType(cached, object_type_, object_free_type_, false))
- return cached;
- }
-
- // Fetch the next "free" object from persistent memory. Rather than restart
- // the iterator at the head each time and likely waste time going again
- // through objects that aren't relevant, the iterator continues from where
- // it last left off and is only reset when the end is reached. If the
- // returned reference matches |last|, then it has wrapped without finding
- // anything.
- const Reference last = iterator_.GetLast();
- while (true) {
- uint32_t type;
- Reference found = iterator_.GetNext(&type);
- if (found && type == object_free_type_) {
- // Found a free object. Change it to the proper type and return it. If
- // the type-change fails that means another thread has taken this from
- // under us so ignore it and keep trying.
- if (allocator_->ChangeType(found, object_type_, object_free_type_, false))
- return found;
- }
- if (found == last) {
- // Wrapped. No desired object was found.
- break;
- }
- if (!found) {
- // Reached end; start over at the beginning.
- iterator_.Reset();
- }
- }
-
- // No free block was found so instead allocate a new one.
- Reference allocated = allocator_->Allocate(object_size_, object_type_);
- if (allocated && make_iterable_)
- allocator_->MakeIterable(allocated);
- return allocated;
-}
-
-void ActivityTrackerMemoryAllocator::ReleaseObjectReference(Reference ref) {
- // Mark object as free.
- bool success = allocator_->ChangeType(ref, object_free_type_, object_type_,
- /*clear=*/true);
- DCHECK(success);
-
- // Add this reference to our "free" cache if there is space. If not, the type
- // has still been changed to indicate that it is free so this (or another)
- // thread can find it, albeit more slowly, using the iteration method above.
- if (cache_used_ < cache_size_)
- cache_values_[cache_used_++] = ref;
-}
-
-// static
-void Activity::FillFrom(Activity* activity,
- const void* program_counter,
- const void* origin,
- Type type,
- const ActivityData& data) {
- activity->time_internal = base::TimeTicks::Now().ToInternalValue();
- activity->calling_address = reinterpret_cast<uintptr_t>(program_counter);
- activity->origin_address = reinterpret_cast<uintptr_t>(origin);
- activity->activity_type = type;
- activity->data = data;
-
-#if (!defined(OS_NACL) && DCHECK_IS_ON()) || defined(ADDRESS_SANITIZER)
- // Create a stacktrace from the current location and get the addresses for
- // improved debuggability.
- StackTrace stack_trace;
- size_t stack_depth;
- const void* const* stack_addrs = stack_trace.Addresses(&stack_depth);
- // Copy the stack addresses, ignoring the first one (here).
- size_t i;
- for (i = 1; i < stack_depth && i < kActivityCallStackSize; ++i) {
- activity->call_stack[i - 1] = reinterpret_cast<uintptr_t>(stack_addrs[i]);
- }
- activity->call_stack[i - 1] = 0;
-#else
- activity->call_stack[0] = 0;
-#endif
-}
-
-ActivityUserData::TypedValue::TypedValue() = default;
-ActivityUserData::TypedValue::TypedValue(const TypedValue& other) = default;
-ActivityUserData::TypedValue::~TypedValue() = default;
-
-StringPiece ActivityUserData::TypedValue::Get() const {
- DCHECK_EQ(RAW_VALUE, type_);
- return long_value_;
-}
-
-StringPiece ActivityUserData::TypedValue::GetString() const {
- DCHECK_EQ(STRING_VALUE, type_);
- return long_value_;
-}
-
-bool ActivityUserData::TypedValue::GetBool() const {
- DCHECK_EQ(BOOL_VALUE, type_);
- return short_value_ != 0;
-}
-
-char ActivityUserData::TypedValue::GetChar() const {
- DCHECK_EQ(CHAR_VALUE, type_);
- return static_cast<char>(short_value_);
-}
-
-int64_t ActivityUserData::TypedValue::GetInt() const {
- DCHECK_EQ(SIGNED_VALUE, type_);
- return static_cast<int64_t>(short_value_);
-}
-
-uint64_t ActivityUserData::TypedValue::GetUint() const {
- DCHECK_EQ(UNSIGNED_VALUE, type_);
- return static_cast<uint64_t>(short_value_);
-}
-
-StringPiece ActivityUserData::TypedValue::GetReference() const {
- DCHECK_EQ(RAW_VALUE_REFERENCE, type_);
- return ref_value_;
-}
-
-StringPiece ActivityUserData::TypedValue::GetStringReference() const {
- DCHECK_EQ(STRING_VALUE_REFERENCE, type_);
- return ref_value_;
-}
-
-// These are required because std::atomic is (currently) not a POD type and
-// thus clang requires explicit out-of-line constructors and destructors even
-// when they do nothing.
-ActivityUserData::ValueInfo::ValueInfo() = default;
-ActivityUserData::ValueInfo::ValueInfo(ValueInfo&&) = default;
-ActivityUserData::ValueInfo::~ValueInfo() = default;
-ActivityUserData::MemoryHeader::MemoryHeader() = default;
-ActivityUserData::MemoryHeader::~MemoryHeader() = default;
-ActivityUserData::FieldHeader::FieldHeader() = default;
-ActivityUserData::FieldHeader::~FieldHeader() = default;
-
-ActivityUserData::ActivityUserData() : ActivityUserData(nullptr, 0, -1) {}
-
-ActivityUserData::ActivityUserData(void* memory, size_t size, int64_t pid)
- : memory_(reinterpret_cast<char*>(memory)),
- available_(RoundDownToAlignment(size, kMemoryAlignment)),
- header_(reinterpret_cast<MemoryHeader*>(memory)),
- orig_data_id(0),
- orig_process_id(0),
- orig_create_stamp(0) {
- // It's possible that no user data is being stored.
- if (!memory_)
- return;
-
- static_assert(0 == sizeof(MemoryHeader) % kMemoryAlignment, "invalid header");
- DCHECK_LT(sizeof(MemoryHeader), available_);
- if (header_->owner.data_id.load(std::memory_order_acquire) == 0)
- header_->owner.Release_Initialize(pid);
- memory_ += sizeof(MemoryHeader);
- available_ -= sizeof(MemoryHeader);
-
- // Make a copy of identifying information for later comparison.
- *const_cast<uint32_t*>(&orig_data_id) =
- header_->owner.data_id.load(std::memory_order_acquire);
- *const_cast<int64_t*>(&orig_process_id) = header_->owner.process_id;
- *const_cast<int64_t*>(&orig_create_stamp) = header_->owner.create_stamp;
-
- // If there is already data present, load that. This allows the same class
- // to be used for analysis through snapshots.
- ImportExistingData();
-}
-
-ActivityUserData::~ActivityUserData() = default;
-
-bool ActivityUserData::CreateSnapshot(Snapshot* output_snapshot) const {
- DCHECK(output_snapshot);
- DCHECK(output_snapshot->empty());
-
- // Find any new data that may have been added by an active instance of this
- // class that is adding records.
- ImportExistingData();
-
- // Add all the values to the snapshot.
- for (const auto& entry : values_) {
- TypedValue value;
- const size_t size = entry.second.size_ptr->load(std::memory_order_acquire);
- value.type_ = entry.second.type;
- DCHECK_GE(entry.second.extent, size);
-
- switch (entry.second.type) {
- case RAW_VALUE:
- case STRING_VALUE:
- value.long_value_ =
- std::string(reinterpret_cast<char*>(entry.second.memory), size);
- break;
- case RAW_VALUE_REFERENCE:
- case STRING_VALUE_REFERENCE: {
- ReferenceRecord* ref =
- reinterpret_cast<ReferenceRecord*>(entry.second.memory);
- value.ref_value_ = StringPiece(
- reinterpret_cast<char*>(static_cast<uintptr_t>(ref->address)),
- static_cast<size_t>(ref->size));
- } break;
- case BOOL_VALUE:
- case CHAR_VALUE:
- value.short_value_ = *reinterpret_cast<char*>(entry.second.memory);
- break;
- case SIGNED_VALUE:
- case UNSIGNED_VALUE:
- value.short_value_ = *reinterpret_cast<uint64_t*>(entry.second.memory);
- break;
- case END_OF_VALUES: // Included for completeness purposes.
- NOTREACHED();
- }
- auto inserted = output_snapshot->insert(
- std::make_pair(entry.second.name.as_string(), std::move(value)));
- DCHECK(inserted.second); // True if inserted, false if existed.
- }
-
- // Another import attempt will validate that the underlying memory has not
- // been reused for another purpose. Entries added since the first import
- // will be ignored here but will be returned if another snapshot is created.
- ImportExistingData();
- if (!memory_) {
- output_snapshot->clear();
- return false;
- }
-
- // Successful snapshot.
- return true;
-}
-
-const void* ActivityUserData::GetBaseAddress() const {
- // The |memory_| pointer advances as elements are written but the |header_|
- // value is always at the start of the block so just return that.
- return header_;
-}
-
-void ActivityUserData::SetOwningProcessIdForTesting(int64_t pid,
- int64_t stamp) {
- if (!header_)
- return;
- header_->owner.SetOwningProcessIdForTesting(pid, stamp);
-}
-
-// static
-bool ActivityUserData::GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp) {
- const MemoryHeader* header = reinterpret_cast<const MemoryHeader*>(memory);
- return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp);
-}
-
-void ActivityUserData::Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) {
- DCHECK_GE(std::numeric_limits<uint8_t>::max(), name.length());
- size = std::min(std::numeric_limits<uint16_t>::max() - (kMemoryAlignment - 1),
- size);
-
- // It's possible that no user data is being stored.
- if (!memory_)
- return;
-
- // The storage of a name is limited so use that limit during lookup.
- if (name.length() > kMaxUserDataNameLength)
- name.set(name.data(), kMaxUserDataNameLength);
-
- ValueInfo* info;
- auto existing = values_.find(name);
- if (existing != values_.end()) {
- info = &existing->second;
- } else {
- // The name size is limited to what can be held in a single byte but
- // because there are not alignment constraints on strings, it's set tight
- // against the header. Its extent (the reserved space, even if it's not
- // all used) is calculated so that, when pressed against the header, the
- // following field will be aligned properly.
- size_t name_size = name.length();
- size_t name_extent =
- RoundUpToAlignment(sizeof(FieldHeader) + name_size, kMemoryAlignment) -
- sizeof(FieldHeader);
- size_t value_extent = RoundUpToAlignment(size, kMemoryAlignment);
-
- // The "base size" is the size of the header and (padded) string key. Stop
- // now if there's not room enough for even this.
- size_t base_size = sizeof(FieldHeader) + name_extent;
- if (base_size > available_)
- return;
-
- // The "full size" is the size for storing the entire value.
- size_t full_size = std::min(base_size + value_extent, available_);
-
- // If the value is actually a single byte, see if it can be stuffed at the
- // end of the name extent rather than wasting kMemoryAlignment bytes.
- if (size == 1 && name_extent > name_size) {
- full_size = base_size;
- --name_extent;
- --base_size;
- }
-
- // Truncate the stored size to the amount of available memory. Stop now if
- // there's not any room for even part of the value.
- if (size != 0) {
- size = std::min(full_size - base_size, size);
- if (size == 0)
- return;
- }
-
- // Allocate a chunk of memory.
- FieldHeader* header = reinterpret_cast<FieldHeader*>(memory_);
- memory_ += full_size;
- available_ -= full_size;
-
- // Datafill the header and name records. Memory must be zeroed. The |type|
- // is written last, atomically, to release all the other values.
- DCHECK_EQ(END_OF_VALUES, header->type.load(std::memory_order_relaxed));
- DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed));
- header->name_size = static_cast<uint8_t>(name_size);
- header->record_size = full_size;
- char* name_memory = reinterpret_cast<char*>(header) + sizeof(FieldHeader);
- void* value_memory =
- reinterpret_cast<char*>(header) + sizeof(FieldHeader) + name_extent;
- memcpy(name_memory, name.data(), name_size);
- header->type.store(type, std::memory_order_release);
-
- // Create an entry in |values_| so that this field can be found and changed
- // later on without having to allocate new entries.
- StringPiece persistent_name(name_memory, name_size);
- auto inserted =
- values_.insert(std::make_pair(persistent_name, ValueInfo()));
- DCHECK(inserted.second); // True if inserted, false if existed.
- info = &inserted.first->second;
- info->name = persistent_name;
- info->memory = value_memory;
- info->size_ptr = &header->value_size;
- info->extent = full_size - sizeof(FieldHeader) - name_extent;
- info->type = type;
- }
-
- // Copy the value data to storage. The |size| is written last, atomically, to
- // release the copied data. Until then, a parallel reader will just ignore
- // records with a zero size.
- DCHECK_EQ(type, info->type);
- size = std::min(size, info->extent);
- info->size_ptr->store(0, std::memory_order_seq_cst);
- memcpy(info->memory, memory, size);
- info->size_ptr->store(size, std::memory_order_release);
-}
-
-void ActivityUserData::SetReference(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) {
- ReferenceRecord rec;
- rec.address = reinterpret_cast<uintptr_t>(memory);
- rec.size = size;
- Set(name, type, &rec, sizeof(rec));
-}
-
-void ActivityUserData::ImportExistingData() const {
- // It's possible that no user data is being stored.
- if (!memory_)
- return;
-
- while (available_ > sizeof(FieldHeader)) {
- FieldHeader* header = reinterpret_cast<FieldHeader*>(memory_);
- ValueType type =
- static_cast<ValueType>(header->type.load(std::memory_order_acquire));
- if (type == END_OF_VALUES)
- return;
- if (header->record_size > available_)
- return;
-
- size_t value_offset = RoundUpToAlignment(
- sizeof(FieldHeader) + header->name_size, kMemoryAlignment);
- if (header->record_size == value_offset &&
- header->value_size.load(std::memory_order_relaxed) == 1) {
- value_offset -= 1;
- }
- if (value_offset + header->value_size > header->record_size)
- return;
-
- ValueInfo info;
- info.name = StringPiece(memory_ + sizeof(FieldHeader), header->name_size);
- info.type = type;
- info.memory = memory_ + value_offset;
- info.size_ptr = &header->value_size;
- info.extent = header->record_size - value_offset;
-
- StringPiece key(info.name);
- values_.insert(std::make_pair(key, std::move(info)));
-
- memory_ += header->record_size;
- available_ -= header->record_size;
- }
-
- // Check if memory has been completely reused.
- if (header_->owner.data_id.load(std::memory_order_acquire) != orig_data_id ||
- header_->owner.process_id != orig_process_id ||
- header_->owner.create_stamp != orig_create_stamp) {
- memory_ = nullptr;
- values_.clear();
- }
-}
-
-// This information is kept for every thread that is tracked. It is filled
-// the very first time the thread is seen. All fields must be of exact sizes
-// so there is no issue moving between 32 and 64-bit builds.
-struct ThreadActivityTracker::Header {
- // Defined in .h for analyzer access. Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId =
- GlobalActivityTracker::kTypeIdActivityTracker;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize =
- OwningProcess::kExpectedInstanceSize + Activity::kExpectedInstanceSize +
- 72;
-
- // This information uniquely identifies a process.
- OwningProcess owner;
-
- // The thread-id (thread_ref.as_id) to which this data belongs. This number
- // is not guaranteed to mean anything but combined with the process-id from
- // OwningProcess is unique among all active trackers.
- ThreadRef thread_ref;
-
- // The start-time and start-ticks when the data was created. Each activity
- // record has a |time_internal| value that can be converted to a "wall time"
- // with these two values.
- int64_t start_time;
- int64_t start_ticks;
-
- // The number of Activity slots (spaces that can hold an Activity) that
- // immediately follow this structure in memory.
- uint32_t stack_slots;
-
- // Some padding to keep everything 64-bit aligned.
- uint32_t padding;
-
- // The current depth of the stack. This may be greater than the number of
- // slots. If the depth exceeds the number of slots, the newest entries
- // won't be recorded.
- std::atomic<uint32_t> current_depth;
-
- // A memory location used to indicate if changes have been made to the data
- // that would invalidate an in-progress read of its contents. The active
- // tracker will increment the value whenever something gets popped from the
- // stack. A monitoring tracker can check the value before and after access
- // to know, if it's still the same, that the contents didn't change while
- // being copied.
- std::atomic<uint32_t> data_version;
-
- // The last "exception" activity. This can't be stored on the stack because
- // that could get popped as things unwind.
- Activity last_exception;
-
- // The name of the thread (up to a maximum length). Dynamic-length names
- // are not practical since the memory has to come from the same persistent
- // allocator that holds this structure and to which this object has no
- // reference.
- char thread_name[32];
-};
-
-ThreadActivityTracker::Snapshot::Snapshot() = default;
-ThreadActivityTracker::Snapshot::~Snapshot() = default;
-
-ThreadActivityTracker::ScopedActivity::ScopedActivity(
- ThreadActivityTracker* tracker,
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data)
- : tracker_(tracker) {
- if (tracker_)
- activity_id_ = tracker_->PushActivity(program_counter, origin, type, data);
-}
-
-ThreadActivityTracker::ScopedActivity::~ScopedActivity() {
- if (tracker_)
- tracker_->PopActivity(activity_id_);
-}
-
-void ThreadActivityTracker::ScopedActivity::ChangeTypeAndData(
- Activity::Type type,
- const ActivityData& data) {
- if (tracker_)
- tracker_->ChangeActivity(activity_id_, type, data);
-}
-
-ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size)
- : header_(static_cast<Header*>(base)),
- stack_(reinterpret_cast<Activity*>(reinterpret_cast<char*>(base) +
- sizeof(Header))),
-#if DCHECK_IS_ON()
- thread_id_(PlatformThreadRef()),
-#endif
- stack_slots_(
- static_cast<uint32_t>((size - sizeof(Header)) / sizeof(Activity))) {
-
- // Verify the parameters but fail gracefully if they're not valid so that
- // production code based on external inputs will not crash. IsValid() will
- // return false in this case.
- if (!base ||
- // Ensure there is enough space for the header and at least a few records.
- size < sizeof(Header) + kMinStackDepth * sizeof(Activity) ||
- // Ensure that the |stack_slots_| calculation didn't overflow.
- (size - sizeof(Header)) / sizeof(Activity) >
- std::numeric_limits<uint32_t>::max()) {
- NOTREACHED();
- return;
- }
-
- // Ensure that the thread reference doesn't exceed the size of the ID number.
- // This won't compile at the global scope because Header is a private struct.
- static_assert(
- sizeof(header_->thread_ref) == sizeof(header_->thread_ref.as_id),
- "PlatformThreadHandle::Handle is too big to hold in 64-bit ID");
-
- // Ensure that the alignment of Activity.data is properly aligned to a
- // 64-bit boundary so there are no interoperability-issues across cpu
- // architectures.
- static_assert(offsetof(Activity, data) % sizeof(uint64_t) == 0,
- "ActivityData.data is not 64-bit aligned");
-
- // Provided memory should either be completely initialized or all zeros.
- if (header_->owner.data_id.load(std::memory_order_relaxed) == 0) {
- // This is a new file. Double-check other fields and then initialize.
- DCHECK_EQ(0, header_->owner.process_id);
- DCHECK_EQ(0, header_->owner.create_stamp);
- DCHECK_EQ(0, header_->thread_ref.as_id);
- DCHECK_EQ(0, header_->start_time);
- DCHECK_EQ(0, header_->start_ticks);
- DCHECK_EQ(0U, header_->stack_slots);
- DCHECK_EQ(0U, header_->current_depth.load(std::memory_order_relaxed));
- DCHECK_EQ(0U, header_->data_version.load(std::memory_order_relaxed));
- DCHECK_EQ(0, stack_[0].time_internal);
- DCHECK_EQ(0U, stack_[0].origin_address);
- DCHECK_EQ(0U, stack_[0].call_stack[0]);
- DCHECK_EQ(0U, stack_[0].data.task.sequence_id);
-
-#if defined(OS_WIN)
- header_->thread_ref.as_tid = PlatformThread::CurrentId();
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- header_->thread_ref.as_handle =
- PlatformThread::CurrentHandle().platform_handle();
-#endif
-
- header_->start_time = base::Time::Now().ToInternalValue();
- header_->start_ticks = base::TimeTicks::Now().ToInternalValue();
- header_->stack_slots = stack_slots_;
- strlcpy(header_->thread_name, PlatformThread::GetName(),
- sizeof(header_->thread_name));
-
- // This is done last so as to guarantee that everything above is "released"
- // by the time this value gets written.
- header_->owner.Release_Initialize();
-
- valid_ = true;
- DCHECK(IsValid());
- } else {
- // This is a file with existing data. Perform basic consistency checks.
- valid_ = true;
- valid_ = IsValid();
- }
-}
-
-ThreadActivityTracker::~ThreadActivityTracker() = default;
-
-ThreadActivityTracker::ActivityId ThreadActivityTracker::PushActivity(
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data) {
- // A thread-checker creates a lock to check the thread-id which means
- // re-entry into this code if lock acquisitions are being tracked.
- DCHECK(type == Activity::ACT_LOCK_ACQUIRE || CalledOnValidThread());
-
- // Get the current depth of the stack. No access to other memory guarded
- // by this variable is done here so a "relaxed" load is acceptable.
- uint32_t depth = header_->current_depth.load(std::memory_order_relaxed);
-
- // Handle the case where the stack depth has exceeded the storage capacity.
- // Extra entries will be lost leaving only the base of the stack.
- if (depth >= stack_slots_) {
- // Since no other threads modify the data, no compare/exchange is needed.
- // Since no other memory is being modified, a "relaxed" store is acceptable.
- header_->current_depth.store(depth + 1, std::memory_order_relaxed);
- return depth;
- }
-
- // Get a pointer to the next activity and load it. No atomicity is required
- // here because the memory is known only to this thread. It will be made
- // known to other threads once the depth is incremented.
- Activity::FillFrom(&stack_[depth], program_counter, origin, type, data);
-
- // Save the incremented depth. Because this guards |activity| memory filled
- // above that may be read by another thread once the recorded depth changes,
- // a "release" store is required.
- header_->current_depth.store(depth + 1, std::memory_order_release);
-
- // The current depth is used as the activity ID because it simply identifies
- // an entry. Once an entry is pop'd, it's okay to reuse the ID.
- return depth;
-}
-
-void ThreadActivityTracker::ChangeActivity(ActivityId id,
- Activity::Type type,
- const ActivityData& data) {
- DCHECK(CalledOnValidThread());
- DCHECK(type != Activity::ACT_NULL || &data != &kNullActivityData);
- DCHECK_LT(id, header_->current_depth.load(std::memory_order_acquire));
-
- // Update the information if it is being recorded (i.e. within slot limit).
- if (id < stack_slots_) {
- Activity* activity = &stack_[id];
-
- if (type != Activity::ACT_NULL) {
- DCHECK_EQ(activity->activity_type & Activity::ACT_CATEGORY_MASK,
- type & Activity::ACT_CATEGORY_MASK);
- activity->activity_type = type;
- }
-
- if (&data != &kNullActivityData)
- activity->data = data;
- }
-}
-
-void ThreadActivityTracker::PopActivity(ActivityId id) {
- // Do an atomic decrement of the depth. No changes to stack entries guarded
- // by this variable are done here so a "relaxed" operation is acceptable.
- // |depth| will receive the value BEFORE it was modified which means the
- // return value must also be decremented. The slot will be "free" after
- // this call but since only a single thread can access this object, the
- // data will remain valid until this method returns or calls outside.
- uint32_t depth =
- header_->current_depth.fetch_sub(1, std::memory_order_relaxed) - 1;
-
- // Validate that everything is running correctly.
- DCHECK_EQ(id, depth);
-
- // A thread-checker creates a lock to check the thread-id which means
- // re-entry into this code if lock acquisitions are being tracked.
- DCHECK(stack_[depth].activity_type == Activity::ACT_LOCK_ACQUIRE ||
- CalledOnValidThread());
-
- // The stack has shrunk meaning that some other thread trying to copy the
- // contents for reporting purposes could get bad data. Increment the data
- // version so that it con tell that things have changed. This needs to
- // happen after the atomic |depth| operation above so a "release" store
- // is required.
- header_->data_version.fetch_add(1, std::memory_order_release);
-}
-
-std::unique_ptr<ActivityUserData> ThreadActivityTracker::GetUserData(
- ActivityId id,
- ActivityTrackerMemoryAllocator* allocator) {
- // Don't allow user data for lock acquisition as recursion may occur.
- if (stack_[id].activity_type == Activity::ACT_LOCK_ACQUIRE) {
- NOTREACHED();
- return std::make_unique<ActivityUserData>();
- }
-
- // User-data is only stored for activities actually held in the stack.
- if (id >= stack_slots_)
- return std::make_unique<ActivityUserData>();
-
- // Create and return a real UserData object.
- return CreateUserDataForActivity(&stack_[id], allocator);
-}
-
-bool ThreadActivityTracker::HasUserData(ActivityId id) {
- // User-data is only stored for activities actually held in the stack.
- return (id < stack_slots_ && stack_[id].user_data_ref);
-}
-
-void ThreadActivityTracker::ReleaseUserData(
- ActivityId id,
- ActivityTrackerMemoryAllocator* allocator) {
- // User-data is only stored for activities actually held in the stack.
- if (id < stack_slots_ && stack_[id].user_data_ref) {
- allocator->ReleaseObjectReference(stack_[id].user_data_ref);
- stack_[id].user_data_ref = 0;
- }
-}
-
-void ThreadActivityTracker::RecordExceptionActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data) {
- // A thread-checker creates a lock to check the thread-id which means
- // re-entry into this code if lock acquisitions are being tracked.
- DCHECK(CalledOnValidThread());
-
- // Fill the reusable exception activity.
- Activity::FillFrom(&header_->last_exception, program_counter, origin, type,
- data);
-
- // The data has changed meaning that some other thread trying to copy the
- // contents for reporting purposes could get bad data.
- header_->data_version.fetch_add(1, std::memory_order_relaxed);
-}
-
-bool ThreadActivityTracker::IsValid() const {
- if (header_->owner.data_id.load(std::memory_order_acquire) == 0 ||
- header_->owner.process_id == 0 || header_->thread_ref.as_id == 0 ||
- header_->start_time == 0 || header_->start_ticks == 0 ||
- header_->stack_slots != stack_slots_ ||
- header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') {
- return false;
- }
-
- return valid_;
-}
-
-bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const {
- DCHECK(output_snapshot);
-
- // There is no "called on valid thread" check for this method as it can be
- // called from other threads or even other processes. It is also the reason
- // why atomic operations must be used in certain places above.
-
- // It's possible for the data to change while reading it in such a way that it
- // invalidates the read. Make several attempts but don't try forever.
- const int kMaxAttempts = 10;
- uint32_t depth;
-
- // Stop here if the data isn't valid.
- if (!IsValid())
- return false;
-
- // Allocate the maximum size for the stack so it doesn't have to be done
- // during the time-sensitive snapshot operation. It is shrunk once the
- // actual size is known.
- output_snapshot->activity_stack.reserve(stack_slots_);
-
- for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
- // Remember the data IDs to ensure nothing is replaced during the snapshot
- // operation. Use "acquire" so that all the non-atomic fields of the
- // structure are valid (at least at the current moment in time).
- const uint32_t starting_id =
- header_->owner.data_id.load(std::memory_order_acquire);
- const int64_t starting_create_stamp = header_->owner.create_stamp;
- const int64_t starting_process_id = header_->owner.process_id;
- const int64_t starting_thread_id = header_->thread_ref.as_id;
-
- // Note the current |data_version| so it's possible to detect at the end
- // that nothing has changed since copying the data began. A "cst" operation
- // is required to ensure it occurs before everything else. Using "cst"
- // memory ordering is relatively expensive but this is only done during
- // analysis so doesn't directly affect the worker threads.
- const uint32_t pre_version =
- header_->data_version.load(std::memory_order_seq_cst);
-
- // Fetching the current depth also "acquires" the contents of the stack.
- depth = header_->current_depth.load(std::memory_order_acquire);
- uint32_t count = std::min(depth, stack_slots_);
- output_snapshot->activity_stack.resize(count);
- if (count > 0) {
- // Copy the existing contents. Memcpy is used for speed.
- memcpy(&output_snapshot->activity_stack[0], stack_,
- count * sizeof(Activity));
- }
-
- // Capture the last exception.
- memcpy(&output_snapshot->last_exception, &header_->last_exception,
- sizeof(Activity));
-
- // TODO(bcwhite): Snapshot other things here.
-
- // Retry if something changed during the copy. A "cst" operation ensures
- // it must happen after all the above operations.
- if (header_->data_version.load(std::memory_order_seq_cst) != pre_version)
- continue;
-
- // Stack copied. Record it's full depth.
- output_snapshot->activity_stack_depth = depth;
-
- // Get the general thread information.
- output_snapshot->thread_name =
- std::string(header_->thread_name, sizeof(header_->thread_name) - 1);
- output_snapshot->create_stamp = header_->owner.create_stamp;
- output_snapshot->thread_id = header_->thread_ref.as_id;
- output_snapshot->process_id = header_->owner.process_id;
-
- // All characters of the thread-name buffer were copied so as to not break
- // if the trailing NUL were missing. Now limit the length if the actual
- // name is shorter.
- output_snapshot->thread_name.resize(
- strlen(output_snapshot->thread_name.c_str()));
-
- // If the data ID has changed then the tracker has exited and the memory
- // reused by a new one. Try again.
- if (header_->owner.data_id.load(std::memory_order_seq_cst) != starting_id ||
- output_snapshot->create_stamp != starting_create_stamp ||
- output_snapshot->process_id != starting_process_id ||
- output_snapshot->thread_id != starting_thread_id) {
- continue;
- }
-
- // Only successful if the data is still valid once everything is done since
- // it's possible for the thread to end somewhere in the middle and all its
- // values become garbage.
- if (!IsValid())
- return false;
-
- // Change all the timestamps in the activities from "ticks" to "wall" time.
- const Time start_time = Time::FromInternalValue(header_->start_time);
- const int64_t start_ticks = header_->start_ticks;
- for (Activity& activity : output_snapshot->activity_stack) {
- activity.time_internal =
- WallTimeFromTickTime(start_ticks, activity.time_internal, start_time)
- .ToInternalValue();
- }
- output_snapshot->last_exception.time_internal =
- WallTimeFromTickTime(start_ticks,
- output_snapshot->last_exception.time_internal,
- start_time)
- .ToInternalValue();
-
- // Success!
- return true;
- }
-
- // Too many attempts.
- return false;
-}
-
-const void* ThreadActivityTracker::GetBaseAddress() {
- return header_;
-}
-
-uint32_t ThreadActivityTracker::GetDataVersionForTesting() {
- return header_->data_version.load(std::memory_order_relaxed);
-}
-
-void ThreadActivityTracker::SetOwningProcessIdForTesting(int64_t pid,
- int64_t stamp) {
- header_->owner.SetOwningProcessIdForTesting(pid, stamp);
-}
-
-// static
-bool ThreadActivityTracker::GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp) {
- const Header* header = reinterpret_cast<const Header*>(memory);
- return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp);
-}
-
-// static
-size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) {
- return static_cast<size_t>(stack_depth) * sizeof(Activity) + sizeof(Header);
-}
-
-bool ThreadActivityTracker::CalledOnValidThread() {
-#if DCHECK_IS_ON()
- return thread_id_ == PlatformThreadRef();
-#else
- return true;
-#endif
-}
-
-std::unique_ptr<ActivityUserData>
-ThreadActivityTracker::CreateUserDataForActivity(
- Activity* activity,
- ActivityTrackerMemoryAllocator* allocator) {
- DCHECK_EQ(0U, activity->user_data_ref);
-
- PersistentMemoryAllocator::Reference ref = allocator->GetObjectReference();
- void* memory = allocator->GetAsArray<char>(ref, kUserDataSize);
- if (memory) {
- std::unique_ptr<ActivityUserData> user_data =
- std::make_unique<ActivityUserData>(memory, kUserDataSize);
- activity->user_data_ref = ref;
- activity->user_data_id = user_data->id();
- return user_data;
- }
-
- // Return a dummy object that will still accept (but ignore) Set() calls.
- return std::make_unique<ActivityUserData>();
-}
-
-// The instantiation of the GlobalActivityTracker object.
-// The object held here will obviously not be destructed at process exit
-// but that's best since PersistentMemoryAllocator objects (that underlie
-// GlobalActivityTracker objects) are explicitly forbidden from doing anything
-// essential at exit anyway due to the fact that they depend on data managed
-// elsewhere and which could be destructed first. An AtomicWord is used instead
-// of std::atomic because the latter can create global ctors and dtors.
-subtle::AtomicWord GlobalActivityTracker::g_tracker_ = 0;
-
-GlobalActivityTracker::ModuleInfo::ModuleInfo() = default;
-GlobalActivityTracker::ModuleInfo::ModuleInfo(ModuleInfo&& rhs) = default;
-GlobalActivityTracker::ModuleInfo::ModuleInfo(const ModuleInfo& rhs) = default;
-GlobalActivityTracker::ModuleInfo::~ModuleInfo() = default;
-
-GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=(
- ModuleInfo&& rhs) = default;
-GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=(
- const ModuleInfo& rhs) = default;
-
-GlobalActivityTracker::ModuleInfoRecord::ModuleInfoRecord() = default;
-GlobalActivityTracker::ModuleInfoRecord::~ModuleInfoRecord() = default;
-
-bool GlobalActivityTracker::ModuleInfoRecord::DecodeTo(
- GlobalActivityTracker::ModuleInfo* info,
- size_t record_size) const {
- // Get the current "changes" indicator, acquiring all the other values.
- uint32_t current_changes = changes.load(std::memory_order_acquire);
-
- // Copy out the dynamic information.
- info->is_loaded = loaded != 0;
- info->address = static_cast<uintptr_t>(address);
- info->load_time = load_time;
-
- // Check to make sure no information changed while being read. A "seq-cst"
- // operation is expensive but is only done during analysis and it's the only
- // way to ensure this occurs after all the accesses above. If changes did
- // occur then return a "not loaded" result so that |size| and |address|
- // aren't expected to be accurate.
- if ((current_changes & kModuleInformationChanging) != 0 ||
- changes.load(std::memory_order_seq_cst) != current_changes) {
- info->is_loaded = false;
- }
-
- // Copy out the static information. These never change so don't have to be
- // protected by the atomic |current_changes| operations.
- info->size = static_cast<size_t>(size);
- info->timestamp = timestamp;
- info->age = age;
- memcpy(info->identifier, identifier, sizeof(info->identifier));
-
- if (offsetof(ModuleInfoRecord, pickle) + pickle_size > record_size)
- return false;
- Pickle pickler(pickle, pickle_size);
- PickleIterator iter(pickler);
- return iter.ReadString(&info->file) && iter.ReadString(&info->debug_file);
-}
-
-GlobalActivityTracker::ModuleInfoRecord*
-GlobalActivityTracker::ModuleInfoRecord::CreateFrom(
- const GlobalActivityTracker::ModuleInfo& info,
- PersistentMemoryAllocator* allocator) {
- Pickle pickler;
- pickler.WriteString(info.file);
- pickler.WriteString(info.debug_file);
- size_t required_size = offsetof(ModuleInfoRecord, pickle) + pickler.size();
- ModuleInfoRecord* record = allocator->New<ModuleInfoRecord>(required_size);
- if (!record)
- return nullptr;
-
- // These fields never changes and are done before the record is made
- // iterable so no thread protection is necessary.
- record->size = info.size;
- record->timestamp = info.timestamp;
- record->age = info.age;
- memcpy(record->identifier, info.identifier, sizeof(identifier));
- memcpy(record->pickle, pickler.data(), pickler.size());
- record->pickle_size = pickler.size();
- record->changes.store(0, std::memory_order_relaxed);
-
- // Initialize the owner info.
- record->owner.Release_Initialize();
-
- // Now set those fields that can change.
- bool success = record->UpdateFrom(info);
- DCHECK(success);
- return record;
-}
-
-bool GlobalActivityTracker::ModuleInfoRecord::UpdateFrom(
- const GlobalActivityTracker::ModuleInfo& info) {
- // Updates can occur after the record is made visible so make changes atomic.
- // A "strong" exchange ensures no false failures.
- uint32_t old_changes = changes.load(std::memory_order_relaxed);
- uint32_t new_changes = old_changes | kModuleInformationChanging;
- if ((old_changes & kModuleInformationChanging) != 0 ||
- !changes.compare_exchange_strong(old_changes, new_changes,
- std::memory_order_acquire,
- std::memory_order_acquire)) {
- NOTREACHED() << "Multiple sources are updating module information.";
- return false;
- }
-
- loaded = info.is_loaded ? 1 : 0;
- address = info.address;
- load_time = Time::Now().ToInternalValue();
-
- bool success = changes.compare_exchange_strong(new_changes, old_changes + 1,
- std::memory_order_release,
- std::memory_order_relaxed);
- DCHECK(success);
- return true;
-}
-
-GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity(
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data,
- bool lock_allowed)
- : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed),
- program_counter,
- origin,
- type,
- data) {}
-
-GlobalActivityTracker::ScopedThreadActivity::~ScopedThreadActivity() {
- if (tracker_ && tracker_->HasUserData(activity_id_)) {
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- AutoLock lock(global->user_data_allocator_lock_);
- tracker_->ReleaseUserData(activity_id_, &global->user_data_allocator_);
- }
-}
-
-ActivityUserData& GlobalActivityTracker::ScopedThreadActivity::user_data() {
- if (!user_data_) {
- if (tracker_) {
- GlobalActivityTracker* global = GlobalActivityTracker::Get();
- AutoLock lock(global->user_data_allocator_lock_);
- user_data_ =
- tracker_->GetUserData(activity_id_, &global->user_data_allocator_);
- } else {
- user_data_ = std::make_unique<ActivityUserData>();
- }
- }
- return *user_data_;
-}
-
-GlobalActivityTracker::ThreadSafeUserData::ThreadSafeUserData(void* memory,
- size_t size,
- int64_t pid)
- : ActivityUserData(memory, size, pid) {}
-
-GlobalActivityTracker::ThreadSafeUserData::~ThreadSafeUserData() = default;
-
-void GlobalActivityTracker::ThreadSafeUserData::Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) {
- AutoLock lock(data_lock_);
- ActivityUserData::Set(name, type, memory, size);
-}
-
-GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker(
- PersistentMemoryAllocator::Reference mem_reference,
- void* base,
- size_t size)
- : ThreadActivityTracker(base, size),
- mem_reference_(mem_reference),
- mem_base_(base) {}
-
-GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() {
- // The global |g_tracker_| must point to the owner of this class since all
- // objects of this type must be destructed before |g_tracker_| can be changed
- // (something that only occurs in tests).
- DCHECK(g_tracker_);
- GlobalActivityTracker::Get()->ReturnTrackerMemory(this);
-}
-
-void GlobalActivityTracker::CreateWithAllocator(
- std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id) {
- // There's no need to do anything with the result. It is self-managing.
- GlobalActivityTracker* global_tracker =
- new GlobalActivityTracker(std::move(allocator), stack_depth, process_id);
- // Create a tracker for this thread since it is known.
- global_tracker->CreateTrackerForCurrentThread();
-}
-
-#if !defined(OS_NACL)
-// static
-bool GlobalActivityTracker::CreateWithFile(const FilePath& file_path,
- size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth) {
- DCHECK(!file_path.empty());
- DCHECK_GE(static_cast<uint64_t>(std::numeric_limits<int64_t>::max()), size);
-
- // Create and map the file into memory and make it globally available.
- std::unique_ptr<MemoryMappedFile> mapped_file(new MemoryMappedFile());
- bool success = mapped_file->Initialize(
- File(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
- File::FLAG_WRITE | File::FLAG_SHARE_DELETE),
- {0, size}, MemoryMappedFile::READ_WRITE_EXTEND);
- if (!success)
- return false;
- if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mapped_file, false))
- return false;
- CreateWithAllocator(std::make_unique<FilePersistentMemoryAllocator>(
- std::move(mapped_file), size, id, name, false),
- stack_depth, 0);
- return true;
-}
-#endif // !defined(OS_NACL)
-
-// static
-bool GlobalActivityTracker::CreateWithLocalMemory(size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth,
- int64_t process_id) {
- CreateWithAllocator(
- std::make_unique<LocalPersistentMemoryAllocator>(size, id, name),
- stack_depth, process_id);
- return true;
-}
-
-// static
-bool GlobalActivityTracker::CreateWithSharedMemory(
- std::unique_ptr<SharedMemory> shm,
- uint64_t id,
- StringPiece name,
- int stack_depth) {
- if (shm->mapped_size() == 0 ||
- !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) {
- return false;
- }
- CreateWithAllocator(std::make_unique<SharedPersistentMemoryAllocator>(
- std::move(shm), id, name, false),
- stack_depth, 0);
- return true;
-}
-
-// static
-bool GlobalActivityTracker::CreateWithSharedMemoryHandle(
- const SharedMemoryHandle& handle,
- size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth) {
- std::unique_ptr<SharedMemory> shm(
- new SharedMemory(handle, /*readonly=*/false));
- if (!shm->Map(size))
- return false;
- return CreateWithSharedMemory(std::move(shm), id, name, stack_depth);
-}
-
-// static
-void GlobalActivityTracker::SetForTesting(
- std::unique_ptr<GlobalActivityTracker> tracker) {
- CHECK(!subtle::NoBarrier_Load(&g_tracker_));
- subtle::Release_Store(&g_tracker_,
- reinterpret_cast<uintptr_t>(tracker.release()));
-}
-
-// static
-std::unique_ptr<GlobalActivityTracker>
-GlobalActivityTracker::ReleaseForTesting() {
- GlobalActivityTracker* tracker = Get();
- if (!tracker)
- return nullptr;
-
- // Thread trackers assume that the global tracker is present for some
- // operations so ensure that there aren't any.
- tracker->ReleaseTrackerForCurrentThreadForTesting();
- DCHECK_EQ(0, tracker->thread_tracker_count_.load(std::memory_order_relaxed));
-
- subtle::Release_Store(&g_tracker_, 0);
- return WrapUnique(tracker);
-}
-
-ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() {
- DCHECK(!this_thread_tracker_.Get());
-
- PersistentMemoryAllocator::Reference mem_reference;
-
- {
- base::AutoLock autolock(thread_tracker_allocator_lock_);
- mem_reference = thread_tracker_allocator_.GetObjectReference();
- }
-
- if (!mem_reference) {
- // Failure. This shouldn't happen. But be graceful if it does, probably
- // because the underlying allocator wasn't given enough memory to satisfy
- // to all possible requests.
- NOTREACHED();
- // Report the thread-count at which the allocator was full so that the
- // failure can be seen and underlying memory resized appropriately.
- UMA_HISTOGRAM_COUNTS_1000(
- "ActivityTracker.ThreadTrackers.MemLimitTrackerCount",
- thread_tracker_count_.load(std::memory_order_relaxed));
- // Return null, just as if tracking wasn't enabled.
- return nullptr;
- }
-
- // Convert the memory block found above into an actual memory address.
- // Doing the conversion as a Header object enacts the 32/64-bit size
- // consistency checks which would not otherwise be done. Unfortunately,
- // some older compilers and MSVC don't have standard-conforming definitions
- // of std::atomic which cause it not to be plain-old-data. Don't check on
- // those platforms assuming that the checks on other platforms will be
- // sufficient.
- // TODO(bcwhite): Review this after major compiler releases.
- DCHECK(mem_reference);
- void* mem_base;
- mem_base =
- allocator_->GetAsObject<ThreadActivityTracker::Header>(mem_reference);
-
- DCHECK(mem_base);
- DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference));
-
- // Create a tracker with the acquired memory and set it as the tracker
- // for this particular thread in thread-local-storage.
- ManagedActivityTracker* tracker =
- new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_);
- DCHECK(tracker->IsValid());
- this_thread_tracker_.Set(tracker);
- int old_count = thread_tracker_count_.fetch_add(1, std::memory_order_relaxed);
-
- UMA_HISTOGRAM_EXACT_LINEAR("ActivityTracker.ThreadTrackers.Count",
- old_count + 1, static_cast<int>(kMaxThreadCount));
- return tracker;
-}
-
-void GlobalActivityTracker::ReleaseTrackerForCurrentThreadForTesting() {
- ThreadActivityTracker* tracker =
- reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get());
- if (tracker) {
- this_thread_tracker_.Set(nullptr);
- delete tracker;
- }
-}
-
-void GlobalActivityTracker::SetBackgroundTaskRunner(
- const scoped_refptr<TaskRunner>& runner) {
- AutoLock lock(global_tracker_lock_);
- background_task_runner_ = runner;
-}
-
-void GlobalActivityTracker::SetProcessExitCallback(
- ProcessExitCallback callback) {
- AutoLock lock(global_tracker_lock_);
- process_exit_callback_ = callback;
-}
-
-void GlobalActivityTracker::RecordProcessLaunch(
- ProcessId process_id,
- const FilePath::StringType& cmd) {
- const int64_t pid = process_id;
- DCHECK_NE(GetProcessId(), pid);
- DCHECK_NE(0, pid);
-
- base::AutoLock lock(global_tracker_lock_);
- if (base::ContainsKey(known_processes_, pid)) {
- // TODO(bcwhite): Measure this in UMA.
- NOTREACHED() << "Process #" << process_id
- << " was previously recorded as \"launched\""
- << " with no corresponding exit.\n"
- << known_processes_[pid];
- known_processes_.erase(pid);
- }
-
-#if defined(OS_WIN)
- known_processes_.insert(std::make_pair(pid, UTF16ToUTF8(cmd)));
-#else
- known_processes_.insert(std::make_pair(pid, cmd));
-#endif
-}
-
-void GlobalActivityTracker::RecordProcessLaunch(
- ProcessId process_id,
- const FilePath::StringType& exe,
- const FilePath::StringType& args) {
- if (exe.find(FILE_PATH_LITERAL(" "))) {
- RecordProcessLaunch(process_id,
- FilePath::StringType(FILE_PATH_LITERAL("\"")) + exe +
- FILE_PATH_LITERAL("\" ") + args);
- } else {
- RecordProcessLaunch(process_id, exe + FILE_PATH_LITERAL(' ') + args);
- }
-}
-
-void GlobalActivityTracker::RecordProcessExit(ProcessId process_id,
- int exit_code) {
- const int64_t pid = process_id;
- DCHECK_NE(GetProcessId(), pid);
- DCHECK_NE(0, pid);
-
- scoped_refptr<TaskRunner> task_runner;
- std::string command_line;
- {
- base::AutoLock lock(global_tracker_lock_);
- task_runner = background_task_runner_;
- auto found = known_processes_.find(pid);
- if (found != known_processes_.end()) {
- command_line = std::move(found->second);
- known_processes_.erase(found);
- } else {
- DLOG(ERROR) << "Recording exit of unknown process #" << process_id;
- }
- }
-
- // Use the current time to differentiate the process that just exited
- // from any that might be created in the future with the same ID.
- int64_t now_stamp = Time::Now().ToInternalValue();
-
- // The persistent allocator is thread-safe so run the iteration and
- // adjustments on a worker thread if one was provided.
- if (task_runner && !task_runner->RunsTasksInCurrentSequence()) {
- task_runner->PostTask(
- FROM_HERE,
- BindOnce(&GlobalActivityTracker::CleanupAfterProcess, Unretained(this),
- pid, now_stamp, exit_code, std::move(command_line)));
- return;
- }
-
- CleanupAfterProcess(pid, now_stamp, exit_code, std::move(command_line));
-}
-
-void GlobalActivityTracker::SetProcessPhase(ProcessPhase phase) {
- process_data().SetInt(kProcessPhaseDataKey, phase);
-}
-
-void GlobalActivityTracker::CleanupAfterProcess(int64_t process_id,
- int64_t exit_stamp,
- int exit_code,
- std::string&& command_line) {
- // The process may not have exited cleanly so its necessary to go through
- // all the data structures it may have allocated in the persistent memory
- // segment and mark them as "released". This will allow them to be reused
- // later on.
-
- PersistentMemoryAllocator::Iterator iter(allocator_.get());
- PersistentMemoryAllocator::Reference ref;
-
- ProcessExitCallback process_exit_callback;
- {
- AutoLock lock(global_tracker_lock_);
- process_exit_callback = process_exit_callback_;
- }
- if (process_exit_callback) {
- // Find the processes user-data record so the process phase can be passed
- // to the callback.
- ActivityUserData::Snapshot process_data_snapshot;
- while ((ref = iter.GetNextOfType(kTypeIdProcessDataRecord)) != 0) {
- const void* memory = allocator_->GetAsArray<char>(
- ref, kTypeIdProcessDataRecord, PersistentMemoryAllocator::kSizeAny);
- if (!memory)
- continue;
- int64_t found_id;
- int64_t create_stamp;
- if (ActivityUserData::GetOwningProcessId(memory, &found_id,
- &create_stamp)) {
- if (found_id == process_id && create_stamp < exit_stamp) {
- const ActivityUserData process_data(const_cast<void*>(memory),
- allocator_->GetAllocSize(ref));
- process_data.CreateSnapshot(&process_data_snapshot);
- break; // No need to look for any others.
- }
- }
- }
- iter.Reset(); // So it starts anew when used below.
-
- // Record the process's phase at exit so callback doesn't need to go
- // searching based on a private key value.
- ProcessPhase exit_phase = PROCESS_PHASE_UNKNOWN;
- auto phase = process_data_snapshot.find(kProcessPhaseDataKey);
- if (phase != process_data_snapshot.end())
- exit_phase = static_cast<ProcessPhase>(phase->second.GetInt());
-
- // Perform the callback.
- process_exit_callback.Run(process_id, exit_stamp, exit_code, exit_phase,
- std::move(command_line),
- std::move(process_data_snapshot));
- }
-
- // Find all allocations associated with the exited process and free them.
- uint32_t type;
- while ((ref = iter.GetNext(&type)) != 0) {
- switch (type) {
- case kTypeIdActivityTracker:
- case kTypeIdUserDataRecord:
- case kTypeIdProcessDataRecord:
- case ModuleInfoRecord::kPersistentTypeId: {
- const void* memory = allocator_->GetAsArray<char>(
- ref, type, PersistentMemoryAllocator::kSizeAny);
- if (!memory)
- continue;
- int64_t found_id;
- int64_t create_stamp;
-
- // By convention, the OwningProcess structure is always the first
- // field of the structure so there's no need to handle all the
- // cases separately.
- if (OwningProcess::GetOwningProcessId(memory, &found_id,
- &create_stamp)) {
- // Only change the type to be "free" if the process ID matches and
- // the creation time is before the exit time (so PID re-use doesn't
- // cause the erasure of something that is in-use). Memory is cleared
- // here, rather than when it's needed, so as to limit the impact at
- // that critical time.
- if (found_id == process_id && create_stamp < exit_stamp)
- allocator_->ChangeType(ref, ~type, type, /*clear=*/true);
- }
- } break;
- }
- }
-}
-
-void GlobalActivityTracker::RecordLogMessage(StringPiece message) {
- // Allocate at least one extra byte so the string is NUL terminated. All
- // memory returned by the allocator is guaranteed to be zeroed.
- PersistentMemoryAllocator::Reference ref =
- allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage);
- char* memory = allocator_->GetAsArray<char>(ref, kTypeIdGlobalLogMessage,
- message.size() + 1);
- if (memory) {
- memcpy(memory, message.data(), message.size());
- allocator_->MakeIterable(ref);
- }
-}
-
-void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) {
- AutoLock lock(modules_lock_);
- auto found = modules_.find(info.file);
- if (found != modules_.end()) {
- ModuleInfoRecord* record = found->second;
- DCHECK(record);
-
- // Update the basic state of module information that has been already
- // recorded. It is assumed that the string information (identifier,
- // version, etc.) remain unchanged which means that there's no need
- // to create a new record to accommodate a possibly longer length.
- record->UpdateFrom(info);
- return;
- }
-
- ModuleInfoRecord* record =
- ModuleInfoRecord::CreateFrom(info, allocator_.get());
- if (!record)
- return;
- allocator_->MakeIterable(record);
- modules_.emplace(info.file, record);
-}
-
-void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name,
- StringPiece group_name) {
- const std::string key = std::string("FieldTrial.") + trial_name;
- process_data_.SetString(key, group_name);
-}
-
-void GlobalActivityTracker::RecordException(const void* pc,
- const void* origin,
- uint32_t code) {
- RecordExceptionImpl(pc, origin, code);
-}
-
-void GlobalActivityTracker::MarkDeleted() {
- allocator_->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
-}
-
-GlobalActivityTracker::GlobalActivityTracker(
- std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id)
- : allocator_(std::move(allocator)),
- stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)),
- process_id_(process_id == 0 ? GetCurrentProcId() : process_id),
- this_thread_tracker_(&OnTLSDestroy),
- thread_tracker_count_(0),
- thread_tracker_allocator_(allocator_.get(),
- kTypeIdActivityTracker,
- kTypeIdActivityTrackerFree,
- stack_memory_size_,
- kCachedThreadMemories,
- /*make_iterable=*/true),
- user_data_allocator_(allocator_.get(),
- kTypeIdUserDataRecord,
- kTypeIdUserDataRecordFree,
- kUserDataSize,
- kCachedUserDataMemories,
- /*make_iterable=*/true),
- process_data_(allocator_->GetAsArray<char>(
- AllocateFrom(allocator_.get(),
- kTypeIdProcessDataRecordFree,
- kProcessDataSize,
- kTypeIdProcessDataRecord),
- kTypeIdProcessDataRecord,
- kProcessDataSize),
- kProcessDataSize,
- process_id_) {
- DCHECK_NE(0, process_id_);
-
- // Ensure that there is no other global object and then make this one such.
- DCHECK(!g_tracker_);
- subtle::Release_Store(&g_tracker_, reinterpret_cast<uintptr_t>(this));
-
- // The data records must be iterable in order to be found by an analyzer.
- allocator_->MakeIterable(allocator_->GetAsReference(
- process_data_.GetBaseAddress(), kTypeIdProcessDataRecord));
-
- // Note that this process has launched.
- SetProcessPhase(PROCESS_LAUNCHED);
-
- // Fetch and record all activated field trials.
- FieldTrial::ActiveGroups active_groups;
- FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
- for (auto& group : active_groups)
- RecordFieldTrial(group.trial_name, group.group_name);
-}
-
-GlobalActivityTracker::~GlobalActivityTracker() {
- DCHECK(Get() == nullptr || Get() == this);
- DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed));
- subtle::Release_Store(&g_tracker_, 0);
-}
-
-void GlobalActivityTracker::ReturnTrackerMemory(
- ManagedActivityTracker* tracker) {
- PersistentMemoryAllocator::Reference mem_reference = tracker->mem_reference_;
- void* mem_base = tracker->mem_base_;
- DCHECK(mem_reference);
- DCHECK(mem_base);
-
- // Remove the destructed tracker from the set of known ones.
- DCHECK_LE(1, thread_tracker_count_.load(std::memory_order_relaxed));
- thread_tracker_count_.fetch_sub(1, std::memory_order_relaxed);
-
- // Release this memory for re-use at a later time.
- base::AutoLock autolock(thread_tracker_allocator_lock_);
- thread_tracker_allocator_.ReleaseObjectReference(mem_reference);
-}
-
-void GlobalActivityTracker::RecordExceptionImpl(const void* pc,
- const void* origin,
- uint32_t code) {
- // Get an existing tracker for this thread. It's not possible to create
- // one at this point because such would involve memory allocations and
- // other potentially complex operations that can cause failures if done
- // within an exception handler. In most cases various operations will
- // have already created the tracker so this shouldn't generally be a
- // problem.
- ThreadActivityTracker* tracker = GetTrackerForCurrentThread();
- if (!tracker)
- return;
-
- tracker->RecordExceptionActivity(pc, origin, Activity::ACT_EXCEPTION,
- ActivityData::ForException(code));
-}
-
-// static
-void GlobalActivityTracker::OnTLSDestroy(void* value) {
- delete reinterpret_cast<ManagedActivityTracker*>(value);
-}
-
-ScopedActivity::ScopedActivity(const void* program_counter,
- uint8_t action,
- uint32_t id,
- int32_t info)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- static_cast<Activity::Type>(Activity::ACT_GENERIC | action),
- ActivityData::ForGeneric(id, info),
- /*lock_allowed=*/true),
- id_(id) {
- // The action must not affect the category bits of the activity type.
- DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK);
-}
-
-void ScopedActivity::ChangeAction(uint8_t action) {
- DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK);
- ChangeTypeAndData(static_cast<Activity::Type>(Activity::ACT_GENERIC | action),
- kNullActivityData);
-}
-
-void ScopedActivity::ChangeInfo(int32_t info) {
- ChangeTypeAndData(Activity::ACT_NULL, ActivityData::ForGeneric(id_, info));
-}
-
-void ScopedActivity::ChangeActionAndInfo(uint8_t action, int32_t info) {
- DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK);
- ChangeTypeAndData(static_cast<Activity::Type>(Activity::ACT_GENERIC | action),
- ActivityData::ForGeneric(id_, info));
-}
-
-ScopedTaskRunActivity::ScopedTaskRunActivity(
- const void* program_counter,
- const base::PendingTask& task)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- task.posted_from.program_counter(),
- Activity::ACT_TASK_RUN,
- ActivityData::ForTask(task.sequence_num),
- /*lock_allowed=*/true) {}
-
-ScopedLockAcquireActivity::ScopedLockAcquireActivity(
- const void* program_counter,
- const base::internal::LockImpl* lock)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_LOCK_ACQUIRE,
- ActivityData::ForLock(lock),
- /*lock_allowed=*/false) {}
-
-ScopedEventWaitActivity::ScopedEventWaitActivity(
- const void* program_counter,
- const base::WaitableEvent* event)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_EVENT_WAIT,
- ActivityData::ForEvent(event),
- /*lock_allowed=*/true) {}
-
-ScopedThreadJoinActivity::ScopedThreadJoinActivity(
- const void* program_counter,
- const base::PlatformThreadHandle* thread)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_THREAD_JOIN,
- ActivityData::ForThread(*thread),
- /*lock_allowed=*/true) {}
-
-#if !defined(OS_NACL) && !defined(OS_IOS)
-ScopedProcessWaitActivity::ScopedProcessWaitActivity(
- const void* program_counter,
- const base::Process* process)
- : GlobalActivityTracker::ScopedThreadActivity(
- program_counter,
- nullptr,
- Activity::ACT_PROCESS_WAIT,
- ActivityData::ForProcess(process->Pid()),
- /*lock_allowed=*/true) {}
-#endif
-
-} // namespace debug
-} // namespace base
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
deleted file mode 100644
index bfd9f9d..0000000
--- a/base/debug/activity_tracker.h
+++ /dev/null
@@ -1,1360 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Activity tracking provides a low-overhead method of collecting information
-// about the state of the application for analysis both while it is running
-// and after it has terminated unexpectedly. Its primary purpose is to help
-// locate reasons the browser becomes unresponsive by providing insight into
-// what all the various threads and processes are (or were) doing.
-
-#ifndef BASE_DEBUG_ACTIVITY_TRACKER_H_
-#define BASE_DEBUG_ACTIVITY_TRACKER_H_
-
-// std::atomic is undesired due to performance issues when used as global
-// variables. There are no such instances here. This module uses the
-// PersistentMemoryAllocator which also uses std::atomic and is written
-// by the same author.
-#include <atomic>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/location.h"
-#include "base/memory/shared_memory.h"
-#include "base/metrics/persistent_memory_allocator.h"
-#include "base/process/process_handle.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task_runner.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_local_storage.h"
-
-namespace base {
-
-struct PendingTask;
-
-class FilePath;
-class Lock;
-class PlatformThreadHandle;
-class Process;
-class WaitableEvent;
-
-namespace debug {
-
-class ThreadActivityTracker;
-
-
-enum : int {
- // The maximum number of call-stack addresses stored per activity. This
- // cannot be changed without also changing the version number of the
- // structure. See kTypeIdActivityTracker in GlobalActivityTracker.
- kActivityCallStackSize = 10,
-};
-
-// A class for keeping all information needed to verify that a structure is
-// associated with a given process.
-struct OwningProcess {
- OwningProcess();
- ~OwningProcess();
-
- // Initializes structure with the current process id and the current time.
- // These can uniquely identify a process. A unique non-zero data_id will be
- // set making it possible to tell using atomic reads if the data has changed.
- void Release_Initialize(int64_t pid = 0);
-
- // Explicitly sets the process ID.
- void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp);
-
- // Gets the associated process ID, in native form, and the creation timestamp
- // from memory without loading the entire structure for analysis. This will
- // return false if no valid process ID is available.
- static bool GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp);
-
- // SHA1(base::debug::OwningProcess): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0xB1179672 + 1;
-
- // Expected size for 32/64-bit check by PersistentMemoryAllocator.
- static constexpr size_t kExpectedInstanceSize = 24;
-
- std::atomic<uint32_t> data_id;
- uint32_t padding;
- int64_t process_id;
- int64_t create_stamp;
-};
-
-// The data associated with an activity is dependent upon the activity type.
-// This union defines all of the various fields. All fields must be explicitly
-// sized types to ensure no interoperability problems between 32-bit and
-// 64-bit systems.
-union ActivityData {
- // Expected size for 32/64-bit check.
- // TODO(bcwhite): VC2015 doesn't allow statics in unions. Fix when it does.
- // static constexpr size_t kExpectedInstanceSize = 8;
-
- // Generic activities don't have any defined structure.
- struct {
- uint32_t id; // An arbitrary identifier used for association.
- int32_t info; // An arbitrary value used for information purposes.
- } generic;
- struct {
- uint64_t sequence_id; // The sequence identifier of the posted task.
- } task;
- struct {
- uint64_t lock_address; // The memory address of the lock object.
- } lock;
- struct {
- uint64_t event_address; // The memory address of the event object.
- } event;
- struct {
- int64_t thread_id; // A unique identifier for a thread within a process.
- } thread;
- struct {
- int64_t process_id; // A unique identifier for a process.
- } process;
- struct {
- uint32_t code; // An "exception code" number.
- } exception;
-
- // These methods create an ActivityData object from the appropriate
- // parameters. Objects of this type should always be created this way to
- // ensure that no fields remain unpopulated should the set of recorded
- // fields change. They're defined inline where practical because they
- // reduce to loading a small local structure with a few values, roughly
- // the same as loading all those values into parameters.
-
- static ActivityData ForGeneric(uint32_t id, int32_t info) {
- ActivityData data;
- data.generic.id = id;
- data.generic.info = info;
- return data;
- }
-
- static ActivityData ForTask(uint64_t sequence) {
- ActivityData data;
- data.task.sequence_id = sequence;
- return data;
- }
-
- static ActivityData ForLock(const void* lock) {
- ActivityData data;
- data.lock.lock_address = reinterpret_cast<uintptr_t>(lock);
- return data;
- }
-
- static ActivityData ForEvent(const void* event) {
- ActivityData data;
- data.event.event_address = reinterpret_cast<uintptr_t>(event);
- return data;
- }
-
- static ActivityData ForThread(const PlatformThreadHandle& handle);
- static ActivityData ForThread(const int64_t id) {
- ActivityData data;
- data.thread.thread_id = id;
- return data;
- }
-
- static ActivityData ForProcess(const int64_t id) {
- ActivityData data;
- data.process.process_id = id;
- return data;
- }
-
- static ActivityData ForException(const uint32_t code) {
- ActivityData data;
- data.exception.code = code;
- return data;
- }
-};
-
-// A "null" activity-data that can be passed to indicate "do not change".
-extern const ActivityData kNullActivityData;
-
-
-// A helper class that is used for managing memory allocations within a
-// persistent memory allocator. Instances of this class are NOT thread-safe.
-// Use from a single thread or protect access with a lock.
-class BASE_EXPORT ActivityTrackerMemoryAllocator {
- public:
- using Reference = PersistentMemoryAllocator::Reference;
-
- // Creates a instance for allocating objects of a fixed |object_type|, a
- // corresponding |object_free| type, and the |object_size|. An internal
- // cache of the last |cache_size| released references will be kept for
- // quick future fetches. If |make_iterable| then allocated objects will
- // be marked "iterable" in the allocator.
- ActivityTrackerMemoryAllocator(PersistentMemoryAllocator* allocator,
- uint32_t object_type,
- uint32_t object_free_type,
- size_t object_size,
- size_t cache_size,
- bool make_iterable);
- ~ActivityTrackerMemoryAllocator();
-
- // Gets a reference to an object of the configured type. This can return
- // a null reference if it was not possible to allocate the memory.
- Reference GetObjectReference();
-
- // Returns an object to the "free" pool.
- void ReleaseObjectReference(Reference ref);
-
- // Helper function to access an object allocated using this instance.
- template <typename T>
- T* GetAsObject(Reference ref) {
- return allocator_->GetAsObject<T>(ref);
- }
-
- // Similar to GetAsObject() but converts references to arrays of objects.
- template <typename T>
- T* GetAsArray(Reference ref, size_t count) {
- return allocator_->GetAsArray<T>(ref, object_type_, count);
- }
-
- // The current "used size" of the internal cache, visible for testing.
- size_t cache_used() const { return cache_used_; }
-
- private:
- PersistentMemoryAllocator* const allocator_;
- const uint32_t object_type_;
- const uint32_t object_free_type_;
- const size_t object_size_;
- const size_t cache_size_;
- const bool make_iterable_;
-
- // An iterator for going through persistent memory looking for free'd objects.
- PersistentMemoryAllocator::Iterator iterator_;
-
- // The cache of released object memories.
- std::unique_ptr<Reference[]> cache_values_;
- size_t cache_used_;
-
- DISALLOW_COPY_AND_ASSIGN(ActivityTrackerMemoryAllocator);
-};
-
-
-// This structure is the full contents recorded for every activity pushed
-// onto the stack. The |activity_type| indicates what is actually stored in
-// the |data| field. All fields must be explicitly sized types to ensure no
-// interoperability problems between 32-bit and 64-bit systems.
-struct Activity {
- // SHA1(base::debug::Activity): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x99425159 + 1;
- // Expected size for 32/64-bit check. Update this if structure changes!
- static constexpr size_t kExpectedInstanceSize =
- 48 + 8 * kActivityCallStackSize;
-
- // The type of an activity on the stack. Activities are broken into
- // categories with the category ID taking the top 4 bits and the lower
- // bits representing an action within that category. This combination
- // makes it easy to "switch" based on the type during analysis.
- enum Type : uint8_t {
- // This "null" constant is used to indicate "do not change" in calls.
- ACT_NULL = 0,
-
- // Task activities involve callbacks posted to a thread or thread-pool
- // using the PostTask() method or any of its friends.
- ACT_TASK = 1 << 4,
- ACT_TASK_RUN = ACT_TASK,
-
- // Lock activities involve the acquisition of "mutex" locks.
- ACT_LOCK = 2 << 4,
- ACT_LOCK_ACQUIRE = ACT_LOCK,
- ACT_LOCK_RELEASE,
-
- // Event activities involve operations on a WaitableEvent.
- ACT_EVENT = 3 << 4,
- ACT_EVENT_WAIT = ACT_EVENT,
- ACT_EVENT_SIGNAL,
-
- // Thread activities involve the life management of threads.
- ACT_THREAD = 4 << 4,
- ACT_THREAD_START = ACT_THREAD,
- ACT_THREAD_JOIN,
-
- // Process activities involve the life management of processes.
- ACT_PROCESS = 5 << 4,
- ACT_PROCESS_START = ACT_PROCESS,
- ACT_PROCESS_WAIT,
-
- // Exception activities indicate the occurence of something unexpected.
- ACT_EXCEPTION = 14 << 4,
-
- // Generic activities are user defined and can be anything.
- ACT_GENERIC = 15 << 4,
-
- // These constants can be used to separate the category and action from
- // a combined activity type.
- ACT_CATEGORY_MASK = 0xF << 4,
- ACT_ACTION_MASK = 0xF
- };
-
- // Internal representation of time. During collection, this is in "ticks"
- // but when returned in a snapshot, it is "wall time".
- int64_t time_internal;
-
- // The address that pushed the activity onto the stack as a raw number.
- uint64_t calling_address;
-
- // The address that is the origin of the activity if it not obvious from
- // the call stack. This is useful for things like tasks that are posted
- // from a completely different thread though most activities will leave
- // it null.
- uint64_t origin_address;
-
- // Array of program-counters that make up the top of the call stack.
- // Despite the fixed size, this list is always null-terminated. Entries
- // after the terminator have no meaning and may or may not also be null.
- // The list will be completely empty if call-stack collection is not
- // enabled.
- uint64_t call_stack[kActivityCallStackSize];
-
- // Reference to arbitrary user data within the persistent memory segment
- // and a unique identifier for it.
- uint32_t user_data_ref;
- uint32_t user_data_id;
-
- // The (enumerated) type of the activity. This defines what fields of the
- // |data| record are valid.
- uint8_t activity_type;
-
- // Padding to ensure that the next member begins on a 64-bit boundary
- // even on 32-bit builds which ensures inter-operability between CPU
- // architectures. New fields can be taken from this space.
- uint8_t padding[7];
-
- // Information specific to the |activity_type|.
- ActivityData data;
-
- static void FillFrom(Activity* activity,
- const void* program_counter,
- const void* origin,
- Type type,
- const ActivityData& data);
-};
-
-// This class manages arbitrary user data that can be associated with activities
-// done by a thread by supporting key/value pairs of any type. This can provide
-// additional information during debugging. It is also used to store arbitrary
-// global data. All updates must be done from the same thread though other
-// threads can read it concurrently if they create new objects using the same
-// memory.
-class BASE_EXPORT ActivityUserData {
- public:
- // List of known value type. REFERENCE types must immediately follow the non-
- // external types.
- enum ValueType : uint8_t {
- END_OF_VALUES = 0,
- RAW_VALUE,
- RAW_VALUE_REFERENCE,
- STRING_VALUE,
- STRING_VALUE_REFERENCE,
- CHAR_VALUE,
- BOOL_VALUE,
- SIGNED_VALUE,
- UNSIGNED_VALUE,
- };
-
- class BASE_EXPORT TypedValue {
- public:
- TypedValue();
- TypedValue(const TypedValue& other);
- ~TypedValue();
-
- ValueType type() const { return type_; }
-
- // These methods return the extracted value in the correct format.
- StringPiece Get() const;
- StringPiece GetString() const;
- bool GetBool() const;
- char GetChar() const;
- int64_t GetInt() const;
- uint64_t GetUint() const;
-
- // These methods return references to process memory as originally provided
- // to corresponding Set calls. USE WITH CAUTION! There is no guarantee that
- // the referenced memory is assessible or useful. It's possible that:
- // - the memory was free'd and reallocated for a different purpose
- // - the memory has been released back to the OS
- // - the memory belongs to a different process's address space
- // Dereferencing the returned StringPiece when the memory is not accessible
- // will cause the program to SEGV!
- StringPiece GetReference() const;
- StringPiece GetStringReference() const;
-
- private:
- friend class ActivityUserData;
-
- ValueType type_ = END_OF_VALUES;
- uint64_t short_value_; // Used to hold copy of numbers, etc.
- std::string long_value_; // Used to hold copy of raw/string data.
- StringPiece ref_value_; // Used to hold reference to external data.
- };
-
- using Snapshot = std::map<std::string, TypedValue>;
-
- // Initialize the object either as a "sink" that just accepts and discards
- // data or an active one that writes to a given (zeroed) memory block.
- ActivityUserData();
- ActivityUserData(void* memory, size_t size, int64_t pid = 0);
- virtual ~ActivityUserData();
-
- // Gets the unique ID number for this user data. If this changes then the
- // contents have been overwritten by another thread. The return value is
- // always non-zero unless it's actually just a data "sink".
- uint32_t id() const {
- return header_ ? header_->owner.data_id.load(std::memory_order_relaxed) : 0;
- }
-
- // Writes a |value| (as part of a key/value pair) that will be included with
- // the activity in any reports. The same |name| can be written multiple times
- // with each successive call overwriting the previously stored |value|. For
- // raw and string values, the maximum size of successive writes is limited by
- // the first call. The length of "name" is limited to 255 characters.
- //
- // This information is stored on a "best effort" basis. It may be dropped if
- // the memory buffer is full or the associated activity is beyond the maximum
- // recording depth.
- void Set(StringPiece name, const void* memory, size_t size) {
- Set(name, RAW_VALUE, memory, size);
- }
- void SetString(StringPiece name, StringPiece value) {
- Set(name, STRING_VALUE, value.data(), value.length());
- }
- void SetString(StringPiece name, StringPiece16 value) {
- SetString(name, UTF16ToUTF8(value));
- }
- void SetBool(StringPiece name, bool value) {
- char cvalue = value ? 1 : 0;
- Set(name, BOOL_VALUE, &cvalue, sizeof(cvalue));
- }
- void SetChar(StringPiece name, char value) {
- Set(name, CHAR_VALUE, &value, sizeof(value));
- }
- void SetInt(StringPiece name, int64_t value) {
- Set(name, SIGNED_VALUE, &value, sizeof(value));
- }
- void SetUint(StringPiece name, uint64_t value) {
- Set(name, UNSIGNED_VALUE, &value, sizeof(value));
- }
-
- // These function as above but don't actually copy the data into the
- // persistent memory. They store unaltered pointers along with a size. These
- // can be used in conjuction with a memory dump to find certain large pieces
- // of information.
- void SetReference(StringPiece name, const void* memory, size_t size) {
- SetReference(name, RAW_VALUE_REFERENCE, memory, size);
- }
- void SetStringReference(StringPiece name, StringPiece value) {
- SetReference(name, STRING_VALUE_REFERENCE, value.data(), value.length());
- }
-
- // Creates a snapshot of the key/value pairs contained within. The returned
- // data will be fixed, independent of whatever changes afterward. There is
- // some protection against concurrent modification. This will return false
- // if the data is invalid or if a complete overwrite of the contents is
- // detected.
- bool CreateSnapshot(Snapshot* output_snapshot) const;
-
- // Gets the base memory address used for storing data.
- const void* GetBaseAddress() const;
-
- // Explicitly sets the process ID.
- void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp);
-
- // Gets the associated process ID, in native form, and the creation timestamp
- // from tracker memory without loading the entire structure for analysis. This
- // will return false if no valid process ID is available.
- static bool GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp);
-
- protected:
- virtual void Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(ActivityTrackerTest, UserDataTest);
-
- enum : size_t { kMemoryAlignment = sizeof(uint64_t) };
-
- // A structure that defines the structure header in memory.
- struct MemoryHeader {
- MemoryHeader();
- ~MemoryHeader();
-
- OwningProcess owner; // Information about the creating process.
- };
-
- // Header to a key/value record held in persistent memory.
- struct FieldHeader {
- FieldHeader();
- ~FieldHeader();
-
- std::atomic<uint8_t> type; // Encoded ValueType
- uint8_t name_size; // Length of "name" key.
- std::atomic<uint16_t> value_size; // Actual size of of the stored value.
- uint16_t record_size; // Total storage of name, value, header.
- };
-
- // A structure used to reference data held outside of persistent memory.
- struct ReferenceRecord {
- uint64_t address;
- uint64_t size;
- };
-
- // This record is used to hold known value is a map so that they can be
- // found and overwritten later.
- struct ValueInfo {
- ValueInfo();
- ValueInfo(ValueInfo&&);
- ~ValueInfo();
-
- StringPiece name; // The "key" of the record.
- ValueType type; // The type of the value.
- void* memory; // Where the "value" is held.
- std::atomic<uint16_t>* size_ptr; // Address of the actual size of value.
- size_t extent; // The total storage of the value,
- }; // typically rounded up for alignment.
-
- void SetReference(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size);
-
- // Loads any data already in the memory segment. This allows for accessing
- // records created previously. If this detects that the underlying data has
- // gone away (cleared by another thread/process), it will invalidate all the
- // data in this object and turn it into simple "sink" with no values to
- // return.
- void ImportExistingData() const;
-
- // A map of all the values within the memory block, keyed by name for quick
- // updates of the values. This is "mutable" because it changes on "const"
- // objects even when the actual data values can't change.
- mutable std::map<StringPiece, ValueInfo> values_;
-
- // Information about the memory block in which new data can be stored. These
- // are "mutable" because they change even on "const" objects that are just
- // skipping already set values.
- mutable char* memory_;
- mutable size_t available_;
-
- // A pointer to the memory header for this instance.
- MemoryHeader* const header_;
-
- // These hold values used when initially creating the object. They are
- // compared against current header values to check for outside changes.
- const uint32_t orig_data_id;
- const int64_t orig_process_id;
- const int64_t orig_create_stamp;
-
- DISALLOW_COPY_AND_ASSIGN(ActivityUserData);
-};
-
-// This class manages tracking a stack of activities for a single thread in
-// a persistent manner, implementing a bounded-size stack in a fixed-size
-// memory allocation. In order to support an operational mode where another
-// thread is analyzing this data in real-time, atomic operations are used
-// where necessary to guarantee a consistent view from the outside.
-//
-// This class is not generally used directly but instead managed by the
-// GlobalActivityTracker instance and updated using Scoped*Activity local
-// objects.
-class BASE_EXPORT ThreadActivityTracker {
- public:
- using ActivityId = uint32_t;
-
- // This structure contains all the common information about the thread so
- // it doesn't have to be repeated in every entry on the stack. It is defined
- // and used completely within the .cc file.
- struct Header;
-
- // This structure holds a copy of all the internal data at the moment the
- // "snapshot" operation is done. It is disconnected from the live tracker
- // so that continued operation of the thread will not cause changes here.
- struct BASE_EXPORT Snapshot {
- // Explicit constructor/destructor are needed because of complex types
- // with non-trivial default constructors and destructors.
- Snapshot();
- ~Snapshot();
-
- // The name of the thread as set when it was created. The name may be
- // truncated due to internal length limitations.
- std::string thread_name;
-
- // The timestamp at which this process was created.
- int64_t create_stamp;
-
- // The process and thread IDs. These values have no meaning other than
- // they uniquely identify a running process and a running thread within
- // that process. Thread-IDs can be re-used across different processes
- // and both can be re-used after the process/thread exits.
- int64_t process_id = 0;
- int64_t thread_id = 0;
-
- // The current stack of activities that are underway for this thread. It
- // is limited in its maximum size with later entries being left off.
- std::vector<Activity> activity_stack;
-
- // The current total depth of the activity stack, including those later
- // entries not recorded in the |activity_stack| vector.
- uint32_t activity_stack_depth = 0;
-
- // The last recorded "exception" activity.
- Activity last_exception;
- };
-
- // This is the base class for having the compiler manage an activity on the
- // tracker's stack. It does nothing but call methods on the passed |tracker|
- // if it is not null, making it safe (and cheap) to create these objects
- // even if activity tracking is not enabled.
- class BASE_EXPORT ScopedActivity {
- public:
- ScopedActivity(ThreadActivityTracker* tracker,
- const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data);
- ~ScopedActivity();
-
- // Changes some basic metadata about the activity.
- void ChangeTypeAndData(Activity::Type type, const ActivityData& data);
-
- protected:
- // The thread tracker to which this object reports. It can be null if
- // activity tracking is not (yet) enabled.
- ThreadActivityTracker* const tracker_;
-
- // An identifier that indicates a specific activity on the stack.
- ActivityId activity_id_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ScopedActivity);
- };
-
- // A ThreadActivityTracker runs on top of memory that is managed externally.
- // It must be large enough for the internal header and a few Activity
- // blocks. See SizeForStackDepth().
- ThreadActivityTracker(void* base, size_t size);
- virtual ~ThreadActivityTracker();
-
- // Indicates that an activity has started from a given |origin| address in
- // the code, though it can be null if the creator's address is not known.
- // The |type| and |data| describe the activity. |program_counter| should be
- // the result of GetProgramCounter() where push is called. Returned is an
- // ID that can be used to adjust the pushed activity.
- ActivityId PushActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data);
-
- // An inlined version of the above that gets the program counter where it
- // is called.
- ALWAYS_INLINE
- ActivityId PushActivity(const void* origin,
- Activity::Type type,
- const ActivityData& data) {
- return PushActivity(GetProgramCounter(), origin, type, data);
- }
-
- // Changes the activity |type| and |data| of the top-most entry on the stack.
- // This is useful if the information has changed and it is desireable to
- // track that change without creating a new stack entry. If the type is
- // ACT_NULL or the data is kNullActivityData then that value will remain
- // unchanged. The type, if changed, must remain in the same category.
- // Changing both is not atomic so a snapshot operation could occur between
- // the update of |type| and |data| or between update of |data| fields.
- void ChangeActivity(ActivityId id,
- Activity::Type type,
- const ActivityData& data);
-
- // Indicates that an activity has completed.
- void PopActivity(ActivityId id);
-
- // Sets the user-data information for an activity.
- std::unique_ptr<ActivityUserData> GetUserData(
- ActivityId id,
- ActivityTrackerMemoryAllocator* allocator);
-
- // Returns if there is true use-data associated with a given ActivityId since
- // it's possible than any returned object is just a sink.
- bool HasUserData(ActivityId id);
-
- // Release the user-data information for an activity.
- void ReleaseUserData(ActivityId id,
- ActivityTrackerMemoryAllocator* allocator);
-
- // Save an exception. |origin| is the location of the exception.
- void RecordExceptionActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data);
-
- // Returns whether the current data is valid or not. It is not valid if
- // corruption has been detected in the header or other data structures.
- bool IsValid() const;
-
- // Gets a copy of the tracker contents for analysis. Returns false if a
- // snapshot was not possible, perhaps because the data is not valid; the
- // contents of |output_snapshot| are undefined in that case. The current
- // implementation does not support concurrent snapshot operations.
- bool CreateSnapshot(Snapshot* output_snapshot) const;
-
- // Gets the base memory address used for storing data.
- const void* GetBaseAddress();
-
- // Access the "data version" value so tests can determine if an activity
- // was pushed and popped in a single call.
- uint32_t GetDataVersionForTesting();
-
- // Explicitly sets the process ID.
- void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp);
-
- // Gets the associated process ID, in native form, and the creation timestamp
- // from tracker memory without loading the entire structure for analysis. This
- // will return false if no valid process ID is available.
- static bool GetOwningProcessId(const void* memory,
- int64_t* out_id,
- int64_t* out_stamp);
-
- // Calculates the memory size required for a given stack depth, including
- // the internal header structure for the stack.
- static size_t SizeForStackDepth(int stack_depth);
-
- private:
- friend class ActivityTrackerTest;
-
- bool CalledOnValidThread();
-
- std::unique_ptr<ActivityUserData> CreateUserDataForActivity(
- Activity* activity,
- ActivityTrackerMemoryAllocator* allocator);
-
- Header* const header_; // Pointer to the Header structure.
- Activity* const stack_; // The stack of activities.
-
-#if DCHECK_IS_ON()
- // The ActivityTracker is thread bound, and will be invoked across all the
- // sequences that run on the thread. A ThreadChecker does not work here, as it
- // asserts on running in the same sequence each time.
- const PlatformThreadRef thread_id_; // The thread this instance is bound to.
-#endif
- const uint32_t stack_slots_; // The total number of stack slots.
-
- bool valid_ = false; // Tracks whether the data is valid or not.
-
- DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker);
-};
-
-
-// The global tracker manages all the individual thread trackers. Memory for
-// the thread trackers is taken from a PersistentMemoryAllocator which allows
-// for the data to be analyzed by a parallel process or even post-mortem.
-class BASE_EXPORT GlobalActivityTracker {
- public:
- // Type identifiers used when storing in persistent memory so they can be
- // identified during extraction; the first 4 bytes of the SHA1 of the name
- // is used as a unique integer. A "version number" is added to the base
- // so that, if the structure of that object changes, stored older versions
- // will be safely ignored. These are public so that an external process
- // can recognize records of this type within an allocator.
- enum : uint32_t {
- kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4
- kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3
- kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1
- kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100,
-
- kTypeIdActivityTrackerFree = ~kTypeIdActivityTracker,
- kTypeIdUserDataRecordFree = ~kTypeIdUserDataRecord,
- kTypeIdProcessDataRecordFree = ~kTypeIdProcessDataRecord,
- };
-
- // An enumeration of common process life stages. All entries are given an
- // explicit number so they are known and remain constant; this allows for
- // cross-version analysis either locally or on a server.
- enum ProcessPhase : int {
- // The phases are generic and may have meaning to the tracker.
- PROCESS_PHASE_UNKNOWN = 0,
- PROCESS_LAUNCHED = 1,
- PROCESS_LAUNCH_FAILED = 2,
- PROCESS_EXITED_CLEANLY = 10,
- PROCESS_EXITED_WITH_CODE = 11,
-
- // Add here whatever is useful for analysis.
- PROCESS_SHUTDOWN_STARTED = 100,
- PROCESS_MAIN_LOOP_STARTED = 101,
- };
-
- // A callback made when a process exits to allow immediate analysis of its
- // data. Note that the system may reuse the |process_id| so when fetching
- // records it's important to ensure that what is returned was created before
- // the |exit_stamp|. Movement of |process_data| information is allowed.
- using ProcessExitCallback =
- Callback<void(int64_t process_id,
- int64_t exit_stamp,
- int exit_code,
- ProcessPhase exit_phase,
- std::string&& command_line,
- ActivityUserData::Snapshot&& process_data)>;
-
- // This structure contains information about a loaded module, as shown to
- // users of the tracker.
- struct BASE_EXPORT ModuleInfo {
- ModuleInfo();
- ModuleInfo(ModuleInfo&& rhs);
- ModuleInfo(const ModuleInfo& rhs);
- ~ModuleInfo();
-
- ModuleInfo& operator=(ModuleInfo&& rhs);
- ModuleInfo& operator=(const ModuleInfo& rhs);
-
- // Information about where and when the module was loaded/unloaded.
- bool is_loaded = false; // Was the last operation a load or unload?
- uintptr_t address = 0; // Address of the last load operation.
- int64_t load_time = 0; // Time of last change; set automatically.
-
- // Information about the module itself. These never change no matter how
- // many times a module may be loaded and unloaded.
- size_t size = 0; // The size of the loaded module.
- uint32_t timestamp = 0; // Opaque "timestamp" for the module.
- uint32_t age = 0; // Opaque "age" for the module.
- uint8_t identifier[16]; // Opaque identifier (GUID, etc.) for the module.
- std::string file; // The full path to the file. (UTF-8)
- std::string debug_file; // The full path to the debug file.
- };
-
- // This is a thin wrapper around the thread-tracker's ScopedActivity that
- // allows thread-safe access to data values. It is safe to use even if
- // activity tracking is not enabled.
- class BASE_EXPORT ScopedThreadActivity
- : public ThreadActivityTracker::ScopedActivity {
- public:
- ScopedThreadActivity(const void* program_counter,
- const void* origin,
- Activity::Type type,
- const ActivityData& data,
- bool lock_allowed);
- ~ScopedThreadActivity();
-
- // Returns an object for manipulating user data.
- ActivityUserData& user_data();
-
- private:
- // Gets (or creates) a tracker for the current thread. If locking is not
- // allowed (because a lock is being tracked which would cause recursion)
- // then the attempt to create one if none found will be skipped. Once
- // the tracker for this thread has been created for other reasons, locks
- // will be tracked. The thread-tracker uses locks.
- static ThreadActivityTracker* GetOrCreateTracker(bool lock_allowed) {
- GlobalActivityTracker* global_tracker = Get();
- if (!global_tracker)
- return nullptr;
- if (lock_allowed)
- return global_tracker->GetOrCreateTrackerForCurrentThread();
- else
- return global_tracker->GetTrackerForCurrentThread();
- }
-
- // An object that manages additional user data, created only upon request.
- std::unique_ptr<ActivityUserData> user_data_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedThreadActivity);
- };
-
- ~GlobalActivityTracker();
-
- // Creates a global tracker using a given persistent-memory |allocator| and
- // providing the given |stack_depth| to each thread tracker it manages. The
- // created object is activated so tracking will begin immediately upon return.
- // The |process_id| can be zero to get it from the OS but is taken for testing
- // purposes.
- static void CreateWithAllocator(
- std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id);
-
-#if !defined(OS_NACL)
- // Like above but internally creates an allocator around a disk file with
- // the specified |size| at the given |file_path|. Any existing file will be
- // overwritten. The |id| and |name| are arbitrary and stored in the allocator
- // for reference by whatever process reads it. Returns true if successful.
- static bool CreateWithFile(const FilePath& file_path,
- size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth);
-#endif // !defined(OS_NACL)
-
- // Like above but internally creates an allocator using local heap memory of
- // the specified size. This is used primarily for unit tests. The |process_id|
- // can be zero to get it from the OS but is taken for testing purposes.
- static bool CreateWithLocalMemory(size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth,
- int64_t process_id);
-
- // Like above but internally creates an allocator using a shared-memory
- // segment. The segment must already be mapped into the local memory space.
- static bool CreateWithSharedMemory(std::unique_ptr<SharedMemory> shm,
- uint64_t id,
- StringPiece name,
- int stack_depth);
-
- // Like above but takes a handle to an existing shared memory segment and
- // maps it before creating the tracker.
- static bool CreateWithSharedMemoryHandle(const SharedMemoryHandle& handle,
- size_t size,
- uint64_t id,
- StringPiece name,
- int stack_depth);
-
- // Gets the global activity-tracker or null if none exists.
- static GlobalActivityTracker* Get() {
- return reinterpret_cast<GlobalActivityTracker*>(
- subtle::Acquire_Load(&g_tracker_));
- }
-
- // Sets the global activity-tracker for testing purposes.
- static void SetForTesting(std::unique_ptr<GlobalActivityTracker> tracker);
-
- // This access to the persistent allocator is only for testing; it extracts
- // the global tracker completely. All tracked threads must exit before
- // calling this. Tracking for the current thread will be automatically
- // stopped.
- static std::unique_ptr<GlobalActivityTracker> ReleaseForTesting();
-
- // Convenience method for determining if a global tracker is active.
- static bool IsEnabled() { return Get() != nullptr; }
-
- // Gets the persistent-memory-allocator in which data is stored. Callers
- // can store additional records here to pass more information to the
- // analysis process.
- PersistentMemoryAllocator* allocator() { return allocator_.get(); }
-
- // Gets the thread's activity-tracker if it exists. This is inline for
- // performance reasons and it uses thread-local-storage (TLS) so that there
- // is no significant lookup time required to find the one for the calling
- // thread. Ownership remains with the global tracker.
- ThreadActivityTracker* GetTrackerForCurrentThread() {
- return reinterpret_cast<ThreadActivityTracker*>(this_thread_tracker_.Get());
- }
-
- // Gets the thread's activity-tracker or creates one if none exists. This
- // is inline for performance reasons. Ownership remains with the global
- // tracker.
- ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() {
- ThreadActivityTracker* tracker = GetTrackerForCurrentThread();
- if (tracker)
- return tracker;
- return CreateTrackerForCurrentThread();
- }
-
- // Creates an activity-tracker for the current thread.
- ThreadActivityTracker* CreateTrackerForCurrentThread();
-
- // Releases the activity-tracker for the current thread (for testing only).
- void ReleaseTrackerForCurrentThreadForTesting();
-
- // Sets a task-runner that can be used for background work.
- void SetBackgroundTaskRunner(const scoped_refptr<TaskRunner>& runner);
-
- // Sets an optional callback to be called when a process exits.
- void SetProcessExitCallback(ProcessExitCallback callback);
-
- // Manages process lifetimes. These are called by the process that launched
- // and reaped the subprocess, not the subprocess itself. If it is expensive
- // to generate the parameters, Get() the global tracker and call these
- // conditionally rather than using the static versions.
- void RecordProcessLaunch(ProcessId process_id,
- const FilePath::StringType& cmd);
- void RecordProcessLaunch(ProcessId process_id,
- const FilePath::StringType& exe,
- const FilePath::StringType& args);
- void RecordProcessExit(ProcessId process_id, int exit_code);
- static void RecordProcessLaunchIfEnabled(ProcessId process_id,
- const FilePath::StringType& cmd) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordProcessLaunch(process_id, cmd);
- }
- static void RecordProcessLaunchIfEnabled(ProcessId process_id,
- const FilePath::StringType& exe,
- const FilePath::StringType& args) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordProcessLaunch(process_id, exe, args);
- }
- static void RecordProcessExitIfEnabled(ProcessId process_id, int exit_code) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordProcessExit(process_id, exit_code);
- }
-
- // Sets the "phase" of the current process, useful for knowing what it was
- // doing when it last reported.
- void SetProcessPhase(ProcessPhase phase);
- static void SetProcessPhaseIfEnabled(ProcessPhase phase) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->SetProcessPhase(phase);
- }
-
- // Records a log message. The current implementation does NOT recycle these
- // only store critical messages such as FATAL ones.
- void RecordLogMessage(StringPiece message);
- static void RecordLogMessageIfEnabled(StringPiece message) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordLogMessage(message);
- }
-
- // Records a module load/unload event. This is safe to call multiple times
- // even with the same information.
- void RecordModuleInfo(const ModuleInfo& info);
- static void RecordModuleInfoIfEnabled(const ModuleInfo& info) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordModuleInfo(info);
- }
-
- // Record field trial information. This call is thread-safe. In addition to
- // this, construction of a GlobalActivityTracker will cause all existing
- // active field trials to be fetched and recorded.
- void RecordFieldTrial(const std::string& trial_name, StringPiece group_name);
- static void RecordFieldTrialIfEnabled(const std::string& trial_name,
- StringPiece group_name) {
- GlobalActivityTracker* tracker = Get();
- if (tracker)
- tracker->RecordFieldTrial(trial_name, group_name);
- }
-
- // Record exception information for the current thread.
- ALWAYS_INLINE
- void RecordException(const void* origin, uint32_t code) {
- return RecordExceptionImpl(GetProgramCounter(), origin, code);
- }
- void RecordException(const void* pc, const void* origin, uint32_t code);
-
- // Marks the tracked data as deleted.
- void MarkDeleted();
-
- // Gets the process ID used for tracking. This is typically the same as what
- // the OS thinks is the current process but can be overridden for testing.
- int64_t process_id() { return process_id_; }
-
- // Accesses the process data record for storing arbitrary key/value pairs.
- // Updates to this are thread-safe.
- ActivityUserData& process_data() { return process_data_; }
-
- private:
- friend class GlobalActivityAnalyzer;
- friend class ScopedThreadActivity;
- friend class ActivityTrackerTest;
-
- enum : int {
- // The maximum number of threads that can be tracked within a process. If
- // more than this number run concurrently, tracking of new ones may cease.
- kMaxThreadCount = 100,
- kCachedThreadMemories = 10,
- kCachedUserDataMemories = 10,
- };
-
- // A wrapper around ActivityUserData that is thread-safe and thus can be used
- // in the global scope without the requirement of being called from only one
- // thread.
- class ThreadSafeUserData : public ActivityUserData {
- public:
- ThreadSafeUserData(void* memory, size_t size, int64_t pid = 0);
- ~ThreadSafeUserData() override;
-
- private:
- void Set(StringPiece name,
- ValueType type,
- const void* memory,
- size_t size) override;
-
- Lock data_lock_;
-
- DISALLOW_COPY_AND_ASSIGN(ThreadSafeUserData);
- };
-
- // State of a module as stored in persistent memory. This supports a single
- // loading of a module only. If modules are loaded multiple times at
- // different addresses, only the last will be recorded and an unload will
- // not revert to the information of any other addresses.
- struct BASE_EXPORT ModuleInfoRecord {
- // SHA1(ModuleInfoRecord): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x05DB5F41 + 1;
-
- // Expected size for 32/64-bit check by PersistentMemoryAllocator.
- static constexpr size_t kExpectedInstanceSize =
- OwningProcess::kExpectedInstanceSize + 56;
-
- // The atomic unfortunately makes this a "complex" class on some compilers
- // and thus requires an out-of-line constructor & destructor even though
- // they do nothing.
- ModuleInfoRecord();
- ~ModuleInfoRecord();
-
- OwningProcess owner; // The process that created this record.
- uint64_t address; // The base address of the module.
- uint64_t load_time; // Time of last load/unload.
- uint64_t size; // The size of the module in bytes.
- uint32_t timestamp; // Opaque timestamp of the module.
- uint32_t age; // Opaque "age" associated with the module.
- uint8_t identifier[16]; // Opaque identifier for the module.
- std::atomic<uint32_t> changes; // Number load/unload actions.
- uint16_t pickle_size; // The size of the following pickle.
- uint8_t loaded; // Flag if module is loaded or not.
- char pickle[1]; // Other strings; may allocate larger.
-
- // Decodes/encodes storage structure from more generic info structure.
- bool DecodeTo(GlobalActivityTracker::ModuleInfo* info,
- size_t record_size) const;
- static ModuleInfoRecord* CreateFrom(
- const GlobalActivityTracker::ModuleInfo& info,
- PersistentMemoryAllocator* allocator);
-
- // Updates the core information without changing the encoded strings. This
- // is useful when a known module changes state (i.e. new load or unload).
- bool UpdateFrom(const GlobalActivityTracker::ModuleInfo& info);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ModuleInfoRecord);
- };
-
- // A thin wrapper around the main thread-tracker that keeps additional
- // information that the global tracker needs to handle joined threads.
- class ManagedActivityTracker : public ThreadActivityTracker {
- public:
- ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference,
- void* base,
- size_t size);
- ~ManagedActivityTracker() override;
-
- // The reference into persistent memory from which the thread-tracker's
- // memory was created.
- const PersistentMemoryAllocator::Reference mem_reference_;
-
- // The physical address used for the thread-tracker's memory.
- void* const mem_base_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ManagedActivityTracker);
- };
-
- // Creates a global tracker using a given persistent-memory |allocator| and
- // providing the given |stack_depth| to each thread tracker it manages. The
- // created object is activated so tracking has already started upon return.
- // The |process_id| can be zero to get it from the OS but is taken for testing
- // purposes.
- GlobalActivityTracker(std::unique_ptr<PersistentMemoryAllocator> allocator,
- int stack_depth,
- int64_t process_id);
-
- // Returns the memory used by an activity-tracker managed by this class.
- // It is called during the destruction of a ManagedActivityTracker object.
- void ReturnTrackerMemory(ManagedActivityTracker* tracker);
-
- // Records exception information.
- void RecordExceptionImpl(const void* pc, const void* origin, uint32_t code);
-
- // Releases the activity-tracker associcated with thread. It is called
- // automatically when a thread is joined and thus there is nothing more to
- // be tracked. |value| is a pointer to a ManagedActivityTracker.
- static void OnTLSDestroy(void* value);
-
- // Does process-exit work. This can be run on any thread.
- void CleanupAfterProcess(int64_t process_id,
- int64_t exit_stamp,
- int exit_code,
- std::string&& command_line);
-
- // The persistent-memory allocator from which the memory for all trackers
- // is taken.
- std::unique_ptr<PersistentMemoryAllocator> allocator_;
-
- // The size (in bytes) of memory required by a ThreadActivityTracker to
- // provide the stack-depth requested during construction.
- const size_t stack_memory_size_;
-
- // The process-id of the current process. This is kept as a member variable,
- // defined during initialization, for testing purposes.
- const int64_t process_id_;
-
- // The activity tracker for the currently executing thread.
- ThreadLocalStorage::Slot this_thread_tracker_;
-
- // The number of thread trackers currently active.
- std::atomic<int> thread_tracker_count_;
-
- // A caching memory allocator for thread-tracker objects.
- ActivityTrackerMemoryAllocator thread_tracker_allocator_;
- Lock thread_tracker_allocator_lock_;
-
- // A caching memory allocator for user data attached to activity data.
- ActivityTrackerMemoryAllocator user_data_allocator_;
- Lock user_data_allocator_lock_;
-
- // An object for holding arbitrary key value pairs with thread-safe access.
- ThreadSafeUserData process_data_;
-
- // A map of global module information, keyed by module path.
- std::map<const std::string, ModuleInfoRecord*> modules_;
- Lock modules_lock_;
-
- // The active global activity tracker.
- static subtle::AtomicWord g_tracker_;
-
- // A lock that is used to protect access to the following fields.
- Lock global_tracker_lock_;
-
- // The collection of processes being tracked and their command-lines.
- std::map<int64_t, std::string> known_processes_;
-
- // A task-runner that can be used for doing background processing.
- scoped_refptr<TaskRunner> background_task_runner_;
-
- // A callback performed when a subprocess exits, including its exit-code
- // and the phase it was in when that occurred. This will be called via
- // the |background_task_runner_| if one is set or whatever thread reaped
- // the process otherwise.
- ProcessExitCallback process_exit_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker);
-};
-
-
-// Record entry in to and out of an arbitrary block of code.
-class BASE_EXPORT ScopedActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- // Track activity at the specified FROM_HERE location for an arbitrary
- // 4-bit |action|, an arbitrary 32-bit |id|, and 32-bits of arbitrary
- // |info|. None of these values affect operation; they're all purely
- // for association and analysis. To have unique identifiers across a
- // diverse code-base, create the number by taking the first 8 characters
- // of the hash of the activity being tracked.
- //
- // For example:
- // Tracking method: void MayNeverExit(uint32_t foo) {...}
- // echo -n "MayNeverExit" | sha1sum => e44873ccab21e2b71270da24aa1...
- //
- // void MayNeverExit(int32_t foo) {
- // base::debug::ScopedActivity track_me(0, 0xE44873CC, foo);
- // ...
- // }
- ALWAYS_INLINE
- ScopedActivity(uint8_t action, uint32_t id, int32_t info)
- : ScopedActivity(GetProgramCounter(), action, id, info) {}
- ScopedActivity() : ScopedActivity(0, 0, 0) {}
-
- // Changes the |action| and/or |info| of this activity on the stack. This
- // is useful for tracking progress through a function, updating the action
- // to indicate "milestones" in the block (max 16 milestones: 0-15) or the
- // info to reflect other changes. Changing both is not atomic so a snapshot
- // operation could occur between the update of |action| and |info|.
- void ChangeAction(uint8_t action);
- void ChangeInfo(int32_t info);
- void ChangeActionAndInfo(uint8_t action, int32_t info);
-
- private:
- // Constructs the object using a passed-in program-counter.
- ScopedActivity(const void* program_counter,
- uint8_t action,
- uint32_t id,
- int32_t info);
-
- // A copy of the ID code so it doesn't have to be passed by the caller when
- // changing the |info| field.
- uint32_t id_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedActivity);
-};
-
-
-// These "scoped" classes provide easy tracking of various blocking actions.
-
-class BASE_EXPORT ScopedTaskRunActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedTaskRunActivity(const PendingTask& task)
- : ScopedTaskRunActivity(GetProgramCounter(), task) {}
-
- private:
- ScopedTaskRunActivity(const void* program_counter, const PendingTask& task);
- DISALLOW_COPY_AND_ASSIGN(ScopedTaskRunActivity);
-};
-
-class BASE_EXPORT ScopedLockAcquireActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedLockAcquireActivity(const base::internal::LockImpl* lock)
- : ScopedLockAcquireActivity(GetProgramCounter(), lock) {}
-
- private:
- ScopedLockAcquireActivity(const void* program_counter,
- const base::internal::LockImpl* lock);
- DISALLOW_COPY_AND_ASSIGN(ScopedLockAcquireActivity);
-};
-
-class BASE_EXPORT ScopedEventWaitActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedEventWaitActivity(const WaitableEvent* event)
- : ScopedEventWaitActivity(GetProgramCounter(), event) {}
-
- private:
- ScopedEventWaitActivity(const void* program_counter,
- const WaitableEvent* event);
- DISALLOW_COPY_AND_ASSIGN(ScopedEventWaitActivity);
-};
-
-class BASE_EXPORT ScopedThreadJoinActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedThreadJoinActivity(const PlatformThreadHandle* thread)
- : ScopedThreadJoinActivity(GetProgramCounter(), thread) {}
-
- private:
- ScopedThreadJoinActivity(const void* program_counter,
- const PlatformThreadHandle* thread);
- DISALLOW_COPY_AND_ASSIGN(ScopedThreadJoinActivity);
-};
-
-// Some systems don't have base::Process
-#if !defined(OS_NACL) && !defined(OS_IOS)
-class BASE_EXPORT ScopedProcessWaitActivity
- : public GlobalActivityTracker::ScopedThreadActivity {
- public:
- ALWAYS_INLINE
- explicit ScopedProcessWaitActivity(const Process* process)
- : ScopedProcessWaitActivity(GetProgramCounter(), process) {}
-
- private:
- ScopedProcessWaitActivity(const void* program_counter,
- const Process* process);
- DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity);
-};
-#endif
-
-} // namespace debug
-} // namespace base
-
-#endif // BASE_DEBUG_ACTIVITY_TRACKER_H_
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc
index def2d7a..69b9c04 100644
--- a/base/debug/task_annotator.cc
+++ b/base/debug/task_annotator.cc
@@ -6,7 +6,6 @@
#include <array>
-#include "base/debug/activity_tracker.h"
#include "base/debug/alias.h"
#include "base/no_destructor.h"
#include "base/pending_task.h"
@@ -54,8 +53,6 @@
void TaskAnnotator::RunTask(const char* queue_function,
PendingTask* pending_task) {
- ScopedTaskRunActivity task_activity(*pending_task);
-
// Before running the task, store the task backtrace with the chain of
// PostTasks that resulted in this call and deliberately alias it to ensure
// it is on the stack if the task crashes. Be careful not to assume that the
diff --git a/base/feature_list.cc b/base/feature_list.cc
deleted file mode 100644
index 1610eec..0000000
--- a/base/feature_list.cc
+++ /dev/null
@@ -1,438 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/feature_list.h"
-
-#include <stddef.h>
-
-#include <utility>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/pickle.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-
-namespace base {
-
-namespace {
-
-// Pointer to the FeatureList instance singleton that was set via
-// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
-// have more control over initialization timing. Leaky.
-FeatureList* g_feature_list_instance = nullptr;
-
-// Tracks whether the FeatureList instance was initialized via an accessor.
-bool g_initialized_from_accessor = false;
-
-// An allocator entry for a feature in shared memory. The FeatureEntry is
-// followed by a base::Pickle object that contains the feature and trial name.
-struct FeatureEntry {
- // SHA1(FeatureEntry): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 8;
-
- // Specifies whether a feature override enables or disables the feature. Same
- // values as the OverrideState enum in feature_list.h
- uint32_t override_state;
-
- // Size of the pickled structure, NOT the total size of this entry.
- uint32_t pickle_size;
-
- // Reads the feature and trial name from the pickle. Calling this is only
- // valid on an initialized entry that's in shared memory.
- bool GetFeatureAndTrialName(StringPiece* feature_name,
- StringPiece* trial_name) const {
- const char* src =
- reinterpret_cast<const char*>(this) + sizeof(FeatureEntry);
-
- Pickle pickle(src, pickle_size);
- PickleIterator pickle_iter(pickle);
-
- if (!pickle_iter.ReadStringPiece(feature_name))
- return false;
-
- // Return true because we are not guaranteed to have a trial name anyways.
- auto sink = pickle_iter.ReadStringPiece(trial_name);
- ALLOW_UNUSED_LOCAL(sink);
- return true;
- }
-};
-
-// Some characters are not allowed to appear in feature names or the associated
-// field trial names, as they are used as special characters for command-line
-// serialization. This function checks that the strings are ASCII (since they
-// are used in command-line API functions that require ASCII) and whether there
-// are any reserved characters present, returning true if the string is valid.
-// Only called in DCHECKs.
-bool IsValidFeatureOrFieldTrialName(const std::string& name) {
- return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
-}
-
-} // namespace
-
-#if DCHECK_IS_CONFIGURABLE
-const Feature kDCheckIsFatalFeature{"DcheckIsFatal",
- base::FEATURE_DISABLED_BY_DEFAULT};
-#endif // DCHECK_IS_CONFIGURABLE
-
-FeatureList::FeatureList() = default;
-
-FeatureList::~FeatureList() = default;
-
-void FeatureList::InitializeFromCommandLine(
- const std::string& enable_features,
- const std::string& disable_features) {
- DCHECK(!initialized_);
-
- // Process disabled features first, so that disabled ones take precedence over
- // enabled ones (since RegisterOverride() uses insert()).
- RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
- RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE);
-
- initialized_from_command_line_ = true;
-}
-
-void FeatureList::InitializeFromSharedMemory(
- PersistentMemoryAllocator* allocator) {
- DCHECK(!initialized_);
-
- PersistentMemoryAllocator::Iterator iter(allocator);
- const FeatureEntry* entry;
- while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
- OverrideState override_state =
- static_cast<OverrideState>(entry->override_state);
-
- StringPiece feature_name;
- StringPiece trial_name;
- if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
- continue;
-
- FieldTrial* trial = FieldTrialList::Find(trial_name.as_string());
- RegisterOverride(feature_name, override_state, trial);
- }
-}
-
-bool FeatureList::IsFeatureOverriddenFromCommandLine(
- const std::string& feature_name,
- OverrideState state) const {
- auto it = overrides_.find(feature_name);
- return it != overrides_.end() && it->second.overridden_state == state &&
- !it->second.overridden_by_field_trial;
-}
-
-void FeatureList::AssociateReportingFieldTrial(
- const std::string& feature_name,
- OverrideState for_overridden_state,
- FieldTrial* field_trial) {
- DCHECK(
- IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
-
- // Only one associated field trial is supported per feature. This is generally
- // enforced server-side.
- OverrideEntry* entry = &overrides_.find(feature_name)->second;
- if (entry->field_trial) {
- NOTREACHED() << "Feature " << feature_name
- << " already has trial: " << entry->field_trial->trial_name()
- << ", associating trial: " << field_trial->trial_name();
- return;
- }
-
- entry->field_trial = field_trial;
-}
-
-void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
- OverrideState override_state,
- FieldTrial* field_trial) {
- DCHECK(field_trial);
- DCHECK(!ContainsKey(overrides_, feature_name) ||
- !overrides_.find(feature_name)->second.field_trial)
- << "Feature " << feature_name
- << " has conflicting field trial overrides: "
- << overrides_.find(feature_name)->second.field_trial->trial_name()
- << " / " << field_trial->trial_name();
-
- RegisterOverride(feature_name, override_state, field_trial);
-}
-
-void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
- DCHECK(initialized_);
-
- for (const auto& override : overrides_) {
- Pickle pickle;
- pickle.WriteString(override.first);
- if (override.second.field_trial)
- pickle.WriteString(override.second.field_trial->trial_name());
-
- size_t total_size = sizeof(FeatureEntry) + pickle.size();
- FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
- if (!entry)
- return;
-
- entry->override_state = override.second.overridden_state;
- entry->pickle_size = pickle.size();
-
- char* dst = reinterpret_cast<char*>(entry) + sizeof(FeatureEntry);
- memcpy(dst, pickle.data(), pickle.size());
-
- allocator->MakeIterable(entry);
- }
-}
-
-void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
- std::string* disable_overrides) {
- GetFeatureOverridesImpl(enable_overrides, disable_overrides, false);
-}
-
-void FeatureList::GetCommandLineFeatureOverrides(
- std::string* enable_overrides,
- std::string* disable_overrides) {
- GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
-}
-
-// static
-bool FeatureList::IsEnabled(const Feature& feature) {
- if (!g_feature_list_instance) {
- g_initialized_from_accessor = true;
- return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
- }
- return g_feature_list_instance->IsFeatureEnabled(feature);
-}
-
-// static
-FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
- if (!g_feature_list_instance) {
- g_initialized_from_accessor = true;
- return nullptr;
- }
- return g_feature_list_instance->GetAssociatedFieldTrial(feature);
-}
-
-// static
-std::vector<base::StringPiece> FeatureList::SplitFeatureListString(
- base::StringPiece input) {
- return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
-}
-
-// static
-bool FeatureList::InitializeInstance(const std::string& enable_features,
- const std::string& disable_features) {
- // We want to initialize a new instance here to support command-line features
- // in testing better. For example, we initialize a dummy instance in
- // base/test/test_suite.cc, and override it in content/browser/
- // browser_main_loop.cc.
- // On the other hand, we want to avoid re-initialization from command line.
- // For example, we initialize an instance in chrome/browser/
- // chrome_browser_main.cc and do not override it in content/browser/
- // browser_main_loop.cc.
- // If the singleton was previously initialized from within an accessor, we
- // want to prevent callers from reinitializing the singleton and masking the
- // accessor call(s) which likely returned incorrect information.
- CHECK(!g_initialized_from_accessor);
- bool instance_existed_before = false;
- if (g_feature_list_instance) {
- if (g_feature_list_instance->initialized_from_command_line_)
- return false;
-
- delete g_feature_list_instance;
- g_feature_list_instance = nullptr;
- instance_existed_before = true;
- }
-
- std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- feature_list->InitializeFromCommandLine(enable_features, disable_features);
- base::FeatureList::SetInstance(std::move(feature_list));
- return !instance_existed_before;
-}
-
-// static
-FeatureList* FeatureList::GetInstance() {
- return g_feature_list_instance;
-}
-
-// static
-void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
- DCHECK(!g_feature_list_instance);
- instance->FinalizeInitialization();
-
- // Note: Intentional leak of global singleton.
- g_feature_list_instance = instance.release();
-
-#if DCHECK_IS_CONFIGURABLE
- // Update the behaviour of LOG_DCHECK to match the Feature configuration.
- // DCHECK is also forced to be FATAL if we are running a death-test.
- // TODO(asvitkine): If we find other use-cases that need integrating here
- // then define a proper API/hook for the purpose.
- if (base::FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- "gtest_internal_run_death_test")) {
- logging::LOG_DCHECK = logging::LOG_FATAL;
- } else {
- logging::LOG_DCHECK = logging::LOG_INFO;
- }
-#endif // DCHECK_IS_CONFIGURABLE
-}
-
-// static
-std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
- FeatureList* old_instance = g_feature_list_instance;
- g_feature_list_instance = nullptr;
- g_initialized_from_accessor = false;
- return base::WrapUnique(old_instance);
-}
-
-// static
-void FeatureList::RestoreInstanceForTesting(
- std::unique_ptr<FeatureList> instance) {
- DCHECK(!g_feature_list_instance);
- // Note: Intentional leak of global singleton.
- g_feature_list_instance = instance.release();
-}
-
-void FeatureList::FinalizeInitialization() {
- DCHECK(!initialized_);
- initialized_ = true;
-}
-
-bool FeatureList::IsFeatureEnabled(const Feature& feature) {
- DCHECK(initialized_);
- DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
- DCHECK(CheckFeatureIdentity(feature)) << feature.name;
-
- auto it = overrides_.find(feature.name);
- if (it != overrides_.end()) {
- const OverrideEntry& entry = it->second;
-
- // Activate the corresponding field trial, if necessary.
- if (entry.field_trial)
- entry.field_trial->group();
-
- // TODO(asvitkine) Expand this section as more support is added.
-
- // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
- if (entry.overridden_state != OVERRIDE_USE_DEFAULT)
- return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
- }
- // Otherwise, return the default state.
- return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
-}
-
-FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) {
- DCHECK(initialized_);
- DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
- DCHECK(CheckFeatureIdentity(feature)) << feature.name;
-
- auto it = overrides_.find(feature.name);
- if (it != overrides_.end()) {
- const OverrideEntry& entry = it->second;
- return entry.field_trial;
- }
-
- return nullptr;
-}
-
-void FeatureList::RegisterOverridesFromCommandLine(
- const std::string& feature_list,
- OverrideState overridden_state) {
- for (const auto& value : SplitFeatureListString(feature_list)) {
- StringPiece feature_name = value;
- base::FieldTrial* trial = nullptr;
-
- // The entry may be of the form FeatureName<FieldTrialName - in which case,
- // this splits off the field trial name and associates it with the override.
- std::string::size_type pos = feature_name.find('<');
- if (pos != std::string::npos) {
- feature_name.set(value.data(), pos);
- trial = base::FieldTrialList::Find(value.substr(pos + 1).as_string());
- }
-
- RegisterOverride(feature_name, overridden_state, trial);
- }
-}
-
-void FeatureList::RegisterOverride(StringPiece feature_name,
- OverrideState overridden_state,
- FieldTrial* field_trial) {
- DCHECK(!initialized_);
- if (field_trial) {
- DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
- << field_trial->trial_name();
- }
- if (feature_name.starts_with("*")) {
- feature_name = feature_name.substr(1);
- overridden_state = OVERRIDE_USE_DEFAULT;
- }
-
- // Note: The semantics of insert() is that it does not overwrite the entry if
- // one already exists for the key. Thus, only the first override for a given
- // feature name takes effect.
- overrides_.insert(std::make_pair(
- feature_name.as_string(), OverrideEntry(overridden_state, field_trial)));
-}
-
-void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
- std::string* disable_overrides,
- bool command_line_only) {
- DCHECK(initialized_);
-
- enable_overrides->clear();
- disable_overrides->clear();
-
- // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
- // order. This is not guaranteed to users of this function, but is useful for
- // tests to assume the order.
- for (const auto& entry : overrides_) {
- if (command_line_only &&
- (entry.second.field_trial != nullptr ||
- entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
- continue;
- }
-
- std::string* target_list = nullptr;
- switch (entry.second.overridden_state) {
- case OVERRIDE_USE_DEFAULT:
- case OVERRIDE_ENABLE_FEATURE:
- target_list = enable_overrides;
- break;
- case OVERRIDE_DISABLE_FEATURE:
- target_list = disable_overrides;
- break;
- }
-
- if (!target_list->empty())
- target_list->push_back(',');
- if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
- target_list->push_back('*');
- target_list->append(entry.first);
- if (entry.second.field_trial) {
- target_list->push_back('<');
- target_list->append(entry.second.field_trial->trial_name());
- }
- }
-}
-
-bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
- AutoLock auto_lock(feature_identity_tracker_lock_);
-
- auto it = feature_identity_tracker_.find(feature.name);
- if (it == feature_identity_tracker_.end()) {
- // If it's not tracked yet, register it.
- feature_identity_tracker_[feature.name] = &feature;
- return true;
- }
- // Compare address of |feature| to the existing tracked entry.
- return it->second == &feature;
-}
-
-FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
- FieldTrial* field_trial)
- : overridden_state(overridden_state),
- field_trial(field_trial),
- overridden_by_field_trial(field_trial != nullptr) {}
-
-} // namespace base
diff --git a/base/feature_list.h b/base/feature_list.h
deleted file mode 100644
index 2237507..0000000
--- a/base/feature_list.h
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_FEATURE_LIST_H_
-#define BASE_FEATURE_LIST_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/base_export.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/metrics/persistent_memory_allocator.h"
-#include "base/strings/string_piece.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-class FieldTrial;
-
-// Specifies whether a given feature is enabled or disabled by default.
-enum FeatureState {
- FEATURE_DISABLED_BY_DEFAULT,
- FEATURE_ENABLED_BY_DEFAULT,
-};
-
-// The Feature struct is used to define the default state for a feature. See
-// comment below for more details. There must only ever be one struct instance
-// for a given feature name - generally defined as a constant global variable or
-// file static. It should never be used as a constexpr as it breaks
-// pointer-based identity lookup.
-struct BASE_EXPORT Feature {
- // The name of the feature. This should be unique to each feature and is used
- // for enabling/disabling features via command line flags and experiments.
- // It is strongly recommended to use CamelCase style for feature names, e.g.
- // "MyGreatFeature".
- const char* const name;
-
- // The default state (i.e. enabled or disabled) for this feature.
- const FeatureState default_state;
-};
-
-#if DCHECK_IS_CONFIGURABLE
-// DCHECKs have been built-in, and are configurable at run-time to be fatal, or
-// not, via a DcheckIsFatal feature. We define the Feature here since it is
-// checked in FeatureList::SetInstance(). See https://crbug.com/596231.
-extern BASE_EXPORT const Feature kDCheckIsFatalFeature;
-#endif // DCHECK_IS_CONFIGURABLE
-
-// The FeatureList class is used to determine whether a given feature is on or
-// off. It provides an authoritative answer, taking into account command-line
-// overrides and experimental control.
-//
-// The basic use case is for any feature that can be toggled (e.g. through
-// command-line or an experiment) to have a defined Feature struct, e.g.:
-//
-// const base::Feature kMyGreatFeature {
-// "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT
-// };
-//
-// Then, client code that wishes to query the state of the feature would check:
-//
-// if (base::FeatureList::IsEnabled(kMyGreatFeature)) {
-// // Feature code goes here.
-// }
-//
-// Behind the scenes, the above call would take into account any command-line
-// flags to enable or disable the feature, any experiments that may control it
-// and finally its default state (in that order of priority), to determine
-// whether the feature is on.
-//
-// Features can be explicitly forced on or off by specifying a list of comma-
-// separated feature names via the following command-line flags:
-//
-// --enable-features=Feature5,Feature7
-// --disable-features=Feature1,Feature2,Feature3
-//
-// To enable/disable features in a test, do NOT append --enable-features or
-// --disable-features to the command-line directly. Instead, use
-// ScopedFeatureList. See base/test/scoped_feature_list.h for details.
-//
-// After initialization (which should be done single-threaded), the FeatureList
-// API is thread safe.
-//
-// Note: This class is a singleton, but does not use base/memory/singleton.h in
-// order to have control over its initialization sequence. Specifically, the
-// intended use is to create an instance of this class and fully initialize it,
-// before setting it as the singleton for a process, via SetInstance().
-class BASE_EXPORT FeatureList {
- public:
- FeatureList();
- ~FeatureList();
-
- // Initializes feature overrides via command-line flags |enable_features| and
- // |disable_features|, each of which is a comma-separated list of features to
- // enable or disable, respectively. If a feature appears on both lists, then
- // it will be disabled. If a list entry has the format "FeatureName<TrialName"
- // then this initialization will also associate the feature state override
- // with the named field trial, if it exists. If a feature name is prefixed
- // with the '*' character, it will be created with OVERRIDE_USE_DEFAULT -
- // which is useful for associating with a trial while using the default state.
- // Must only be invoked during the initialization phase (before
- // FinalizeInitialization() has been called).
- void InitializeFromCommandLine(const std::string& enable_features,
- const std::string& disable_features);
-
- // Initializes feature overrides through the field trial allocator, which
- // we're using to store the feature names, their override state, and the name
- // of the associated field trial.
- void InitializeFromSharedMemory(PersistentMemoryAllocator* allocator);
-
- // Specifies whether a feature override enables or disables the feature.
- enum OverrideState {
- OVERRIDE_USE_DEFAULT,
- OVERRIDE_DISABLE_FEATURE,
- OVERRIDE_ENABLE_FEATURE,
- };
-
- // Returns true if the state of |feature_name| has been overridden via
- // |InitializeFromCommandLine()|.
- bool IsFeatureOverriddenFromCommandLine(const std::string& feature_name,
- OverrideState state) const;
-
- // Associates a field trial for reporting purposes corresponding to the
- // command-line setting the feature state to |for_overridden_state|. The trial
- // will be activated when the state of the feature is first queried. This
- // should be called during registration, after InitializeFromCommandLine() has
- // been called but before the instance is registered via SetInstance().
- void AssociateReportingFieldTrial(const std::string& feature_name,
- OverrideState for_overridden_state,
- FieldTrial* field_trial);
-
- // Registers a field trial to override the enabled state of the specified
- // feature to |override_state|. Command-line overrides still take precedence
- // over field trials, so this will have no effect if the feature is being
- // overridden from the command-line. The associated field trial will be
- // activated when the feature state for this feature is queried. This should
- // be called during registration, after InitializeFromCommandLine() has been
- // called but before the instance is registered via SetInstance().
- void RegisterFieldTrialOverride(const std::string& feature_name,
- OverrideState override_state,
- FieldTrial* field_trial);
-
- // Loops through feature overrides and serializes them all into |allocator|.
- void AddFeaturesToAllocator(PersistentMemoryAllocator* allocator);
-
- // Returns comma-separated lists of feature names (in the same format that is
- // accepted by InitializeFromCommandLine()) corresponding to features that
- // have been overridden - either through command-line or via FieldTrials. For
- // those features that have an associated FieldTrial, the output entry will be
- // of the format "FeatureName<TrialName", where "TrialName" is the name of the
- // FieldTrial. Features that have overrides with OVERRIDE_USE_DEFAULT will be
- // added to |enable_overrides| with a '*' character prefix. Must be called
- // only after the instance has been initialized and registered.
- void GetFeatureOverrides(std::string* enable_overrides,
- std::string* disable_overrides);
-
- // Like GetFeatureOverrides(), but only returns overrides that were specified
- // explicitly on the command-line, omitting the ones from field trials.
- void GetCommandLineFeatureOverrides(std::string* enable_overrides,
- std::string* disable_overrides);
-
- // Returns whether the given |feature| is enabled. Must only be called after
- // the singleton instance has been registered via SetInstance(). Additionally,
- // a feature with a given name must only have a single corresponding Feature
- // struct, which is checked in builds with DCHECKs enabled.
- static bool IsEnabled(const Feature& feature);
-
- // Returns the field trial associated with the given |feature|. Must only be
- // called after the singleton instance has been registered via SetInstance().
- static FieldTrial* GetFieldTrial(const Feature& feature);
-
- // Splits a comma-separated string containing feature names into a vector. The
- // resulting pieces point to parts of |input|.
- static std::vector<base::StringPiece> SplitFeatureListString(
- base::StringPiece input);
-
- // Initializes and sets an instance of FeatureList with feature overrides via
- // command-line flags |enable_features| and |disable_features| if one has not
- // already been set from command-line flags. Returns true if an instance did
- // not previously exist. See InitializeFromCommandLine() for more details
- // about |enable_features| and |disable_features| parameters.
- static bool InitializeInstance(const std::string& enable_features,
- const std::string& disable_features);
-
- // Returns the singleton instance of FeatureList. Will return null until an
- // instance is registered via SetInstance().
- static FeatureList* GetInstance();
-
- // Registers the given |instance| to be the singleton feature list for this
- // process. This should only be called once and |instance| must not be null.
- // Note: If you are considering using this for the purposes of testing, take
- // a look at using base/test/scoped_feature_list.h instead.
- static void SetInstance(std::unique_ptr<FeatureList> instance);
-
- // Clears the previously-registered singleton instance for tests and returns
- // the old instance.
- // Note: Most tests should never call this directly. Instead consider using
- // base::test::ScopedFeatureList.
- static std::unique_ptr<FeatureList> ClearInstanceForTesting();
-
- // Sets a given (initialized) |instance| to be the singleton feature list,
- // for testing. Existing instance must be null. This is primarily intended
- // to support base::test::ScopedFeatureList helper class.
- static void RestoreInstanceForTesting(std::unique_ptr<FeatureList> instance);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity);
- FRIEND_TEST_ALL_PREFIXES(FeatureListTest,
- StoreAndRetrieveFeaturesFromSharedMemory);
- FRIEND_TEST_ALL_PREFIXES(FeatureListTest,
- StoreAndRetrieveAssociatedFeaturesFromSharedMemory);
-
- struct OverrideEntry {
- // The overridden enable (on/off) state of the feature.
- const OverrideState overridden_state;
-
- // An optional associated field trial, which will be activated when the
- // state of the feature is queried for the first time. Weak pointer to the
- // FieldTrial object that is owned by the FieldTrialList singleton.
- base::FieldTrial* field_trial;
-
- // Specifies whether the feature's state is overridden by |field_trial|.
- // If it's not, and |field_trial| is not null, it means it is simply an
- // associated field trial for reporting purposes (and |overridden_state|
- // came from the command-line).
- const bool overridden_by_field_trial;
-
- // TODO(asvitkine): Expand this as more support is added.
-
- // Constructs an OverrideEntry for the given |overridden_state|. If
- // |field_trial| is not null, it implies that |overridden_state| comes from
- // the trial, so |overridden_by_field_trial| will be set to true.
- OverrideEntry(OverrideState overridden_state, FieldTrial* field_trial);
- };
-
- // Finalizes the initialization state of the FeatureList, so that no further
- // overrides can be registered. This is called by SetInstance() on the
- // singleton feature list that is being registered.
- void FinalizeInitialization();
-
- // Returns whether the given |feature| is enabled. This is invoked by the
- // public FeatureList::IsEnabled() static function on the global singleton.
- // Requires the FeatureList to have already been fully initialized.
- bool IsFeatureEnabled(const Feature& feature);
-
- // Returns the field trial associated with the given |feature|. This is
- // invoked by the public FeatureList::GetFieldTrial() static function on the
- // global singleton. Requires the FeatureList to have already been fully
- // initialized.
- base::FieldTrial* GetAssociatedFieldTrial(const Feature& feature);
-
- // For each feature name in comma-separated list of strings |feature_list|,
- // registers an override with the specified |overridden_state|. Also, will
- // associate an optional named field trial if the entry is of the format
- // "FeatureName<TrialName".
- void RegisterOverridesFromCommandLine(const std::string& feature_list,
- OverrideState overridden_state);
-
- // Registers an override for feature |feature_name|. The override specifies
- // whether the feature should be on or off (via |overridden_state|), which
- // will take precedence over the feature's default state. If |field_trial| is
- // not null, registers the specified field trial object to be associated with
- // the feature, which will activate the field trial when the feature state is
- // queried. If an override is already registered for the given feature, it
- // will not be changed.
- void RegisterOverride(StringPiece feature_name,
- OverrideState overridden_state,
- FieldTrial* field_trial);
-
- // Implementation of GetFeatureOverrides() with a parameter that specifies
- // whether only command-line enabled overrides should be emitted. See that
- // function's comments for more details.
- void GetFeatureOverridesImpl(std::string* enable_overrides,
- std::string* disable_overrides,
- bool command_line_only);
-
- // Verifies that there's only a single definition of a Feature struct for a
- // given feature name. Keeps track of the first seen Feature struct for each
- // feature. Returns false when called on a Feature struct with a different
- // address than the first one it saw for that feature name. Used only from
- // DCHECKs and tests.
- bool CheckFeatureIdentity(const Feature& feature);
-
- // Map from feature name to an OverrideEntry struct for the feature, if it
- // exists.
- std::map<std::string, OverrideEntry> overrides_;
-
- // Locked map that keeps track of seen features, to ensure a single feature is
- // only defined once. This verification is only done in builds with DCHECKs
- // enabled.
- Lock feature_identity_tracker_lock_;
- std::map<std::string, const Feature*> feature_identity_tracker_;
-
- // Whether this object has been fully initialized. This gets set to true as a
- // result of FinalizeInitialization().
- bool initialized_ = false;
-
- // Whether this object has been initialized from command line.
- bool initialized_from_command_line_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(FeatureList);
-};
-
-} // namespace base
-
-#endif // BASE_FEATURE_LIST_H_
diff --git a/base/files/file.cc b/base/files/file.cc
index 92ff6dd..b79802b 100644
--- a/base/files/file.cc
+++ b/base/files/file.cc
@@ -5,7 +5,6 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_tracing.h"
-#include "base/metrics/histogram.h"
#include "base/timer/elapsed_timer.h"
#include "build_config.h"
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index d6b7641..9f6ae16 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -11,7 +11,6 @@
#include <unistd.h>
#include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
@@ -421,9 +420,6 @@
case ENOTDIR:
return FILE_ERROR_NOT_A_DIRECTORY;
default:
-#if !defined(OS_NACL) // NaCl build has no metrics code.
- UmaHistogramSparse("PlatformFile.UnknownErrors.Posix", saved_errno);
-#endif
// This function should only be called for errors.
DCHECK_NE(0, saved_errno);
return FILE_ERROR_FAILED;
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc
deleted file mode 100644
index 7fd9d79..0000000
--- a/base/files/important_file_writer.cc
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/important_file_writer.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/critical_closure.h"
-#include "base/debug/alias.h"
-#include "base/files/file.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/task_runner.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread.h"
-#include "base/time/time.h"
-#include "build_config.h"
-
-namespace base {
-
-namespace {
-
-constexpr auto kDefaultCommitInterval = TimeDelta::FromSeconds(10);
-
-// This enum is used to define the buckets for an enumerated UMA histogram.
-// Hence,
-// (a) existing enumerated constants should never be deleted or reordered, and
-// (b) new constants should only be appended at the end of the enumeration.
-enum TempFileFailure {
- FAILED_CREATING,
- FAILED_OPENING,
- FAILED_CLOSING, // Unused.
- FAILED_WRITING,
- FAILED_RENAMING,
- FAILED_FLUSHING,
- TEMP_FILE_FAILURE_MAX
-};
-
-// Helper function to write samples to a histogram with a dynamically assigned
-// histogram name. Works with different error code types convertible to int
-// which is the actual argument type of UmaHistogramExactLinear.
-template <typename SampleType>
-void UmaHistogramExactLinearWithSuffix(const char* histogram_name,
- StringPiece histogram_suffix,
- SampleType add_sample,
- SampleType max_sample) {
- static_assert(std::is_convertible<SampleType, int>::value,
- "SampleType should be convertible to int");
- DCHECK(histogram_name);
- std::string histogram_full_name(histogram_name);
- if (!histogram_suffix.empty()) {
- histogram_full_name.append(".");
- histogram_full_name.append(histogram_suffix.data(),
- histogram_suffix.length());
- }
- UmaHistogramExactLinear(histogram_full_name, static_cast<int>(add_sample),
- static_cast<int>(max_sample));
-}
-
-// Helper function to write samples to a histogram with a dynamically assigned
-// histogram name. Works with short timings from 1 ms up to 10 seconds (50
-// buckets) which is the actual argument type of UmaHistogramTimes.
-void UmaHistogramTimesWithSuffix(const char* histogram_name,
- StringPiece histogram_suffix,
- TimeDelta sample) {
- DCHECK(histogram_name);
- std::string histogram_full_name(histogram_name);
- if (!histogram_suffix.empty()) {
- histogram_full_name.append(".");
- histogram_full_name.append(histogram_suffix.data(),
- histogram_suffix.length());
- }
- UmaHistogramTimes(histogram_full_name, sample);
-}
-
-void LogFailure(const FilePath& path,
- StringPiece histogram_suffix,
- TempFileFailure failure_code,
- StringPiece message) {
- UmaHistogramExactLinearWithSuffix("ImportantFile.TempFileFailures",
- histogram_suffix, failure_code,
- TEMP_FILE_FAILURE_MAX);
- DPLOG(WARNING) << "temp file failure: " << path.value() << " : " << message;
-}
-
-// Helper function to call WriteFileAtomically() with a
-// std::unique_ptr<std::string>.
-void WriteScopedStringToFileAtomically(
- const FilePath& path,
- std::unique_ptr<std::string> data,
- Closure before_write_callback,
- Callback<void(bool success)> after_write_callback,
- const std::string& histogram_suffix) {
- if (!before_write_callback.is_null())
- before_write_callback.Run();
-
- TimeTicks start_time = TimeTicks::Now();
- bool result =
- ImportantFileWriter::WriteFileAtomically(path, *data, histogram_suffix);
- if (result) {
- UmaHistogramTimesWithSuffix("ImportantFile.TimeToWrite", histogram_suffix,
- TimeTicks::Now() - start_time);
- }
-
- if (!after_write_callback.is_null())
- after_write_callback.Run(result);
-}
-
-void DeleteTmpFile(const FilePath& tmp_file_path,
- StringPiece histogram_suffix) {
- if (!DeleteFile(tmp_file_path, false)) {
- UmaHistogramExactLinearWithSuffix(
- "ImportantFile.FileDeleteError", histogram_suffix,
- -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
- }
-}
-
-} // namespace
-
-// static
-bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
- StringPiece data,
- StringPiece histogram_suffix) {
-#if defined(OS_CHROMEOS)
- // On Chrome OS, chrome gets killed when it cannot finish shutdown quickly,
- // and this function seems to be one of the slowest shutdown steps.
- // Include some info to the report for investigation. crbug.com/418627
- // TODO(hashimoto): Remove this.
- struct {
- size_t data_size;
- char path[128];
- } file_info;
- file_info.data_size = data.size();
- strlcpy(file_info.path, path.value().c_str(), arraysize(file_info.path));
- debug::Alias(&file_info);
-#endif
-
- // Write the data to a temp file then rename to avoid data loss if we crash
- // while writing the file. Ensure that the temp file is on the same volume
- // as target file, so it can be moved in one step, and that the temp file
- // is securely created.
- FilePath tmp_file_path;
- if (!CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
- UmaHistogramExactLinearWithSuffix(
- "ImportantFile.FileCreateError", histogram_suffix,
- -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
- LogFailure(path, histogram_suffix, FAILED_CREATING,
- "could not create temporary file");
- return false;
- }
-
- File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
- if (!tmp_file.IsValid()) {
- UmaHistogramExactLinearWithSuffix(
- "ImportantFile.FileOpenError", histogram_suffix,
- -tmp_file.error_details(), -base::File::FILE_ERROR_MAX);
- LogFailure(path, histogram_suffix, FAILED_OPENING,
- "could not open temporary file");
- DeleteFile(tmp_file_path, false);
- return false;
- }
-
- // If this fails in the wild, something really bad is going on.
- const int data_length = checked_cast<int32_t>(data.length());
- int bytes_written = tmp_file.Write(0, data.data(), data_length);
- if (bytes_written < data_length) {
- UmaHistogramExactLinearWithSuffix(
- "ImportantFile.FileWriteError", histogram_suffix,
- -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX);
- }
- bool flush_success = tmp_file.Flush();
- tmp_file.Close();
-
- if (bytes_written < data_length) {
- LogFailure(path, histogram_suffix, FAILED_WRITING,
- "error writing, bytes_written=" + IntToString(bytes_written));
- DeleteTmpFile(tmp_file_path, histogram_suffix);
- return false;
- }
-
- if (!flush_success) {
- LogFailure(path, histogram_suffix, FAILED_FLUSHING, "error flushing");
- DeleteTmpFile(tmp_file_path, histogram_suffix);
- return false;
- }
-
- base::File::Error replace_file_error = base::File::FILE_OK;
- if (!ReplaceFile(tmp_file_path, path, &replace_file_error)) {
- UmaHistogramExactLinearWithSuffix("ImportantFile.FileRenameError",
- histogram_suffix, -replace_file_error,
- -base::File::FILE_ERROR_MAX);
- LogFailure(path, histogram_suffix, FAILED_RENAMING,
- "could not rename temporary file");
- DeleteTmpFile(tmp_file_path, histogram_suffix);
- return false;
- }
-
- return true;
-}
-
-ImportantFileWriter::ImportantFileWriter(
- const FilePath& path,
- scoped_refptr<SequencedTaskRunner> task_runner,
- const char* histogram_suffix)
- : ImportantFileWriter(path,
- std::move(task_runner),
- kDefaultCommitInterval,
- histogram_suffix) {}
-
-ImportantFileWriter::ImportantFileWriter(
- const FilePath& path,
- scoped_refptr<SequencedTaskRunner> task_runner,
- TimeDelta interval,
- const char* histogram_suffix)
- : path_(path),
- task_runner_(std::move(task_runner)),
- serializer_(nullptr),
- commit_interval_(interval),
- histogram_suffix_(histogram_suffix ? histogram_suffix : ""),
- weak_factory_(this) {
- DCHECK(task_runner_);
-}
-
-ImportantFileWriter::~ImportantFileWriter() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // We're usually a member variable of some other object, which also tends
- // to be our serializer. It may not be safe to call back to the parent object
- // being destructed.
- DCHECK(!HasPendingWrite());
-}
-
-bool ImportantFileWriter::HasPendingWrite() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return timer().IsRunning();
-}
-
-void ImportantFileWriter::WriteNow(std::unique_ptr<std::string> data) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!IsValueInRangeForNumericType<int32_t>(data->length())) {
- NOTREACHED();
- return;
- }
-
- Closure task = AdaptCallbackForRepeating(
- BindOnce(&WriteScopedStringToFileAtomically, path_, std::move(data),
- std::move(before_next_write_callback_),
- std::move(after_next_write_callback_), histogram_suffix_));
-
- if (!task_runner_->PostTask(FROM_HERE, MakeCriticalClosure(task))) {
- // Posting the task to background message loop is not expected
- // to fail, but if it does, avoid losing data and just hit the disk
- // on the current thread.
- NOTREACHED();
-
- task.Run();
- }
- ClearPendingWrite();
-}
-
-void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- DCHECK(serializer);
- serializer_ = serializer;
-
- if (!timer().IsRunning()) {
- timer().Start(
- FROM_HERE, commit_interval_,
- Bind(&ImportantFileWriter::DoScheduledWrite, Unretained(this)));
- }
-}
-
-void ImportantFileWriter::DoScheduledWrite() {
- DCHECK(serializer_);
- std::unique_ptr<std::string> data(new std::string);
- if (serializer_->SerializeData(data.get())) {
- WriteNow(std::move(data));
- } else {
- DLOG(WARNING) << "failed to serialize data to be saved in "
- << path_.value();
- }
- ClearPendingWrite();
-}
-
-void ImportantFileWriter::RegisterOnNextWriteCallbacks(
- const Closure& before_next_write_callback,
- const Callback<void(bool success)>& after_next_write_callback) {
- before_next_write_callback_ = before_next_write_callback;
- after_next_write_callback_ = after_next_write_callback;
-}
-
-void ImportantFileWriter::ClearPendingWrite() {
- timer().Stop();
- serializer_ = nullptr;
-}
-
-void ImportantFileWriter::SetTimerForTesting(Timer* timer_override) {
- timer_override_ = timer_override;
-}
-
-} // namespace base
diff --git a/base/files/important_file_writer.h b/base/files/important_file_writer.h
deleted file mode 100644
index 08a7ee3..0000000
--- a/base/files/important_file_writer.h
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_FILES_IMPORTANT_FILE_WRITER_H_
-#define BASE_FILES_IMPORTANT_FILE_WRITER_H_
-
-#include <string>
-
-#include "base/base_export.h"
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-
-namespace base {
-
-class SequencedTaskRunner;
-
-// Helper for atomically writing a file to ensure that it won't be corrupted by
-// *application* crash during write (implemented as create, flush, rename).
-//
-// As an added benefit, ImportantFileWriter makes it less likely that the file
-// is corrupted by *system* crash, though even if the ImportantFileWriter call
-// has already returned at the time of the crash it is not specified which
-// version of the file (old or new) is preserved. And depending on system
-// configuration (hardware and software) a significant likelihood of file
-// corruption may remain, thus using ImportantFileWriter is not a valid
-// substitute for file integrity checks and recovery codepaths for malformed
-// files.
-//
-// Also note that ImportantFileWriter can be *really* slow (cf. File::Flush()
-// for details) and thus please don't block shutdown on ImportantFileWriter.
-class BASE_EXPORT ImportantFileWriter {
- public:
- // Used by ScheduleSave to lazily provide the data to be saved. Allows us
- // to also batch data serializations.
- class BASE_EXPORT DataSerializer {
- public:
- // Should put serialized string in |data| and return true on successful
- // serialization. Will be called on the same thread on which
- // ImportantFileWriter has been created.
- virtual bool SerializeData(std::string* data) = 0;
-
- protected:
- virtual ~DataSerializer() = default;
- };
-
- // Save |data| to |path| in an atomic manner. Blocks and writes data on the
- // current thread. Does not guarantee file integrity across system crash (see
- // the class comment above).
- static bool WriteFileAtomically(const FilePath& path,
- StringPiece data,
- StringPiece histogram_suffix = StringPiece());
-
- // Initialize the writer.
- // |path| is the name of file to write.
- // |task_runner| is the SequencedTaskRunner instance where on which we will
- // execute file I/O operations.
- // All non-const methods, ctor and dtor must be called on the same thread.
- ImportantFileWriter(const FilePath& path,
- scoped_refptr<SequencedTaskRunner> task_runner,
- const char* histogram_suffix = nullptr);
-
- // Same as above, but with a custom commit interval.
- ImportantFileWriter(const FilePath& path,
- scoped_refptr<SequencedTaskRunner> task_runner,
- TimeDelta interval,
- const char* histogram_suffix = nullptr);
-
- // You have to ensure that there are no pending writes at the moment
- // of destruction.
- ~ImportantFileWriter();
-
- const FilePath& path() const { return path_; }
-
- // Returns true if there is a scheduled write pending which has not yet
- // been started.
- bool HasPendingWrite() const;
-
- // Save |data| to target filename. Does not block. If there is a pending write
- // scheduled by ScheduleWrite(), it is cancelled.
- void WriteNow(std::unique_ptr<std::string> data);
-
- // Schedule a save to target filename. Data will be serialized and saved
- // to disk after the commit interval. If another ScheduleWrite is issued
- // before that, only one serialization and write to disk will happen, and
- // the most recent |serializer| will be used. This operation does not block.
- // |serializer| should remain valid through the lifetime of
- // ImportantFileWriter.
- void ScheduleWrite(DataSerializer* serializer);
-
- // Serialize data pending to be saved and execute write on backend thread.
- void DoScheduledWrite();
-
- // Registers |before_next_write_callback| and |after_next_write_callback| to
- // be synchronously invoked from WriteFileAtomically() before its next write
- // and after its next write, respectively. The boolean passed to
- // |after_next_write_callback| indicates whether the write was successful.
- // Both callbacks must be thread safe as they will be called on |task_runner_|
- // and may be called during Chrome shutdown.
- // If called more than once before a write is scheduled on |task_runner|, the
- // latest callbacks clobber the others.
- void RegisterOnNextWriteCallbacks(
- const Closure& before_next_write_callback,
- const Callback<void(bool success)>& after_next_write_callback);
-
- TimeDelta commit_interval() const {
- return commit_interval_;
- }
-
- // Overrides the timer to use for scheduling writes with |timer_override|.
- void SetTimerForTesting(Timer* timer_override);
-
- private:
- const Timer& timer() const {
- return timer_override_ ? const_cast<const Timer&>(*timer_override_)
- : timer_;
- }
- Timer& timer() { return timer_override_ ? *timer_override_ : timer_; }
-
- void ClearPendingWrite();
-
- // Invoked synchronously on the next write event.
- Closure before_next_write_callback_;
- Callback<void(bool success)> after_next_write_callback_;
-
- // Path being written to.
- const FilePath path_;
-
- // TaskRunner for the thread on which file I/O can be done.
- const scoped_refptr<SequencedTaskRunner> task_runner_;
-
- // Timer used to schedule commit after ScheduleWrite.
- OneShotTimer timer_;
-
- // An override for |timer_| used for testing.
- Timer* timer_override_ = nullptr;
-
- // Serializer which will provide the data to be saved.
- DataSerializer* serializer_;
-
- // Time delta after which scheduled data will be written to disk.
- const TimeDelta commit_interval_;
-
- // Custom histogram suffix.
- const std::string histogram_suffix_;
-
- SEQUENCE_CHECKER(sequence_checker_);
-
- WeakPtrFactory<ImportantFileWriter> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ImportantFileWriter);
-};
-
-} // namespace base
-
-#endif // BASE_FILES_IMPORTANT_FILE_WRITER_H_
diff --git a/base/logging.cc b/base/logging.cc
index cf96aeb..72ca87a 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -88,7 +88,6 @@
#include "base/callback.h"
#include "base/command_line.h"
#include "base/containers/stack.h"
-#include "base/debug/activity_tracker.h"
#include "base/debug/alias.h"
#include "base/debug/debugger.h"
#include "base/debug/stack_trace.h"
@@ -816,12 +815,6 @@
}
if (severity_ == LOG_FATAL) {
- // Write the log message to the global activity tracker, if running.
- base::debug::GlobalActivityTracker* tracker =
- base::debug::GlobalActivityTracker::Get();
- if (tracker)
- tracker->RecordLogMessage(str_newline);
-
// Ensure the first characters of the string are on the stack so they
// are contained in minidumps for diagnostic purposes.
DEBUG_ALIAS_FOR_CSTR(str_stack, str_newline.c_str(), 1024);
diff --git a/base/memory/shared_memory_mac.cc b/base/memory/shared_memory_mac.cc
index 3990948..5e9e8fc 100644
--- a/base/memory/shared_memory_mac.cc
+++ b/base/memory/shared_memory_mac.cc
@@ -19,8 +19,6 @@
#include "base/mac/scoped_mach_vm.h"
#include "base/memory/shared_memory_helper.h"
#include "base/memory/shared_memory_tracker.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/safe_strerror.h"
#include "base/process/process_metrics.h"
diff --git a/base/metrics/OWNERS b/base/metrics/OWNERS
deleted file mode 100644
index 4cc69ff..0000000
--- a/base/metrics/OWNERS
+++ /dev/null
@@ -1,10 +0,0 @@
-asvitkine@chromium.org
-bcwhite@chromium.org
-gayane@chromium.org
-holte@chromium.org
-isherman@chromium.org
-jwd@chromium.org
-mpearson@chromium.org
-rkaplow@chromium.org
-
-# COMPONENT: Internals>Metrics
diff --git a/base/metrics/bucket_ranges.cc b/base/metrics/bucket_ranges.cc
deleted file mode 100644
index 39b3793..0000000
--- a/base/metrics/bucket_ranges.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/bucket_ranges.h"
-
-#include <cmath>
-
-#include "base/logging.h"
-
-namespace base {
-
-// Static table of checksums for all possible 8 bit bytes.
-const uint32_t kCrcTable[256] = {
- 0x0, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x76dc419L,
- 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, 0x79dcb8a4L,
- 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
- 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
- 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
- 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
- 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
- 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
- 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
- 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
- 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
- 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
- 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
- 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x6b6b51fL,
- 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL,
- 0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L,
- 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
- 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
- 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
- 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
- 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
- 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
- 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
- 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
- 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
- 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
- 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L,
- 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L,
- 0xe40ecf0bL, 0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L,
- 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
- 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
- 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
- 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
- 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
- 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
- 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
- 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
- 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
- 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
- 0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L,
- 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL,
- 0xe5d5be0dL, 0x7cdcefb7L, 0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
- 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
- 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
- 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
- 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
- 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
- 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
- 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
- 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
- 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
- 0x2d02ef8dL,
-};
-
-// We generate the CRC-32 using the low order bits to select whether to XOR in
-// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us
-// to keep the quotient in a uint32_t. Since we're not concerned about the
-// nature of corruptions (i.e., we don't care about bit sequencing, since we are
-// handling memory changes, which are more grotesque) so we don't bother to get
-// the CRC correct for big-endian vs little-ending calculations. All we need is
-// a nice hash, that tends to depend on all the bits of the sample, with very
-// little chance of changes in one place impacting changes in another place.
-static uint32_t Crc32(uint32_t sum, HistogramBase::Sample value) {
- union {
- HistogramBase::Sample range;
- unsigned char bytes[sizeof(HistogramBase::Sample)];
- } converter;
- converter.range = value;
- for (size_t i = 0; i < sizeof(converter); ++i) {
- sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8);
- }
- return sum;
-}
-
-BucketRanges::BucketRanges(size_t num_ranges)
- : ranges_(num_ranges, 0),
- checksum_(0) {}
-
-BucketRanges::~BucketRanges() = default;
-
-uint32_t BucketRanges::CalculateChecksum() const {
- // Seed checksum.
- uint32_t checksum = static_cast<uint32_t>(ranges_.size());
-
- for (size_t index = 0; index < ranges_.size(); ++index)
- checksum = Crc32(checksum, ranges_[index]);
- return checksum;
-}
-
-bool BucketRanges::HasValidChecksum() const {
- return CalculateChecksum() == checksum_;
-}
-
-void BucketRanges::ResetChecksum() {
- checksum_ = CalculateChecksum();
-}
-
-bool BucketRanges::Equals(const BucketRanges* other) const {
- if (checksum_ != other->checksum_)
- return false;
- if (ranges_.size() != other->ranges_.size())
- return false;
- for (size_t index = 0; index < ranges_.size(); ++index) {
- if (ranges_[index] != other->ranges_[index])
- return false;
- }
- return true;
-}
-
-} // namespace base
diff --git a/base/metrics/bucket_ranges.h b/base/metrics/bucket_ranges.h
deleted file mode 100644
index 1b6d069..0000000
--- a/base/metrics/bucket_ranges.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// BucketRanges stores the vector of ranges that delimit what samples are
-// tallied in the corresponding buckets of a histogram. Histograms that have
-// same ranges for all their corresponding buckets should share the same
-// BucketRanges object.
-//
-// E.g. A 5 buckets LinearHistogram with 1 as minimal value and 4 as maximal
-// value will need a BucketRanges with 6 ranges:
-// 0, 1, 2, 3, 4, INT_MAX
-//
-// TODO(kaiwang): Currently we keep all negative values in 0~1 bucket. Consider
-// changing 0 to INT_MIN.
-
-#ifndef BASE_METRICS_BUCKET_RANGES_H_
-#define BASE_METRICS_BUCKET_RANGES_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <vector>
-
-#include <limits.h>
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
-
-namespace base {
-
-class BASE_EXPORT BucketRanges {
- public:
- typedef std::vector<HistogramBase::Sample> Ranges;
-
- explicit BucketRanges(size_t num_ranges);
- ~BucketRanges();
-
- size_t size() const { return ranges_.size(); }
- HistogramBase::Sample range(size_t i) const { return ranges_[i]; }
- void set_range(size_t i, HistogramBase::Sample value) {
- DCHECK_LT(i, ranges_.size());
- DCHECK_GE(value, 0);
- ranges_[i] = value;
- }
- uint32_t checksum() const { return checksum_; }
- void set_checksum(uint32_t checksum) { checksum_ = checksum; }
-
- // A bucket is defined by a consecutive pair of entries in |ranges|, so there
- // is one fewer bucket than there are ranges. For example, if |ranges| is
- // [0, 1, 3, 7, INT_MAX], then the buckets in this histogram are
- // [0, 1), [1, 3), [3, 7), and [7, INT_MAX).
- size_t bucket_count() const { return ranges_.size() - 1; }
-
- // Checksum methods to verify whether the ranges are corrupted (e.g. bad
- // memory access).
- uint32_t CalculateChecksum() const;
- bool HasValidChecksum() const;
- void ResetChecksum();
-
- // Return true iff |other| object has same ranges_ as |this| object's ranges_.
- bool Equals(const BucketRanges* other) const;
-
- // Set and get a reference into persistent memory where this bucket data
- // can be found (and re-used). These calls are internally atomic with no
- // safety against overwriting an existing value since though it is wasteful
- // to have multiple identical persistent records, it is still safe.
- void set_persistent_reference(uint32_t ref) const {
- subtle::Release_Store(&persistent_reference_, ref);
- }
- uint32_t persistent_reference() const {
- return subtle::Acquire_Load(&persistent_reference_);
- }
-
- private:
- // A monotonically increasing list of values which determine which bucket to
- // put a sample into. For each index, show the smallest sample that can be
- // added to the corresponding bucket.
- Ranges ranges_;
-
- // Checksum for the conntents of ranges_. Used to detect random over-writes
- // of our data, and to quickly see if some other BucketRanges instance is
- // possibly Equal() to this instance.
- // TODO(kaiwang): Consider change this to uint64_t. Because we see a lot of
- // noise on UMA dashboard.
- uint32_t checksum_;
-
- // A reference into a global PersistentMemoryAllocator where the ranges
- // information is stored. This allows for the record to be created once and
- // re-used simply by having all histograms with the same ranges use the
- // same reference.
- mutable subtle::Atomic32 persistent_reference_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(BucketRanges);
-};
-
-//////////////////////////////////////////////////////////////////////////////
-// Expose only for test.
-BASE_EXPORT extern const uint32_t kCrcTable[256];
-
-} // namespace base
-
-#endif // BASE_METRICS_BUCKET_RANGES_H_
diff --git a/base/metrics/dummy_histogram.cc b/base/metrics/dummy_histogram.cc
deleted file mode 100644
index 2707733..0000000
--- a/base/metrics/dummy_histogram.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/dummy_histogram.h"
-
-#include <memory>
-
-#include "base/logging.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/metrics_hashes.h"
-
-namespace base {
-
-namespace {
-
-// Helper classes for DummyHistogram.
-class DummySampleCountIterator : public SampleCountIterator {
- public:
- DummySampleCountIterator() {}
- ~DummySampleCountIterator() override {}
-
- // SampleCountIterator:
- bool Done() const override { return true; }
- void Next() override { NOTREACHED(); }
- void Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const override {
- NOTREACHED();
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DummySampleCountIterator);
-};
-
-class DummyHistogramSamples : public HistogramSamples {
- public:
- explicit DummyHistogramSamples() : HistogramSamples(0, new LocalMetadata()) {}
- ~DummyHistogramSamples() override {
- delete static_cast<LocalMetadata*>(meta());
- }
-
- // HistogramSamples:
- void Accumulate(HistogramBase::Sample value,
- HistogramBase::Count count) override {}
- HistogramBase::Count GetCount(HistogramBase::Sample value) const override {
- return HistogramBase::Count();
- }
- HistogramBase::Count TotalCount() const override {
- return HistogramBase::Count();
- }
- std::unique_ptr<SampleCountIterator> Iterator() const override {
- return std::make_unique<DummySampleCountIterator>();
- }
- bool AddSubtractImpl(SampleCountIterator* iter, Operator op) override {
- return true;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DummyHistogramSamples);
-};
-
-} // namespace
-
-// static
-DummyHistogram* DummyHistogram::GetInstance() {
- static base::NoDestructor<DummyHistogram> dummy_histogram;
- return dummy_histogram.get();
-}
-
-uint64_t DummyHistogram::name_hash() const {
- return HashMetricName(histogram_name());
-}
-
-HistogramType DummyHistogram::GetHistogramType() const {
- return DUMMY_HISTOGRAM;
-}
-
-bool DummyHistogram::HasConstructionArguments(
- Sample expected_minimum,
- Sample expected_maximum,
- uint32_t expected_bucket_count) const {
- return true;
-}
-
-bool DummyHistogram::AddSamplesFromPickle(PickleIterator* iter) {
- return true;
-}
-
-std::unique_ptr<HistogramSamples> DummyHistogram::SnapshotSamples() const {
- return std::make_unique<DummyHistogramSamples>();
-}
-
-std::unique_ptr<HistogramSamples> DummyHistogram::SnapshotDelta() {
- return std::make_unique<DummyHistogramSamples>();
-}
-
-std::unique_ptr<HistogramSamples> DummyHistogram::SnapshotFinalDelta() const {
- return std::make_unique<DummyHistogramSamples>();
-}
-
-} // namespace base
diff --git a/base/metrics/dummy_histogram.h b/base/metrics/dummy_histogram.h
deleted file mode 100644
index e2cb64e..0000000
--- a/base/metrics/dummy_histogram.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_DUMMY_HISTOGRAM_H_
-#define BASE_METRICS_DUMMY_HISTOGRAM_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-
-#include "base/base_export.h"
-#include "base/metrics/histogram_base.h"
-#include "base/no_destructor.h"
-
-namespace base {
-
-// DummyHistogram is used for mocking histogram objects for histograms that
-// shouldn't be recorded. It doesn't do any actual processing.
-class BASE_EXPORT DummyHistogram : public HistogramBase {
- public:
- static DummyHistogram* GetInstance();
-
- // HistogramBase:
- void CheckName(const StringPiece& name) const override {}
- uint64_t name_hash() const override;
- HistogramType GetHistogramType() const override;
- bool HasConstructionArguments(Sample expected_minimum,
- Sample expected_maximum,
- uint32_t expected_bucket_count) const override;
- void Add(Sample value) override {}
- void AddCount(Sample value, int count) override {}
- void AddSamples(const HistogramSamples& samples) override {}
- bool AddSamplesFromPickle(PickleIterator* iter) override;
- std::unique_ptr<HistogramSamples> SnapshotSamples() const override;
- std::unique_ptr<HistogramSamples> SnapshotDelta() override;
- std::unique_ptr<HistogramSamples> SnapshotFinalDelta() const override;
- void WriteHTMLGraph(std::string* output) const override {}
- void WriteAscii(std::string* output) const override {}
-
- protected:
- // HistogramBase:
- void SerializeInfoImpl(Pickle* pickle) const override {}
- void GetParameters(DictionaryValue* params) const override {}
- void GetCountAndBucketData(Count* count,
- int64_t* sum,
- ListValue* buckets) const override {}
-
- private:
- friend class NoDestructor<DummyHistogram>;
-
- DummyHistogram() : HistogramBase("dummy_histogram") {}
- ~DummyHistogram() override {}
-
- DISALLOW_COPY_AND_ASSIGN(DummyHistogram);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_DUMMY_HISTOGRAM_H_
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
deleted file mode 100644
index 25f21ca..0000000
--- a/base/metrics/field_trial.cc
+++ /dev/null
@@ -1,1517 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/field_trial.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/base_switches.h"
-#include "base/command_line.h"
-#include "base/debug/activity_tracker.h"
-#include "base/logging.h"
-#include "base/metrics/field_trial_param_associator.h"
-#include "base/process/memory.h"
-#include "base/process/process_handle.h"
-#include "base/process/process_info.h"
-#include "base/rand_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/unguessable_token.h"
-
-// On POSIX, the fd is shared using the mapping in GlobalDescriptors.
-#if defined(OS_POSIX) && !defined(OS_NACL)
-#include "base/posix/global_descriptors.h"
-#endif
-
-namespace base {
-
-namespace {
-
-// Define a separator character to use when creating a persistent form of an
-// instance. This is intended for use as a command line argument, passed to a
-// second process to mimic our state (i.e., provide the same group name).
-const char kPersistentStringSeparator = '/'; // Currently a slash.
-
-// Define a marker character to be used as a prefix to a trial name on the
-// command line which forces its activation.
-const char kActivationMarker = '*';
-
-// Use shared memory to communicate field trial (experiment) state. Set to false
-// for now while the implementation is fleshed out (e.g. data format, single
-// shared memory segment). See https://codereview.chromium.org/2365273004/ and
-// crbug.com/653874
-// The browser is the only process that has write access to the shared memory.
-// This is safe from race conditions because MakeIterable is a release operation
-// and GetNextOfType is an acquire operation, so memory writes before
-// MakeIterable happen before memory reads after GetNextOfType.
-#if defined(OS_FUCHSIA) // TODO(752368): Not yet supported on Fuchsia.
-const bool kUseSharedMemoryForFieldTrials = false;
-#else
-const bool kUseSharedMemoryForFieldTrials = true;
-#endif
-
-// Constants for the field trial allocator.
-const char kAllocatorName[] = "FieldTrialAllocator";
-
-// We allocate 128 KiB to hold all the field trial data. This should be enough,
-// as most people use 3 - 25 KiB for field trials (as of 11/25/2016).
-// This also doesn't allocate all 128 KiB at once -- the pages only get mapped
-// to physical memory when they are touched. If the size of the allocated field
-// trials does get larger than 128 KiB, then we will drop some field trials in
-// child processes, leading to an inconsistent view between browser and child
-// processes and possibly causing crashes (see crbug.com/661617).
-const size_t kFieldTrialAllocationSize = 128 << 10; // 128 KiB
-
-// Writes out string1 and then string2 to pickle.
-void WriteStringPair(Pickle* pickle,
- const StringPiece& string1,
- const StringPiece& string2) {
- pickle->WriteString(string1);
- pickle->WriteString(string2);
-}
-
-// Writes out the field trial's contents (via trial_state) to the pickle. The
-// format of the pickle looks like:
-// TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ...
-// If there are no parameters, then it just ends at GroupName.
-void PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) {
- WriteStringPair(pickle, *trial_state.trial_name, *trial_state.group_name);
-
- // Get field trial params.
- std::map<std::string, std::string> params;
- FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback(
- *trial_state.trial_name, *trial_state.group_name, ¶ms);
-
- // Write params to pickle.
- for (const auto& param : params)
- WriteStringPair(pickle, param.first, param.second);
-}
-
-// Created a time value based on |year|, |month| and |day_of_month| parameters.
-Time CreateTimeFromParams(int year, int month, int day_of_month) {
- DCHECK_GT(year, 1970);
- DCHECK_GT(month, 0);
- DCHECK_LT(month, 13);
- DCHECK_GT(day_of_month, 0);
- DCHECK_LT(day_of_month, 32);
-
- Time::Exploded exploded;
- exploded.year = year;
- exploded.month = month;
- exploded.day_of_week = 0; // Should be unused.
- exploded.day_of_month = day_of_month;
- exploded.hour = 0;
- exploded.minute = 0;
- exploded.second = 0;
- exploded.millisecond = 0;
- Time out_time;
- if (!Time::FromLocalExploded(exploded, &out_time)) {
- // TODO(maksims): implement failure handling.
- // We might just return |out_time|, which is Time(0).
- NOTIMPLEMENTED();
- }
-
- return out_time;
-}
-
-// Returns the boundary value for comparing against the FieldTrial's added
-// groups for a given |divisor| (total probability) and |entropy_value|.
-FieldTrial::Probability GetGroupBoundaryValue(
- FieldTrial::Probability divisor,
- double entropy_value) {
- // Add a tiny epsilon value to get consistent results when converting floating
- // points to int. Without it, boundary values have inconsistent results, e.g.:
- //
- // static_cast<FieldTrial::Probability>(100 * 0.56) == 56
- // static_cast<FieldTrial::Probability>(100 * 0.57) == 56
- // static_cast<FieldTrial::Probability>(100 * 0.58) == 57
- // static_cast<FieldTrial::Probability>(100 * 0.59) == 59
- const double kEpsilon = 1e-8;
- const FieldTrial::Probability result =
- static_cast<FieldTrial::Probability>(divisor * entropy_value + kEpsilon);
- // Ensure that adding the epsilon still results in a value < |divisor|.
- return std::min(result, divisor - 1);
-}
-
-// Separate type from FieldTrial::State so that it can use StringPieces.
-struct FieldTrialStringEntry {
- StringPiece trial_name;
- StringPiece group_name;
- bool activated = false;
-};
-
-// Parses the --force-fieldtrials string |trials_string| into |entries|.
-// Returns true if the string was parsed correctly. On failure, the |entries|
-// array may end up being partially filled.
-bool ParseFieldTrialsString(const std::string& trials_string,
- std::vector<FieldTrialStringEntry>* entries) {
- const StringPiece trials_string_piece(trials_string);
-
- size_t next_item = 0;
- while (next_item < trials_string.length()) {
- size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
- if (name_end == trials_string.npos || next_item == name_end)
- return false;
- size_t group_name_end =
- trials_string.find(kPersistentStringSeparator, name_end + 1);
- if (name_end + 1 == group_name_end)
- return false;
- if (group_name_end == trials_string.npos)
- group_name_end = trials_string.length();
-
- FieldTrialStringEntry entry;
- // Verify if the trial should be activated or not.
- if (trials_string[next_item] == kActivationMarker) {
- // Name cannot be only the indicator.
- if (name_end - next_item == 1)
- return false;
- next_item++;
- entry.activated = true;
- }
- entry.trial_name =
- trials_string_piece.substr(next_item, name_end - next_item);
- entry.group_name =
- trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1);
- next_item = group_name_end + 1;
-
- entries->push_back(std::move(entry));
- }
- return true;
-}
-
-void AddFeatureAndFieldTrialFlags(const char* enable_features_switch,
- const char* disable_features_switch,
- CommandLine* cmd_line) {
- std::string enabled_features;
- std::string disabled_features;
- FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features,
- &disabled_features);
-
- if (!enabled_features.empty())
- cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features);
- if (!disabled_features.empty())
- cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features);
-
- std::string field_trial_states;
- FieldTrialList::AllStatesToString(&field_trial_states, false);
- if (!field_trial_states.empty()) {
- cmd_line->AppendSwitchASCII(switches::kForceFieldTrials,
- field_trial_states);
- }
-}
-
-void OnOutOfMemory(size_t size) {
-#if defined(OS_NACL)
- NOTREACHED();
-#else
- TerminateBecauseOutOfMemory(size);
-#endif
-}
-
-#if !defined(OS_NACL)
-// Returns whether the operation succeeded.
-bool DeserializeGUIDFromStringPieces(base::StringPiece first,
- base::StringPiece second,
- base::UnguessableToken* guid) {
- uint64_t high = 0;
- uint64_t low = 0;
- if (!base::StringToUint64(first, &high) ||
- !base::StringToUint64(second, &low)) {
- return false;
- }
-
- *guid = base::UnguessableToken::Deserialize(high, low);
- return true;
-}
-
-// Extract a read-only SharedMemoryHandle from an existing |shared_memory|
-// handle. Note that on Android, this also makes the whole region read-only.
-SharedMemoryHandle GetSharedMemoryReadOnlyHandle(SharedMemory* shared_memory) {
- SharedMemoryHandle result = shared_memory->GetReadOnlyHandle();
-#if defined(OS_ANDROID)
- // On Android, turn the region read-only. This prevents any future
- // writable mapping attempts, but the original one in |shm| survives
- // and is still usable in the current process.
- result.SetRegionReadOnly();
-#endif // OS_ANDROID
- return result;
-}
-#endif // !OS_NACL
-
-} // namespace
-
-// statics
-const int FieldTrial::kNotFinalized = -1;
-const int FieldTrial::kDefaultGroupNumber = 0;
-bool FieldTrial::enable_benchmarking_ = false;
-
-int FieldTrialList::kNoExpirationYear = 0;
-
-//------------------------------------------------------------------------------
-// FieldTrial methods and members.
-
-FieldTrial::EntropyProvider::~EntropyProvider() = default;
-
-FieldTrial::State::State() = default;
-
-FieldTrial::State::State(const State& other) = default;
-
-FieldTrial::State::~State() = default;
-
-bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName(
- StringPiece* trial_name,
- StringPiece* group_name) const {
- PickleIterator iter = GetPickleIterator();
- return ReadStringPair(&iter, trial_name, group_name);
-}
-
-bool FieldTrial::FieldTrialEntry::GetParams(
- std::map<std::string, std::string>* params) const {
- PickleIterator iter = GetPickleIterator();
- StringPiece tmp;
- // Skip reading trial and group name.
- if (!ReadStringPair(&iter, &tmp, &tmp))
- return false;
-
- while (true) {
- StringPiece key;
- StringPiece value;
- if (!ReadStringPair(&iter, &key, &value))
- return key.empty(); // Non-empty is bad: got one of a pair.
- (*params)[key.as_string()] = value.as_string();
- }
-}
-
-PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const {
- const char* src =
- reinterpret_cast<const char*>(this) + sizeof(FieldTrialEntry);
-
- Pickle pickle(src, pickle_size);
- return PickleIterator(pickle);
-}
-
-bool FieldTrial::FieldTrialEntry::ReadStringPair(
- PickleIterator* iter,
- StringPiece* trial_name,
- StringPiece* group_name) const {
- if (!iter->ReadStringPiece(trial_name))
- return false;
- if (!iter->ReadStringPiece(group_name))
- return false;
- return true;
-}
-
-void FieldTrial::Disable() {
- DCHECK(!group_reported_);
- enable_field_trial_ = false;
-
- // In case we are disabled after initialization, we need to switch
- // the trial to the default group.
- if (group_ != kNotFinalized) {
- // Only reset when not already the default group, because in case we were
- // forced to the default group, the group number may not be
- // kDefaultGroupNumber, so we should keep it as is.
- if (group_name_ != default_group_name_)
- SetGroupChoice(default_group_name_, kDefaultGroupNumber);
- }
-}
-
-int FieldTrial::AppendGroup(const std::string& name,
- Probability group_probability) {
- // When the group choice was previously forced, we only need to return the
- // the id of the chosen group, and anything can be returned for the others.
- if (forced_) {
- DCHECK(!group_name_.empty());
- if (name == group_name_) {
- // Note that while |group_| may be equal to |kDefaultGroupNumber| on the
- // forced trial, it will not have the same value as the default group
- // number returned from the non-forced |FactoryGetFieldTrial()| call,
- // which takes care to ensure that this does not happen.
- return group_;
- }
- DCHECK_NE(next_group_number_, group_);
- // We still return different numbers each time, in case some caller need
- // them to be different.
- return next_group_number_++;
- }
-
- DCHECK_LE(group_probability, divisor_);
- DCHECK_GE(group_probability, 0);
-
- if (enable_benchmarking_ || !enable_field_trial_)
- group_probability = 0;
-
- accumulated_group_probability_ += group_probability;
-
- DCHECK_LE(accumulated_group_probability_, divisor_);
- if (group_ == kNotFinalized && accumulated_group_probability_ > random_) {
- // This is the group that crossed the random line, so we do the assignment.
- SetGroupChoice(name, next_group_number_);
- }
- return next_group_number_++;
-}
-
-int FieldTrial::group() {
- FinalizeGroupChoice();
- if (trial_registered_)
- FieldTrialList::NotifyFieldTrialGroupSelection(this);
- return group_;
-}
-
-const std::string& FieldTrial::group_name() {
- // Call |group()| to ensure group gets assigned and observers are notified.
- group();
- DCHECK(!group_name_.empty());
- return group_name_;
-}
-
-const std::string& FieldTrial::GetGroupNameWithoutActivation() {
- FinalizeGroupChoice();
- return group_name_;
-}
-
-void FieldTrial::SetForced() {
- // We might have been forced before (e.g., by CreateFieldTrial) and it's
- // first come first served, e.g., command line switch has precedence.
- if (forced_)
- return;
-
- // And we must finalize the group choice before we mark ourselves as forced.
- FinalizeGroupChoice();
- forced_ = true;
-}
-
-// static
-void FieldTrial::EnableBenchmarking() {
- DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
- enable_benchmarking_ = true;
-}
-
-// static
-FieldTrial* FieldTrial::CreateSimulatedFieldTrial(
- const std::string& trial_name,
- Probability total_probability,
- const std::string& default_group_name,
- double entropy_value) {
- return new FieldTrial(trial_name, total_probability, default_group_name,
- entropy_value);
-}
-
-FieldTrial::FieldTrial(const std::string& trial_name,
- const Probability total_probability,
- const std::string& default_group_name,
- double entropy_value)
- : trial_name_(trial_name),
- divisor_(total_probability),
- default_group_name_(default_group_name),
- random_(GetGroupBoundaryValue(total_probability, entropy_value)),
- accumulated_group_probability_(0),
- next_group_number_(kDefaultGroupNumber + 1),
- group_(kNotFinalized),
- enable_field_trial_(true),
- forced_(false),
- group_reported_(false),
- trial_registered_(false),
- ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull) {
- DCHECK_GT(total_probability, 0);
- DCHECK(!trial_name_.empty());
- DCHECK(!default_group_name_.empty())
- << "Trial " << trial_name << " is missing a default group name.";
-}
-
-FieldTrial::~FieldTrial() = default;
-
-void FieldTrial::SetTrialRegistered() {
- DCHECK_EQ(kNotFinalized, group_);
- DCHECK(!trial_registered_);
- trial_registered_ = true;
-}
-
-void FieldTrial::SetGroupChoice(const std::string& group_name, int number) {
- group_ = number;
- if (group_name.empty())
- StringAppendF(&group_name_, "%d", group_);
- else
- group_name_ = group_name;
- DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_;
-}
-
-void FieldTrial::FinalizeGroupChoice() {
- FinalizeGroupChoiceImpl(false);
-}
-
-void FieldTrial::FinalizeGroupChoiceImpl(bool is_locked) {
- if (group_ != kNotFinalized)
- return;
- accumulated_group_probability_ = divisor_;
- // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not
- // finalized.
- DCHECK(!forced_);
- SetGroupChoice(default_group_name_, kDefaultGroupNumber);
-
- // Add the field trial to shared memory.
- if (kUseSharedMemoryForFieldTrials && trial_registered_)
- FieldTrialList::OnGroupFinalized(is_locked, this);
-}
-
-bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const {
- if (!group_reported_ || !enable_field_trial_)
- return false;
- DCHECK_NE(group_, kNotFinalized);
- active_group->trial_name = trial_name_;
- active_group->group_name = group_name_;
- return true;
-}
-
-bool FieldTrial::GetStateWhileLocked(State* field_trial_state,
- bool include_expired) {
- if (!include_expired && !enable_field_trial_)
- return false;
- FinalizeGroupChoiceImpl(true);
- field_trial_state->trial_name = &trial_name_;
- field_trial_state->group_name = &group_name_;
- field_trial_state->activated = group_reported_;
- return true;
-}
-
-//------------------------------------------------------------------------------
-// FieldTrialList methods and members.
-
-// static
-FieldTrialList* FieldTrialList::global_ = nullptr;
-
-// static
-bool FieldTrialList::used_without_global_ = false;
-
-FieldTrialList::Observer::~Observer() = default;
-
-FieldTrialList::FieldTrialList(
- std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider)
- : entropy_provider_(std::move(entropy_provider)),
- observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>(
- ObserverListPolicy::EXISTING_ONLY)) {
- DCHECK(!global_);
- DCHECK(!used_without_global_);
- global_ = this;
-
- CHECK(false); // TODO(scottmg): Remove FieldTrialList.
-}
-
-FieldTrialList::~FieldTrialList() {
- AutoLock auto_lock(lock_);
- while (!registered_.empty()) {
- RegistrationMap::iterator it = registered_.begin();
- it->second->Release();
- registered_.erase(it->first);
- }
- DCHECK_EQ(this, global_);
- global_ = nullptr;
-}
-
-// static
-FieldTrial* FieldTrialList::FactoryGetFieldTrial(
- const std::string& trial_name,
- FieldTrial::Probability total_probability,
- const std::string& default_group_name,
- const int year,
- const int month,
- const int day_of_month,
- FieldTrial::RandomizationType randomization_type,
- int* default_group_number) {
- return FactoryGetFieldTrialWithRandomizationSeed(
- trial_name, total_probability, default_group_name, year, month,
- day_of_month, randomization_type, 0, default_group_number, nullptr);
-}
-
-// static
-FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
- const std::string& trial_name,
- FieldTrial::Probability total_probability,
- const std::string& default_group_name,
- const int year,
- const int month,
- const int day_of_month,
- FieldTrial::RandomizationType randomization_type,
- uint32_t randomization_seed,
- int* default_group_number,
- const FieldTrial::EntropyProvider* override_entropy_provider) {
- if (default_group_number)
- *default_group_number = FieldTrial::kDefaultGroupNumber;
- // Check if the field trial has already been created in some other way.
- FieldTrial* existing_trial = Find(trial_name);
- if (existing_trial) {
- CHECK(existing_trial->forced_);
- // If the default group name differs between the existing forced trial
- // and this trial, then use a different value for the default group number.
- if (default_group_number &&
- default_group_name != existing_trial->default_group_name()) {
- // If the new default group number corresponds to the group that was
- // chosen for the forced trial (which has been finalized when it was
- // forced), then set the default group number to that.
- if (default_group_name == existing_trial->group_name_internal()) {
- *default_group_number = existing_trial->group_;
- } else {
- // Otherwise, use |kNonConflictingGroupNumber| (-2) for the default
- // group number, so that it does not conflict with the |AppendGroup()|
- // result for the chosen group.
- const int kNonConflictingGroupNumber = -2;
- static_assert(
- kNonConflictingGroupNumber != FieldTrial::kDefaultGroupNumber,
- "The 'non-conflicting' group number conflicts");
- static_assert(kNonConflictingGroupNumber != FieldTrial::kNotFinalized,
- "The 'non-conflicting' group number conflicts");
- *default_group_number = kNonConflictingGroupNumber;
- }
- }
- return existing_trial;
- }
-
- double entropy_value;
- if (randomization_type == FieldTrial::ONE_TIME_RANDOMIZED) {
- // If an override entropy provider is given, use it.
- const FieldTrial::EntropyProvider* entropy_provider =
- override_entropy_provider ? override_entropy_provider
- : GetEntropyProviderForOneTimeRandomization();
- CHECK(entropy_provider);
- entropy_value = entropy_provider->GetEntropyForTrial(trial_name,
- randomization_seed);
- } else {
- DCHECK_EQ(FieldTrial::SESSION_RANDOMIZED, randomization_type);
- DCHECK_EQ(0U, randomization_seed);
- entropy_value = RandDouble();
- }
-
- FieldTrial* field_trial = new FieldTrial(trial_name, total_probability,
- default_group_name, entropy_value);
- CHECK(false); // TODO(scottmg): Remove FieldTrialList.
- return field_trial;
-}
-
-// static
-FieldTrial* FieldTrialList::Find(const std::string& trial_name) {
- if (!global_)
- return nullptr;
- AutoLock auto_lock(global_->lock_);
- return global_->PreLockedFind(trial_name);
-}
-
-// static
-int FieldTrialList::FindValue(const std::string& trial_name) {
- FieldTrial* field_trial = Find(trial_name);
- if (field_trial)
- return field_trial->group();
- return FieldTrial::kNotFinalized;
-}
-
-// static
-std::string FieldTrialList::FindFullName(const std::string& trial_name) {
- FieldTrial* field_trial = Find(trial_name);
- if (field_trial)
- return field_trial->group_name();
- return std::string();
-}
-
-// static
-bool FieldTrialList::TrialExists(const std::string& trial_name) {
- return Find(trial_name) != nullptr;
-}
-
-// static
-bool FieldTrialList::IsTrialActive(const std::string& trial_name) {
- FieldTrial* field_trial = Find(trial_name);
- FieldTrial::ActiveGroup active_group;
- return field_trial && field_trial->GetActiveGroup(&active_group);
-}
-
-// static
-void FieldTrialList::StatesToString(std::string* output) {
- FieldTrial::ActiveGroups active_groups;
- GetActiveFieldTrialGroups(&active_groups);
- for (FieldTrial::ActiveGroups::const_iterator it = active_groups.begin();
- it != active_groups.end(); ++it) {
- DCHECK_EQ(std::string::npos,
- it->trial_name.find(kPersistentStringSeparator));
- DCHECK_EQ(std::string::npos,
- it->group_name.find(kPersistentStringSeparator));
- output->append(it->trial_name);
- output->append(1, kPersistentStringSeparator);
- output->append(it->group_name);
- output->append(1, kPersistentStringSeparator);
- }
-}
-
-// static
-void FieldTrialList::AllStatesToString(std::string* output,
- bool include_expired) {
- if (!global_)
- return;
- AutoLock auto_lock(global_->lock_);
-
- for (const auto& registered : global_->registered_) {
- FieldTrial::State trial;
- if (!registered.second->GetStateWhileLocked(&trial, include_expired))
- continue;
- DCHECK_EQ(std::string::npos,
- trial.trial_name->find(kPersistentStringSeparator));
- DCHECK_EQ(std::string::npos,
- trial.group_name->find(kPersistentStringSeparator));
- if (trial.activated)
- output->append(1, kActivationMarker);
- output->append(*trial.trial_name);
- output->append(1, kPersistentStringSeparator);
- output->append(*trial.group_name);
- output->append(1, kPersistentStringSeparator);
- }
-}
-
-// static
-std::string FieldTrialList::AllParamsToString(bool include_expired,
- EscapeDataFunc encode_data_func) {
- FieldTrialParamAssociator* params_associator =
- FieldTrialParamAssociator::GetInstance();
- std::string output;
- for (const auto& registered : GetRegisteredTrials()) {
- FieldTrial::State trial;
- if (!registered.second->GetStateWhileLocked(&trial, include_expired))
- continue;
- DCHECK_EQ(std::string::npos,
- trial.trial_name->find(kPersistentStringSeparator));
- DCHECK_EQ(std::string::npos,
- trial.group_name->find(kPersistentStringSeparator));
- std::map<std::string, std::string> params;
- if (params_associator->GetFieldTrialParamsWithoutFallback(
- *trial.trial_name, *trial.group_name, ¶ms)) {
- if (params.size() > 0) {
- // Add comma to seprate from previous entry if it exists.
- if (!output.empty())
- output.append(1, ',');
-
- output.append(encode_data_func(*trial.trial_name));
- output.append(1, '.');
- output.append(encode_data_func(*trial.group_name));
- output.append(1, ':');
-
- std::string param_str;
- for (const auto& param : params) {
- // Add separator from previous param information if it exists.
- if (!param_str.empty())
- param_str.append(1, kPersistentStringSeparator);
- param_str.append(encode_data_func(param.first));
- param_str.append(1, kPersistentStringSeparator);
- param_str.append(encode_data_func(param.second));
- }
-
- output.append(param_str);
- }
- }
- }
- return output;
-}
-
-// static
-void FieldTrialList::GetActiveFieldTrialGroups(
- FieldTrial::ActiveGroups* active_groups) {
- DCHECK(active_groups->empty());
- if (!global_)
- return;
- AutoLock auto_lock(global_->lock_);
-
- for (RegistrationMap::iterator it = global_->registered_.begin();
- it != global_->registered_.end(); ++it) {
- FieldTrial::ActiveGroup active_group;
- if (it->second->GetActiveGroup(&active_group))
- active_groups->push_back(active_group);
- }
-}
-
-// static
-void FieldTrialList::GetActiveFieldTrialGroupsFromString(
- const std::string& trials_string,
- FieldTrial::ActiveGroups* active_groups) {
- std::vector<FieldTrialStringEntry> entries;
- if (!ParseFieldTrialsString(trials_string, &entries))
- return;
-
- for (const auto& entry : entries) {
- if (entry.activated) {
- FieldTrial::ActiveGroup group;
- group.trial_name = entry.trial_name.as_string();
- group.group_name = entry.group_name.as_string();
- active_groups->push_back(group);
- }
- }
-}
-
-// static
-void FieldTrialList::GetInitiallyActiveFieldTrials(
- const base::CommandLine& command_line,
- FieldTrial::ActiveGroups* active_groups) {
- DCHECK(global_);
- DCHECK(global_->create_trials_from_command_line_called_);
-
- if (!global_->field_trial_allocator_) {
- GetActiveFieldTrialGroupsFromString(
- command_line.GetSwitchValueASCII(switches::kForceFieldTrials),
- active_groups);
- return;
- }
-
- FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
- FieldTrialAllocator::Iterator mem_iter(allocator);
- const FieldTrial::FieldTrialEntry* entry;
- while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
- nullptr) {
- StringPiece trial_name;
- StringPiece group_name;
- if (subtle::NoBarrier_Load(&entry->activated) &&
- entry->GetTrialAndGroupName(&trial_name, &group_name)) {
- FieldTrial::ActiveGroup group;
- group.trial_name = trial_name.as_string();
- group.group_name = group_name.as_string();
- active_groups->push_back(group);
- }
- }
-}
-
-// static
-bool FieldTrialList::CreateTrialsFromString(
- const std::string& trials_string,
- const std::set<std::string>& ignored_trial_names) {
- DCHECK(global_);
- if (trials_string.empty() || !global_)
- return true;
-
- std::vector<FieldTrialStringEntry> entries;
- if (!ParseFieldTrialsString(trials_string, &entries))
- return false;
-
- for (const auto& entry : entries) {
- const std::string trial_name = entry.trial_name.as_string();
- const std::string group_name = entry.group_name.as_string();
-
- if (ContainsKey(ignored_trial_names, trial_name)) {
- // This is to warn that the field trial forced through command-line
- // input is unforcable.
- // Use --enable-logging or --enable-logging=stderr to see this warning.
- LOG(WARNING) << "Field trial: " << trial_name << " cannot be forced.";
- continue;
- }
-
- FieldTrial* trial = CreateFieldTrial(trial_name, group_name);
- if (!trial)
- return false;
- if (entry.activated) {
- // Call |group()| to mark the trial as "used" and notify observers, if
- // any. This is useful to ensure that field trials created in child
- // processes are properly reported in crash reports.
- trial->group();
- }
- }
- return true;
-}
-
-// static
-void FieldTrialList::CreateTrialsFromCommandLine(
- const CommandLine& cmd_line,
- const char* field_trial_handle_switch,
- int fd_key) {
- global_->create_trials_from_command_line_called_ = true;
-
-#if defined(OS_WIN) || defined(OS_FUCHSIA)
- if (cmd_line.HasSwitch(field_trial_handle_switch)) {
- std::string switch_value =
- cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
- bool result = CreateTrialsFromSwitchValue(switch_value);
- DCHECK(result);
- }
-#elif defined(OS_POSIX) && !defined(OS_NACL)
- // On POSIX, we check if the handle is valid by seeing if the browser process
- // sent over the switch (we don't care about the value). Invalid handles
- // occur in some browser tests which don't initialize the allocator.
- if (cmd_line.HasSwitch(field_trial_handle_switch)) {
- std::string switch_value =
- cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
- bool result = CreateTrialsFromDescriptor(fd_key, switch_value);
- DCHECK(result);
- }
-#endif
-
- if (cmd_line.HasSwitch(switches::kForceFieldTrials)) {
- bool result = FieldTrialList::CreateTrialsFromString(
- cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials),
- std::set<std::string>());
- DCHECK(result);
- }
-}
-
-// static
-void FieldTrialList::CreateFeaturesFromCommandLine(
- const base::CommandLine& command_line,
- const char* enable_features_switch,
- const char* disable_features_switch,
- FeatureList* feature_list) {
- // Fallback to command line if not using shared memory.
- if (!kUseSharedMemoryForFieldTrials ||
- !global_->field_trial_allocator_.get()) {
- return feature_list->InitializeFromCommandLine(
- command_line.GetSwitchValueASCII(enable_features_switch),
- command_line.GetSwitchValueASCII(disable_features_switch));
- }
-
- feature_list->InitializeFromSharedMemory(
- global_->field_trial_allocator_.get());
-}
-
-#if defined(OS_WIN)
-// static
-void FieldTrialList::AppendFieldTrialHandleIfNeeded(
- HandlesToInheritVector* handles) {
- if (!global_)
- return;
- if (kUseSharedMemoryForFieldTrials) {
- InstantiateFieldTrialAllocatorIfNeeded();
- if (global_->readonly_allocator_handle_.IsValid())
- handles->push_back(global_->readonly_allocator_handle_.GetHandle());
- }
-}
-#elif defined(OS_FUCHSIA)
-// TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368).
-#elif defined(OS_POSIX) && !defined(OS_NACL)
-// static
-SharedMemoryHandle FieldTrialList::GetFieldTrialHandle() {
- if (global_ && kUseSharedMemoryForFieldTrials) {
- InstantiateFieldTrialAllocatorIfNeeded();
- // We check for an invalid handle where this gets called.
- return global_->readonly_allocator_handle_;
- }
- return SharedMemoryHandle();
-}
-#endif
-
-// static
-void FieldTrialList::CopyFieldTrialStateToFlags(
- const char* field_trial_handle_switch,
- const char* enable_features_switch,
- const char* disable_features_switch,
- CommandLine* cmd_line) {
- // TODO(lawrencewu): Ideally, having the global would be guaranteed. However,
- // content browser tests currently don't create a FieldTrialList because they
- // don't run ChromeBrowserMainParts code where it's done for Chrome.
- // Some tests depend on the enable and disable features flag switch, though,
- // so we can still add those even though AllStatesToString() will be a no-op.
- if (!global_) {
- AddFeatureAndFieldTrialFlags(enable_features_switch,
- disable_features_switch, cmd_line);
- return;
- }
-
- // Use shared memory to pass the state if the feature is enabled, otherwise
- // fallback to passing it via the command line as a string.
- if (kUseSharedMemoryForFieldTrials) {
- InstantiateFieldTrialAllocatorIfNeeded();
- // If the readonly handle didn't get duplicated properly, then fallback to
- // original behavior.
- if (!global_->readonly_allocator_handle_.IsValid()) {
- AddFeatureAndFieldTrialFlags(enable_features_switch,
- disable_features_switch, cmd_line);
- return;
- }
-
- global_->field_trial_allocator_->UpdateTrackingHistograms();
- std::string switch_value = SerializeSharedMemoryHandleMetadata(
- global_->readonly_allocator_handle_);
- cmd_line->AppendSwitchASCII(field_trial_handle_switch, switch_value);
-
- // Append --enable-features and --disable-features switches corresponding
- // to the features enabled on the command-line, so that child and browser
- // process command lines match and clearly show what has been specified
- // explicitly by the user.
- std::string enabled_features;
- std::string disabled_features;
- FeatureList::GetInstance()->GetCommandLineFeatureOverrides(
- &enabled_features, &disabled_features);
-
- if (!enabled_features.empty())
- cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features);
- if (!disabled_features.empty())
- cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features);
-
- return;
- }
-
- AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch,
- cmd_line);
-}
-
-// static
-FieldTrial* FieldTrialList::CreateFieldTrial(
- const std::string& name,
- const std::string& group_name) {
- DCHECK(global_);
- DCHECK_GE(name.size(), 0u);
- DCHECK_GE(group_name.size(), 0u);
- if (name.empty() || group_name.empty() || !global_)
- return nullptr;
-
- FieldTrial* field_trial = FieldTrialList::Find(name);
- if (field_trial) {
- // In single process mode, or when we force them from the command line,
- // we may have already created the field trial.
- if (field_trial->group_name_internal() != group_name)
- return nullptr;
- return field_trial;
- }
- const int kTotalProbability = 100;
- field_trial = new FieldTrial(name, kTotalProbability, group_name, 0);
- FieldTrialList::Register(field_trial);
- // Force the trial, which will also finalize the group choice.
- field_trial->SetForced();
- return field_trial;
-}
-
-// static
-bool FieldTrialList::AddObserver(Observer* observer) {
- if (!global_)
- return false;
- global_->observer_list_->AddObserver(observer);
- return true;
-}
-
-// static
-void FieldTrialList::RemoveObserver(Observer* observer) {
- if (!global_)
- return;
- global_->observer_list_->RemoveObserver(observer);
-}
-
-// static
-void FieldTrialList::SetSynchronousObserver(Observer* observer) {
- DCHECK(!global_->synchronous_observer_);
- global_->synchronous_observer_ = observer;
-}
-
-// static
-void FieldTrialList::RemoveSynchronousObserver(Observer* observer) {
- DCHECK_EQ(global_->synchronous_observer_, observer);
- global_->synchronous_observer_ = nullptr;
-}
-
-// static
-void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) {
- if (!global_)
- return;
- if (is_locked) {
- AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
- field_trial);
- } else {
- AutoLock auto_lock(global_->lock_);
- AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
- field_trial);
- }
-}
-
-// static
-void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) {
- if (!global_)
- return;
-
- {
- AutoLock auto_lock(global_->lock_);
- if (field_trial->group_reported_)
- return;
- field_trial->group_reported_ = true;
-
- if (!field_trial->enable_field_trial_)
- return;
-
- if (kUseSharedMemoryForFieldTrials)
- ActivateFieldTrialEntryWhileLocked(field_trial);
- }
-
- // Recording for stability debugging has to be done inline as a task posted
- // to an observer may not get executed before a crash.
- base::debug::GlobalActivityTracker* tracker =
- base::debug::GlobalActivityTracker::Get();
- if (tracker) {
- tracker->RecordFieldTrial(field_trial->trial_name(),
- field_trial->group_name_internal());
- }
-
- if (global_->synchronous_observer_) {
- global_->synchronous_observer_->OnFieldTrialGroupFinalized(
- field_trial->trial_name(), field_trial->group_name_internal());
- }
-
- global_->observer_list_->Notify(
- FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized,
- field_trial->trial_name(), field_trial->group_name_internal());
-}
-
-// static
-size_t FieldTrialList::GetFieldTrialCount() {
- if (!global_)
- return 0;
- AutoLock auto_lock(global_->lock_);
- return global_->registered_.size();
-}
-
-// static
-bool FieldTrialList::GetParamsFromSharedMemory(
- FieldTrial* field_trial,
- std::map<std::string, std::string>* params) {
- DCHECK(global_);
- // If the field trial allocator is not set up yet, then there are several
- // cases:
- // - We are in the browser process and the allocator has not been set up
- // yet. If we got here, then we couldn't find the params in
- // FieldTrialParamAssociator, so it's definitely not here. Return false.
- // - Using shared memory for field trials is not enabled. If we got here,
- // then there's nothing in shared memory. Return false.
- // - We are in the child process and the allocator has not been set up yet.
- // If this is the case, then you are calling this too early. The field trial
- // allocator should get set up very early in the lifecycle. Try to see if
- // you can call it after it's been set up.
- AutoLock auto_lock(global_->lock_);
- if (!global_->field_trial_allocator_)
- return false;
-
- // If ref_ isn't set, then the field trial data can't be in shared memory.
- if (!field_trial->ref_)
- return false;
-
- const FieldTrial::FieldTrialEntry* entry =
- global_->field_trial_allocator_->GetAsObject<FieldTrial::FieldTrialEntry>(
- field_trial->ref_);
-
- size_t allocated_size =
- global_->field_trial_allocator_->GetAllocSize(field_trial->ref_);
- size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size;
- if (allocated_size < actual_size)
- return false;
-
- return entry->GetParams(params);
-}
-
-// static
-void FieldTrialList::ClearParamsFromSharedMemoryForTesting() {
- if (!global_)
- return;
-
- AutoLock auto_lock(global_->lock_);
- if (!global_->field_trial_allocator_)
- return;
-
- // To clear the params, we iterate through every item in the allocator, copy
- // just the trial and group name into a newly-allocated segment and then clear
- // the existing item.
- FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
- FieldTrialAllocator::Iterator mem_iter(allocator);
-
- // List of refs to eventually be made iterable. We can't make it in the loop,
- // since it would go on forever.
- std::vector<FieldTrial::FieldTrialRef> new_refs;
-
- FieldTrial::FieldTrialRef prev_ref;
- while ((prev_ref = mem_iter.GetNextOfType<FieldTrial::FieldTrialEntry>()) !=
- FieldTrialAllocator::kReferenceNull) {
- // Get the existing field trial entry in shared memory.
- const FieldTrial::FieldTrialEntry* prev_entry =
- allocator->GetAsObject<FieldTrial::FieldTrialEntry>(prev_ref);
- StringPiece trial_name;
- StringPiece group_name;
- if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name))
- continue;
-
- // Write a new entry, minus the params.
- Pickle pickle;
- pickle.WriteString(trial_name);
- pickle.WriteString(group_name);
- size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
- FieldTrial::FieldTrialEntry* new_entry =
- allocator->New<FieldTrial::FieldTrialEntry>(total_size);
- subtle::NoBarrier_Store(&new_entry->activated,
- subtle::NoBarrier_Load(&prev_entry->activated));
- new_entry->pickle_size = pickle.size();
-
- // TODO(lawrencewu): Modify base::Pickle to be able to write over a section
- // in memory, so we can avoid this memcpy.
- char* dst = reinterpret_cast<char*>(new_entry) +
- sizeof(FieldTrial::FieldTrialEntry);
- memcpy(dst, pickle.data(), pickle.size());
-
- // Update the ref on the field trial and add it to the list to be made
- // iterable.
- FieldTrial::FieldTrialRef new_ref = allocator->GetAsReference(new_entry);
- FieldTrial* trial = global_->PreLockedFind(trial_name.as_string());
- trial->ref_ = new_ref;
- new_refs.push_back(new_ref);
-
- // Mark the existing entry as unused.
- allocator->ChangeType(prev_ref, 0,
- FieldTrial::FieldTrialEntry::kPersistentTypeId,
- /*clear=*/false);
- }
-
- for (const auto& ref : new_refs) {
- allocator->MakeIterable(ref);
- }
-}
-
-// static
-void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(
- PersistentMemoryAllocator* allocator) {
- if (!global_)
- return;
- AutoLock auto_lock(global_->lock_);
- for (const auto& registered : global_->registered_) {
- AddToAllocatorWhileLocked(allocator, registered.second);
- }
-}
-
-// static
-std::vector<const FieldTrial::FieldTrialEntry*>
-FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(
- PersistentMemoryAllocator const& allocator) {
- std::vector<const FieldTrial::FieldTrialEntry*> entries;
- FieldTrialAllocator::Iterator iter(&allocator);
- const FieldTrial::FieldTrialEntry* entry;
- while ((entry = iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
- nullptr) {
- entries.push_back(entry);
- }
- return entries;
-}
-
-// static
-bool FieldTrialList::IsGlobalSetForTesting() {
- return global_ != nullptr;
-}
-
-// static
-std::string FieldTrialList::SerializeSharedMemoryHandleMetadata(
- const SharedMemoryHandle& shm) {
- std::stringstream ss;
-#if defined(OS_WIN)
- // Tell the child process the name of the inherited HANDLE.
- uintptr_t uintptr_handle = reinterpret_cast<uintptr_t>(shm.GetHandle());
- ss << uintptr_handle << ",";
-#elif defined(OS_FUCHSIA)
- ss << shm.GetHandle() << ",";
-#elif !defined(OS_POSIX)
-#error Unsupported OS
-#endif
-
- base::UnguessableToken guid = shm.GetGUID();
- ss << guid.GetHighForSerialization() << "," << guid.GetLowForSerialization();
- ss << "," << shm.GetSize();
- return ss.str();
-}
-
-#if defined(OS_WIN) || defined(OS_FUCHSIA)
-
-// static
-SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata(
- const std::string& switch_value) {
- std::vector<base::StringPiece> tokens = base::SplitStringPiece(
- switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-
- if (tokens.size() != 4)
- return SharedMemoryHandle();
-
- int field_trial_handle = 0;
- if (!base::StringToInt(tokens[0], &field_trial_handle))
- return SharedMemoryHandle();
-#if defined(OS_FUCHSIA)
- zx_handle_t handle = static_cast<zx_handle_t>(field_trial_handle);
-#elif defined(OS_WIN)
- HANDLE handle =
- reinterpret_cast<HANDLE>(static_cast<uintptr_t>(field_trial_handle));
- if (base::IsCurrentProcessElevated()) {
- // base::LaunchElevatedProcess doesn't have a way to duplicate the handle,
- // but this process can since by definition it's not sandboxed.
- base::ProcessId parent_pid = base::GetParentProcessId(GetCurrentProcess());
- HANDLE parent_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parent_pid);
- DuplicateHandle(parent_handle, handle, GetCurrentProcess(), &handle, 0,
- FALSE, DUPLICATE_SAME_ACCESS);
- CloseHandle(parent_handle);
- }
-#endif // defined(OS_WIN)
-
- base::UnguessableToken guid;
- if (!DeserializeGUIDFromStringPieces(tokens[1], tokens[2], &guid))
- return SharedMemoryHandle();
-
- int size;
- if (!base::StringToInt(tokens[3], &size))
- return SharedMemoryHandle();
-
- return SharedMemoryHandle(handle, static_cast<size_t>(size), guid);
-}
-
-#elif defined(OS_POSIX) && !defined(OS_NACL)
-
-// static
-SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata(
- int fd,
- const std::string& switch_value) {
- std::vector<base::StringPiece> tokens = base::SplitStringPiece(
- switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-
- if (tokens.size() != 3)
- return SharedMemoryHandle();
-
- base::UnguessableToken guid;
- if (!DeserializeGUIDFromStringPieces(tokens[0], tokens[1], &guid))
- return SharedMemoryHandle();
-
- int size;
- if (!base::StringToInt(tokens[2], &size))
- return SharedMemoryHandle();
-
- return SharedMemoryHandle(FileDescriptor(fd, true), static_cast<size_t>(size),
- guid);
-}
-
-#endif
-
-#if defined(OS_WIN) || defined(OS_FUCHSIA)
-// static
-bool FieldTrialList::CreateTrialsFromSwitchValue(
- const std::string& switch_value) {
- SharedMemoryHandle shm = DeserializeSharedMemoryHandleMetadata(switch_value);
- if (!shm.IsValid())
- return false;
- return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm);
-}
-#elif defined(OS_POSIX) && !defined(OS_NACL)
-// static
-bool FieldTrialList::CreateTrialsFromDescriptor(
- int fd_key,
- const std::string& switch_value) {
- if (!kUseSharedMemoryForFieldTrials)
- return false;
-
- if (fd_key == -1)
- return false;
-
- int fd = GlobalDescriptors::GetInstance()->MaybeGet(fd_key);
- if (fd == -1)
- return false;
-
- SharedMemoryHandle shm =
- DeserializeSharedMemoryHandleMetadata(fd, switch_value);
- if (!shm.IsValid())
- return false;
-
- bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm);
- DCHECK(result);
- return true;
-}
-#endif // defined(OS_POSIX) && !defined(OS_NACL)
-
-// static
-bool FieldTrialList::CreateTrialsFromSharedMemoryHandle(
- SharedMemoryHandle shm_handle) {
- // shm gets deleted when it gets out of scope, but that's OK because we need
- // it only for the duration of this method.
- std::unique_ptr<SharedMemory> shm(new SharedMemory(shm_handle, true));
- if (!shm.get()->Map(kFieldTrialAllocationSize))
- OnOutOfMemory(kFieldTrialAllocationSize);
-
- return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm));
-}
-
-// static
-bool FieldTrialList::CreateTrialsFromSharedMemory(
- std::unique_ptr<SharedMemory> shm) {
- global_->field_trial_allocator_.reset(
- new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true));
- FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get();
- FieldTrialAllocator::Iterator mem_iter(shalloc);
-
- const FieldTrial::FieldTrialEntry* entry;
- while ((entry = mem_iter.GetNextOfObject<FieldTrial::FieldTrialEntry>()) !=
- nullptr) {
- StringPiece trial_name;
- StringPiece group_name;
- if (!entry->GetTrialAndGroupName(&trial_name, &group_name))
- return false;
-
- // TODO(lawrencewu): Convert the API for CreateFieldTrial to take
- // StringPieces.
- FieldTrial* trial =
- CreateFieldTrial(trial_name.as_string(), group_name.as_string());
-
- trial->ref_ = mem_iter.GetAsReference(entry);
- if (subtle::NoBarrier_Load(&entry->activated)) {
- // Call |group()| to mark the trial as "used" and notify observers, if
- // any. This is useful to ensure that field trials created in child
- // processes are properly reported in crash reports.
- trial->group();
- }
- }
- return true;
-}
-
-// static
-void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() {
- if (!global_)
- return;
- AutoLock auto_lock(global_->lock_);
- // Create the allocator if not already created and add all existing trials.
- if (global_->field_trial_allocator_ != nullptr)
- return;
-
- SharedMemoryCreateOptions options;
- options.size = kFieldTrialAllocationSize;
- options.share_read_only = true;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
- options.type = SharedMemoryHandle::POSIX;
-#endif
-
- std::unique_ptr<SharedMemory> shm(new SharedMemory());
- if (!shm->Create(options))
- OnOutOfMemory(kFieldTrialAllocationSize);
-
- if (!shm->Map(kFieldTrialAllocationSize))
- OnOutOfMemory(kFieldTrialAllocationSize);
-
- global_->field_trial_allocator_.reset(
- new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false));
- global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName);
-
- // Add all existing field trials.
- for (const auto& registered : global_->registered_) {
- AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(),
- registered.second);
- }
-
- // Add all existing features.
- FeatureList::GetInstance()->AddFeaturesToAllocator(
- global_->field_trial_allocator_.get());
-
-#if !defined(OS_NACL)
- global_->readonly_allocator_handle_ = GetSharedMemoryReadOnlyHandle(
- global_->field_trial_allocator_->shared_memory());
-#endif
-}
-
-// static
-void FieldTrialList::AddToAllocatorWhileLocked(
- PersistentMemoryAllocator* allocator,
- FieldTrial* field_trial) {
- // Don't do anything if the allocator hasn't been instantiated yet.
- if (allocator == nullptr)
- return;
-
- // Or if the allocator is read only, which means we are in a child process and
- // shouldn't be writing to it.
- if (allocator->IsReadonly())
- return;
-
- FieldTrial::State trial_state;
- if (!field_trial->GetStateWhileLocked(&trial_state, false))
- return;
-
- // Or if we've already added it. We must check after GetState since it can
- // also add to the allocator.
- if (field_trial->ref_)
- return;
-
- Pickle pickle;
- PickleFieldTrial(trial_state, &pickle);
-
- size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size();
- FieldTrial::FieldTrialRef ref = allocator->Allocate(
- total_size, FieldTrial::FieldTrialEntry::kPersistentTypeId);
- if (ref == FieldTrialAllocator::kReferenceNull) {
- NOTREACHED();
- return;
- }
-
- FieldTrial::FieldTrialEntry* entry =
- allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
- subtle::NoBarrier_Store(&entry->activated, trial_state.activated);
- entry->pickle_size = pickle.size();
-
- // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in
- // memory, so we can avoid this memcpy.
- char* dst =
- reinterpret_cast<char*>(entry) + sizeof(FieldTrial::FieldTrialEntry);
- memcpy(dst, pickle.data(), pickle.size());
-
- allocator->MakeIterable(ref);
- field_trial->ref_ = ref;
-}
-
-// static
-void FieldTrialList::ActivateFieldTrialEntryWhileLocked(
- FieldTrial* field_trial) {
- FieldTrialAllocator* allocator = global_->field_trial_allocator_.get();
-
- // Check if we're in the child process and return early if so.
- if (!allocator || allocator->IsReadonly())
- return;
-
- FieldTrial::FieldTrialRef ref = field_trial->ref_;
- if (ref == FieldTrialAllocator::kReferenceNull) {
- // It's fine to do this even if the allocator hasn't been instantiated
- // yet -- it'll just return early.
- AddToAllocatorWhileLocked(allocator, field_trial);
- } else {
- // It's also okay to do this even though the callee doesn't have a lock --
- // the only thing that happens on a stale read here is a slight performance
- // hit from the child re-synchronizing activation state.
- FieldTrial::FieldTrialEntry* entry =
- allocator->GetAsObject<FieldTrial::FieldTrialEntry>(ref);
- subtle::NoBarrier_Store(&entry->activated, 1);
- }
-}
-
-// static
-const FieldTrial::EntropyProvider*
- FieldTrialList::GetEntropyProviderForOneTimeRandomization() {
- if (!global_) {
- used_without_global_ = true;
- return nullptr;
- }
-
- return global_->entropy_provider_.get();
-}
-
-FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
- RegistrationMap::iterator it = registered_.find(name);
- if (registered_.end() == it)
- return nullptr;
- return it->second;
-}
-
-// static
-void FieldTrialList::Register(FieldTrial* trial) {
- if (!global_) {
- used_without_global_ = true;
- return;
- }
- AutoLock auto_lock(global_->lock_);
- CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name();
- trial->AddRef();
- trial->SetTrialRegistered();
- global_->registered_[trial->trial_name()] = trial;
-}
-
-// static
-FieldTrialList::RegistrationMap FieldTrialList::GetRegisteredTrials() {
- RegistrationMap output;
- if (global_) {
- AutoLock auto_lock(global_->lock_);
- output = global_->registered_;
- }
- return output;
-}
-
-} // namespace base
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
deleted file mode 100644
index d5d6cb8..0000000
--- a/base/metrics/field_trial.h
+++ /dev/null
@@ -1,802 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// FieldTrial is a class for handling details of statistical experiments
-// performed by actual users in the field (i.e., in a shipped or beta product).
-// All code is called exclusively on the UI thread currently.
-//
-// The simplest example is an experiment to see whether one of two options
-// produces "better" results across our user population. In that scenario, UMA
-// data is uploaded to aggregate the test results, and this FieldTrial class
-// manages the state of each such experiment (state == which option was
-// pseudo-randomly selected).
-//
-// States are typically generated randomly, either based on a one time
-// randomization (which will yield the same results, in terms of selecting
-// the client for a field trial or not, for every run of the program on a
-// given machine), or by a session randomization (generated each time the
-// application starts up, but held constant during the duration of the
-// process).
-
-//------------------------------------------------------------------------------
-// Example: Suppose we have an experiment involving memory, such as determining
-// the impact of some pruning algorithm.
-// We assume that we already have a histogram of memory usage, such as:
-
-// UMA_HISTOGRAM_COUNTS("Memory.RendererTotal", count);
-
-// Somewhere in main thread initialization code, we'd probably define an
-// instance of a FieldTrial, with code such as:
-
-// // FieldTrials are reference counted, and persist automagically until
-// // process teardown, courtesy of their automatic registration in
-// // FieldTrialList.
-// // Note: This field trial will run in Chrome instances compiled through
-// // 8 July, 2015, and after that all instances will be in "StandardMem".
-// scoped_refptr<base::FieldTrial> trial(
-// base::FieldTrialList::FactoryGetFieldTrial(
-// "MemoryExperiment", 1000, "StandardMem", 2015, 7, 8,
-// base::FieldTrial::ONE_TIME_RANDOMIZED, NULL));
-//
-// const int high_mem_group =
-// trial->AppendGroup("HighMem", 20); // 2% in HighMem group.
-// const int low_mem_group =
-// trial->AppendGroup("LowMem", 20); // 2% in LowMem group.
-// // Take action depending of which group we randomly land in.
-// if (trial->group() == high_mem_group)
-// SetPruningAlgorithm(kType1); // Sample setting of browser state.
-// else if (trial->group() == low_mem_group)
-// SetPruningAlgorithm(kType2); // Sample alternate setting.
-
-//------------------------------------------------------------------------------
-
-#ifndef BASE_METRICS_FIELD_TRIAL_H_
-#define BASE_METRICS_FIELD_TRIAL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/command_line.h"
-#include "base/feature_list.h"
-#include "base/files/file.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory.h"
-#include "base/memory/shared_memory_handle.h"
-#include "base/metrics/persistent_memory_allocator.h"
-#include "base/observer_list_threadsafe.h"
-#include "base/pickle.h"
-#include "base/process/launch.h"
-#include "base/strings/string_piece.h"
-#include "base/synchronization/lock.h"
-#include "base/time/time.h"
-#include "build_config.h"
-
-namespace base {
-
-class FieldTrialList;
-
-class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
- public:
- typedef int Probability; // Probability type for being selected in a trial.
-
- // TODO(665129): Make private again after crash has been resolved.
- typedef SharedPersistentMemoryAllocator::Reference FieldTrialRef;
-
- // Specifies the persistence of the field trial group choice.
- enum RandomizationType {
- // One time randomized trials will persist the group choice between
- // restarts, which is recommended for most trials, especially those that
- // change user visible behavior.
- ONE_TIME_RANDOMIZED,
- // Session randomized trials will roll the dice to select a group on every
- // process restart.
- SESSION_RANDOMIZED,
- };
-
- // EntropyProvider is an interface for providing entropy for one-time
- // randomized (persistent) field trials.
- class BASE_EXPORT EntropyProvider {
- public:
- virtual ~EntropyProvider();
-
- // Returns a double in the range of [0, 1) to be used for the dice roll for
- // the specified field trial. If |randomization_seed| is not 0, it will be
- // used in preference to |trial_name| for generating the entropy by entropy
- // providers that support it. A given instance should always return the same
- // value given the same input |trial_name| and |randomization_seed| values.
- virtual double GetEntropyForTrial(const std::string& trial_name,
- uint32_t randomization_seed) const = 0;
- };
-
- // A pair representing a Field Trial and its selected group.
- struct ActiveGroup {
- std::string trial_name;
- std::string group_name;
- };
-
- // A triplet representing a FieldTrial, its selected group and whether it's
- // active. String members are pointers to the underlying strings owned by the
- // FieldTrial object. Does not use StringPiece to avoid conversions back to
- // std::string.
- struct BASE_EXPORT State {
- const std::string* trial_name = nullptr;
- const std::string* group_name = nullptr;
- bool activated = false;
-
- State();
- State(const State& other);
- ~State();
- };
-
- // We create one FieldTrialEntry per field trial in shared memory, via
- // AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a
- // base::Pickle object that we unpickle and read from.
- struct BASE_EXPORT FieldTrialEntry {
- // SHA1(FieldTrialEntry): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0xABA17E13 + 2;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 8;
-
- // Whether or not this field trial is activated. This is really just a
- // boolean but using a 32 bit value for portability reasons. It should be
- // accessed via NoBarrier_Load()/NoBarrier_Store() to prevent the compiler
- // from doing unexpected optimizations because it thinks that only one
- // thread is accessing the memory location.
- subtle::Atomic32 activated;
-
- // Size of the pickled structure, NOT the total size of this entry.
- uint32_t pickle_size;
-
- // Calling this is only valid when the entry is initialized. That is, it
- // resides in shared memory and has a pickle containing the trial name and
- // group name following it.
- bool GetTrialAndGroupName(StringPiece* trial_name,
- StringPiece* group_name) const;
-
- // Calling this is only valid when the entry is initialized as well. Reads
- // the parameters following the trial and group name and stores them as
- // key-value mappings in |params|.
- bool GetParams(std::map<std::string, std::string>* params) const;
-
- private:
- // Returns an iterator over the data containing names and params.
- PickleIterator GetPickleIterator() const;
-
- // Takes the iterator and writes out the first two items into |trial_name|
- // and |group_name|.
- bool ReadStringPair(PickleIterator* iter,
- StringPiece* trial_name,
- StringPiece* group_name) const;
- };
-
- typedef std::vector<ActiveGroup> ActiveGroups;
-
- // A return value to indicate that a given instance has not yet had a group
- // assignment (and hence is not yet participating in the trial).
- static const int kNotFinalized;
-
- // Disables this trial, meaning it always determines the default group
- // has been selected. May be called immediately after construction, or
- // at any time after initialization (should not be interleaved with
- // AppendGroup calls). Once disabled, there is no way to re-enable a
- // trial.
- // TODO(mad): http://code.google.com/p/chromium/issues/detail?id=121446
- // This doesn't properly reset to Default when a group was forced.
- void Disable();
-
- // Establish the name and probability of the next group in this trial.
- // Sometimes, based on construction randomization, this call may cause the
- // provided group to be *THE* group selected for use in this instance.
- // The return value is the group number of the new group.
- int AppendGroup(const std::string& name, Probability group_probability);
-
- // Return the name of the FieldTrial (excluding the group name).
- const std::string& trial_name() const { return trial_name_; }
-
- // Return the randomly selected group number that was assigned, and notify
- // any/all observers that this finalized group number has presumably been used
- // (queried), and will never change. Note that this will force an instance to
- // participate, and make it illegal to attempt to probabilistically add any
- // other groups to the trial.
- int group();
-
- // If the group's name is empty, a string version containing the group number
- // is used as the group name. This causes a winner to be chosen if none was.
- const std::string& group_name();
-
- // Finalizes the group choice and returns the chosen group, but does not mark
- // the trial as active - so its state will not be reported until group_name()
- // or similar is called.
- const std::string& GetGroupNameWithoutActivation();
-
- // Set the field trial as forced, meaning that it was setup earlier than
- // the hard coded registration of the field trial to override it.
- // This allows the code that was hard coded to register the field trial to
- // still succeed even though the field trial has already been registered.
- // This must be called after appending all the groups, since we will make
- // the group choice here. Note that this is a NOOP for already forced trials.
- // And, as the rest of the FieldTrial code, this is not thread safe and must
- // be done from the UI thread.
- void SetForced();
-
- // Enable benchmarking sets field trials to a common setting.
- static void EnableBenchmarking();
-
- // Creates a FieldTrial object with the specified parameters, to be used for
- // simulation of group assignment without actually affecting global field
- // trial state in the running process. Group assignment will be done based on
- // |entropy_value|, which must have a range of [0, 1).
- //
- // Note: Using this function will not register the field trial globally in the
- // running process - for that, use FieldTrialList::FactoryGetFieldTrial().
- //
- // The ownership of the returned FieldTrial is transfered to the caller which
- // is responsible for deref'ing it (e.g. by using scoped_refptr<FieldTrial>).
- static FieldTrial* CreateSimulatedFieldTrial(
- const std::string& trial_name,
- Probability total_probability,
- const std::string& default_group_name,
- double entropy_value);
-
- private:
- // Allow tests to access our innards for testing purposes.
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AbsoluteProbabilities);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, RemainingProbability);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FiftyFiftyProbability);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MiddleProbabilities);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AllGroups);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SaveAll);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOff);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
- DoNotAddSimulatedFieldTrialsToAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory);
-
- friend class base::FieldTrialList;
-
- friend class RefCounted<FieldTrial>;
-
- // This is the group number of the 'default' group when a choice wasn't forced
- // by a call to FieldTrialList::CreateFieldTrial. It is kept private so that
- // consumers don't use it by mistake in cases where the group was forced.
- static const int kDefaultGroupNumber;
-
- // Creates a field trial with the specified parameters. Group assignment will
- // be done based on |entropy_value|, which must have a range of [0, 1).
- FieldTrial(const std::string& trial_name,
- Probability total_probability,
- const std::string& default_group_name,
- double entropy_value);
- virtual ~FieldTrial();
-
- // Return the default group name of the FieldTrial.
- std::string default_group_name() const { return default_group_name_; }
-
- // Marks this trial as having been registered with the FieldTrialList. Must be
- // called no more than once and before any |group()| calls have occurred.
- void SetTrialRegistered();
-
- // Sets the chosen group name and number.
- void SetGroupChoice(const std::string& group_name, int number);
-
- // Ensures that a group is chosen, if it hasn't yet been. The field trial
- // might yet be disabled, so this call will *not* notify observers of the
- // status.
- void FinalizeGroupChoice();
-
- // Implements FinalizeGroupChoice() with the added flexibility of being
- // deadlock-free if |is_locked| is true and the caller is holding a lock.
- void FinalizeGroupChoiceImpl(bool is_locked);
-
- // Returns the trial name and selected group name for this field trial via
- // the output parameter |active_group|, but only if the group has already
- // been chosen and has been externally observed via |group()| and the trial
- // has not been disabled. In that case, true is returned and |active_group|
- // is filled in; otherwise, the result is false and |active_group| is left
- // untouched.
- bool GetActiveGroup(ActiveGroup* active_group) const;
-
- // Returns the trial name and selected group name for this field trial via
- // the output parameter |field_trial_state| for all the studies when
- // |bool include_expired| is true. In case when |bool include_expired| is
- // false, if the trial has not been disabled true is returned and
- // |field_trial_state| is filled in; otherwise, the result is false and
- // |field_trial_state| is left untouched.
- // This function is deadlock-free if the caller is holding a lock.
- bool GetStateWhileLocked(State* field_trial_state, bool include_expired);
-
- // Returns the group_name. A winner need not have been chosen.
- std::string group_name_internal() const { return group_name_; }
-
- // The name of the field trial, as can be found via the FieldTrialList.
- const std::string trial_name_;
-
- // The maximum sum of all probabilities supplied, which corresponds to 100%.
- // This is the scaling factor used to adjust supplied probabilities.
- const Probability divisor_;
-
- // The name of the default group.
- const std::string default_group_name_;
-
- // The randomly selected probability that is used to select a group (or have
- // the instance not participate). It is the product of divisor_ and a random
- // number between [0, 1).
- Probability random_;
-
- // Sum of the probabilities of all appended groups.
- Probability accumulated_group_probability_;
-
- // The number that will be returned by the next AppendGroup() call.
- int next_group_number_;
-
- // The pseudo-randomly assigned group number.
- // This is kNotFinalized if no group has been assigned.
- int group_;
-
- // A textual name for the randomly selected group. Valid after |group()|
- // has been called.
- std::string group_name_;
-
- // When enable_field_trial_ is false, field trial reverts to the 'default'
- // group.
- bool enable_field_trial_;
-
- // When forced_ is true, we return the chosen group from AppendGroup when
- // appropriate.
- bool forced_;
-
- // Specifies whether the group choice has been reported to observers.
- bool group_reported_;
-
- // Whether this trial is registered with the global FieldTrialList and thus
- // should notify it when its group is queried.
- bool trial_registered_;
-
- // Reference to related field trial struct and data in shared memory.
- FieldTrialRef ref_;
-
- // When benchmarking is enabled, field trials all revert to the 'default'
- // group.
- static bool enable_benchmarking_;
-
- DISALLOW_COPY_AND_ASSIGN(FieldTrial);
-};
-
-//------------------------------------------------------------------------------
-// Class with a list of all active field trials. A trial is active if it has
-// been registered, which includes evaluating its state based on its probaility.
-// Only one instance of this class exists and outside of testing, will live for
-// the entire life time of the process.
-class BASE_EXPORT FieldTrialList {
- public:
- typedef SharedPersistentMemoryAllocator FieldTrialAllocator;
-
- // Type for function pointer passed to |AllParamsToString| used to escape
- // special characters from |input|.
- typedef std::string (*EscapeDataFunc)(const std::string& input);
-
- // Year that is guaranteed to not be expired when instantiating a field trial
- // via |FactoryGetFieldTrial()|. Set to two years from the build date.
- static int kNoExpirationYear;
-
- // Observer is notified when a FieldTrial's group is selected.
- class BASE_EXPORT Observer {
- public:
- // Notify observers when FieldTrials's group is selected.
- virtual void OnFieldTrialGroupFinalized(const std::string& trial_name,
- const std::string& group_name) = 0;
-
- protected:
- virtual ~Observer();
- };
-
- // This singleton holds the global list of registered FieldTrials.
- //
- // To support one-time randomized field trials, specify a non-null
- // |entropy_provider| which should be a source of uniformly distributed
- // entropy values. If one time randomization is not desired, pass in null for
- // |entropy_provider|.
- explicit FieldTrialList(
- std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider);
-
- // Destructor Release()'s references to all registered FieldTrial instances.
- ~FieldTrialList();
-
- // Get a FieldTrial instance from the factory.
- //
- // |name| is used to register the instance with the FieldTrialList class,
- // and can be used to find the trial (only one trial can be present for each
- // name). |default_group_name| is the name of the default group which will
- // be chosen if none of the subsequent appended groups get to be chosen.
- // |default_group_number| can receive the group number of the default group as
- // AppendGroup returns the number of the subsequence groups. |trial_name| and
- // |default_group_name| may not be empty but |default_group_number| can be
- // NULL if the value is not needed.
- //
- // Group probabilities that are later supplied must sum to less than or equal
- // to the |total_probability|. Arguments |year|, |month| and |day_of_month|
- // specify the expiration time. If the build time is after the expiration time
- // then the field trial reverts to the 'default' group.
- //
- // Use this static method to get a startup-randomized FieldTrial or a
- // previously created forced FieldTrial.
- static FieldTrial* FactoryGetFieldTrial(
- const std::string& trial_name,
- FieldTrial::Probability total_probability,
- const std::string& default_group_name,
- const int year,
- const int month,
- const int day_of_month,
- FieldTrial::RandomizationType randomization_type,
- int* default_group_number);
-
- // Same as FactoryGetFieldTrial(), but allows specifying a custom seed to be
- // used on one-time randomized field trials (instead of a hash of the trial
- // name, which is used otherwise or if |randomization_seed| has value 0). The
- // |randomization_seed| value (other than 0) should never be the same for two
- // trials, else this would result in correlated group assignments. Note:
- // Using a custom randomization seed is only supported by the
- // PermutedEntropyProvider (which is used when UMA is not enabled). If
- // |override_entropy_provider| is not null, then it will be used for
- // randomization instead of the provider given when the FieldTrialList was
- // instantiated.
- static FieldTrial* FactoryGetFieldTrialWithRandomizationSeed(
- const std::string& trial_name,
- FieldTrial::Probability total_probability,
- const std::string& default_group_name,
- const int year,
- const int month,
- const int day_of_month,
- FieldTrial::RandomizationType randomization_type,
- uint32_t randomization_seed,
- int* default_group_number,
- const FieldTrial::EntropyProvider* override_entropy_provider);
-
- // The Find() method can be used to test to see if a named trial was already
- // registered, or to retrieve a pointer to it from the global map.
- static FieldTrial* Find(const std::string& trial_name);
-
- // Returns the group number chosen for the named trial, or
- // FieldTrial::kNotFinalized if the trial does not exist.
- static int FindValue(const std::string& trial_name);
-
- // Returns the group name chosen for the named trial, or the empty string if
- // the trial does not exist. The first call of this function on a given field
- // trial will mark it as active, so that its state will be reported with usage
- // metrics, crashes, etc.
- static std::string FindFullName(const std::string& trial_name);
-
- // Returns true if the named trial has been registered.
- static bool TrialExists(const std::string& trial_name);
-
- // Returns true if the named trial exists and has been activated.
- static bool IsTrialActive(const std::string& trial_name);
-
- // Creates a persistent representation of active FieldTrial instances for
- // resurrection in another process. This allows randomization to be done in
- // one process, and secondary processes can be synchronized on the result.
- // The resulting string contains the name and group name pairs of all
- // registered FieldTrials for which the group has been chosen and externally
- // observed (via |group()|) and which have not been disabled, with "/" used
- // to separate all names and to terminate the string. This string is parsed
- // by |CreateTrialsFromString()|.
- static void StatesToString(std::string* output);
-
- // Creates a persistent representation of all FieldTrial instances for
- // resurrection in another process. This allows randomization to be done in
- // one process, and secondary processes can be synchronized on the result.
- // The resulting string contains the name and group name pairs of all
- // registered FieldTrials including disabled based on |include_expired|,
- // with "/" used to separate all names and to terminate the string. All
- // activated trials have their name prefixed with "*". This string is parsed
- // by |CreateTrialsFromString()|.
- static void AllStatesToString(std::string* output, bool include_expired);
-
- // Creates a persistent representation of all FieldTrial params for
- // resurrection in another process. The returned string contains the trial
- // name and group name pairs of all registered FieldTrials including disabled
- // based on |include_expired| separated by '.'. The pair is followed by ':'
- // separator and list of param name and values separated by '/'. It also takes
- // |encode_data_func| function pointer for encodeing special charactors.
- // This string is parsed by |AssociateParamsFromString()|.
- static std::string AllParamsToString(bool include_expired,
- EscapeDataFunc encode_data_func);
-
- // Fills in the supplied vector |active_groups| (which must be empty when
- // called) with a snapshot of all registered FieldTrials for which the group
- // has been chosen and externally observed (via |group()|) and which have
- // not been disabled.
- static void GetActiveFieldTrialGroups(
- FieldTrial::ActiveGroups* active_groups);
-
- // Returns the field trials that are marked active in |trials_string|.
- static void GetActiveFieldTrialGroupsFromString(
- const std::string& trials_string,
- FieldTrial::ActiveGroups* active_groups);
-
- // Returns the field trials that were active when the process was
- // created. Either parses the field trial string or the shared memory
- // holding field trial information.
- // Must be called only after a call to CreateTrialsFromCommandLine().
- static void GetInitiallyActiveFieldTrials(
- const base::CommandLine& command_line,
- FieldTrial::ActiveGroups* active_groups);
-
- // Use a state string (re: StatesToString()) to augment the current list of
- // field trials to include the supplied trials, and using a 100% probability
- // for each trial, force them to have the same group string. This is commonly
- // used in a non-browser process, to carry randomly selected state in a
- // browser process into this non-browser process, but could also be invoked
- // through a command line argument to the browser process. Created field
- // trials will be marked "used" for the purposes of active trial reporting
- // if they are prefixed with |kActivationMarker|. Trial names in
- // |ignored_trial_names| are ignored when parsing |trials_string|.
- static bool CreateTrialsFromString(
- const std::string& trials_string,
- const std::set<std::string>& ignored_trial_names);
-
- // Achieves the same thing as CreateTrialsFromString, except wraps the logic
- // by taking in the trials from the command line, either via shared memory
- // handle or command line argument. A bit of a misnomer since on POSIX we
- // simply get the trials from opening |fd_key| if using shared memory. On
- // Windows, we expect the |cmd_line| switch for |field_trial_handle_switch| to
- // contain the shared memory handle that contains the field trial allocator.
- // We need the |field_trial_handle_switch| and |fd_key| arguments to be passed
- // in since base/ can't depend on content/.
- static void CreateTrialsFromCommandLine(const base::CommandLine& cmd_line,
- const char* field_trial_handle_switch,
- int fd_key);
-
- // Creates base::Feature overrides from the command line by first trying to
- // use shared memory and then falling back to the command line if it fails.
- static void CreateFeaturesFromCommandLine(
- const base::CommandLine& command_line,
- const char* enable_features_switch,
- const char* disable_features_switch,
- FeatureList* feature_list);
-
-#if defined(OS_WIN)
- // On Windows, we need to explicitly pass down any handles to be inherited.
- // This function adds the shared memory handle to field trial state to the
- // list of handles to be inherited.
- static void AppendFieldTrialHandleIfNeeded(
- base::HandlesToInheritVector* handles);
-#elif defined(OS_FUCHSIA)
- // TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368).
-#elif defined(OS_POSIX) && !defined(OS_NACL)
- // On POSIX, we also need to explicitly pass down this file descriptor that
- // should be shared with the child process. Returns an invalid handle if it
- // was not initialized properly.
- static base::SharedMemoryHandle GetFieldTrialHandle();
-#endif
-
- // Adds a switch to the command line containing the field trial state as a
- // string (if not using shared memory to share field trial state), or the
- // shared memory handle + length.
- // Needs the |field_trial_handle_switch| argument to be passed in since base/
- // can't depend on content/.
- static void CopyFieldTrialStateToFlags(const char* field_trial_handle_switch,
- const char* enable_features_switch,
- const char* disable_features_switch,
- base::CommandLine* cmd_line);
-
- // Create a FieldTrial with the given |name| and using 100% probability for
- // the FieldTrial, force FieldTrial to have the same group string as
- // |group_name|. This is commonly used in a non-browser process, to carry
- // randomly selected state in a browser process into this non-browser process.
- // It returns NULL if there is a FieldTrial that is already registered with
- // the same |name| but has different finalized group string (|group_name|).
- static FieldTrial* CreateFieldTrial(const std::string& name,
- const std::string& group_name);
-
- // Add an observer to be notified when a field trial is irrevocably committed
- // to being part of some specific field_group (and hence the group_name is
- // also finalized for that field_trial). Returns false and does nothing if
- // there is no FieldTrialList singleton.
- static bool AddObserver(Observer* observer);
-
- // Remove an observer.
- static void RemoveObserver(Observer* observer);
-
- // Similar to AddObserver(), but the passed observer will be notified
- // synchronously when a field trial is activated and its group selected. It
- // will be notified synchronously on the same thread where the activation and
- // group selection happened. It is the responsibility of the observer to make
- // sure that this is a safe operation and the operation must be fast, as this
- // work is done synchronously as part of group() or related APIs. Only a
- // single such observer is supported, exposed specifically for crash
- // reporting. Must be called on the main thread before any other threads
- // have been started.
- static void SetSynchronousObserver(Observer* observer);
-
- // Removes the single synchronous observer.
- static void RemoveSynchronousObserver(Observer* observer);
-
- // Grabs the lock if necessary and adds the field trial to the allocator. This
- // should only be called from FinalizeGroupChoice().
- static void OnGroupFinalized(bool is_locked, FieldTrial* field_trial);
-
- // Notify all observers that a group has been finalized for |field_trial|.
- static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial);
-
- // Return the number of active field trials.
- static size_t GetFieldTrialCount();
-
- // Gets the parameters for |field_trial| from shared memory and stores them in
- // |params|. This is only exposed for use by FieldTrialParamAssociator and
- // shouldn't be used by anything else.
- static bool GetParamsFromSharedMemory(
- FieldTrial* field_trial,
- std::map<std::string, std::string>* params);
-
- // Clears all the params in the allocator.
- static void ClearParamsFromSharedMemoryForTesting();
-
- // Dumps field trial state to an allocator so that it can be analyzed after a
- // crash.
- static void DumpAllFieldTrialsToPersistentAllocator(
- PersistentMemoryAllocator* allocator);
-
- // Retrieves field trial state from an allocator so that it can be analyzed
- // after a crash. The pointers in the returned vector are into the persistent
- // memory segment and so are only valid as long as the allocator is valid.
- static std::vector<const FieldTrial::FieldTrialEntry*>
- GetAllFieldTrialsFromPersistentAllocator(
- PersistentMemoryAllocator const& allocator);
-
- // Returns true if a global field trial list is set. Only used for testing.
- static bool IsGlobalSetForTesting();
-
- private:
- // Allow tests to access our innards for testing purposes.
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AddTrialsToAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
- DoNotAddSimulatedFieldTrialsToAllocator);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AssociateFieldTrialParams);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
- SerializeSharedMemoryHandleMetadata);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, CheckReadOnlySharedMemoryHandle);
-
- // Serialization is used to pass information about the handle to child
- // processes. It passes a reference to the relevant OS resource, and it passes
- // a GUID. Serialization and deserialization doesn't actually transport the
- // underlying OS resource - that must be done by the Process launcher.
- static std::string SerializeSharedMemoryHandleMetadata(
- const SharedMemoryHandle& shm);
-#if defined(OS_WIN) || defined(OS_FUCHSIA)
- static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata(
- const std::string& switch_value);
-#elif defined(OS_POSIX) && !defined(OS_NACL)
- static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata(
- int fd,
- const std::string& switch_value);
-#endif
-
-#if defined(OS_WIN) || defined(OS_FUCHSIA)
- // Takes in |handle_switch| from the command line which represents the shared
- // memory handle for field trials, parses it, and creates the field trials.
- // Returns true on success, false on failure.
- // |switch_value| also contains the serialized GUID.
- static bool CreateTrialsFromSwitchValue(const std::string& switch_value);
-#elif defined(OS_POSIX) && !defined(OS_NACL)
- // On POSIX systems that use the zygote, we look up the correct fd that backs
- // the shared memory segment containing the field trials by looking it up via
- // an fd key in GlobalDescriptors. Returns true on success, false on failure.
- // |switch_value| also contains the serialized GUID.
- static bool CreateTrialsFromDescriptor(int fd_key,
- const std::string& switch_value);
-#endif
-
- // Takes an unmapped SharedMemoryHandle, creates a SharedMemory object from it
- // and maps it with the correct size.
- static bool CreateTrialsFromSharedMemoryHandle(SharedMemoryHandle shm_handle);
-
- // Expects a mapped piece of shared memory |shm| that was created from the
- // browser process's field_trial_allocator and shared via the command line.
- // This function recreates the allocator, iterates through all the field
- // trials in it, and creates them via CreateFieldTrial(). Returns true if
- // successful and false otherwise.
- static bool CreateTrialsFromSharedMemory(
- std::unique_ptr<base::SharedMemory> shm);
-
- // Instantiate the field trial allocator, add all existing field trials to it,
- // and duplicates its handle to a read-only handle, which gets stored in
- // |readonly_allocator_handle|.
- static void InstantiateFieldTrialAllocatorIfNeeded();
-
- // Adds the field trial to the allocator. Caller must hold a lock before
- // calling this.
- static void AddToAllocatorWhileLocked(PersistentMemoryAllocator* allocator,
- FieldTrial* field_trial);
-
- // Activate the corresponding field trial entry struct in shared memory.
- static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial);
-
- // A map from FieldTrial names to the actual instances.
- typedef std::map<std::string, FieldTrial*> RegistrationMap;
-
- // If one-time randomization is enabled, returns a weak pointer to the
- // corresponding EntropyProvider. Otherwise, returns NULL.
- static const FieldTrial::EntropyProvider*
- GetEntropyProviderForOneTimeRandomization();
-
- // Helper function should be called only while holding lock_.
- FieldTrial* PreLockedFind(const std::string& name);
-
- // Register() stores a pointer to the given trial in a global map.
- // This method also AddRef's the indicated trial.
- // This should always be called after creating a new FieldTrial instance.
- static void Register(FieldTrial* trial);
-
- // Returns all the registered trials.
- static RegistrationMap GetRegisteredTrials();
-
- static FieldTrialList* global_; // The singleton of this class.
-
- // This will tell us if there is an attempt to register a field
- // trial or check if one-time randomization is enabled without
- // creating the FieldTrialList. This is not an error, unless a
- // FieldTrialList is created after that.
- static bool used_without_global_;
-
- // Lock for access to registered_ and field_trial_allocator_.
- Lock lock_;
- RegistrationMap registered_;
-
- std::map<std::string, std::string> seen_states_;
-
- // Entropy provider to be used for one-time randomized field trials. If NULL,
- // one-time randomization is not supported.
- std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider_;
-
- // List of observers to be notified when a group is selected for a FieldTrial.
- scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
-
- // Single synchronous observer to be notified when a trial group is chosen.
- Observer* synchronous_observer_ = nullptr;
-
- // Allocator in shared memory containing field trial data. Used in both
- // browser and child processes, but readonly in the child.
- // In the future, we may want to move this to a more generic place if we want
- // to start passing more data other than field trials.
- std::unique_ptr<FieldTrialAllocator> field_trial_allocator_ = nullptr;
-
- // Readonly copy of the handle to the allocator. Needs to be a member variable
- // because it's needed from both CopyFieldTrialStateToFlags() and
- // AppendFieldTrialHandleIfNeeded().
- base::SharedMemoryHandle readonly_allocator_handle_;
-
- // Tracks whether CreateTrialsFromCommandLine() has been called.
- bool create_trials_from_command_line_called_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(FieldTrialList);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_FIELD_TRIAL_H_
diff --git a/base/metrics/field_trial_param_associator.cc b/base/metrics/field_trial_param_associator.cc
deleted file mode 100644
index af76eaf..0000000
--- a/base/metrics/field_trial_param_associator.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/field_trial_param_associator.h"
-
-#include "base/metrics/field_trial.h"
-
-namespace base {
-
-FieldTrialParamAssociator::FieldTrialParamAssociator() = default;
-FieldTrialParamAssociator::~FieldTrialParamAssociator() = default;
-
-// static
-FieldTrialParamAssociator* FieldTrialParamAssociator::GetInstance() {
- return Singleton<FieldTrialParamAssociator,
- LeakySingletonTraits<FieldTrialParamAssociator>>::get();
-}
-
-bool FieldTrialParamAssociator::AssociateFieldTrialParams(
- const std::string& trial_name,
- const std::string& group_name,
- const FieldTrialParams& params) {
- if (FieldTrialList::IsTrialActive(trial_name))
- return false;
-
- AutoLock scoped_lock(lock_);
- const FieldTrialKey key(trial_name, group_name);
- if (ContainsKey(field_trial_params_, key))
- return false;
-
- field_trial_params_[key] = params;
- return true;
-}
-
-bool FieldTrialParamAssociator::GetFieldTrialParams(
- const std::string& trial_name,
- FieldTrialParams* params) {
- FieldTrial* field_trial = FieldTrialList::Find(trial_name);
- if (!field_trial)
- return false;
-
- // First try the local map, falling back to getting it from shared memory.
- if (GetFieldTrialParamsWithoutFallback(trial_name, field_trial->group_name(),
- params)) {
- return true;
- }
-
- // TODO(lawrencewu): add the params to field_trial_params_ for next time.
- return FieldTrialList::GetParamsFromSharedMemory(field_trial, params);
-}
-
-bool FieldTrialParamAssociator::GetFieldTrialParamsWithoutFallback(
- const std::string& trial_name,
- const std::string& group_name,
- FieldTrialParams* params) {
- AutoLock scoped_lock(lock_);
-
- const FieldTrialKey key(trial_name, group_name);
- if (!ContainsKey(field_trial_params_, key))
- return false;
-
- *params = field_trial_params_[key];
- return true;
-}
-
-void FieldTrialParamAssociator::ClearAllParamsForTesting() {
- {
- AutoLock scoped_lock(lock_);
- field_trial_params_.clear();
- }
- FieldTrialList::ClearParamsFromSharedMemoryForTesting();
-}
-
-void FieldTrialParamAssociator::ClearParamsForTesting(
- const std::string& trial_name,
- const std::string& group_name) {
- AutoLock scoped_lock(lock_);
- const FieldTrialKey key(trial_name, group_name);
- field_trial_params_.erase(key);
-}
-
-void FieldTrialParamAssociator::ClearAllCachedParamsForTesting() {
- AutoLock scoped_lock(lock_);
- field_trial_params_.clear();
-}
-
-} // namespace base
diff --git a/base/metrics/field_trial_param_associator.h b/base/metrics/field_trial_param_associator.h
deleted file mode 100644
index b35e2cc..0000000
--- a/base/metrics/field_trial_param_associator.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_
-#define BASE_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_
-
-#include <map>
-#include <string>
-#include <utility>
-
-#include "base/base_export.h"
-#include "base/memory/singleton.h"
-#include "base/metrics/field_trial.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-// Keeps track of the parameters of all field trials and ensures access to them
-// is thread-safe.
-class BASE_EXPORT FieldTrialParamAssociator {
- public:
- FieldTrialParamAssociator();
- ~FieldTrialParamAssociator();
-
- // Key-value mapping type for field trial parameters.
- typedef std::map<std::string, std::string> FieldTrialParams;
-
- // Retrieve the singleton.
- static FieldTrialParamAssociator* GetInstance();
-
- // Sets parameters for the given field trial name and group.
- bool AssociateFieldTrialParams(const std::string& trial_name,
- const std::string& group_name,
- const FieldTrialParams& params);
-
- // Gets the parameters for a field trial and its chosen group. If not found in
- // field_trial_params_, then tries to looks it up in shared memory.
- bool GetFieldTrialParams(const std::string& trial_name,
- FieldTrialParams* params);
-
- // Gets the parameters for a field trial and its chosen group. Does not
- // fallback to looking it up in shared memory. This should only be used if you
- // know for sure the params are in the mapping, like if you're in the browser
- // process, and even then you should probably just use GetFieldTrialParams().
- bool GetFieldTrialParamsWithoutFallback(const std::string& trial_name,
- const std::string& group_name,
- FieldTrialParams* params);
-
- // Clears the internal field_trial_params_ mapping, plus removes all params in
- // shared memory.
- void ClearAllParamsForTesting();
-
- // Clears a single field trial param.
- // Note: this does NOT remove the param in shared memory.
- void ClearParamsForTesting(const std::string& trial_name,
- const std::string& group_name);
-
- // Clears the internal field_trial_params_ mapping.
- void ClearAllCachedParamsForTesting();
-
- private:
- friend struct DefaultSingletonTraits<FieldTrialParamAssociator>;
-
- // (field_trial_name, field_trial_group)
- typedef std::pair<std::string, std::string> FieldTrialKey;
-
- Lock lock_;
- std::map<FieldTrialKey, FieldTrialParams> field_trial_params_;
-
- DISALLOW_COPY_AND_ASSIGN(FieldTrialParamAssociator);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_
diff --git a/base/metrics/field_trial_params.cc b/base/metrics/field_trial_params.cc
deleted file mode 100644
index 7195f4a..0000000
--- a/base/metrics/field_trial_params.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/field_trial_params.h"
-
-#include "base/feature_list.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/field_trial_param_associator.h"
-#include "base/strings/string_number_conversions.h"
-
-namespace base {
-
-bool AssociateFieldTrialParams(
- const std::string& trial_name,
- const std::string& group_name,
- const std::map<std::string, std::string>& params) {
- return base::FieldTrialParamAssociator::GetInstance()
- ->AssociateFieldTrialParams(trial_name, group_name, params);
-}
-
-bool GetFieldTrialParams(const std::string& trial_name,
- std::map<std::string, std::string>* params) {
- return base::FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(
- trial_name, params);
-}
-
-bool GetFieldTrialParamsByFeature(const base::Feature& feature,
- std::map<std::string, std::string>* params) {
- if (!base::FeatureList::IsEnabled(feature))
- return false;
-
- base::FieldTrial* trial = base::FeatureList::GetFieldTrial(feature);
- if (!trial)
- return false;
-
- return GetFieldTrialParams(trial->trial_name(), params);
-}
-
-std::string GetFieldTrialParamValue(const std::string& trial_name,
- const std::string& param_name) {
- std::map<std::string, std::string> params;
- if (GetFieldTrialParams(trial_name, ¶ms)) {
- std::map<std::string, std::string>::iterator it = params.find(param_name);
- if (it != params.end())
- return it->second;
- }
- return std::string();
-}
-
-std::string GetFieldTrialParamValueByFeature(const base::Feature& feature,
- const std::string& param_name) {
- if (!base::FeatureList::IsEnabled(feature))
- return std::string();
-
- base::FieldTrial* trial = base::FeatureList::GetFieldTrial(feature);
- if (!trial)
- return std::string();
-
- return GetFieldTrialParamValue(trial->trial_name(), param_name);
-}
-
-int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature,
- const std::string& param_name,
- int default_value) {
- std::string value_as_string =
- GetFieldTrialParamValueByFeature(feature, param_name);
- int value_as_int = 0;
- if (!base::StringToInt(value_as_string, &value_as_int)) {
- if (!value_as_string.empty()) {
- DLOG(WARNING) << "Failed to parse field trial param " << param_name
- << " with string value " << value_as_string
- << " under feature " << feature.name
- << " into an int. Falling back to default value of "
- << default_value;
- }
- value_as_int = default_value;
- }
- return value_as_int;
-}
-
-double GetFieldTrialParamByFeatureAsDouble(const base::Feature& feature,
- const std::string& param_name,
- double default_value) {
- std::string value_as_string =
- GetFieldTrialParamValueByFeature(feature, param_name);
- double value_as_double = 0;
- if (!base::StringToDouble(value_as_string, &value_as_double)) {
- if (!value_as_string.empty()) {
- DLOG(WARNING) << "Failed to parse field trial param " << param_name
- << " with string value " << value_as_string
- << " under feature " << feature.name
- << " into a double. Falling back to default value of "
- << default_value;
- }
- value_as_double = default_value;
- }
- return value_as_double;
-}
-
-bool GetFieldTrialParamByFeatureAsBool(const base::Feature& feature,
- const std::string& param_name,
- bool default_value) {
- std::string value_as_string =
- GetFieldTrialParamValueByFeature(feature, param_name);
- if (value_as_string == "true")
- return true;
- if (value_as_string == "false")
- return false;
-
- if (!value_as_string.empty()) {
- DLOG(WARNING) << "Failed to parse field trial param " << param_name
- << " with string value " << value_as_string
- << " under feature " << feature.name
- << " into a bool. Falling back to default value of "
- << default_value;
- }
- return default_value;
-}
-
-std::string FeatureParam<std::string>::Get() const {
- const std::string value = GetFieldTrialParamValueByFeature(*feature, name);
- return value.empty() ? default_value : value;
-}
-
-double FeatureParam<double>::Get() const {
- return GetFieldTrialParamByFeatureAsDouble(*feature, name, default_value);
-}
-
-int FeatureParam<int>::Get() const {
- return GetFieldTrialParamByFeatureAsInt(*feature, name, default_value);
-}
-
-bool FeatureParam<bool>::Get() const {
- return GetFieldTrialParamByFeatureAsBool(*feature, name, default_value);
-}
-
-void LogInvalidEnumValue(const base::Feature& feature,
- const std::string& param_name,
- const std::string& value_as_string,
- int default_value_as_int) {
- DLOG(WARNING) << "Failed to parse field trial param " << param_name
- << " with string value " << value_as_string << " under feature "
- << feature.name
- << " into an enum. Falling back to default value of "
- << default_value_as_int;
-}
-
-} // namespace base
diff --git a/base/metrics/field_trial_params.h b/base/metrics/field_trial_params.h
deleted file mode 100644
index 8682226..0000000
--- a/base/metrics/field_trial_params.h
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_FIELD_TRIAL_PARAMS_H_
-#define BASE_METRICS_FIELD_TRIAL_PARAMS_H_
-
-#include <map>
-#include <string>
-
-#include "base/base_export.h"
-
-namespace base {
-
-struct Feature;
-
-// Associates the specified set of key-value |params| with the field trial
-// specified by |trial_name| and |group_name|. Fails and returns false if the
-// specified field trial already has params associated with it or the trial
-// is already active (group() has been called on it). Thread safe.
-BASE_EXPORT bool AssociateFieldTrialParams(
- const std::string& trial_name,
- const std::string& group_name,
- const std::map<std::string, std::string>& params);
-
-// Retrieves the set of key-value |params| for the specified field trial, based
-// on its selected group. If the field trial does not exist or its selected
-// group does not have any parameters associated with it, returns false and
-// does not modify |params|. Calling this function will result in the field
-// trial being marked as active if found (i.e. group() will be called on it),
-// if it wasn't already. Thread safe.
-BASE_EXPORT bool GetFieldTrialParams(
- const std::string& trial_name,
- std::map<std::string, std::string>* params);
-
-// Retrieves the set of key-value |params| for the field trial associated with
-// the specified |feature|. A feature is associated with at most one field
-// trial and selected group. See base/feature_list.h for more information on
-// features. If the feature is not enabled, or if there's no associated params,
-// returns false and does not modify |params|. Calling this function will
-// result in the associated field trial being marked as active if found (i.e.
-// group() will be called on it), if it wasn't already. Thread safe.
-BASE_EXPORT bool GetFieldTrialParamsByFeature(
- const base::Feature& feature,
- std::map<std::string, std::string>* params);
-
-// Retrieves a specific parameter value corresponding to |param_name| for the
-// specified field trial, based on its selected group. If the field trial does
-// not exist or the specified parameter does not exist, returns an empty
-// string. Calling this function will result in the field trial being marked as
-// active if found (i.e. group() will be called on it), if it wasn't already.
-// Thread safe.
-BASE_EXPORT std::string GetFieldTrialParamValue(const std::string& trial_name,
- const std::string& param_name);
-
-// Retrieves a specific parameter value corresponding to |param_name| for the
-// field trial associated with the specified |feature|. A feature is associated
-// with at most one field trial and selected group. See base/feature_list.h for
-// more information on features. If the feature is not enabled, or the
-// specified parameter does not exist, returns an empty string. Calling this
-// function will result in the associated field trial being marked as active if
-// found (i.e. group() will be called on it), if it wasn't already. Thread safe.
-BASE_EXPORT std::string GetFieldTrialParamValueByFeature(
- const base::Feature& feature,
- const std::string& param_name);
-
-// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
-// string value into an int using base::StringToInt() and returns it, if
-// successful. Otherwise, it returns |default_value|. If the string value is not
-// empty and the conversion does not succeed, it produces a warning to LOG.
-BASE_EXPORT int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature,
- const std::string& param_name,
- int default_value);
-
-// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
-// string value into a double using base::StringToDouble() and returns it, if
-// successful. Otherwise, it returns |default_value|. If the string value is not
-// empty and the conversion does not succeed, it produces a warning to LOG.
-BASE_EXPORT double GetFieldTrialParamByFeatureAsDouble(
- const base::Feature& feature,
- const std::string& param_name,
- double default_value);
-
-// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the
-// string value into a boolean and returns it, if successful. Otherwise, it
-// returns |default_value|. The only string representations accepted here are
-// "true" and "false". If the string value is not empty and the conversion does
-// not succeed, it produces a warning to LOG.
-BASE_EXPORT bool GetFieldTrialParamByFeatureAsBool(
- const base::Feature& feature,
- const std::string& param_name,
- bool default_value);
-
-// Shared declaration for various FeatureParam<T> types.
-//
-// This template is defined for the following types T:
-// bool
-// int
-// double
-// std::string
-// enum types
-//
-// See the individual definitions below for the appropriate interfaces.
-// Attempting to use it with any other type is a compile error.
-template <typename T, bool IsEnum = std::is_enum<T>::value>
-struct FeatureParam {
- // Prevent use of FeatureParam<> with unsupported types (e.g. void*). Uses T
- // in its definition so that evaluation is deferred until the template is
- // instantiated.
- static_assert(!std::is_same<T, T>::value, "unsupported FeatureParam<> type");
-};
-
-// Declares a string-valued parameter. Example:
-//
-// constexpr FeatureParam<string> kAssistantName{
-// &kAssistantFeature, "assistant_name", "HAL"};
-//
-// If the feature is not set, or set to the empty string, then Get() will return
-// the default value.
-template <>
-struct FeatureParam<std::string> {
- constexpr FeatureParam(const Feature* feature,
- const char* name,
- const char* default_value)
- : feature(feature), name(name), default_value(default_value) {}
-
- BASE_EXPORT std::string Get() const;
-
- const Feature* const feature;
- const char* const name;
- const char* const default_value;
-};
-
-// Declares a double-valued parameter. Example:
-//
-// constexpr FeatureParam<double> kAssistantTriggerThreshold{
-// &kAssistantFeature, "trigger_threshold", 0.10};
-//
-// If the feature is not set, or set to an invalid double value, then Get() will
-// return the default value.
-template <>
-struct FeatureParam<double> {
- constexpr FeatureParam(const Feature* feature,
- const char* name,
- double default_value)
- : feature(feature), name(name), default_value(default_value) {}
-
- BASE_EXPORT double Get() const;
-
- const Feature* const feature;
- const char* const name;
- const double default_value;
-};
-
-// Declares an int-valued parameter. Example:
-//
-// constexpr FeatureParam<int> kAssistantParallelism{
-// &kAssistantFeature, "parallelism", 4};
-//
-// If the feature is not set, or set to an invalid int value, then Get() will
-// return the default value.
-template <>
-struct FeatureParam<int> {
- constexpr FeatureParam(const Feature* feature,
- const char* name,
- int default_value)
- : feature(feature), name(name), default_value(default_value) {}
-
- BASE_EXPORT int Get() const;
-
- const Feature* const feature;
- const char* const name;
- const int default_value;
-};
-
-// Declares a bool-valued parameter. Example:
-//
-// constexpr FeatureParam<int> kAssistantIsHelpful{
-// &kAssistantFeature, "is_helpful", true};
-//
-// If the feature is not set, or set to value other than "true" or "false", then
-// Get() will return the default value.
-template <>
-struct FeatureParam<bool> {
- constexpr FeatureParam(const Feature* feature,
- const char* name,
- bool default_value)
- : feature(feature), name(name), default_value(default_value) {}
-
- BASE_EXPORT bool Get() const;
-
- const Feature* const feature;
- const char* const name;
- const bool default_value;
-};
-
-BASE_EXPORT void LogInvalidEnumValue(const Feature& feature,
- const std::string& param_name,
- const std::string& value_as_string,
- int default_value_as_int);
-
-// Feature param declaration for an enum, with associated options. Example:
-//
-// constexpr FeatureParam<ShapeEnum>::Option[] kShapeParamOptions[] = {
-// {SHAPE_CIRCLE, "circle"},
-// {SHAPE_CYLINDER, "cylinder"},
-// {SHAPE_PAPERCLIP, "paperclip"}};
-// constexpr FeatureParam<ShapeEnum> kAssistantShapeParam{
-// &kAssistantFeature, "shape", SHAPE_CIRCLE, &kShapeParamOptions};
-//
-// With this declaration, the parameter may be set to "circle", "cylinder", or
-// "paperclip", and that will be translated to one of the three enum values. By
-// default, or if the param is set to an unknown value, the parameter will be
-// assumed to be SHAPE_CIRCLE.
-template <typename Enum>
-struct FeatureParam<Enum, true> {
- struct Option {
- constexpr Option(Enum value, const char* name) : value(value), name(name) {}
-
- const Enum value;
- const char* const name;
- };
-
- template <size_t option_count>
- constexpr FeatureParam(const Feature* feature,
- const char* name,
- const Enum default_value,
- const Option (*options)[option_count])
- : feature(feature),
- name(name),
- default_value(default_value),
- options(*options),
- option_count(option_count) {
- static_assert(option_count >= 1, "FeatureParam<enum> has no options");
- }
-
- Enum Get() const {
- std::string value = GetFieldTrialParamValueByFeature(*feature, name);
- if (value.empty())
- return default_value;
- for (size_t i = 0; i < option_count; ++i) {
- if (value == options[i].name)
- return options[i].value;
- }
- LogInvalidEnumValue(*feature, name, value, static_cast<int>(default_value));
- return default_value;
- }
-
- const base::Feature* const feature;
- const char* const name;
- const Enum default_value;
- const Option* const options;
- const size_t option_count;
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_FIELD_TRIAL_PARAMS_H_
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
deleted file mode 100644
index 07e09cf..0000000
--- a/base/metrics/histogram.cc
+++ /dev/null
@@ -1,1315 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Histogram is an object that aggregates statistics, and can summarize them in
-// various forms, including ASCII graphical, HTML, and numerically (as a
-// vector of numbers corresponding to each of the aggregating buckets).
-// See header file for details and examples.
-
-#include "base/metrics/histogram.h"
-
-#include <inttypes.h>
-#include <limits.h>
-#include <math.h>
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "base/compiler_specific.h"
-#include "base/debug/alias.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/dummy_histogram.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/metrics_hashes.h"
-#include "base/metrics/persistent_histogram_allocator.h"
-#include "base/metrics/persistent_memory_allocator.h"
-#include "base/metrics/sample_vector.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/pickle.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
-#include "base/sys_info.h"
-#include "base/values.h"
-#include "build_config.h"
-
-namespace base {
-
-namespace {
-
-bool ReadHistogramArguments(PickleIterator* iter,
- std::string* histogram_name,
- int* flags,
- int* declared_min,
- int* declared_max,
- uint32_t* bucket_count,
- uint32_t* range_checksum) {
- if (!iter->ReadString(histogram_name) ||
- !iter->ReadInt(flags) ||
- !iter->ReadInt(declared_min) ||
- !iter->ReadInt(declared_max) ||
- !iter->ReadUInt32(bucket_count) ||
- !iter->ReadUInt32(range_checksum)) {
- DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name;
- return false;
- }
-
- // Since these fields may have come from an untrusted renderer, do additional
- // checks above and beyond those in Histogram::Initialize()
- if (*declared_max <= 0 ||
- *declared_min <= 0 ||
- *declared_max < *declared_min ||
- INT_MAX / sizeof(HistogramBase::Count) <= *bucket_count ||
- *bucket_count < 2) {
- DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
- return false;
- }
-
- // We use the arguments to find or create the local version of the histogram
- // in this process, so we need to clear any IPC flag.
- *flags &= ~HistogramBase::kIPCSerializationSourceFlag;
-
- return true;
-}
-
-bool ValidateRangeChecksum(const HistogramBase& histogram,
- uint32_t range_checksum) {
- // Normally, |histogram| should have type HISTOGRAM or be inherited from it.
- // However, if it's expired, it will actually be a DUMMY_HISTOGRAM.
- // Skip the checks in that case.
- if (histogram.GetHistogramType() == DUMMY_HISTOGRAM)
- return true;
- const Histogram& casted_histogram =
- static_cast<const Histogram&>(histogram);
-
- return casted_histogram.bucket_ranges()->checksum() == range_checksum;
-}
-
-} // namespace
-
-typedef HistogramBase::Count Count;
-typedef HistogramBase::Sample Sample;
-
-// static
-const uint32_t Histogram::kBucketCount_MAX = 16384u;
-
-class Histogram::Factory {
- public:
- Factory(const std::string& name,
- HistogramBase::Sample minimum,
- HistogramBase::Sample maximum,
- uint32_t bucket_count,
- int32_t flags)
- : Factory(name, HISTOGRAM, minimum, maximum, bucket_count, flags) {}
-
- // Create histogram based on construction parameters. Caller takes
- // ownership of the returned object.
- HistogramBase* Build();
-
- protected:
- Factory(const std::string& name,
- HistogramType histogram_type,
- HistogramBase::Sample minimum,
- HistogramBase::Sample maximum,
- uint32_t bucket_count,
- int32_t flags)
- : name_(name),
- histogram_type_(histogram_type),
- minimum_(minimum),
- maximum_(maximum),
- bucket_count_(bucket_count),
- flags_(flags) {}
-
- // Create a BucketRanges structure appropriate for this histogram.
- virtual BucketRanges* CreateRanges() {
- BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
- Histogram::InitializeBucketRanges(minimum_, maximum_, ranges);
- return ranges;
- }
-
- // Allocate the correct Histogram object off the heap (in case persistent
- // memory is not available).
- virtual std::unique_ptr<HistogramBase> HeapAlloc(const BucketRanges* ranges) {
- return WrapUnique(
- new Histogram(GetPermanentName(name_), minimum_, maximum_, ranges));
- }
-
- // Perform any required datafill on the just-created histogram. If
- // overridden, be sure to call the "super" version -- this method may not
- // always remain empty.
- virtual void FillHistogram(HistogramBase* histogram) {}
-
- // These values are protected (instead of private) because they need to
- // be accessible to methods of sub-classes in order to avoid passing
- // unnecessary parameters everywhere.
- const std::string& name_;
- const HistogramType histogram_type_;
- HistogramBase::Sample minimum_;
- HistogramBase::Sample maximum_;
- uint32_t bucket_count_;
- int32_t flags_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Factory);
-};
-
-HistogramBase* Histogram::Factory::Build() {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_);
- if (!histogram) {
- // TODO(gayane): |HashMetricName()| is called again in Histogram
- // constructor. Refactor code to avoid the additional call.
- bool should_record =
- StatisticsRecorder::ShouldRecordHistogram(HashMetricName(name_));
- if (!should_record)
- return DummyHistogram::GetInstance();
- // To avoid racy destruction at shutdown, the following will be leaked.
- const BucketRanges* created_ranges = CreateRanges();
- const BucketRanges* registered_ranges =
- StatisticsRecorder::RegisterOrDeleteDuplicateRanges(created_ranges);
-
- // In most cases, the bucket-count, minimum, and maximum values are known
- // when the code is written and so are passed in explicitly. In other
- // cases (such as with a CustomHistogram), they are calculated dynamically
- // at run-time. In the latter case, those ctor parameters are zero and
- // the results extracted from the result of CreateRanges().
- if (bucket_count_ == 0) {
- bucket_count_ = static_cast<uint32_t>(registered_ranges->bucket_count());
- minimum_ = registered_ranges->range(1);
- maximum_ = registered_ranges->range(bucket_count_ - 1);
- }
- DCHECK_EQ(minimum_, registered_ranges->range(1));
- DCHECK_EQ(maximum_, registered_ranges->range(bucket_count_ - 1));
-
- // Try to create the histogram using a "persistent" allocator. As of
- // 2016-02-25, the availability of such is controlled by a base::Feature
- // that is off by default. If the allocator doesn't exist or if
- // allocating from it fails, code below will allocate the histogram from
- // the process heap.
- PersistentHistogramAllocator::Reference histogram_ref = 0;
- std::unique_ptr<HistogramBase> tentative_histogram;
- PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
- if (allocator) {
- tentative_histogram = allocator->AllocateHistogram(
- histogram_type_,
- name_,
- minimum_,
- maximum_,
- registered_ranges,
- flags_,
- &histogram_ref);
- }
-
- // Handle the case where no persistent allocator is present or the
- // persistent allocation fails (perhaps because it is full).
- if (!tentative_histogram) {
- DCHECK(!histogram_ref); // Should never have been set.
- DCHECK(!allocator); // Shouldn't have failed.
- flags_ &= ~HistogramBase::kIsPersistent;
- tentative_histogram = HeapAlloc(registered_ranges);
- tentative_histogram->SetFlags(flags_);
- }
-
- FillHistogram(tentative_histogram.get());
-
- // Register this histogram with the StatisticsRecorder. Keep a copy of
- // the pointer value to tell later whether the locally created histogram
- // was registered or deleted. The type is "void" because it could point
- // to released memory after the following line.
- const void* tentative_histogram_ptr = tentative_histogram.get();
- histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
- tentative_histogram.release());
-
- // Persistent histograms need some follow-up processing.
- if (histogram_ref) {
- allocator->FinalizeHistogram(histogram_ref,
- histogram == tentative_histogram_ptr);
- }
- }
-
- if (histogram_type_ != histogram->GetHistogramType() ||
- (bucket_count_ != 0 && !histogram->HasConstructionArguments(
- minimum_, maximum_, bucket_count_))) {
- // The construction arguments do not match the existing histogram. This can
- // come about if an extension updates in the middle of a chrome run and has
- // changed one of them, or simply by bad code within Chrome itself. A NULL
- // return would cause Chrome to crash; better to just record it for later
- // analysis.
- UmaHistogramSparse("Histogram.MismatchedConstructionArguments",
- static_cast<Sample>(HashMetricName(name_)));
- DLOG(ERROR) << "Histogram " << name_
- << " has mismatched construction arguments";
- return DummyHistogram::GetInstance();
- }
- return histogram;
-}
-
-HistogramBase* Histogram::FactoryGet(const std::string& name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags) {
- bool valid_arguments =
- InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
- DCHECK(valid_arguments);
-
- return Factory(name, minimum, maximum, bucket_count, flags).Build();
-}
-
-HistogramBase* Histogram::FactoryTimeGet(const std::string& name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryGet(name, static_cast<Sample>(minimum.InMilliseconds()),
- static_cast<Sample>(maximum.InMilliseconds()), bucket_count,
- flags);
-}
-
-HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const std::string& name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryGet(name, static_cast<Sample>(minimum.InMicroseconds()),
- static_cast<Sample>(maximum.InMicroseconds()), bucket_count,
- flags);
-}
-
-HistogramBase* Histogram::FactoryGet(const char* name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
-}
-
-HistogramBase* Histogram::FactoryTimeGet(const char* name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count,
- flags);
-}
-
-HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const char* name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryMicrosecondsTimeGet(std::string(name), minimum, maximum,
- bucket_count, flags);
-}
-
-std::unique_ptr<HistogramBase> Histogram::PersistentCreate(
- const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta) {
- return WrapUnique(new Histogram(name, minimum, maximum, ranges, counts,
- logged_counts, meta, logged_meta));
-}
-
-// Calculate what range of values are held in each bucket.
-// We have to be careful that we don't pick a ratio between starting points in
-// consecutive buckets that is sooo small, that the integer bounds are the same
-// (effectively making one bucket get no values). We need to avoid:
-// ranges(i) == ranges(i + 1)
-// To avoid that, we just do a fine-grained bucket width as far as we need to
-// until we get a ratio that moves us along at least 2 units at a time. From
-// that bucket onward we do use the exponential growth of buckets.
-//
-// static
-void Histogram::InitializeBucketRanges(Sample minimum,
- Sample maximum,
- BucketRanges* ranges) {
- double log_max = log(static_cast<double>(maximum));
- double log_ratio;
- double log_next;
- size_t bucket_index = 1;
- Sample current = minimum;
- ranges->set_range(bucket_index, current);
- size_t bucket_count = ranges->bucket_count();
- while (bucket_count > ++bucket_index) {
- double log_current;
- log_current = log(static_cast<double>(current));
- // Calculate the count'th root of the range.
- log_ratio = (log_max - log_current) / (bucket_count - bucket_index);
- // See where the next bucket would start.
- log_next = log_current + log_ratio;
- Sample next;
- next = static_cast<int>(std::round(exp(log_next)));
- if (next > current)
- current = next;
- else
- ++current; // Just do a narrow bucket, and keep trying.
- ranges->set_range(bucket_index, current);
- }
- ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
- ranges->ResetChecksum();
-}
-
-// static
-const int Histogram::kCommonRaceBasedCountMismatch = 5;
-
-uint32_t Histogram::FindCorruption(const HistogramSamples& samples) const {
- int inconsistencies = NO_INCONSISTENCIES;
- Sample previous_range = -1; // Bottom range is always 0.
- for (uint32_t index = 0; index < bucket_count(); ++index) {
- int new_range = ranges(index);
- if (previous_range >= new_range)
- inconsistencies |= BUCKET_ORDER_ERROR;
- previous_range = new_range;
- }
-
- if (!bucket_ranges()->HasValidChecksum())
- inconsistencies |= RANGE_CHECKSUM_ERROR;
-
- int64_t delta64 = samples.redundant_count() - samples.TotalCount();
- if (delta64 != 0) {
- int delta = static_cast<int>(delta64);
- if (delta != delta64)
- delta = INT_MAX; // Flag all giant errors as INT_MAX.
- if (delta > 0) {
- if (delta > kCommonRaceBasedCountMismatch)
- inconsistencies |= COUNT_HIGH_ERROR;
- } else {
- DCHECK_GT(0, delta);
- if (-delta > kCommonRaceBasedCountMismatch)
- inconsistencies |= COUNT_LOW_ERROR;
- }
- }
- return inconsistencies;
-}
-
-const BucketRanges* Histogram::bucket_ranges() const {
- return unlogged_samples_->bucket_ranges();
-}
-
-Sample Histogram::declared_min() const {
- const BucketRanges* ranges = bucket_ranges();
- if (ranges->bucket_count() < 2)
- return -1;
- return ranges->range(1);
-}
-
-Sample Histogram::declared_max() const {
- const BucketRanges* ranges = bucket_ranges();
- if (ranges->bucket_count() < 2)
- return -1;
- return ranges->range(ranges->bucket_count() - 1);
-}
-
-Sample Histogram::ranges(uint32_t i) const {
- return bucket_ranges()->range(i);
-}
-
-uint32_t Histogram::bucket_count() const {
- return static_cast<uint32_t>(bucket_ranges()->bucket_count());
-}
-
-// static
-bool Histogram::InspectConstructionArguments(StringPiece name,
- Sample* minimum,
- Sample* maximum,
- uint32_t* bucket_count) {
- // Defensive code for backward compatibility.
- if (*minimum < 1) {
- DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum;
- *minimum = 1;
- }
- if (*maximum >= kSampleType_MAX) {
- DVLOG(1) << "Histogram: " << name << " has bad maximum: " << *maximum;
- *maximum = kSampleType_MAX - 1;
- }
- if (*bucket_count >= kBucketCount_MAX) {
- DVLOG(1) << "Histogram: " << name << " has bad bucket_count: "
- << *bucket_count;
- *bucket_count = kBucketCount_MAX - 1;
- }
-
- bool check_okay = true;
-
- if (*minimum > *maximum) {
- check_okay = false;
- std::swap(*minimum, *maximum);
- }
- if (*maximum == *minimum) {
- check_okay = false;
- *maximum = *minimum + 1;
- }
- if (*bucket_count < 3) {
- check_okay = false;
- *bucket_count = 3;
- }
- // Very high bucket counts are wasteful. Use a sparse histogram instead.
- // Value of 10002 equals a user-supplied value of 10k + 2 overflow buckets.
- constexpr uint32_t kMaxBucketCount = 10002;
- if (*bucket_count > kMaxBucketCount) {
- check_okay = false;
- *bucket_count = kMaxBucketCount;
- }
- if (*bucket_count > static_cast<uint32_t>(*maximum - *minimum + 2)) {
- check_okay = false;
- *bucket_count = static_cast<uint32_t>(*maximum - *minimum + 2);
- }
-
- if (!check_okay) {
- UmaHistogramSparse("Histogram.BadConstructionArguments",
- static_cast<Sample>(HashMetricName(name)));
- }
-
- return check_okay;
-}
-
-uint64_t Histogram::name_hash() const {
- return unlogged_samples_->id();
-}
-
-HistogramType Histogram::GetHistogramType() const {
- return HISTOGRAM;
-}
-
-bool Histogram::HasConstructionArguments(Sample expected_minimum,
- Sample expected_maximum,
- uint32_t expected_bucket_count) const {
- return (expected_bucket_count == bucket_count() &&
- expected_minimum == declared_min() &&
- expected_maximum == declared_max());
-}
-
-void Histogram::Add(int value) {
- AddCount(value, 1);
-}
-
-void Histogram::AddCount(int value, int count) {
- DCHECK_EQ(0, ranges(0));
- DCHECK_EQ(kSampleType_MAX, ranges(bucket_count()));
-
- if (value > kSampleType_MAX - 1)
- value = kSampleType_MAX - 1;
- if (value < 0)
- value = 0;
- if (count <= 0) {
- NOTREACHED();
- return;
- }
- unlogged_samples_->Accumulate(value, count);
-
- FindAndRunCallback(value);
-}
-
-std::unique_ptr<HistogramSamples> Histogram::SnapshotSamples() const {
- return SnapshotAllSamples();
-}
-
-std::unique_ptr<HistogramSamples> Histogram::SnapshotDelta() {
-#if DCHECK_IS_ON()
- DCHECK(!final_delta_created_);
-#endif
-
- // The code below has subtle thread-safety guarantees! All changes to
- // the underlying SampleVectors use atomic integer operations, which guarantee
- // eventual consistency, but do not guarantee full synchronization between
- // different entries in the SampleVector. In particular, this means that
- // concurrent updates to the histogram might result in the reported sum not
- // matching the individual bucket counts; or there being some buckets that are
- // logically updated "together", but end up being only partially updated when
- // a snapshot is captured. Note that this is why it's important to subtract
- // exactly the snapshotted unlogged samples, rather than simply resetting the
- // vector: this way, the next snapshot will include any concurrent updates
- // missed by the current snapshot.
-
- std::unique_ptr<HistogramSamples> snapshot = SnapshotUnloggedSamples();
- unlogged_samples_->Subtract(*snapshot);
- logged_samples_->Add(*snapshot);
-
- return snapshot;
-}
-
-std::unique_ptr<HistogramSamples> Histogram::SnapshotFinalDelta() const {
-#if DCHECK_IS_ON()
- DCHECK(!final_delta_created_);
- final_delta_created_ = true;
-#endif
-
- return SnapshotUnloggedSamples();
-}
-
-void Histogram::AddSamples(const HistogramSamples& samples) {
- unlogged_samples_->Add(samples);
-}
-
-bool Histogram::AddSamplesFromPickle(PickleIterator* iter) {
- return unlogged_samples_->AddFromPickle(iter);
-}
-
-// The following methods provide a graphical histogram display.
-void Histogram::WriteHTMLGraph(std::string* output) const {
- // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
- output->append("<PRE>");
- WriteAsciiImpl(true, "<br>", output);
- output->append("</PRE>");
-}
-
-void Histogram::WriteAscii(std::string* output) const {
- WriteAsciiImpl(true, "\n", output);
-}
-
-void Histogram::ValidateHistogramContents() const {
- CHECK(unlogged_samples_);
- CHECK(unlogged_samples_->bucket_ranges());
- CHECK(logged_samples_);
- CHECK(logged_samples_->bucket_ranges());
-#if !defined(OS_NACL)
- if (0U == logged_samples_->id() && (flags() & kIsPersistent)) {
- // ID should never be zero. If it is, then it's probably because the
- // entire memory page was cleared. Check that this is true.
- // TODO(bcwhite): Remove this.
- // https://bugs.chromium.org/p/chromium/issues/detail?id=836875
- size_t page_size = SysInfo::VMAllocationGranularity();
- if (page_size == 0)
- page_size = 1024;
- const int* address = reinterpret_cast<const int*>(
- reinterpret_cast<uintptr_t>(logged_samples_->meta()) &
- ~(page_size - 1));
- // Check a couple places so there is evidence in a crash report as to
- // where it was non-zero.
- CHECK_EQ(0, address[0]);
- CHECK_EQ(0, address[1]);
- CHECK_EQ(0, address[2]);
- CHECK_EQ(0, address[4]);
- CHECK_EQ(0, address[8]);
- CHECK_EQ(0, address[16]);
- CHECK_EQ(0, address[32]);
- CHECK_EQ(0, address[64]);
- CHECK_EQ(0, address[128]);
- CHECK_EQ(0, address[256]);
- CHECK_EQ(0, address[512]);
- // Now check every address.
- for (size_t i = 0; i < page_size / sizeof(int); ++i)
- CHECK_EQ(0, address[i]);
- }
-#endif
- CHECK_NE(0U, logged_samples_->id());
-}
-
-void Histogram::SerializeInfoImpl(Pickle* pickle) const {
- DCHECK(bucket_ranges()->HasValidChecksum());
- pickle->WriteString(histogram_name());
- pickle->WriteInt(flags());
- pickle->WriteInt(declared_min());
- pickle->WriteInt(declared_max());
- pickle->WriteUInt32(bucket_count());
- pickle->WriteUInt32(bucket_ranges()->checksum());
-}
-
-// TODO(bcwhite): Remove minimum/maximum parameters from here and call chain.
-Histogram::Histogram(const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges)
- : HistogramBase(name) {
- DCHECK(ranges) << name << ": " << minimum << "-" << maximum;
- unlogged_samples_.reset(new SampleVector(HashMetricName(name), ranges));
- logged_samples_.reset(new SampleVector(unlogged_samples_->id(), ranges));
-}
-
-Histogram::Histogram(const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta)
- : HistogramBase(name) {
- DCHECK(ranges) << name << ": " << minimum << "-" << maximum;
- unlogged_samples_.reset(
- new PersistentSampleVector(HashMetricName(name), ranges, meta, counts));
- logged_samples_.reset(new PersistentSampleVector(
- unlogged_samples_->id(), ranges, logged_meta, logged_counts));
-}
-
-Histogram::~Histogram() = default;
-
-bool Histogram::PrintEmptyBucket(uint32_t index) const {
- return true;
-}
-
-// Use the actual bucket widths (like a linear histogram) until the widths get
-// over some transition value, and then use that transition width. Exponentials
-// get so big so fast (and we don't expect to see a lot of entries in the large
-// buckets), so we need this to make it possible to see what is going on and
-// not have 0-graphical-height buckets.
-double Histogram::GetBucketSize(Count current, uint32_t i) const {
- DCHECK_GT(ranges(i + 1), ranges(i));
- static const double kTransitionWidth = 5;
- double denominator = ranges(i + 1) - ranges(i);
- if (denominator > kTransitionWidth)
- denominator = kTransitionWidth; // Stop trying to normalize.
- return current/denominator;
-}
-
-const std::string Histogram::GetAsciiBucketRange(uint32_t i) const {
- return GetSimpleAsciiBucketRange(ranges(i));
-}
-
-//------------------------------------------------------------------------------
-// Private methods
-
-// static
-HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) {
- std::string histogram_name;
- int flags;
- int declared_min;
- int declared_max;
- uint32_t bucket_count;
- uint32_t range_checksum;
-
- if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
- &declared_max, &bucket_count, &range_checksum)) {
- return nullptr;
- }
-
- // Find or create the local version of the histogram in this process.
- HistogramBase* histogram = Histogram::FactoryGet(
- histogram_name, declared_min, declared_max, bucket_count, flags);
- if (!histogram)
- return nullptr;
-
- // The serialized histogram might be corrupted.
- if (!ValidateRangeChecksum(*histogram, range_checksum))
- return nullptr;
-
- return histogram;
-}
-
-std::unique_ptr<SampleVector> Histogram::SnapshotAllSamples() const {
- std::unique_ptr<SampleVector> samples = SnapshotUnloggedSamples();
- samples->Add(*logged_samples_);
- return samples;
-}
-
-std::unique_ptr<SampleVector> Histogram::SnapshotUnloggedSamples() const {
- std::unique_ptr<SampleVector> samples(
- new SampleVector(unlogged_samples_->id(), bucket_ranges()));
- samples->Add(*unlogged_samples_);
- return samples;
-}
-
-void Histogram::WriteAsciiImpl(bool graph_it,
- const std::string& newline,
- std::string* output) const {
- // Get local (stack) copies of all effectively volatile class data so that we
- // are consistent across our output activities.
- std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
- Count sample_count = snapshot->TotalCount();
-
- WriteAsciiHeader(*snapshot, sample_count, output);
- output->append(newline);
-
- // Prepare to normalize graphical rendering of bucket contents.
- double max_size = 0;
- if (graph_it)
- max_size = GetPeakBucketSize(*snapshot);
-
- // Calculate space needed to print bucket range numbers. Leave room to print
- // nearly the largest bucket range without sliding over the histogram.
- uint32_t largest_non_empty_bucket = bucket_count() - 1;
- while (0 == snapshot->GetCountAtIndex(largest_non_empty_bucket)) {
- if (0 == largest_non_empty_bucket)
- break; // All buckets are empty.
- --largest_non_empty_bucket;
- }
-
- // Calculate largest print width needed for any of our bucket range displays.
- size_t print_width = 1;
- for (uint32_t i = 0; i < bucket_count(); ++i) {
- if (snapshot->GetCountAtIndex(i)) {
- size_t width = GetAsciiBucketRange(i).size() + 1;
- if (width > print_width)
- print_width = width;
- }
- }
-
- int64_t remaining = sample_count;
- int64_t past = 0;
- // Output the actual histogram graph.
- for (uint32_t i = 0; i < bucket_count(); ++i) {
- Count current = snapshot->GetCountAtIndex(i);
- if (!current && !PrintEmptyBucket(i))
- continue;
- remaining -= current;
- std::string range = GetAsciiBucketRange(i);
- output->append(range);
- for (size_t j = 0; range.size() + j < print_width + 1; ++j)
- output->push_back(' ');
- if (0 == current && i < bucket_count() - 1 &&
- 0 == snapshot->GetCountAtIndex(i + 1)) {
- while (i < bucket_count() - 1 &&
- 0 == snapshot->GetCountAtIndex(i + 1)) {
- ++i;
- }
- output->append("... ");
- output->append(newline);
- continue; // No reason to plot emptiness.
- }
- double current_size = GetBucketSize(current, i);
- if (graph_it)
- WriteAsciiBucketGraph(current_size, max_size, output);
- WriteAsciiBucketContext(past, current, remaining, i, output);
- output->append(newline);
- past += current;
- }
- DCHECK_EQ(sample_count, past);
-}
-
-double Histogram::GetPeakBucketSize(const SampleVectorBase& samples) const {
- double max = 0;
- for (uint32_t i = 0; i < bucket_count() ; ++i) {
- double current_size = GetBucketSize(samples.GetCountAtIndex(i), i);
- if (current_size > max)
- max = current_size;
- }
- return max;
-}
-
-void Histogram::WriteAsciiHeader(const SampleVectorBase& samples,
- Count sample_count,
- std::string* output) const {
- StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name(),
- sample_count);
- if (sample_count == 0) {
- DCHECK_EQ(samples.sum(), 0);
- } else {
- double mean = static_cast<float>(samples.sum()) / sample_count;
- StringAppendF(output, ", mean = %.1f", mean);
- }
- if (flags())
- StringAppendF(output, " (flags = 0x%x)", flags());
-}
-
-void Histogram::WriteAsciiBucketContext(const int64_t past,
- const Count current,
- const int64_t remaining,
- const uint32_t i,
- std::string* output) const {
- double scaled_sum = (past + current + remaining) / 100.0;
- WriteAsciiBucketValue(current, scaled_sum, output);
- if (0 < i) {
- double percentage = past / scaled_sum;
- StringAppendF(output, " {%3.1f%%}", percentage);
- }
-}
-
-void Histogram::GetParameters(DictionaryValue* params) const {
- params->SetString("type", HistogramTypeToString(GetHistogramType()));
- params->SetInteger("min", declared_min());
- params->SetInteger("max", declared_max());
- params->SetInteger("bucket_count", static_cast<int>(bucket_count()));
-}
-
-void Histogram::GetCountAndBucketData(Count* count,
- int64_t* sum,
- ListValue* buckets) const {
- std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
- *count = snapshot->TotalCount();
- *sum = snapshot->sum();
- uint32_t index = 0;
- for (uint32_t i = 0; i < bucket_count(); ++i) {
- Sample count_at_index = snapshot->GetCountAtIndex(i);
- if (count_at_index > 0) {
- std::unique_ptr<DictionaryValue> bucket_value(new DictionaryValue());
- bucket_value->SetInteger("low", ranges(i));
- if (i != bucket_count() - 1)
- bucket_value->SetInteger("high", ranges(i + 1));
- bucket_value->SetInteger("count", count_at_index);
- buckets->Set(index, std::move(bucket_value));
- ++index;
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// LinearHistogram: This histogram uses a traditional set of evenly spaced
-// buckets.
-//------------------------------------------------------------------------------
-
-class LinearHistogram::Factory : public Histogram::Factory {
- public:
- Factory(const std::string& name,
- HistogramBase::Sample minimum,
- HistogramBase::Sample maximum,
- uint32_t bucket_count,
- int32_t flags,
- const DescriptionPair* descriptions)
- : Histogram::Factory(name, LINEAR_HISTOGRAM, minimum, maximum,
- bucket_count, flags) {
- descriptions_ = descriptions;
- }
-
- protected:
- BucketRanges* CreateRanges() override {
- BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
- LinearHistogram::InitializeBucketRanges(minimum_, maximum_, ranges);
- return ranges;
- }
-
- std::unique_ptr<HistogramBase> HeapAlloc(
- const BucketRanges* ranges) override {
- return WrapUnique(new LinearHistogram(GetPermanentName(name_), minimum_,
- maximum_, ranges));
- }
-
- void FillHistogram(HistogramBase* base_histogram) override {
- Histogram::Factory::FillHistogram(base_histogram);
- // Normally, |base_histogram| should have type LINEAR_HISTOGRAM or be
- // inherited from it. However, if it's expired, it will actually be a
- // DUMMY_HISTOGRAM. Skip filling in that case.
- if (base_histogram->GetHistogramType() == DUMMY_HISTOGRAM)
- return;
- LinearHistogram* histogram = static_cast<LinearHistogram*>(base_histogram);
- // Set range descriptions.
- if (descriptions_) {
- for (int i = 0; descriptions_[i].description; ++i) {
- histogram->bucket_description_[descriptions_[i].sample] =
- descriptions_[i].description;
- }
- }
- }
-
- private:
- const DescriptionPair* descriptions_;
-
- DISALLOW_COPY_AND_ASSIGN(Factory);
-};
-
-LinearHistogram::~LinearHistogram() = default;
-
-HistogramBase* LinearHistogram::FactoryGet(const std::string& name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryGetWithRangeDescription(name, minimum, maximum, bucket_count,
- flags, NULL);
-}
-
-HistogramBase* LinearHistogram::FactoryTimeGet(const std::string& name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryGet(name, static_cast<Sample>(minimum.InMilliseconds()),
- static_cast<Sample>(maximum.InMilliseconds()), bucket_count,
- flags);
-}
-
-HistogramBase* LinearHistogram::FactoryGet(const char* name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
-}
-
-HistogramBase* LinearHistogram::FactoryTimeGet(const char* name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags) {
- return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count,
- flags);
-}
-
-std::unique_ptr<HistogramBase> LinearHistogram::PersistentCreate(
- const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta) {
- return WrapUnique(new LinearHistogram(name, minimum, maximum, ranges, counts,
- logged_counts, meta, logged_meta));
-}
-
-HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
- const std::string& name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags,
- const DescriptionPair descriptions[]) {
- bool valid_arguments = Histogram::InspectConstructionArguments(
- name, &minimum, &maximum, &bucket_count);
- DCHECK(valid_arguments);
-
- return Factory(name, minimum, maximum, bucket_count, flags, descriptions)
- .Build();
-}
-
-HistogramType LinearHistogram::GetHistogramType() const {
- return LINEAR_HISTOGRAM;
-}
-
-LinearHistogram::LinearHistogram(const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges)
- : Histogram(name, minimum, maximum, ranges) {}
-
-LinearHistogram::LinearHistogram(
- const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta)
- : Histogram(name,
- minimum,
- maximum,
- ranges,
- counts,
- logged_counts,
- meta,
- logged_meta) {}
-
-double LinearHistogram::GetBucketSize(Count current, uint32_t i) const {
- DCHECK_GT(ranges(i + 1), ranges(i));
- // Adjacent buckets with different widths would have "surprisingly" many (few)
- // samples in a histogram if we didn't normalize this way.
- double denominator = ranges(i + 1) - ranges(i);
- return current/denominator;
-}
-
-const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const {
- int range = ranges(i);
- BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
- if (it == bucket_description_.end())
- return Histogram::GetAsciiBucketRange(i);
- return it->second;
-}
-
-bool LinearHistogram::PrintEmptyBucket(uint32_t index) const {
- return bucket_description_.find(ranges(index)) == bucket_description_.end();
-}
-
-// static
-void LinearHistogram::InitializeBucketRanges(Sample minimum,
- Sample maximum,
- BucketRanges* ranges) {
- double min = minimum;
- double max = maximum;
- size_t bucket_count = ranges->bucket_count();
- for (size_t i = 1; i < bucket_count; ++i) {
- double linear_range =
- (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2);
- ranges->set_range(i, static_cast<Sample>(linear_range + 0.5));
- }
- ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
- ranges->ResetChecksum();
-}
-
-// static
-HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) {
- std::string histogram_name;
- int flags;
- int declared_min;
- int declared_max;
- uint32_t bucket_count;
- uint32_t range_checksum;
-
- if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
- &declared_max, &bucket_count, &range_checksum)) {
- return nullptr;
- }
-
- HistogramBase* histogram = LinearHistogram::FactoryGet(
- histogram_name, declared_min, declared_max, bucket_count, flags);
- if (!histogram)
- return nullptr;
-
- if (!ValidateRangeChecksum(*histogram, range_checksum)) {
- // The serialized histogram might be corrupted.
- return nullptr;
- }
- return histogram;
-}
-
-//------------------------------------------------------------------------------
-// This section provides implementation for BooleanHistogram.
-//------------------------------------------------------------------------------
-
-class BooleanHistogram::Factory : public Histogram::Factory {
- public:
- Factory(const std::string& name, int32_t flags)
- : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {}
-
- protected:
- BucketRanges* CreateRanges() override {
- BucketRanges* ranges = new BucketRanges(3 + 1);
- LinearHistogram::InitializeBucketRanges(1, 2, ranges);
- return ranges;
- }
-
- std::unique_ptr<HistogramBase> HeapAlloc(
- const BucketRanges* ranges) override {
- return WrapUnique(new BooleanHistogram(GetPermanentName(name_), ranges));
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Factory);
-};
-
-HistogramBase* BooleanHistogram::FactoryGet(const std::string& name,
- int32_t flags) {
- return Factory(name, flags).Build();
-}
-
-HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) {
- return FactoryGet(std::string(name), flags);
-}
-
-std::unique_ptr<HistogramBase> BooleanHistogram::PersistentCreate(
- const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta) {
- return WrapUnique(new BooleanHistogram(name, ranges, counts, logged_counts,
- meta, logged_meta));
-}
-
-HistogramType BooleanHistogram::GetHistogramType() const {
- return BOOLEAN_HISTOGRAM;
-}
-
-BooleanHistogram::BooleanHistogram(const char* name, const BucketRanges* ranges)
- : LinearHistogram(name, 1, 2, ranges) {}
-
-BooleanHistogram::BooleanHistogram(
- const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta)
- : LinearHistogram(name,
- 1,
- 2,
- ranges,
- counts,
- logged_counts,
- meta,
- logged_meta) {}
-
-HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) {
- std::string histogram_name;
- int flags;
- int declared_min;
- int declared_max;
- uint32_t bucket_count;
- uint32_t range_checksum;
-
- if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
- &declared_max, &bucket_count, &range_checksum)) {
- return nullptr;
- }
-
- HistogramBase* histogram = BooleanHistogram::FactoryGet(
- histogram_name, flags);
- if (!histogram)
- return nullptr;
-
- if (!ValidateRangeChecksum(*histogram, range_checksum)) {
- // The serialized histogram might be corrupted.
- return nullptr;
- }
- return histogram;
-}
-
-//------------------------------------------------------------------------------
-// CustomHistogram:
-//------------------------------------------------------------------------------
-
-class CustomHistogram::Factory : public Histogram::Factory {
- public:
- Factory(const std::string& name,
- const std::vector<Sample>* custom_ranges,
- int32_t flags)
- : Histogram::Factory(name, CUSTOM_HISTOGRAM, 0, 0, 0, flags) {
- custom_ranges_ = custom_ranges;
- }
-
- protected:
- BucketRanges* CreateRanges() override {
- // Remove the duplicates in the custom ranges array.
- std::vector<int> ranges = *custom_ranges_;
- ranges.push_back(0); // Ensure we have a zero value.
- ranges.push_back(HistogramBase::kSampleType_MAX);
- std::sort(ranges.begin(), ranges.end());
- ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
-
- BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
- for (uint32_t i = 0; i < ranges.size(); i++) {
- bucket_ranges->set_range(i, ranges[i]);
- }
- bucket_ranges->ResetChecksum();
- return bucket_ranges;
- }
-
- std::unique_ptr<HistogramBase> HeapAlloc(
- const BucketRanges* ranges) override {
- return WrapUnique(new CustomHistogram(GetPermanentName(name_), ranges));
- }
-
- private:
- const std::vector<Sample>* custom_ranges_;
-
- DISALLOW_COPY_AND_ASSIGN(Factory);
-};
-
-HistogramBase* CustomHistogram::FactoryGet(
- const std::string& name,
- const std::vector<Sample>& custom_ranges,
- int32_t flags) {
- CHECK(ValidateCustomRanges(custom_ranges));
-
- return Factory(name, &custom_ranges, flags).Build();
-}
-
-HistogramBase* CustomHistogram::FactoryGet(
- const char* name,
- const std::vector<Sample>& custom_ranges,
- int32_t flags) {
- return FactoryGet(std::string(name), custom_ranges, flags);
-}
-
-std::unique_ptr<HistogramBase> CustomHistogram::PersistentCreate(
- const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta) {
- return WrapUnique(new CustomHistogram(name, ranges, counts, logged_counts,
- meta, logged_meta));
-}
-
-HistogramType CustomHistogram::GetHistogramType() const {
- return CUSTOM_HISTOGRAM;
-}
-
-// static
-std::vector<Sample> CustomHistogram::ArrayToCustomEnumRanges(
- base::span<const Sample> values) {
- std::vector<Sample> all_values;
- for (Sample value : values) {
- all_values.push_back(value);
-
- // Ensure that a guard bucket is added. If we end up with duplicate
- // values, FactoryGet will take care of removing them.
- all_values.push_back(value + 1);
- }
- return all_values;
-}
-
-CustomHistogram::CustomHistogram(const char* name, const BucketRanges* ranges)
- : Histogram(name,
- ranges->range(1),
- ranges->range(ranges->bucket_count() - 1),
- ranges) {}
-
-CustomHistogram::CustomHistogram(
- const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta)
- : Histogram(name,
- ranges->range(1),
- ranges->range(ranges->bucket_count() - 1),
- ranges,
- counts,
- logged_counts,
- meta,
- logged_meta) {}
-
-void CustomHistogram::SerializeInfoImpl(Pickle* pickle) const {
- Histogram::SerializeInfoImpl(pickle);
-
- // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't
- // write them.
- for (uint32_t i = 1; i < bucket_ranges()->bucket_count(); ++i)
- pickle->WriteInt(bucket_ranges()->range(i));
-}
-
-double CustomHistogram::GetBucketSize(Count current, uint32_t i) const {
- // If this is a histogram of enum values, normalizing the bucket count
- // by the bucket range is not helpful, so just return the bucket count.
- return current;
-}
-
-// static
-HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) {
- std::string histogram_name;
- int flags;
- int declared_min;
- int declared_max;
- uint32_t bucket_count;
- uint32_t range_checksum;
-
- if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
- &declared_max, &bucket_count, &range_checksum)) {
- return nullptr;
- }
-
- // First and last ranges are not serialized.
- std::vector<Sample> sample_ranges(bucket_count - 1);
-
- for (uint32_t i = 0; i < sample_ranges.size(); ++i) {
- if (!iter->ReadInt(&sample_ranges[i]))
- return nullptr;
- }
-
- HistogramBase* histogram = CustomHistogram::FactoryGet(
- histogram_name, sample_ranges, flags);
- if (!histogram)
- return nullptr;
-
- if (!ValidateRangeChecksum(*histogram, range_checksum)) {
- // The serialized histogram might be corrupted.
- return nullptr;
- }
- return histogram;
-}
-
-// static
-bool CustomHistogram::ValidateCustomRanges(
- const std::vector<Sample>& custom_ranges) {
- bool has_valid_range = false;
- for (uint32_t i = 0; i < custom_ranges.size(); i++) {
- Sample sample = custom_ranges[i];
- if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1)
- return false;
- if (sample != 0)
- has_valid_range = true;
- }
- return has_valid_range;
-}
-
-} // namespace base
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
deleted file mode 100644
index 35d8370..0000000
--- a/base/metrics/histogram.h
+++ /dev/null
@@ -1,559 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Histogram is an object that aggregates statistics, and can summarize them in
-// various forms, including ASCII graphical, HTML, and numerically (as a
-// vector of numbers corresponding to each of the aggregating buckets).
-
-// It supports calls to accumulate either time intervals (which are processed
-// as integral number of milliseconds), or arbitrary integral units.
-
-// For Histogram (exponential histogram), LinearHistogram and CustomHistogram,
-// the minimum for a declared range is 1 (instead of 0), while the maximum is
-// (HistogramBase::kSampleType_MAX - 1). However, there will always be underflow
-// and overflow buckets added automatically, so a 0 bucket will always exist
-// even when a minimum value of 1 is specified.
-
-// Each use of a histogram with the same name will reference the same underlying
-// data, so it is safe to record to the same histogram from multiple locations
-// in the code. It is a runtime error if all uses of the same histogram do not
-// agree exactly in type, bucket size and range.
-
-// For Histogram and LinearHistogram, the maximum for a declared range should
-// always be larger (not equal) than minimal range. Zero and
-// HistogramBase::kSampleType_MAX are implicitly added as first and last ranges,
-// so the smallest legal bucket_count is 3. However CustomHistogram can have
-// bucket count as 2 (when you give a custom ranges vector containing only 1
-// range).
-// For these 3 kinds of histograms, the max bucket count is always
-// (Histogram::kBucketCount_MAX - 1).
-
-// The buckets layout of class Histogram is exponential. For example, buckets
-// might contain (sequentially) the count of values in the following intervals:
-// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity)
-// That bucket allocation would actually result from construction of a histogram
-// for values between 1 and 64, with 8 buckets, such as:
-// Histogram count("some name", 1, 64, 8);
-// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity)
-// are also counted by the constructor in the user supplied "bucket_count"
-// argument.
-// The above example has an exponential ratio of 2 (doubling the bucket width
-// in each consecutive bucket). The Histogram class automatically calculates
-// the smallest ratio that it can use to construct the number of buckets
-// selected in the constructor. An another example, if you had 50 buckets,
-// and millisecond time values from 1 to 10000, then the ratio between
-// consecutive bucket widths will be approximately somewhere around the 50th
-// root of 10000. This approach provides very fine grain (narrow) buckets
-// at the low end of the histogram scale, but allows the histogram to cover a
-// gigantic range with the addition of very few buckets.
-
-// Usually we use macros to define and use a histogram, which are defined in
-// base/metrics/histogram_macros.h. Note: Callers should include that header
-// directly if they only access the histogram APIs through macros.
-//
-// Macros use a pattern involving a function static variable, that is a pointer
-// to a histogram. This static is explicitly initialized on any thread
-// that detects a uninitialized (NULL) pointer. The potentially racy
-// initialization is not a problem as it is always set to point to the same
-// value (i.e., the FactoryGet always returns the same value). FactoryGet
-// is also completely thread safe, which results in a completely thread safe,
-// and relatively fast, set of counters. To avoid races at shutdown, the static
-// pointer is NOT deleted, and we leak the histograms at process termination.
-
-#ifndef BASE_METRICS_HISTOGRAM_H_
-#define BASE_METRICS_HISTOGRAM_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/base_export.h"
-#include "base/compiler_specific.h"
-#include "base/containers/span.h"
-#include "base/gtest_prod_util.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/metrics/bucket_ranges.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-
-namespace base {
-
-class BooleanHistogram;
-class CustomHistogram;
-class DelayedPersistentAllocation;
-class Histogram;
-class LinearHistogram;
-class Pickle;
-class PickleIterator;
-class SampleVector;
-class SampleVectorBase;
-
-class BASE_EXPORT Histogram : public HistogramBase {
- public:
- // Initialize maximum number of buckets in histograms as 16,384.
- static const uint32_t kBucketCount_MAX;
-
- typedef std::vector<Count> Counts;
-
- ~Histogram() override;
-
- //----------------------------------------------------------------------------
- // For a valid histogram, input should follow these restrictions:
- // minimum > 0 (if a minimum below 1 is specified, it will implicitly be
- // normalized up to 1)
- // maximum > minimum
- // buckets > 2 [minimum buckets needed: underflow, overflow and the range]
- // Additionally,
- // buckets <= (maximum - minimum + 2) - this is to ensure that we don't have
- // more buckets than the range of numbers; having more buckets than 1 per
- // value in the range would be nonsensical.
- static HistogramBase* FactoryGet(const std::string& name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags);
- static HistogramBase* FactoryTimeGet(const std::string& name,
- base::TimeDelta minimum,
- base::TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags);
- static HistogramBase* FactoryMicrosecondsTimeGet(const std::string& name,
- base::TimeDelta minimum,
- base::TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags);
-
- // Overloads of the above functions that take a const char* |name| param, to
- // avoid code bloat from the std::string constructor being inlined into call
- // sites.
- static HistogramBase* FactoryGet(const char* name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags);
- static HistogramBase* FactoryTimeGet(const char* name,
- base::TimeDelta minimum,
- base::TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags);
- static HistogramBase* FactoryMicrosecondsTimeGet(const char* name,
- base::TimeDelta minimum,
- base::TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags);
-
- // Create a histogram using data in persistent storage.
- static std::unique_ptr<HistogramBase> PersistentCreate(
- const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- static void InitializeBucketRanges(Sample minimum,
- Sample maximum,
- BucketRanges* ranges);
-
- // This constant if for FindCorruption. Since snapshots of histograms are
- // taken asynchronously relative to sampling, and our counting code currently
- // does not prevent race conditions, it is pretty likely that we'll catch a
- // redundant count that doesn't match the sample count. We allow for a
- // certain amount of slop before flagging this as an inconsistency. Even with
- // an inconsistency, we'll snapshot it again (for UMA in about a half hour),
- // so we'll eventually get the data, if it was not the result of a corruption.
- static const int kCommonRaceBasedCountMismatch;
-
- // Check to see if bucket ranges, counts and tallies in the snapshot are
- // consistent with the bucket ranges and checksums in our histogram. This can
- // produce a false-alarm if a race occurred in the reading of the data during
- // a SnapShot process, but should otherwise be false at all times (unless we
- // have memory over-writes, or DRAM failures). Flag definitions are located
- // under "enum Inconsistency" in base/metrics/histogram_base.h.
- uint32_t FindCorruption(const HistogramSamples& samples) const override;
-
- //----------------------------------------------------------------------------
- // Accessors for factory construction, serialization and testing.
- //----------------------------------------------------------------------------
- const BucketRanges* bucket_ranges() const;
- Sample declared_min() const;
- Sample declared_max() const;
- virtual Sample ranges(uint32_t i) const;
- virtual uint32_t bucket_count() const;
-
- // This function validates histogram construction arguments. It returns false
- // if some of the arguments are bad but also corrects them so they should
- // function on non-dcheck builds without crashing.
- // Note. Currently it allow some bad input, e.g. 0 as minimum, but silently
- // converts it to good input: 1.
- // TODO(bcwhite): Use false returns to create "sink" histograms so that bad
- // data doesn't create confusion on the servers.
- static bool InspectConstructionArguments(StringPiece name,
- Sample* minimum,
- Sample* maximum,
- uint32_t* bucket_count);
-
- // HistogramBase implementation:
- uint64_t name_hash() const override;
- HistogramType GetHistogramType() const override;
- bool HasConstructionArguments(Sample expected_minimum,
- Sample expected_maximum,
- uint32_t expected_bucket_count) const override;
- void Add(Sample value) override;
- void AddCount(Sample value, int count) override;
- std::unique_ptr<HistogramSamples> SnapshotSamples() const override;
- std::unique_ptr<HistogramSamples> SnapshotDelta() override;
- std::unique_ptr<HistogramSamples> SnapshotFinalDelta() const override;
- void AddSamples(const HistogramSamples& samples) override;
- bool AddSamplesFromPickle(base::PickleIterator* iter) override;
- void WriteHTMLGraph(std::string* output) const override;
- void WriteAscii(std::string* output) const override;
-
- // Validates the histogram contents and CHECKs on errors.
- // TODO(bcwhite): Remove this after https://crbug/836875.
- void ValidateHistogramContents() const override;
-
- protected:
- // This class, defined entirely within the .cc file, contains all the
- // common logic for building a Histogram and can be overridden by more
- // specific types to alter details of how the creation is done. It is
- // defined as an embedded class (rather than an anonymous one) so it
- // can access the protected constructors.
- class Factory;
-
- // |ranges| should contain the underflow and overflow buckets. See top
- // comments for example.
- Histogram(const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges);
-
- // Traditionally, histograms allocate their own memory for the bucket
- // vector but "shared" histograms use memory regions allocated from a
- // special memory segment that is passed in here. It is assumed that
- // the life of this memory is managed externally and exceeds the lifetime
- // of this object. Practically, this memory is never released until the
- // process exits and the OS cleans it up.
- Histogram(const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- // HistogramBase implementation:
- void SerializeInfoImpl(base::Pickle* pickle) const override;
-
- // Method to override to skip the display of the i'th bucket if it's empty.
- virtual bool PrintEmptyBucket(uint32_t index) const;
-
- // Get normalized size, relative to the ranges(i).
- virtual double GetBucketSize(Count current, uint32_t i) const;
-
- // Return a string description of what goes in a given bucket.
- // Most commonly this is the numeric value, but in derived classes it may
- // be a name (or string description) given to the bucket.
- virtual const std::string GetAsciiBucketRange(uint32_t it) const;
-
- private:
- // Allow tests to corrupt our innards for testing purposes.
- FRIEND_TEST_ALL_PREFIXES(HistogramTest, BoundsTest);
- FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketPlacementTest);
- FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
-
- friend class StatisticsRecorder; // To allow it to delete duplicates.
- friend class StatisticsRecorderTest;
-
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
- base::PickleIterator* iter);
- static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter);
-
- // Create a snapshot containing all samples (both logged and unlogged).
- // Implementation of SnapshotSamples method with a more specific type for
- // internal use.
- std::unique_ptr<SampleVector> SnapshotAllSamples() const;
-
- // Create a copy of unlogged samples.
- std::unique_ptr<SampleVector> SnapshotUnloggedSamples() const;
-
- //----------------------------------------------------------------------------
- // Helpers for emitting Ascii graphic. Each method appends data to output.
-
- void WriteAsciiImpl(bool graph_it,
- const std::string& newline,
- std::string* output) const;
-
- // Find out how large (graphically) the largest bucket will appear to be.
- double GetPeakBucketSize(const SampleVectorBase& samples) const;
-
- // Write a common header message describing this histogram.
- void WriteAsciiHeader(const SampleVectorBase& samples,
- Count sample_count,
- std::string* output) const;
-
- // Write information about previous, current, and next buckets.
- // Information such as cumulative percentage, etc.
- void WriteAsciiBucketContext(const int64_t past,
- const Count current,
- const int64_t remaining,
- const uint32_t i,
- std::string* output) const;
-
- // WriteJSON calls these.
- void GetParameters(DictionaryValue* params) const override;
-
- void GetCountAndBucketData(Count* count,
- int64_t* sum,
- ListValue* buckets) const override;
-
- // Samples that have not yet been logged with SnapshotDelta().
- std::unique_ptr<SampleVectorBase> unlogged_samples_;
-
- // Accumulation of all samples that have been logged with SnapshotDelta().
- std::unique_ptr<SampleVectorBase> logged_samples_;
-
-#if DCHECK_IS_ON() // Don't waste memory if it won't be used.
- // Flag to indicate if PrepareFinalDelta has been previously called. It is
- // used to DCHECK that a final delta is not created multiple times.
- mutable bool final_delta_created_ = false;
-#endif
-
- DISALLOW_COPY_AND_ASSIGN(Histogram);
-};
-
-//------------------------------------------------------------------------------
-
-// LinearHistogram is a more traditional histogram, with evenly spaced
-// buckets.
-class BASE_EXPORT LinearHistogram : public Histogram {
- public:
- ~LinearHistogram() override;
-
- /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit
- default underflow bucket. */
- static HistogramBase* FactoryGet(const std::string& name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags);
- static HistogramBase* FactoryTimeGet(const std::string& name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags);
-
- // Overloads of the above two functions that take a const char* |name| param,
- // to avoid code bloat from the std::string constructor being inlined into
- // call sites.
- static HistogramBase* FactoryGet(const char* name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags);
- static HistogramBase* FactoryTimeGet(const char* name,
- TimeDelta minimum,
- TimeDelta maximum,
- uint32_t bucket_count,
- int32_t flags);
-
- // Create a histogram using data in persistent storage.
- static std::unique_ptr<HistogramBase> PersistentCreate(
- const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- struct DescriptionPair {
- Sample sample;
- const char* description; // Null means end of a list of pairs.
- };
-
- // Create a LinearHistogram and store a list of number/text values for use in
- // writing the histogram graph.
- // |descriptions| can be NULL, which means no special descriptions to set. If
- // it's not NULL, the last element in the array must has a NULL in its
- // "description" field.
- static HistogramBase* FactoryGetWithRangeDescription(
- const std::string& name,
- Sample minimum,
- Sample maximum,
- uint32_t bucket_count,
- int32_t flags,
- const DescriptionPair descriptions[]);
-
- static void InitializeBucketRanges(Sample minimum,
- Sample maximum,
- BucketRanges* ranges);
-
- // Overridden from Histogram:
- HistogramType GetHistogramType() const override;
-
- protected:
- class Factory;
-
- LinearHistogram(const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges);
-
- LinearHistogram(const char* name,
- Sample minimum,
- Sample maximum,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- double GetBucketSize(Count current, uint32_t i) const override;
-
- // If we have a description for a bucket, then return that. Otherwise
- // let parent class provide a (numeric) description.
- const std::string GetAsciiBucketRange(uint32_t i) const override;
-
- // Skip printing of name for numeric range if we have a name (and if this is
- // an empty bucket).
- bool PrintEmptyBucket(uint32_t index) const override;
-
- private:
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
- base::PickleIterator* iter);
- static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter);
-
- // For some ranges, we store a printable description of a bucket range.
- // If there is no description, then GetAsciiBucketRange() uses parent class
- // to provide a description.
- typedef std::map<Sample, std::string> BucketDescriptionMap;
- BucketDescriptionMap bucket_description_;
-
- DISALLOW_COPY_AND_ASSIGN(LinearHistogram);
-};
-
-//------------------------------------------------------------------------------
-
-// BooleanHistogram is a histogram for booleans.
-class BASE_EXPORT BooleanHistogram : public LinearHistogram {
- public:
- static HistogramBase* FactoryGet(const std::string& name, int32_t flags);
-
- // Overload of the above function that takes a const char* |name| param,
- // to avoid code bloat from the std::string constructor being inlined into
- // call sites.
- static HistogramBase* FactoryGet(const char* name, int32_t flags);
-
- // Create a histogram using data in persistent storage.
- static std::unique_ptr<HistogramBase> PersistentCreate(
- const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- HistogramType GetHistogramType() const override;
-
- protected:
- class Factory;
-
- private:
- BooleanHistogram(const char* name, const BucketRanges* ranges);
- BooleanHistogram(const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
- base::PickleIterator* iter);
- static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter);
-
- DISALLOW_COPY_AND_ASSIGN(BooleanHistogram);
-};
-
-//------------------------------------------------------------------------------
-
-// CustomHistogram is a histogram for a set of custom integers.
-class BASE_EXPORT CustomHistogram : public Histogram {
- public:
- // |custom_ranges| contains a vector of limits on ranges. Each limit should be
- // > 0 and < kSampleType_MAX. (Currently 0 is still accepted for backward
- // compatibility). The limits can be unordered or contain duplication, but
- // client should not depend on this.
- static HistogramBase* FactoryGet(const std::string& name,
- const std::vector<Sample>& custom_ranges,
- int32_t flags);
-
- // Overload of the above function that takes a const char* |name| param,
- // to avoid code bloat from the std::string constructor being inlined into
- // call sites.
- static HistogramBase* FactoryGet(const char* name,
- const std::vector<Sample>& custom_ranges,
- int32_t flags);
-
- // Create a histogram using data in persistent storage.
- static std::unique_ptr<HistogramBase> PersistentCreate(
- const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- // Overridden from Histogram:
- HistogramType GetHistogramType() const override;
-
- // Helper method for transforming an array of valid enumeration values
- // to the std::vector<int> expected by UMA_HISTOGRAM_CUSTOM_ENUMERATION.
- // This function ensures that a guard bucket exists right after any
- // valid sample value (unless the next higher sample is also a valid value),
- // so that invalid samples never fall into the same bucket as valid samples.
- static std::vector<Sample> ArrayToCustomEnumRanges(
- base::span<const Sample> values);
-
- protected:
- class Factory;
-
- CustomHistogram(const char* name, const BucketRanges* ranges);
-
- CustomHistogram(const char* name,
- const BucketRanges* ranges,
- const DelayedPersistentAllocation& counts,
- const DelayedPersistentAllocation& logged_counts,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- // HistogramBase implementation:
- void SerializeInfoImpl(base::Pickle* pickle) const override;
-
- double GetBucketSize(Count current, uint32_t i) const override;
-
- private:
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
- base::PickleIterator* iter);
- static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter);
-
- static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges);
-
- DISALLOW_COPY_AND_ASSIGN(CustomHistogram);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_H_
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc
deleted file mode 100644
index da3ae93..0000000
--- a/base/metrics/histogram_base.cc
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/histogram_base.h"
-
-#include <limits.h>
-
-#include <memory>
-#include <set>
-#include <utility>
-
-#include "base/json/json_string_value_serializer.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/pickle.h"
-#include "base/process/process_handle.h"
-#include "base/rand_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
-#include "base/values.h"
-
-namespace base {
-
-std::string HistogramTypeToString(HistogramType type) {
- switch (type) {
- case HISTOGRAM:
- return "HISTOGRAM";
- case LINEAR_HISTOGRAM:
- return "LINEAR_HISTOGRAM";
- case BOOLEAN_HISTOGRAM:
- return "BOOLEAN_HISTOGRAM";
- case CUSTOM_HISTOGRAM:
- return "CUSTOM_HISTOGRAM";
- case SPARSE_HISTOGRAM:
- return "SPARSE_HISTOGRAM";
- case DUMMY_HISTOGRAM:
- return "DUMMY_HISTOGRAM";
- }
- NOTREACHED();
- return "UNKNOWN";
-}
-
-HistogramBase* DeserializeHistogramInfo(PickleIterator* iter) {
- int type;
- if (!iter->ReadInt(&type))
- return nullptr;
-
- switch (type) {
- case HISTOGRAM:
- return Histogram::DeserializeInfoImpl(iter);
- case LINEAR_HISTOGRAM:
- return LinearHistogram::DeserializeInfoImpl(iter);
- case BOOLEAN_HISTOGRAM:
- return BooleanHistogram::DeserializeInfoImpl(iter);
- case CUSTOM_HISTOGRAM:
- return CustomHistogram::DeserializeInfoImpl(iter);
- case SPARSE_HISTOGRAM:
- return SparseHistogram::DeserializeInfoImpl(iter);
- default:
- return nullptr;
- }
-}
-
-const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
-
-HistogramBase::HistogramBase(const char* name)
- : histogram_name_(name), flags_(kNoFlags) {}
-
-HistogramBase::~HistogramBase() = default;
-
-void HistogramBase::CheckName(const StringPiece& name) const {
- DCHECK_EQ(StringPiece(histogram_name()), name);
-}
-
-void HistogramBase::SetFlags(int32_t flags) {
- HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_);
- subtle::NoBarrier_Store(&flags_, old_flags | flags);
-}
-
-void HistogramBase::ClearFlags(int32_t flags) {
- HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_);
- subtle::NoBarrier_Store(&flags_, old_flags & ~flags);
-}
-
-void HistogramBase::AddScaled(Sample value, int count, int scale) {
- DCHECK_LT(0, scale);
-
- // Convert raw count and probabilistically round up/down if the remainder
- // is more than a random number [0, scale). This gives a more accurate
- // count when there are a large number of records. RandInt is "inclusive",
- // hence the -1 for the max value.
- int64_t count_scaled = count / scale;
- if (count - (count_scaled * scale) > base::RandInt(0, scale - 1))
- count_scaled += 1;
- if (count_scaled == 0)
- return;
-
- AddCount(value, count_scaled);
-}
-
-void HistogramBase::AddKilo(Sample value, int count) {
- AddScaled(value, count, 1000);
-}
-
-void HistogramBase::AddKiB(Sample value, int count) {
- AddScaled(value, count, 1024);
-}
-
-void HistogramBase::AddTimeMillisecondsGranularity(const TimeDelta& time) {
- Add(static_cast<Sample>(time.InMilliseconds()));
-}
-
-void HistogramBase::AddTimeMicrosecondsGranularity(const TimeDelta& time) {
- // Intentionally drop high-resolution reports on clients with low-resolution
- // clocks. High-resolution metrics cannot make use of low-resolution data and
- // reporting it merely adds noise to the metric. https://crbug.com/807615#c16
- if (TimeTicks::IsHighResolution())
- Add(static_cast<Sample>(time.InMicroseconds()));
-}
-
-void HistogramBase::AddBoolean(bool value) {
- Add(value ? 1 : 0);
-}
-
-void HistogramBase::SerializeInfo(Pickle* pickle) const {
- pickle->WriteInt(GetHistogramType());
- SerializeInfoImpl(pickle);
-}
-
-uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const {
- // Not supported by default.
- return NO_INCONSISTENCIES;
-}
-
-void HistogramBase::ValidateHistogramContents() const {}
-
-void HistogramBase::WriteJSON(std::string* output,
- JSONVerbosityLevel verbosity_level) const {
- Count count;
- int64_t sum;
- std::unique_ptr<ListValue> buckets(new ListValue());
- GetCountAndBucketData(&count, &sum, buckets.get());
- std::unique_ptr<DictionaryValue> parameters(new DictionaryValue());
- GetParameters(parameters.get());
-
- JSONStringValueSerializer serializer(output);
- DictionaryValue root;
- root.SetString("name", histogram_name());
- root.SetInteger("count", count);
- root.SetDouble("sum", static_cast<double>(sum));
- root.SetInteger("flags", flags());
- root.Set("params", std::move(parameters));
- if (verbosity_level != JSON_VERBOSITY_LEVEL_OMIT_BUCKETS)
- root.Set("buckets", std::move(buckets));
- root.SetInteger("pid", GetUniqueIdForProcess());
- serializer.Serialize(root);
-}
-
-void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const {
- if ((flags() & kCallbackExists) == 0)
- return;
-
- StatisticsRecorder::OnSampleCallback cb =
- StatisticsRecorder::FindCallback(histogram_name());
- if (!cb.is_null())
- cb.Run(sample);
-}
-
-void HistogramBase::WriteAsciiBucketGraph(double current_size,
- double max_size,
- std::string* output) const {
- const int k_line_length = 72; // Maximal horizontal width of graph.
- int x_count = static_cast<int>(k_line_length * (current_size / max_size)
- + 0.5);
- int x_remainder = k_line_length - x_count;
-
- while (0 < x_count--)
- output->append("-");
- output->append("O");
- while (0 < x_remainder--)
- output->append(" ");
-}
-
-const std::string HistogramBase::GetSimpleAsciiBucketRange(
- Sample sample) const {
- return StringPrintf("%d", sample);
-}
-
-void HistogramBase::WriteAsciiBucketValue(Count current,
- double scaled_sum,
- std::string* output) const {
- StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
-}
-
-// static
-char const* HistogramBase::GetPermanentName(const std::string& name) {
- // A set of histogram names that provides the "permanent" lifetime required
- // by histogram objects for those strings that are not already code constants
- // or held in persistent memory.
- static LazyInstance<std::set<std::string>>::Leaky permanent_names;
- static LazyInstance<Lock>::Leaky permanent_names_lock;
-
- AutoLock lock(permanent_names_lock.Get());
- auto result = permanent_names.Get().insert(name);
- return result.first->c_str();
-}
-
-} // namespace base
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h
deleted file mode 100644
index 010dc55..0000000
--- a/base/metrics/histogram_base.h
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_BASE_H_
-#define BASE_METRICS_HISTOGRAM_BASE_H_
-
-#include <limits.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-
-namespace base {
-
-class DictionaryValue;
-class HistogramBase;
-class HistogramSamples;
-class ListValue;
-class Pickle;
-class PickleIterator;
-
-////////////////////////////////////////////////////////////////////////////////
-// This enum is used to facilitate deserialization of histograms from other
-// processes into the browser. If you create another class that inherits from
-// HistogramBase, add new histogram types and names below.
-
-enum HistogramType {
- HISTOGRAM,
- LINEAR_HISTOGRAM,
- BOOLEAN_HISTOGRAM,
- CUSTOM_HISTOGRAM,
- SPARSE_HISTOGRAM,
- DUMMY_HISTOGRAM,
-};
-
-// Controls the verbosity of the information when the histogram is serialized to
-// a JSON.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.metrics
-enum JSONVerbosityLevel {
- // The histogram is completely serialized.
- JSON_VERBOSITY_LEVEL_FULL,
- // The bucket information is not serialized.
- JSON_VERBOSITY_LEVEL_OMIT_BUCKETS,
-};
-
-std::string HistogramTypeToString(HistogramType type);
-
-// This enum is used for reporting how many histograms and of what types and
-// variations are being created. It has to be in the main .h file so it is
-// visible to files that define the various histogram types.
-enum HistogramReport {
- // Count the number of reports created. The other counts divided by this
- // number will give the average per run of the program.
- HISTOGRAM_REPORT_CREATED = 0,
-
- // Count the total number of histograms created. It is the limit against
- // which all others are compared.
- HISTOGRAM_REPORT_HISTOGRAM_CREATED = 1,
-
- // Count the total number of histograms looked-up. It's better to cache
- // the result of a single lookup rather than do it repeatedly.
- HISTOGRAM_REPORT_HISTOGRAM_LOOKUP = 2,
-
- // These count the individual histogram types. This must follow the order
- // of HistogramType above.
- HISTOGRAM_REPORT_TYPE_LOGARITHMIC = 3,
- HISTOGRAM_REPORT_TYPE_LINEAR = 4,
- HISTOGRAM_REPORT_TYPE_BOOLEAN = 5,
- HISTOGRAM_REPORT_TYPE_CUSTOM = 6,
- HISTOGRAM_REPORT_TYPE_SPARSE = 7,
-
- // These indicate the individual flags that were set.
- HISTOGRAM_REPORT_FLAG_UMA_TARGETED = 8,
- HISTOGRAM_REPORT_FLAG_UMA_STABILITY = 9,
- HISTOGRAM_REPORT_FLAG_PERSISTENT = 10,
-
- // This must be last.
- HISTOGRAM_REPORT_MAX = 11
-};
-
-// Create or find existing histogram that matches the pickled info.
-// Returns NULL if the pickled data has problems.
-BASE_EXPORT HistogramBase* DeserializeHistogramInfo(base::PickleIterator* iter);
-
-////////////////////////////////////////////////////////////////////////////////
-
-class BASE_EXPORT HistogramBase {
- public:
- typedef int32_t Sample; // Used for samples.
- typedef subtle::Atomic32 AtomicCount; // Used to count samples.
- typedef int32_t Count; // Used to manipulate counts in temporaries.
-
- static const Sample kSampleType_MAX; // INT_MAX
-
- enum Flags {
- kNoFlags = 0x0,
-
- // Histogram should be UMA uploaded.
- kUmaTargetedHistogramFlag = 0x1,
-
- // Indicates that this is a stability histogram. This flag exists to specify
- // which histograms should be included in the initial stability log. Please
- // refer to |MetricsService::PrepareInitialStabilityLog|.
- kUmaStabilityHistogramFlag = kUmaTargetedHistogramFlag | 0x2,
-
- // Indicates that the histogram was pickled to be sent across an IPC
- // Channel. If we observe this flag on a histogram being aggregated into
- // after IPC, then we are running in a single process mode, and the
- // aggregation should not take place (as we would be aggregating back into
- // the source histogram!).
- kIPCSerializationSourceFlag = 0x10,
-
- // Indicates that a callback exists for when a new sample is recorded on
- // this histogram. We store this as a flag with the histogram since
- // histograms can be in performance critical code, and this allows us
- // to shortcut looking up the callback if it doesn't exist.
- kCallbackExists = 0x20,
-
- // Indicates that the histogram is held in "persistent" memory and may
- // be accessible between processes. This is only possible if such a
- // memory segment has been created/attached, used to create a Persistent-
- // MemoryAllocator, and that loaded into the Histogram module before this
- // histogram is created.
- kIsPersistent = 0x40,
- };
-
- // Histogram data inconsistency types.
- enum Inconsistency : uint32_t {
- NO_INCONSISTENCIES = 0x0,
- RANGE_CHECKSUM_ERROR = 0x1,
- BUCKET_ORDER_ERROR = 0x2,
- COUNT_HIGH_ERROR = 0x4,
- COUNT_LOW_ERROR = 0x8,
-
- NEVER_EXCEEDED_VALUE = 0x10,
- };
-
- // Construct the base histogram. The name is not copied; it's up to the
- // caller to ensure that it lives at least as long as this object.
- explicit HistogramBase(const char* name);
- virtual ~HistogramBase();
-
- const char* histogram_name() const { return histogram_name_; }
-
- // Compares |name| to the histogram name and triggers a DCHECK if they do not
- // match. This is a helper function used by histogram macros, which results in
- // in more compact machine code being generated by the macros.
- virtual void CheckName(const StringPiece& name) const;
-
- // Get a unique ID for this histogram's samples.
- virtual uint64_t name_hash() const = 0;
-
- // Operations with Flags enum.
- int32_t flags() const { return subtle::NoBarrier_Load(&flags_); }
- void SetFlags(int32_t flags);
- void ClearFlags(int32_t flags);
-
- virtual HistogramType GetHistogramType() const = 0;
-
- // Whether the histogram has construction arguments as parameters specified.
- // For histograms that don't have the concept of minimum, maximum or
- // bucket_count, this function always returns false.
- virtual bool HasConstructionArguments(
- Sample expected_minimum,
- Sample expected_maximum,
- uint32_t expected_bucket_count) const = 0;
-
- virtual void Add(Sample value) = 0;
-
- // In Add function the |value| bucket is increased by one, but in some use
- // cases we need to increase this value by an arbitrary integer. AddCount
- // function increases the |value| bucket by |count|. |count| should be greater
- // than or equal to 1.
- virtual void AddCount(Sample value, int count) = 0;
-
- // Similar to above but divides |count| by the |scale| amount. Probabilistic
- // rounding is used to yield a reasonably accurate total when many samples
- // are added. Methods for common cases of scales 1000 and 1024 are included.
- void AddScaled(Sample value, int count, int scale);
- void AddKilo(Sample value, int count); // scale=1000
- void AddKiB(Sample value, int count); // scale=1024
-
- // Convenient functions that call Add(Sample).
- void AddTime(const TimeDelta& time) { AddTimeMillisecondsGranularity(time); }
- void AddTimeMillisecondsGranularity(const TimeDelta& time);
- // Note: AddTimeMicrosecondsGranularity() drops the report if this client
- // doesn't have a high-resolution clock.
- void AddTimeMicrosecondsGranularity(const TimeDelta& time);
- void AddBoolean(bool value);
-
- virtual void AddSamples(const HistogramSamples& samples) = 0;
- virtual bool AddSamplesFromPickle(base::PickleIterator* iter) = 0;
-
- // Serialize the histogram info into |pickle|.
- // Note: This only serializes the construction arguments of the histogram, but
- // does not serialize the samples.
- void SerializeInfo(base::Pickle* pickle) const;
-
- // Try to find out data corruption from histogram and the samples.
- // The returned value is a combination of Inconsistency enum.
- virtual uint32_t FindCorruption(const HistogramSamples& samples) const;
-
- // Snapshot the current complete set of sample data.
- // Override with atomic/locked snapshot if needed.
- // NOTE: this data can overflow for long-running sessions. It should be
- // handled with care and this method is recommended to be used only
- // in about:histograms and test code.
- virtual std::unique_ptr<HistogramSamples> SnapshotSamples() const = 0;
-
- // Calculate the change (delta) in histogram counts since the previous call
- // to this method. Each successive call will return only those counts
- // changed since the last call.
- virtual std::unique_ptr<HistogramSamples> SnapshotDelta() = 0;
-
- // Calculate the change (delta) in histogram counts since the previous call
- // to SnapshotDelta() but do so without modifying any internal data as to
- // what was previous logged. After such a call, no further calls to this
- // method or to SnapshotDelta() should be done as the result would include
- // data previously returned. Because no internal data is changed, this call
- // can be made on "const" histograms such as those with data held in
- // read-only memory.
- virtual std::unique_ptr<HistogramSamples> SnapshotFinalDelta() const = 0;
-
- // The following methods provide graphical histogram displays.
- virtual void WriteHTMLGraph(std::string* output) const = 0;
- virtual void WriteAscii(std::string* output) const = 0;
-
- // TODO(bcwhite): Remove this after https://crbug/836875.
- virtual void ValidateHistogramContents() const;
-
- // Produce a JSON representation of the histogram with |verbosity_level| as
- // the serialization verbosity. This is implemented with the help of
- // GetParameters and GetCountAndBucketData; overwrite them to customize the
- // output.
- void WriteJSON(std::string* output, JSONVerbosityLevel verbosity_level) const;
-
- protected:
- enum ReportActivity { HISTOGRAM_CREATED, HISTOGRAM_LOOKUP };
-
- // Subclasses should implement this function to make SerializeInfo work.
- virtual void SerializeInfoImpl(base::Pickle* pickle) const = 0;
-
- // Writes information about the construction parameters in |params|.
- virtual void GetParameters(DictionaryValue* params) const = 0;
-
- // Writes information about the current (non-empty) buckets and their sample
- // counts to |buckets|, the total sample count to |count| and the total sum
- // to |sum|.
- virtual void GetCountAndBucketData(Count* count,
- int64_t* sum,
- ListValue* buckets) const = 0;
-
- //// Produce actual graph (set of blank vs non blank char's) for a bucket.
- void WriteAsciiBucketGraph(double current_size,
- double max_size,
- std::string* output) const;
-
- // Return a string description of what goes in a given bucket.
- const std::string GetSimpleAsciiBucketRange(Sample sample) const;
-
- // Write textual description of the bucket contents (relative to histogram).
- // Output is the count in the buckets, as well as the percentage.
- void WriteAsciiBucketValue(Count current,
- double scaled_sum,
- std::string* output) const;
-
- // Retrieves the callback for this histogram, if one exists, and runs it
- // passing |sample| as the parameter.
- void FindAndRunCallback(Sample sample) const;
-
- // Gets a permanent string that can be used for histogram objects when the
- // original is not a code constant or held in persistent memory.
- static const char* GetPermanentName(const std::string& name);
-
- private:
- friend class HistogramBaseTest;
-
- // A pointer to permanent storage where the histogram name is held. This can
- // be code space or the output of GetPermanentName() or any other storage
- // that is known to never change. This is not StringPiece because (a) char*
- // is 1/2 the size and (b) StringPiece transparently casts from std::string
- // which can easily lead to a pointer to non-permanent space.
- // For persistent histograms, this will simply point into the persistent
- // memory segment, thus avoiding duplication. For heap histograms, the
- // GetPermanentName method will create the necessary copy.
- const char* const histogram_name_;
-
- // Additional information about the histogram.
- AtomicCount flags_;
-
- DISALLOW_COPY_AND_ASSIGN(HistogramBase);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_BASE_H_
diff --git a/base/metrics/histogram_delta_serialization.cc b/base/metrics/histogram_delta_serialization.cc
deleted file mode 100644
index a74b87f..0000000
--- a/base/metrics/histogram_delta_serialization.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/histogram_delta_serialization.h"
-
-#include "base/logging.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_snapshot_manager.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/pickle.h"
-#include "base/values.h"
-
-namespace base {
-
-namespace {
-
-// Create or find existing histogram and add the samples from pickle.
-// Silently returns when seeing any data problem in the pickle.
-void DeserializeHistogramAndAddSamples(PickleIterator* iter) {
- HistogramBase* histogram = DeserializeHistogramInfo(iter);
- if (!histogram)
- return;
-
- if (histogram->flags() & HistogramBase::kIPCSerializationSourceFlag) {
- DVLOG(1) << "Single process mode, histogram observed and not copied: "
- << histogram->histogram_name();
- return;
- }
- histogram->AddSamplesFromPickle(iter);
-}
-
-} // namespace
-
-HistogramDeltaSerialization::HistogramDeltaSerialization(
- const std::string& caller_name)
- : histogram_snapshot_manager_(this), serialized_deltas_(nullptr) {}
-
-HistogramDeltaSerialization::~HistogramDeltaSerialization() = default;
-
-void HistogramDeltaSerialization::PrepareAndSerializeDeltas(
- std::vector<std::string>* serialized_deltas,
- bool include_persistent) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- serialized_deltas_ = serialized_deltas;
- // Note: Before serializing, we set the kIPCSerializationSourceFlag for all
- // the histograms, so that the receiving process can distinguish them from the
- // local histograms.
- StatisticsRecorder::PrepareDeltas(
- include_persistent, Histogram::kIPCSerializationSourceFlag,
- Histogram::kNoFlags, &histogram_snapshot_manager_);
- serialized_deltas_ = nullptr;
-}
-
-// static
-void HistogramDeltaSerialization::DeserializeAndAddSamples(
- const std::vector<std::string>& serialized_deltas) {
- for (std::vector<std::string>::const_iterator it = serialized_deltas.begin();
- it != serialized_deltas.end(); ++it) {
- Pickle pickle(it->data(), checked_cast<int>(it->size()));
- PickleIterator iter(pickle);
- DeserializeHistogramAndAddSamples(&iter);
- }
-}
-
-void HistogramDeltaSerialization::RecordDelta(
- const HistogramBase& histogram,
- const HistogramSamples& snapshot) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_NE(0, snapshot.TotalCount());
-
- Pickle pickle;
- histogram.SerializeInfo(&pickle);
- snapshot.Serialize(&pickle);
- serialized_deltas_->push_back(
- std::string(static_cast<const char*>(pickle.data()), pickle.size()));
-}
-
-} // namespace base
diff --git a/base/metrics/histogram_delta_serialization.h b/base/metrics/histogram_delta_serialization.h
deleted file mode 100644
index 57ebd2c..0000000
--- a/base/metrics/histogram_delta_serialization.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
-#define BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_flattener.h"
-#include "base/metrics/histogram_snapshot_manager.h"
-#include "base/threading/thread_checker.h"
-
-namespace base {
-
-class HistogramBase;
-
-// Serializes and restores histograms deltas.
-class BASE_EXPORT HistogramDeltaSerialization : public HistogramFlattener {
- public:
- // |caller_name| is string used in histograms for counting inconsistencies.
- explicit HistogramDeltaSerialization(const std::string& caller_name);
- ~HistogramDeltaSerialization() override;
-
- // Computes deltas in histogram bucket counts relative to the previous call to
- // this method. Stores the deltas in serialized form into |serialized_deltas|.
- // If |serialized_deltas| is null, no data is serialized, though the next call
- // will compute the deltas relative to this one. Setting |include_persistent|
- // will include histograms held in persistent memory (and thus may be reported
- // elsewhere); otherwise only histograms local to this process are serialized.
- void PrepareAndSerializeDeltas(std::vector<std::string>* serialized_deltas,
- bool include_persistent);
-
- // Deserialize deltas and add samples to corresponding histograms, creating
- // them if necessary. Silently ignores errors in |serialized_deltas|.
- static void DeserializeAndAddSamples(
- const std::vector<std::string>& serialized_deltas);
-
- private:
- // HistogramFlattener implementation.
- void RecordDelta(const HistogramBase& histogram,
- const HistogramSamples& snapshot) override;
-
- ThreadChecker thread_checker_;
-
- // Calculates deltas in histogram counters.
- HistogramSnapshotManager histogram_snapshot_manager_;
-
- // Output buffer for serialized deltas.
- std::vector<std::string>* serialized_deltas_;
-
- DISALLOW_COPY_AND_ASSIGN(HistogramDeltaSerialization);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
diff --git a/base/metrics/histogram_flattener.h b/base/metrics/histogram_flattener.h
deleted file mode 100644
index 6a5e3f4..0000000
--- a/base/metrics/histogram_flattener.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_FLATTENER_H_
-#define BASE_METRICS_HISTOGRAM_FLATTENER_H_
-
-#include <map>
-#include <string>
-
-#include "base/macros.h"
-#include "base/metrics/histogram.h"
-
-namespace base {
-
-class HistogramSamples;
-
-// HistogramFlattener is an interface used by HistogramSnapshotManager, which
-// handles the logistics of gathering up available histograms for recording.
-class BASE_EXPORT HistogramFlattener {
- public:
- virtual void RecordDelta(const HistogramBase& histogram,
- const HistogramSamples& snapshot) = 0;
-
- protected:
- HistogramFlattener() = default;
- virtual ~HistogramFlattener() = default;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HistogramFlattener);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_FLATTENER_H_
diff --git a/base/metrics/histogram_functions.cc b/base/metrics/histogram_functions.cc
deleted file mode 100644
index 31bf219..0000000
--- a/base/metrics/histogram_functions.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/histogram_functions.h"
-
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/time/time.h"
-
-namespace base {
-
-void UmaHistogramBoolean(const std::string& name, bool sample) {
- HistogramBase* histogram = BooleanHistogram::FactoryGet(
- name, HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(sample);
-}
-
-void UmaHistogramExactLinear(const std::string& name,
- int sample,
- int value_max) {
- HistogramBase* histogram =
- LinearHistogram::FactoryGet(name, 1, value_max, value_max + 1,
- HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(sample);
-}
-
-void UmaHistogramPercentage(const std::string& name, int percent) {
- UmaHistogramExactLinear(name, percent, 100);
-}
-
-void UmaHistogramCustomCounts(const std::string& name,
- int sample,
- int min,
- int max,
- int buckets) {
- HistogramBase* histogram = Histogram::FactoryGet(
- name, min, max, buckets, HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(sample);
-}
-
-void UmaHistogramCounts100(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 100, 50);
-}
-
-void UmaHistogramCounts1000(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 1000, 50);
-}
-
-void UmaHistogramCounts10000(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 10000, 50);
-}
-
-void UmaHistogramCounts100000(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 100000, 50);
-}
-
-void UmaHistogramCounts1M(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 1000000, 50);
-}
-
-void UmaHistogramCounts10M(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 10000000, 50);
-}
-
-void UmaHistogramCustomTimes(const std::string& name,
- TimeDelta sample,
- TimeDelta min,
- TimeDelta max,
- int buckets) {
- HistogramBase* histogram = Histogram::FactoryTimeGet(
- name, min, max, buckets, HistogramBase::kUmaTargetedHistogramFlag);
- histogram->AddTimeMillisecondsGranularity(sample);
-}
-
-void UmaHistogramTimes(const std::string& name, TimeDelta sample) {
- UmaHistogramCustomTimes(name, sample, TimeDelta::FromMilliseconds(1),
- TimeDelta::FromSeconds(10), 50);
-}
-
-void UmaHistogramMediumTimes(const std::string& name, TimeDelta sample) {
- UmaHistogramCustomTimes(name, sample, TimeDelta::FromMilliseconds(1),
- TimeDelta::FromMinutes(3), 50);
-}
-
-void UmaHistogramLongTimes(const std::string& name, TimeDelta sample) {
- UmaHistogramCustomTimes(name, sample, TimeDelta::FromMilliseconds(1),
- TimeDelta::FromHours(1), 50);
-}
-
-void UmaHistogramMemoryKB(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1000, 500000, 50);
-}
-
-void UmaHistogramMemoryMB(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 1000, 50);
-}
-
-void UmaHistogramMemoryLargeMB(const std::string& name, int sample) {
- UmaHistogramCustomCounts(name, sample, 1, 64000, 100);
-}
-
-void UmaHistogramSparse(const std::string& name, int sample) {
- HistogramBase* histogram = SparseHistogram::FactoryGet(
- name, HistogramBase::kUmaTargetedHistogramFlag);
- histogram->Add(sample);
-}
-
-} // namespace base
diff --git a/base/metrics/histogram_functions.h b/base/metrics/histogram_functions.h
deleted file mode 100644
index 60c0057..0000000
--- a/base/metrics/histogram_functions.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_FUNCTIONS_H_
-#define BASE_METRICS_HISTOGRAM_FUNCTIONS_H_
-
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_base.h"
-#include "base/time/time.h"
-
-// Functions for recording metrics.
-//
-// For best practices on deciding when to emit to a histogram and what form
-// the histogram should take, see
-// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md
-
-// Functions for recording UMA histograms. These can be used for cases
-// when the histogram name is generated at runtime. The functionality is
-// equivalent to macros defined in histogram_macros.h but allowing non-constant
-// histogram names. These functions are slower compared to their macro
-// equivalent because the histogram objects are not cached between calls.
-// So, these shouldn't be used in performance critical code.
-namespace base {
-
-// For histograms with linear buckets.
-// Used for capturing integer data with a linear bucketing scheme. This can be
-// used when you want the exact value of some small numeric count, with a max of
-// 100 or less. If you need to capture a range of greater than 100, we recommend
-// the use of the COUNT histograms below.
-// Sample usage:
-// base::UmaHistogramExactLinear("Histogram.Linear", some_value, 10);
-BASE_EXPORT void UmaHistogramExactLinear(const std::string& name,
- int sample,
- int value_max);
-
-// For adding a sample to an enumerated histogram.
-// Sample usage:
-// // These values are persisted to logs. Entries should not be renumbered and
-// // numeric values should never be reused.
-// enum class MyEnum {
-// FIRST_VALUE = 0,
-// SECOND_VALUE = 1,
-// ...
-// FINAL_VALUE = N,
-// COUNT
-// };
-// base::UmaHistogramEnumeration("My.Enumeration",
-// MyEnum::SOME_VALUE, MyEnum::COUNT);
-//
-// Note: The value in |sample| must be strictly less than |enum_size|.
-template <typename T>
-void UmaHistogramEnumeration(const std::string& name, T sample, T enum_size) {
- static_assert(std::is_enum<T>::value,
- "Non enum passed to UmaHistogramEnumeration");
- DCHECK_LE(static_cast<uintmax_t>(enum_size), static_cast<uintmax_t>(INT_MAX));
- DCHECK_LT(static_cast<uintmax_t>(sample), static_cast<uintmax_t>(enum_size));
- return UmaHistogramExactLinear(name, static_cast<int>(sample),
- static_cast<int>(enum_size));
-}
-
-// Same as above, but uses T::kMaxValue as the inclusive maximum value of the
-// enum.
-template <typename T>
-void UmaHistogramEnumeration(const std::string& name, T sample) {
- static_assert(std::is_enum<T>::value,
- "Non enum passed to UmaHistogramEnumeration");
- DCHECK_LE(static_cast<uintmax_t>(T::kMaxValue),
- static_cast<uintmax_t>(INT_MAX) - 1);
- DCHECK_LE(static_cast<uintmax_t>(sample),
- static_cast<uintmax_t>(T::kMaxValue));
- return UmaHistogramExactLinear(name, static_cast<int>(sample),
- static_cast<int>(T::kMaxValue) + 1);
-}
-
-// For adding boolean sample to histogram.
-// Sample usage:
-// base::UmaHistogramBoolean("My.Boolean", true)
-BASE_EXPORT void UmaHistogramBoolean(const std::string& name, bool sample);
-
-// For adding histogram with percent.
-// Percents are integer between 1 and 100.
-// Sample usage:
-// base::UmaHistogramPercentage("My.Percent", 69)
-BASE_EXPORT void UmaHistogramPercentage(const std::string& name, int percent);
-
-// For adding counts histogram.
-// Sample usage:
-// base::UmaHistogramCustomCounts("My.Counts", some_value, 1, 600, 30)
-BASE_EXPORT void UmaHistogramCustomCounts(const std::string& name,
- int sample,
- int min,
- int max,
- int buckets);
-
-// Counts specialization for maximum counts 100, 1000, 10k, 100k, 1M and 10M.
-BASE_EXPORT void UmaHistogramCounts100(const std::string& name, int sample);
-BASE_EXPORT void UmaHistogramCounts1000(const std::string& name, int sample);
-BASE_EXPORT void UmaHistogramCounts10000(const std::string& name, int sample);
-BASE_EXPORT void UmaHistogramCounts100000(const std::string& name, int sample);
-BASE_EXPORT void UmaHistogramCounts1M(const std::string& name, int sample);
-BASE_EXPORT void UmaHistogramCounts10M(const std::string& name, int sample);
-
-// For histograms storing times.
-BASE_EXPORT void UmaHistogramCustomTimes(const std::string& name,
- TimeDelta sample,
- TimeDelta min,
- TimeDelta max,
- int buckets);
-// For short timings from 1 ms up to 10 seconds (50 buckets).
-BASE_EXPORT void UmaHistogramTimes(const std::string& name, TimeDelta sample);
-// For medium timings up to 3 minutes (50 buckets).
-BASE_EXPORT void UmaHistogramMediumTimes(const std::string& name,
- TimeDelta sample);
-// For time intervals up to 1 hr (50 buckets).
-BASE_EXPORT void UmaHistogramLongTimes(const std::string& name,
- TimeDelta sample);
-
-// For recording memory related histograms.
-// Used to measure common KB-granularity memory stats. Range is up to 500M.
-BASE_EXPORT void UmaHistogramMemoryKB(const std::string& name, int sample);
-// Used to measure common MB-granularity memory stats. Range is up to ~1G.
-BASE_EXPORT void UmaHistogramMemoryMB(const std::string& name, int sample);
-// Used to measure common MB-granularity memory stats. Range is up to ~64G.
-BASE_EXPORT void UmaHistogramMemoryLargeMB(const std::string& name, int sample);
-
-// For recording sparse histograms.
-// The |sample| can be a negative or non-negative number.
-//
-// Sparse histograms are well suited for recording counts of exact sample values
-// that are sparsely distributed over a relatively large range, in cases where
-// ultra-fast performance is not critical. For instance, Sqlite.Version.* are
-// sparse because for any given database, there's going to be exactly one
-// version logged.
-//
-// Performance:
-// ------------
-// Sparse histograms are typically more memory-efficient but less time-efficient
-// than other histograms. Essentially, they sparse histograms use a map rather
-// than a vector for their backing storage; they also require lock acquisition
-// to increment a sample, whereas other histogram do not. Hence, each increment
-// operation is a bit slower than for other histograms. But, if the data is
-// sparse, then they use less memory client-side, because they allocate buckets
-// on demand rather than preallocating.
-//
-// Data size:
-// ----------
-// Note that server-side, we still need to load all buckets, across all users,
-// at once. Thus, please avoid exploding such histograms, i.e. uploading many
-// many distinct values to the server (across all users). Concretely, keep the
-// number of distinct values <= 100 ideally, definitely <= 1000. If you have no
-// guarantees on the range of your data, use clamping, e.g.:
-// UmaHistogramSparse("MyHistogram", ClampToRange(value, 0, 200));
-BASE_EXPORT void UmaHistogramSparse(const std::string& name, int sample);
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_FUNCTIONS_H_
diff --git a/base/metrics/histogram_macros.h b/base/metrics/histogram_macros.h
deleted file mode 100644
index 0960b19..0000000
--- a/base/metrics/histogram_macros.h
+++ /dev/null
@@ -1,359 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_
-#define BASE_METRICS_HISTOGRAM_MACROS_H_
-
-#include "base/macros.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_macros_internal.h"
-#include "base/metrics/histogram_macros_local.h"
-#include "base/time/time.h"
-
-
-// Macros for efficient use of histograms.
-//
-// For best practices on deciding when to emit to a histogram and what form
-// the histogram should take, see
-// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md
-
-// TODO(rkaplow): Link to proper documentation on metric creation once we have
-// it in a good state.
-
-// All of these macros must be called with |name| as a runtime constant - it
-// doesn't have to literally be a constant, but it must be the same string on
-// all calls from a particular call site. If this rule is violated, it is
-// possible the data will be written to the wrong histogram.
-
-//------------------------------------------------------------------------------
-// Enumeration histograms.
-
-// These macros create histograms for enumerated data. Ideally, the data should
-// be of the form of "event occurs, log the result". We recommended not putting
-// related but not directly connected data as enums within the same histogram.
-// You should be defining an associated Enum, and the input sample should be
-// an element of the Enum.
-// All of these macros must be called with |name| as a runtime constant.
-
-// The first variant of UMA_HISTOGRAM_ENUMERATION accepts two arguments: the
-// histogram name and the enum sample. It deduces the correct boundary value to
-// use by looking for an enumerator with the name kMaxValue. kMaxValue should
-// share the value of the highest enumerator: this avoids switch statements
-// having to handle a sentinel no-op value.
-//
-// Sample usage:
-// // These values are persisted to logs. Entries should not be renumbered and
-// // numeric values should never be reused.
-// enum class MyEnum {
-// kFirstValue = 0,
-// kSecondValue = 1,
-// ...
-// kFinalValue = N,
-// kMaxValue = kFinalValue,
-// };
-// UMA_HISTOGRAM_ENUMERATION("My.Enumeration", MyEnum::kSomeValue);
-//
-// The second variant requires three arguments: the first two are the same as
-// before, and the third argument is the enum boundary: this must be strictly
-// greater than any other enumerator that will be sampled.
-//
-// Sample usage:
-// // These values are persisted to logs. Entries should not be renumbered and
-// // numeric values should never be reused.
-// enum class MyEnum {
-// FIRST_VALUE = 0,
-// SECOND_VALUE = 1,
-// ...
-// FINAL_VALUE = N,
-// COUNT
-// };
-// UMA_HISTOGRAM_ENUMERATION("My.Enumeration",
-// MyEnum::SOME_VALUE, MyEnum::COUNT);
-//
-// Note: If the enum is used in a switch, it is often desirable to avoid writing
-// a case statement to handle an unused sentinel value (i.e. COUNT in the above
-// example). For scoped enums, this is awkward since it requires casting the
-// enum to an arithmetic type and adding one. Instead, prefer the two argument
-// version of the macro which automatically deduces the boundary from kMaxValue.
-#define UMA_HISTOGRAM_ENUMERATION(name, ...) \
- CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \
- __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \
- INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \
- name, __VA_ARGS__, base::HistogramBase::kUmaTargetedHistogramFlag))
-
-// Histogram for boolean values.
-
-// Sample usage:
-// UMA_HISTOGRAM_BOOLEAN("Histogram.Boolean", bool);
-#define UMA_HISTOGRAM_BOOLEAN(name, sample) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \
- base::BooleanHistogram::FactoryGet(name, \
- base::HistogramBase::kUmaTargetedHistogramFlag))
-
-//------------------------------------------------------------------------------
-// Linear histograms.
-
-// All of these macros must be called with |name| as a runtime constant.
-
-// Used for capturing integer data with a linear bucketing scheme. This can be
-// used when you want the exact value of some small numeric count, with a max of
-// 100 or less. If you need to capture a range of greater than 100, we recommend
-// the use of the COUNT histograms below.
-
-// Sample usage:
-// UMA_HISTOGRAM_EXACT_LINEAR("Histogram.Linear", count, 10);
-#define UMA_HISTOGRAM_EXACT_LINEAR(name, sample, value_max) \
- INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG( \
- name, sample, value_max, base::HistogramBase::kUmaTargetedHistogramFlag)
-
-// Used for capturing basic percentages. This will be 100 buckets of size 1.
-
-// Sample usage:
-// UMA_HISTOGRAM_PERCENTAGE("Histogram.Percent", percent_as_int);
-#define UMA_HISTOGRAM_PERCENTAGE(name, percent_as_int) \
- UMA_HISTOGRAM_EXACT_LINEAR(name, percent_as_int, 101)
-
-//------------------------------------------------------------------------------
-// Count histograms. These are used for collecting numeric data. Note that we
-// have macros for more specialized use cases below (memory, time, percentages).
-
-// The number suffixes here refer to the max size of the sample, i.e. COUNT_1000
-// will be able to collect samples of counts up to 1000. The default number of
-// buckets in all default macros is 50. We recommend erring on the side of too
-// large a range versus too short a range.
-// These macros default to exponential histograms - i.e. the lengths of the
-// bucket ranges exponentially increase as the sample range increases.
-// These should *not* be used if you are interested in exact counts, i.e. a
-// bucket range of 1. In these cases, you should use the ENUMERATION macros
-// defined later. These should also not be used to capture the number of some
-// event, i.e. "button X was clicked N times". In this cases, an enum should be
-// used, ideally with an appropriate baseline enum entry included.
-// All of these macros must be called with |name| as a runtime constant.
-
-// Sample usage:
-// UMA_HISTOGRAM_COUNTS_1M("My.Histogram", sample);
-
-#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 100, 50)
-
-#define UMA_HISTOGRAM_COUNTS_1000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 1000, 50)
-
-#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 10000, 50)
-
-#define UMA_HISTOGRAM_COUNTS_100000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 100000, 50)
-
-#define UMA_HISTOGRAM_COUNTS_1M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 1000000, 50)
-
-#define UMA_HISTOGRAM_COUNTS_10M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 10000000, 50)
-
-// This can be used when the default ranges are not sufficient. This macro lets
-// the metric developer customize the min and max of the sampled range, as well
-// as the number of buckets recorded.
-// Any data outside the range here will be put in underflow and overflow
-// buckets. Min values should be >=1 as emitted 0s will still go into the
-// underflow bucket.
-
-// Sample usage:
-// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000, 100);
-#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
- INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \
- name, sample, min, max, bucket_count, \
- base::HistogramBase::kUmaTargetedHistogramFlag)
-
-//------------------------------------------------------------------------------
-// Timing histograms. These are used for collecting timing data (generally
-// latencies).
-
-// These macros create exponentially sized histograms (lengths of the bucket
-// ranges exponentially increase as the sample range increases). The input
-// sample is a base::TimeDelta. The output data is measured in ms granularity.
-// All of these macros must be called with |name| as a runtime constant.
-
-// Sample usage:
-// UMA_HISTOGRAM_TIMES("My.Timing.Histogram", time_delta);
-
-// Short timings - up to 10 seconds. For high-resolution (microseconds) timings,
-// see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES.
-#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
- base::TimeDelta::FromSeconds(10), 50)
-
-// Medium timings - up to 3 minutes. Note this starts at 10ms (no good reason,
-// but not worth changing).
-#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(10), \
- base::TimeDelta::FromMinutes(3), 50)
-
-// Long timings - up to an hour.
-#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
- base::TimeDelta::FromHours(1), 50)
-
-// Long timings with higher granularity - up to an hour with 100 buckets.
-#define UMA_HISTOGRAM_LONG_TIMES_100(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
- base::TimeDelta::FromHours(1), 100)
-
-// This can be used when the default ranges are not sufficient. This macro lets
-// the metric developer customize the min and max of the sampled range, as well
-// as the number of buckets recorded.
-
-// Sample usage:
-// UMA_HISTOGRAM_CUSTOM_TIMES("Very.Long.Timing.Histogram", time_delta,
-// base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(1), 100);
-#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
- STATIC_HISTOGRAM_POINTER_BLOCK( \
- name, AddTimeMillisecondsGranularity(sample), \
- base::Histogram::FactoryTimeGet( \
- name, min, max, bucket_count, \
- base::HistogramBase::kUmaTargetedHistogramFlag))
-
-// Same as UMA_HISTOGRAM_CUSTOM_TIMES but reports |sample| in microseconds,
-// dropping the report if this client doesn't have a high-resolution clock.
-//
-// Note: dropping reports on clients with low-resolution clocks means these
-// reports will be biased to a portion of the population on Windows. See
-// Windows.HasHighResolutionTimeTicks for the affected sample.
-//
-// Sample usage:
-// UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
-// "High.Resolution.TimingMicroseconds.Histogram", time_delta,
-// base::TimeDelta::FromMicroseconds(1),
-// base::TimeDelta::FromMilliseconds(10), 100);
-#define UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(name, sample, min, max, \
- bucket_count) \
- STATIC_HISTOGRAM_POINTER_BLOCK( \
- name, AddTimeMicrosecondsGranularity(sample), \
- base::Histogram::FactoryMicrosecondsTimeGet( \
- name, min, max, bucket_count, \
- base::HistogramBase::kUmaTargetedHistogramFlag))
-
-// Scoped class which logs its time on this earth as a UMA statistic. This is
-// recommended for when you want a histogram which measures the time it takes
-// for a method to execute. This measures up to 10 seconds. It uses
-// UMA_HISTOGRAM_TIMES under the hood.
-
-// Sample usage:
-// void Function() {
-// SCOPED_UMA_HISTOGRAM_TIMER("Component.FunctionTime");
-// ...
-// }
-#define SCOPED_UMA_HISTOGRAM_TIMER(name) \
- INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, false, __COUNTER__)
-
-// Similar scoped histogram timer, but this uses UMA_HISTOGRAM_LONG_TIMES_100,
-// which measures up to an hour, and uses 100 buckets. This is more expensive
-// to store, so only use if this often takes >10 seconds.
-#define SCOPED_UMA_HISTOGRAM_LONG_TIMER(name) \
- INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, true, __COUNTER__)
-
-
-//------------------------------------------------------------------------------
-// Memory histograms.
-
-// These macros create exponentially sized histograms (lengths of the bucket
-// ranges exponentially increase as the sample range increases). The input
-// sample must be a number measured in kilobytes.
-// All of these macros must be called with |name| as a runtime constant.
-
-// Sample usage:
-// UMA_HISTOGRAM_MEMORY_KB("My.Memory.Histogram", memory_in_kb);
-
-// Used to measure common KB-granularity memory stats. Range is up to 500000KB -
-// approximately 500M.
-#define UMA_HISTOGRAM_MEMORY_KB(name, sample) \
- UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1000, 500000, 50)
-
-// Used to measure common MB-granularity memory stats. Range is up to ~64G.
-#define UMA_HISTOGRAM_MEMORY_LARGE_MB(name, sample) \
- UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 64000, 100)
-
-
-//------------------------------------------------------------------------------
-// Stability-specific histograms.
-
-// Histograms logged in as stability histograms will be included in the initial
-// stability log. See comments by declaration of
-// MetricsService::PrepareInitialStabilityLog().
-// All of these macros must be called with |name| as a runtime constant.
-
-// For details on usage, see the documentation on the non-stability equivalents.
-
-#define UMA_STABILITY_HISTOGRAM_COUNTS_100(name, sample) \
- UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50)
-
-#define UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, \
- bucket_count) \
- INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \
- name, sample, min, max, bucket_count, \
- base::HistogramBase::kUmaStabilityHistogramFlag)
-
-#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, enum_max) \
- INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \
- name, sample, enum_max, \
- base::HistogramBase::kUmaStabilityHistogramFlag)
-
-//------------------------------------------------------------------------------
-// Histogram instantiation helpers.
-
-// Support a collection of histograms, perhaps one for each entry in an
-// enumeration. This macro manages a block of pointers, adding to a specific
-// one by its index.
-//
-// A typical instantiation looks something like this:
-// STATIC_HISTOGRAM_POINTER_GROUP(
-// GetHistogramNameForIndex(histogram_index),
-// histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta),
-// base::Histogram::FactoryGet(
-// GetHistogramNameForIndex(histogram_index),
-// MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
-// base::HistogramBase::kUmaTargetedHistogramFlag));
-//
-// Though it seems inefficient to generate the name twice, the first
-// instance will be used only for DCHECK builds and the second will
-// execute only during the first access to the given index, after which
-// the pointer is cached and the name never needed again.
-#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index, \
- constant_maximum, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation) \
- do { \
- static base::subtle::AtomicWord atomic_histograms[constant_maximum]; \
- DCHECK_LE(0, index); \
- DCHECK_LT(index, constant_maximum); \
- HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation); \
- } while (0)
-
-//------------------------------------------------------------------------------
-// Deprecated histogram macros. Not recommended for current use.
-
-// Legacy name for UMA_HISTOGRAM_COUNTS_1M. Suggest using explicit naming
-// and not using this macro going forward.
-#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1, 1000000, 50)
-
-// MB-granularity memory metric. This has a short max (1G).
-#define UMA_HISTOGRAM_MEMORY_MB(name, sample) \
- UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000, 50)
-
-// For an enum with customized range. In general, sparse histograms should be
-// used instead.
-// Samples should be one of the std::vector<int> list provided via
-// |custom_ranges|. See comments above CustomRanges::FactoryGet about the
-// requirement of |custom_ranges|. You can use the helper function
-// CustomHistogram::ArrayToCustomEnumRanges to transform a C-style array of
-// valid sample values to a std::vector<int>.
-#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
- base::CustomHistogram::FactoryGet(name, custom_ranges, \
- base::HistogramBase::kUmaTargetedHistogramFlag))
-
-#endif // BASE_METRICS_HISTOGRAM_MACROS_H_
diff --git a/base/metrics/histogram_macros_internal.h b/base/metrics/histogram_macros_internal.h
deleted file mode 100644
index ff3702b..0000000
--- a/base/metrics/histogram_macros_internal.h
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_
-#define BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_
-
-#include <stdint.h>
-
-#include <limits>
-#include <type_traits>
-
-#include "base/atomicops.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/time/time.h"
-
-// This is for macros and helpers internal to base/metrics. They should not be
-// used outside of this directory. For writing to UMA histograms, see
-// histogram_macros.h.
-
-namespace base {
-namespace internal {
-
-// Helper traits for deducing the boundary value for enums.
-template <typename Enum, typename SFINAE = void>
-struct EnumSizeTraits {
- static constexpr Enum Count() {
- static_assert(
- sizeof(Enum) == 0,
- "enumerator must define kMaxValue enumerator to use this macro!");
- return Enum();
- }
-};
-
-// Since the UMA histogram macros expect a value one larger than the max defined
-// enumerator value, add one.
-template <typename Enum>
-struct EnumSizeTraits<
- Enum,
- std::enable_if_t<std::is_enum<decltype(Enum::kMaxValue)>::value>> {
- static constexpr Enum Count() {
- return static_cast<Enum>(
- static_cast<std::underlying_type_t<Enum>>(Enum::kMaxValue) + 1);
- }
-};
-
-} // namespace internal
-} // namespace base
-
-// TODO(rkaplow): Improve commenting of these methods.
-//------------------------------------------------------------------------------
-// Histograms are often put in areas where they are called many many times, and
-// performance is critical. As a result, they are designed to have a very low
-// recurring cost of executing (adding additional samples). Toward that end,
-// the macros declare a static pointer to the histogram in question, and only
-// take a "slow path" to construct (or find) the histogram on the first run
-// through the macro. We leak the histograms at shutdown time so that we don't
-// have to validate using the pointers at any time during the running of the
-// process.
-
-// In some cases (integration into 3rd party code), it's useful to separate the
-// definition of |atomic_histogram_pointer| from its use. To achieve this we
-// define HISTOGRAM_POINTER_USE, which uses an |atomic_histogram_pointer|, and
-// STATIC_HISTOGRAM_POINTER_BLOCK, which defines an |atomic_histogram_pointer|
-// and forwards to HISTOGRAM_POINTER_USE.
-#define HISTOGRAM_POINTER_USE(atomic_histogram_pointer, \
- constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation) \
- do { \
- /* \
- * Acquire_Load() ensures that we acquire visibility to the \
- * pointed-to data in the histogram. \
- */ \
- base::HistogramBase* histogram_pointer( \
- reinterpret_cast<base::HistogramBase*>( \
- base::subtle::Acquire_Load(atomic_histogram_pointer))); \
- if (!histogram_pointer) { \
- /* \
- * This is the slow path, which will construct OR find the \
- * matching histogram. histogram_factory_get_invocation includes \
- * locks on a global histogram name map and is completely thread \
- * safe. \
- */ \
- histogram_pointer = histogram_factory_get_invocation; \
- \
- /* \
- * Use Release_Store to ensure that the histogram data is made \
- * available globally before we make the pointer visible. Several \
- * threads may perform this store, but the same value will be \
- * stored in all cases (for a given named/spec'ed histogram). \
- * We could do this without any barrier, since FactoryGet entered \
- * and exited a lock after construction, but this barrier makes \
- * things clear. \
- */ \
- base::subtle::Release_Store( \
- atomic_histogram_pointer, \
- reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \
- } \
- if (DCHECK_IS_ON()) \
- histogram_pointer->CheckName(constant_histogram_name); \
- histogram_pointer->histogram_add_method_invocation; \
- } while (0)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-// Defines the static |atomic_histogram_pointer| and forwards to
-// HISTOGRAM_POINTER_USE.
-#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation) \
- do { \
- /* \
- * The pointer's presence indicates that the initialization is complete. \
- * Initialization is idempotent, so it can safely be atomically repeated. \
- */ \
- static base::subtle::AtomicWord atomic_histogram_pointer = 0; \
- HISTOGRAM_POINTER_USE(&atomic_histogram_pointer, constant_histogram_name, \
- histogram_add_method_invocation, \
- histogram_factory_get_invocation); \
- } while (0)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-#define INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG(name, sample, min, max, \
- bucket_count, flag) \
- STATIC_HISTOGRAM_POINTER_BLOCK( \
- name, Add(sample), \
- base::Histogram::FactoryGet(name, min, max, bucket_count, flag))
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-// The bucketing scheme is linear with a bucket size of 1. For N items,
-// recording values in the range [0, N - 1] creates a linear histogram with N +
-// 1 buckets:
-// [0, 1), [1, 2), ..., [N - 1, N)
-// and an overflow bucket [N, infinity).
-//
-// Code should never emit to the overflow bucket; only to the other N buckets.
-// This allows future versions of Chrome to safely increase the boundary size.
-// Otherwise, the histogram would have [N - 1, infinity) as its overflow bucket,
-// and so the maximal value (N - 1) would be emitted to this overflow bucket.
-// But, if an additional value were later added, the bucket label for
-// the value (N - 1) would change to [N - 1, N), which would result in different
-// versions of Chrome using different bucket labels for identical data.
-#define INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG(name, sample, boundary, \
- flag) \
- do { \
- static_assert(!std::is_enum<decltype(sample)>::value, \
- "|sample| should not be an enum type!"); \
- static_assert(!std::is_enum<decltype(boundary)>::value, \
- "|boundary| should not be an enum type!"); \
- STATIC_HISTOGRAM_POINTER_BLOCK( \
- name, Add(sample), \
- base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \
- flag)); \
- } while (0)
-
-// Helper for 'overloading' UMA_HISTOGRAM_ENUMERATION with a variable number of
-// arguments.
-#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO(_1, _2, NAME, ...) NAME
-
-#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY(name, sample, \
- flags) \
- INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \
- name, sample, base::internal::EnumSizeTraits<decltype(sample)>::Count(), \
- flags)
-
-// Note: The value in |sample| must be strictly less than |enum_size|.
-#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY(name, sample, \
- enum_size, flags) \
- INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, enum_size, flags)
-
-// Similar to the previous macro but intended for enumerations. This delegates
-// the work to the previous macro, but supports scoped enumerations as well by
-// forcing an explicit cast to the HistogramBase::Sample integral type.
-//
-// Note the range checks verify two separate issues:
-// - that the declared enum size isn't out of range of HistogramBase::Sample
-// - that the declared enum size is > 0
-//
-// TODO(dcheng): This should assert that the passed in types are actually enum
-// types.
-#define INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \
- do { \
- using decayed_sample = std::decay<decltype(sample)>::type; \
- using decayed_boundary = std::decay<decltype(boundary)>::type; \
- static_assert(!std::is_enum<decayed_boundary>::value || \
- std::is_enum<decayed_sample>::value, \
- "Unexpected: |boundary| is enum, but |sample| is not."); \
- static_assert(!std::is_enum<decayed_sample>::value || \
- !std::is_enum<decayed_boundary>::value || \
- std::is_same<decayed_sample, decayed_boundary>::value, \
- "|sample| and |boundary| shouldn't be of different enums"); \
- static_assert( \
- static_cast<uintmax_t>(boundary) < \
- static_cast<uintmax_t>( \
- std::numeric_limits<base::HistogramBase::Sample>::max()), \
- "|boundary| is out of range of HistogramBase::Sample"); \
- INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG( \
- name, static_cast<base::HistogramBase::Sample>(sample), \
- static_cast<base::HistogramBase::Sample>(boundary), flag); \
- } while (0)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-// This is necessary to expand __COUNTER__ to an actual value.
-#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, is_long, key) \
- INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key)
-
-// This is a helper macro used by other macros and shouldn't be used directly.
-#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) \
- class ScopedHistogramTimer##key { \
- public: \
- ScopedHistogramTimer##key() : constructed_(base::TimeTicks::Now()) {} \
- ~ScopedHistogramTimer##key() { \
- base::TimeDelta elapsed = base::TimeTicks::Now() - constructed_; \
- if (is_long) { \
- UMA_HISTOGRAM_LONG_TIMES_100(name, elapsed); \
- } else { \
- UMA_HISTOGRAM_TIMES(name, elapsed); \
- } \
- } \
- private: \
- base::TimeTicks constructed_; \
- } scoped_histogram_timer_##key
-
-#endif // BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_
diff --git a/base/metrics/histogram_macros_local.h b/base/metrics/histogram_macros_local.h
deleted file mode 100644
index c4d333b..0000000
--- a/base/metrics/histogram_macros_local.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_
-#define BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_
-
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_macros_internal.h"
-#include "base/time/time.h"
-
-// TODO(rkaplow): Migrate all LOCAL_* usage within Chromium to include this
-// file instead of the histogram_macros.h file.
-
-//------------------------------------------------------------------------------
-// Enumeration histograms.
-//
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_ENUMERATION(name, ...) \
- CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \
- __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \
- INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \
- name, __VA_ARGS__, base::HistogramBase::kNoFlags))
-
-#define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \
- base::BooleanHistogram::FactoryGet(name, base::Histogram::kNoFlags))
-
-//------------------------------------------------------------------------------
-// Percentage histograms.
-//
-// For usage details, see the equivalents in histogram_macros.h
-
-#define LOCAL_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
- LOCAL_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
-
-//------------------------------------------------------------------------------
-// Count histograms. These are used for collecting numeric data. Note that we
-// have macros for more specialized use cases below (memory, time, percentages).
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_COUNTS_100(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50)
-
-#define LOCAL_HISTOGRAM_COUNTS_10000(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50)
-
-#define LOCAL_HISTOGRAM_COUNTS_1000000(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50)
-
-#define LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
- INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \
- name, sample, min, max, bucket_count, base::HistogramBase::kNoFlags)
-
-//------------------------------------------------------------------------------
-// Timing histograms. These are used for collecting timing data (generally
-// latencies).
-//
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_TIMES(name, sample) LOCAL_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
- base::TimeDelta::FromSeconds(10), 50)
-
-#define LOCAL_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
- STATIC_HISTOGRAM_POINTER_BLOCK( \
- name, AddTimeMillisecondsGranularity(sample), \
- base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
- base::HistogramBase::kNoFlags))
-
-//------------------------------------------------------------------------------
-// Memory histograms.
-//
-// For usage details, see the equivalents in histogram_macros.h.
-
-#define LOCAL_HISTOGRAM_MEMORY_KB(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \
- name, sample, 1000, 500000, 50)
-
-//------------------------------------------------------------------------------
-// Deprecated histograms. Not recommended for current use.
-
-// TODO(rkaplow): See if we can clean up this macro and usage.
-// Legacy non-explicit version. We suggest using LOCAL_HISTOGRAM_COUNTS_1000000
-// instead.
-#define LOCAL_HISTOGRAM_COUNTS(name, sample) \
- LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50)
-
-#endif // BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_
diff --git a/base/metrics/histogram_samples.cc b/base/metrics/histogram_samples.cc
deleted file mode 100644
index 6830637..0000000
--- a/base/metrics/histogram_samples.cc
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/histogram_samples.h"
-
-#include <limits>
-
-#include "base/compiler_specific.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/numerics/safe_math.h"
-#include "base/pickle.h"
-
-namespace base {
-
-namespace {
-
-// A shorthand constant for the max value of size_t.
-constexpr size_t kSizeMax = std::numeric_limits<size_t>::max();
-
-// A constant stored in an AtomicSingleSample (as_atomic) to indicate that the
-// sample is "disabled" and no further accumulation should be done with it. The
-// value is chosen such that it will be MAX_UINT16 for both |bucket| & |count|,
-// and thus less likely to conflict with real use. Conflicts are explicitly
-// handled in the code but it's worth making them as unlikely as possible.
-constexpr int32_t kDisabledSingleSample = -1;
-
-class SampleCountPickleIterator : public SampleCountIterator {
- public:
- explicit SampleCountPickleIterator(PickleIterator* iter);
-
- bool Done() const override;
- void Next() override;
- void Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const override;
-
- private:
- PickleIterator* const iter_;
-
- HistogramBase::Sample min_;
- int64_t max_;
- HistogramBase::Count count_;
- bool is_done_;
-};
-
-SampleCountPickleIterator::SampleCountPickleIterator(PickleIterator* iter)
- : iter_(iter),
- is_done_(false) {
- Next();
-}
-
-bool SampleCountPickleIterator::Done() const {
- return is_done_;
-}
-
-void SampleCountPickleIterator::Next() {
- DCHECK(!Done());
- if (!iter_->ReadInt(&min_) || !iter_->ReadInt64(&max_) ||
- !iter_->ReadInt(&count_)) {
- is_done_ = true;
- }
-}
-
-void SampleCountPickleIterator::Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const {
- DCHECK(!Done());
- *min = min_;
- *max = max_;
- *count = count_;
-}
-
-} // namespace
-
-static_assert(sizeof(HistogramSamples::AtomicSingleSample) ==
- sizeof(subtle::Atomic32),
- "AtomicSingleSample isn't 32 bits");
-
-HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Load()
- const {
- AtomicSingleSample single_sample = subtle::Acquire_Load(&as_atomic);
-
- // If the sample was extracted/disabled, it's still zero to the outside.
- if (single_sample.as_atomic == kDisabledSingleSample)
- single_sample.as_atomic = 0;
-
- return single_sample.as_parts;
-}
-
-HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Extract(
- bool disable) {
- AtomicSingleSample single_sample = subtle::NoBarrier_AtomicExchange(
- &as_atomic, disable ? kDisabledSingleSample : 0);
- if (single_sample.as_atomic == kDisabledSingleSample)
- single_sample.as_atomic = 0;
- return single_sample.as_parts;
-}
-
-bool HistogramSamples::AtomicSingleSample::Accumulate(
- size_t bucket,
- HistogramBase::Count count) {
- if (count == 0)
- return true;
-
- // Convert the parameters to 16-bit variables because it's all 16-bit below.
- // To support decrements/subtractions, divide the |count| into sign/value and
- // do the proper operation below. The alternative is to change the single-
- // sample's count to be a signed integer (int16_t) and just add an int16_t
- // |count16| but that is somewhat wasteful given that the single-sample is
- // never expected to have a count less than zero.
- if (count < -std::numeric_limits<uint16_t>::max() ||
- count > std::numeric_limits<uint16_t>::max() ||
- bucket > std::numeric_limits<uint16_t>::max()) {
- return false;
- }
- bool count_is_negative = count < 0;
- uint16_t count16 = static_cast<uint16_t>(count_is_negative ? -count : count);
- uint16_t bucket16 = static_cast<uint16_t>(bucket);
-
- // A local, unshared copy of the single-sample is necessary so the parts
- // can be manipulated without worrying about atomicity.
- AtomicSingleSample single_sample;
-
- bool sample_updated;
- do {
- subtle::Atomic32 original = subtle::Acquire_Load(&as_atomic);
- if (original == kDisabledSingleSample)
- return false;
- single_sample.as_atomic = original;
- if (single_sample.as_atomic != 0) {
- // Only the same bucket (parameter and stored) can be counted multiple
- // times.
- if (single_sample.as_parts.bucket != bucket16)
- return false;
- } else {
- // The |single_ sample| was zero so becomes the |bucket| parameter, the
- // contents of which were checked above to fit in 16 bits.
- single_sample.as_parts.bucket = bucket16;
- }
-
- // Update count, making sure that it doesn't overflow.
- CheckedNumeric<uint16_t> new_count(single_sample.as_parts.count);
- if (count_is_negative)
- new_count -= count16;
- else
- new_count += count16;
- if (!new_count.AssignIfValid(&single_sample.as_parts.count))
- return false;
-
- // Don't let this become equivalent to the "disabled" value.
- if (single_sample.as_atomic == kDisabledSingleSample)
- return false;
-
- // Store the updated single-sample back into memory. |existing| is what
- // was in that memory location at the time of the call; if it doesn't
- // match |original| then the swap didn't happen so loop again.
- subtle::Atomic32 existing = subtle::Release_CompareAndSwap(
- &as_atomic, original, single_sample.as_atomic);
- sample_updated = (existing == original);
- } while (!sample_updated);
-
- return true;
-}
-
-bool HistogramSamples::AtomicSingleSample::IsDisabled() const {
- return subtle::Acquire_Load(&as_atomic) == kDisabledSingleSample;
-}
-
-HistogramSamples::LocalMetadata::LocalMetadata() {
- // This is the same way it's done for persistent metadata since no ctor
- // is called for the data members in that case.
- memset(this, 0, sizeof(*this));
-}
-
-HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta)
- : meta_(meta) {
- DCHECK(meta_->id == 0 || meta_->id == id);
-
- // It's possible that |meta| is contained in initialized, read-only memory
- // so it's essential that no write be done in that case.
- if (!meta_->id)
- meta_->id = id;
-}
-
-// This mustn't do anything with |meta_|. It was passed to the ctor and may
-// be invalid by the time this dtor gets called.
-HistogramSamples::~HistogramSamples() = default;
-
-void HistogramSamples::Add(const HistogramSamples& other) {
- IncreaseSumAndCount(other.sum(), other.redundant_count());
- std::unique_ptr<SampleCountIterator> it = other.Iterator();
- bool success = AddSubtractImpl(it.get(), ADD);
- DCHECK(success);
-}
-
-bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
- int64_t sum;
- HistogramBase::Count redundant_count;
-
- if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
- return false;
-
- IncreaseSumAndCount(sum, redundant_count);
-
- SampleCountPickleIterator pickle_iter(iter);
- return AddSubtractImpl(&pickle_iter, ADD);
-}
-
-void HistogramSamples::Subtract(const HistogramSamples& other) {
- IncreaseSumAndCount(-other.sum(), -other.redundant_count());
- std::unique_ptr<SampleCountIterator> it = other.Iterator();
- bool success = AddSubtractImpl(it.get(), SUBTRACT);
- DCHECK(success);
-}
-
-void HistogramSamples::Serialize(Pickle* pickle) const {
- pickle->WriteInt64(sum());
- pickle->WriteInt(redundant_count());
-
- HistogramBase::Sample min;
- int64_t max;
- HistogramBase::Count count;
- for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done();
- it->Next()) {
- it->Get(&min, &max, &count);
- pickle->WriteInt(min);
- pickle->WriteInt64(max);
- pickle->WriteInt(count);
- }
-}
-
-bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value,
- HistogramBase::Count count,
- size_t bucket) {
- if (single_sample().Accumulate(bucket, count)) {
- // Success. Update the (separate) sum and redundant-count.
- IncreaseSumAndCount(strict_cast<int64_t>(value) * count, count);
- return true;
- }
- return false;
-}
-
-void HistogramSamples::IncreaseSumAndCount(int64_t sum,
- HistogramBase::Count count) {
-#ifdef ARCH_CPU_64_BITS
- subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum);
-#else
- meta_->sum += sum;
-#endif
- subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count);
-}
-
-void HistogramSamples::RecordNegativeSample(NegativeSampleReason reason,
- HistogramBase::Count increment) {
- UMA_HISTOGRAM_ENUMERATION("UMA.NegativeSamples.Reason", reason,
- MAX_NEGATIVE_SAMPLE_REASONS);
- UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.NegativeSamples.Increment", increment, 1,
- 1 << 30, 100);
- UmaHistogramSparse("UMA.NegativeSamples.Histogram",
- static_cast<int32_t>(id()));
-}
-
-SampleCountIterator::~SampleCountIterator() = default;
-
-bool SampleCountIterator::GetBucketIndex(size_t* index) const {
- DCHECK(!Done());
- return false;
-}
-
-SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
- int64_t max,
- HistogramBase::Count count)
- : SingleSampleIterator(min, max, count, kSizeMax) {}
-
-SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
- int64_t max,
- HistogramBase::Count count,
- size_t bucket_index)
- : min_(min), max_(max), bucket_index_(bucket_index), count_(count) {}
-
-SingleSampleIterator::~SingleSampleIterator() = default;
-
-bool SingleSampleIterator::Done() const {
- return count_ == 0;
-}
-
-void SingleSampleIterator::Next() {
- DCHECK(!Done());
- count_ = 0;
-}
-
-void SingleSampleIterator::Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const {
- DCHECK(!Done());
- if (min != nullptr)
- *min = min_;
- if (max != nullptr)
- *max = max_;
- if (count != nullptr)
- *count = count_;
-}
-
-bool SingleSampleIterator::GetBucketIndex(size_t* index) const {
- DCHECK(!Done());
- if (bucket_index_ == kSizeMax)
- return false;
- *index = bucket_index_;
- return true;
-}
-
-} // namespace base
diff --git a/base/metrics/histogram_samples.h b/base/metrics/histogram_samples.h
deleted file mode 100644
index 059fd3c..0000000
--- a/base/metrics/histogram_samples.h
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_SAMPLES_H_
-#define BASE_METRICS_HISTOGRAM_SAMPLES_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <limits>
-#include <memory>
-
-#include "base/atomicops.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
-
-namespace base {
-
-class Pickle;
-class PickleIterator;
-class SampleCountIterator;
-
-// HistogramSamples is a container storing all samples of a histogram. All
-// elements must be of a fixed width to ensure 32/64-bit interoperability.
-// If this structure changes, bump the version number for kTypeIdHistogram
-// in persistent_histogram_allocator.cc.
-//
-// Note that though these samples are individually consistent (through the use
-// of atomic operations on the counts), there is only "eventual consistency"
-// overall when multiple threads are accessing this data. That means that the
-// sum, redundant-count, etc. could be momentarily out-of-sync with the stored
-// counts but will settle to a consistent "steady state" once all threads have
-// exited this code.
-class BASE_EXPORT HistogramSamples {
- public:
- // A single bucket and count. To fit within a single atomic on 32-bit build
- // architectures, both |bucket| and |count| are limited in size to 16 bits.
- // This limits the functionality somewhat but if an entry can't fit then
- // the full array of samples can be allocated and used.
- struct SingleSample {
- uint16_t bucket;
- uint16_t count;
- };
-
- // A structure for managing an atomic single sample. Because this is generally
- // used in association with other atomic values, the defined methods use
- // acquire/release operations to guarantee ordering with outside values.
- union BASE_EXPORT AtomicSingleSample {
- AtomicSingleSample() : as_atomic(0) {}
- AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {}
-
- // Returns the single sample in an atomic manner. This in an "acquire"
- // load. The returned sample isn't shared and thus its fields can be safely
- // accessed.
- SingleSample Load() const;
-
- // Extracts the single sample in an atomic manner. If |disable| is true
- // then this object will be set so it will never accumulate another value.
- // This is "no barrier" so doesn't enforce ordering with other atomic ops.
- SingleSample Extract(bool disable);
-
- // Adds a given count to the held bucket. If not possible, it returns false
- // and leaves the parts unchanged. Once extracted/disabled, this always
- // returns false. This in an "acquire/release" operation.
- bool Accumulate(size_t bucket, HistogramBase::Count count);
-
- // Returns if the sample has been "disabled" (via Extract) and thus not
- // allowed to accept further accumulation.
- bool IsDisabled() const;
-
- private:
- // union field: The actual sample bucket and count.
- SingleSample as_parts;
-
- // union field: The sample as an atomic value. Atomic64 would provide
- // more flexibility but isn't available on all builds. This can hold a
- // special, internal "disabled" value indicating that it must not accept
- // further accumulation.
- subtle::Atomic32 as_atomic;
- };
-
- // A structure of information about the data, common to all sample containers.
- // Because of how this is used in persistent memory, it must be a POD object
- // that makes sense when initialized to all zeros.
- struct Metadata {
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 24;
-
- // Initialized when the sample-set is first created with a value provided
- // by the caller. It is generally used to identify the sample-set across
- // threads and processes, though not necessarily uniquely as it is possible
- // to have multiple sample-sets representing subsets of the data.
- uint64_t id;
-
- // The sum of all the entries, effectivly the sum(sample * count) for
- // all samples. Despite being atomic, no guarantees are made on the
- // accuracy of this value; there may be races during histogram
- // accumulation and snapshotting that we choose to accept. It should
- // be treated as approximate.
-#ifdef ARCH_CPU_64_BITS
- subtle::Atomic64 sum;
-#else
- // 32-bit systems don't have atomic 64-bit operations. Use a basic type
- // and don't worry about "shearing".
- int64_t sum;
-#endif
-
- // A "redundant" count helps identify memory corruption. It redundantly
- // stores the total number of samples accumulated in the histogram. We
- // can compare this count to the sum of the counts (TotalCount() function),
- // and detect problems. Note, depending on the implementation of different
- // histogram types, there might be races during histogram accumulation
- // and snapshotting that we choose to accept. In this case, the tallies
- // might mismatch even when no memory corruption has happened.
- HistogramBase::AtomicCount redundant_count;
-
- // A single histogram value and associated count. This allows histograms
- // that typically report only a single value to not require full storage
- // to be allocated.
- AtomicSingleSample single_sample; // 32 bits
- };
-
- // Because structures held in persistent memory must be POD, there can be no
- // default constructor to clear the fields. This derived class exists just
- // to clear them when being allocated on the heap.
- struct BASE_EXPORT LocalMetadata : Metadata {
- LocalMetadata();
- };
-
- HistogramSamples(uint64_t id, Metadata* meta);
- virtual ~HistogramSamples();
-
- virtual void Accumulate(HistogramBase::Sample value,
- HistogramBase::Count count) = 0;
- virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0;
- virtual HistogramBase::Count TotalCount() const = 0;
-
- virtual void Add(const HistogramSamples& other);
-
- // Add from serialized samples.
- virtual bool AddFromPickle(PickleIterator* iter);
-
- virtual void Subtract(const HistogramSamples& other);
-
- virtual std::unique_ptr<SampleCountIterator> Iterator() const = 0;
- virtual void Serialize(Pickle* pickle) const;
-
- // Accessor fuctions.
- uint64_t id() const { return meta_->id; }
- int64_t sum() const {
-#ifdef ARCH_CPU_64_BITS
- return subtle::NoBarrier_Load(&meta_->sum);
-#else
- return meta_->sum;
-#endif
- }
- HistogramBase::Count redundant_count() const {
- return subtle::NoBarrier_Load(&meta_->redundant_count);
- }
-
- // Temporarily visible for crash debugging. Should be protected.
- // TODO(bcwhite): Move this back where it belongs.
- // https://bugs.chromium.org/p/chromium/issues/detail?id=836875
- Metadata* meta() { return meta_; }
-
- protected:
- enum NegativeSampleReason {
- SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE,
- SAMPLES_SAMPLE_LESS_THAN_LOGGED,
- SAMPLES_ADDED_NEGATIVE_COUNT,
- SAMPLES_ADD_WENT_NEGATIVE,
- SAMPLES_ADD_OVERFLOW,
- SAMPLES_ACCUMULATE_NEGATIVE_COUNT,
- SAMPLES_ACCUMULATE_WENT_NEGATIVE,
- DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW,
- SAMPLES_ACCUMULATE_OVERFLOW,
- MAX_NEGATIVE_SAMPLE_REASONS
- };
-
- // Based on |op| type, add or subtract sample counts data from the iterator.
- enum Operator { ADD, SUBTRACT };
- virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0;
-
- // Accumulates to the embedded single-sample field if possible. Returns true
- // on success, false otherwise. Sum and redundant-count are also updated in
- // the success case.
- bool AccumulateSingleSample(HistogramBase::Sample value,
- HistogramBase::Count count,
- size_t bucket);
-
- // Atomically adjust the sum and redundant-count.
- void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count);
-
- // Record a negative-sample observation and the reason why.
- void RecordNegativeSample(NegativeSampleReason reason,
- HistogramBase::Count increment);
-
- AtomicSingleSample& single_sample() { return meta_->single_sample; }
- const AtomicSingleSample& single_sample() const {
- return meta_->single_sample;
- }
-
- private:
- // Depending on derived class meta values can come from local stoarge or
- // external storage in which case HistogramSamples class cannot take ownership
- // of Metadata*.
- Metadata* meta_;
-
- DISALLOW_COPY_AND_ASSIGN(HistogramSamples);
-};
-
-class BASE_EXPORT SampleCountIterator {
- public:
- virtual ~SampleCountIterator();
-
- virtual bool Done() const = 0;
- virtual void Next() = 0;
-
- // Get the sample and count at current position.
- // |min| |max| and |count| can be NULL if the value is not of interest.
- // Note: |max| is int64_t because histograms support logged values in the
- // full int32_t range and bucket max is exclusive, so it needs to support
- // values up to MAXINT32+1.
- // Requires: !Done();
- virtual void Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const = 0;
- static_assert(std::numeric_limits<HistogramBase::Sample>::max() <
- std::numeric_limits<int64_t>::max(),
- "Get() |max| must be able to hold Histogram::Sample max + 1");
-
- // Get the index of current histogram bucket.
- // For histograms that don't use predefined buckets, it returns false.
- // Requires: !Done();
- virtual bool GetBucketIndex(size_t* index) const;
-};
-
-class BASE_EXPORT SingleSampleIterator : public SampleCountIterator {
- public:
- SingleSampleIterator(HistogramBase::Sample min,
- int64_t max,
- HistogramBase::Count count);
- SingleSampleIterator(HistogramBase::Sample min,
- int64_t max,
- HistogramBase::Count count,
- size_t bucket_index);
- ~SingleSampleIterator() override;
-
- // SampleCountIterator:
- bool Done() const override;
- void Next() override;
- void Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const override;
-
- // SampleVector uses predefined buckets so iterator can return bucket index.
- bool GetBucketIndex(size_t* index) const override;
-
- private:
- // Information about the single value to return.
- const HistogramBase::Sample min_;
- const int64_t max_;
- const size_t bucket_index_;
- HistogramBase::Count count_;
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc
deleted file mode 100644
index c1b804e..0000000
--- a/base/metrics/histogram_snapshot_manager.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/histogram_snapshot_manager.h"
-
-#include <memory>
-
-#include "base/debug/alias.h"
-#include "base/metrics/histogram_flattener.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/stl_util.h"
-
-namespace base {
-
-namespace {
-
-// A simple object to set an "active" flag and clear it upon destruction. It is
-// an error if the flag is already set.
-class MakeActive {
- public:
- MakeActive(std::atomic<bool>* is_active) : is_active_(is_active) {
- bool was_active = is_active_->exchange(true, std::memory_order_relaxed);
- CHECK(!was_active);
- }
- ~MakeActive() { is_active_->store(false, std::memory_order_relaxed); }
-
- private:
- std::atomic<bool>* is_active_;
-
- DISALLOW_COPY_AND_ASSIGN(MakeActive);
-};
-
-} // namespace
-
-HistogramSnapshotManager::HistogramSnapshotManager(
- HistogramFlattener* histogram_flattener)
- : histogram_flattener_(histogram_flattener) {
- DCHECK(histogram_flattener_);
- is_active_.store(false, std::memory_order_relaxed);
-}
-
-HistogramSnapshotManager::~HistogramSnapshotManager() = default;
-
-void HistogramSnapshotManager::PrepareDeltas(
- const std::vector<HistogramBase*>& histograms,
- HistogramBase::Flags flags_to_set,
- HistogramBase::Flags required_flags) {
- for (HistogramBase* const histogram : histograms) {
- histogram->SetFlags(flags_to_set);
- if ((histogram->flags() & required_flags) == required_flags)
- PrepareDelta(histogram);
- }
-}
-
-void HistogramSnapshotManager::PrepareDelta(HistogramBase* histogram) {
- histogram->ValidateHistogramContents();
- PrepareSamples(histogram, histogram->SnapshotDelta());
-}
-
-void HistogramSnapshotManager::PrepareFinalDelta(
- const HistogramBase* histogram) {
- histogram->ValidateHistogramContents();
- PrepareSamples(histogram, histogram->SnapshotFinalDelta());
-}
-
-void HistogramSnapshotManager::PrepareSamples(
- const HistogramBase* histogram,
- std::unique_ptr<HistogramSamples> samples) {
- DCHECK(histogram_flattener_);
-
- // Ensure that there is no concurrent access going on while accessing the
- // set of known histograms. The flag will be reset when this object goes
- // out of scope.
- MakeActive make_active(&is_active_);
-
- // Get information known about this histogram. If it did not previously
- // exist, one will be created and initialized.
- SampleInfo* sample_info = &known_histograms_[histogram->name_hash()];
-
- // Crash if we detect that our histograms have been overwritten. This may be
- // a fair distance from the memory smasher, but we hope to correlate these
- // crashes with other events, such as plugins, or usage patterns, etc.
- uint32_t corruption = histogram->FindCorruption(*samples);
- if (HistogramBase::BUCKET_ORDER_ERROR & corruption) {
- // Extract fields useful during debug.
- const BucketRanges* ranges =
- static_cast<const Histogram*>(histogram)->bucket_ranges();
- uint32_t ranges_checksum = ranges->checksum();
- uint32_t ranges_calc_checksum = ranges->CalculateChecksum();
- int32_t flags = histogram->flags();
- // The checksum should have caught this, so crash separately if it didn't.
- CHECK_NE(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption);
- CHECK(false); // Crash for the bucket order corruption.
- // Ensure that compiler keeps around pointers to |histogram| and its
- // internal |bucket_ranges_| for any minidumps.
- base::debug::Alias(&ranges_checksum);
- base::debug::Alias(&ranges_calc_checksum);
- base::debug::Alias(&flags);
- }
- // Checksum corruption might not have caused order corruption.
- CHECK_EQ(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption);
-
- // Note, at this point corruption can only be COUNT_HIGH_ERROR or
- // COUNT_LOW_ERROR and they never arise together, so we don't need to extract
- // bits from corruption.
- if (corruption) {
- DLOG(ERROR) << "Histogram: \"" << histogram->histogram_name()
- << "\" has data corruption: " << corruption;
- // Don't record corrupt data to metrics services.
- const uint32_t old_corruption = sample_info->inconsistencies;
- if (old_corruption == (corruption | old_corruption))
- return; // We've already seen this corruption for this histogram.
- sample_info->inconsistencies |= corruption;
- return;
- }
-
- if (samples->TotalCount() > 0)
- histogram_flattener_->RecordDelta(*histogram, *samples);
-}
-
-} // namespace base
diff --git a/base/metrics/histogram_snapshot_manager.h b/base/metrics/histogram_snapshot_manager.h
deleted file mode 100644
index cf7c149..0000000
--- a/base/metrics/histogram_snapshot_manager.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_
-#define BASE_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_
-
-#include <stdint.h>
-
-#include <atomic>
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
-
-namespace base {
-
-class HistogramSamples;
-class HistogramFlattener;
-
-// HistogramSnapshotManager handles the logistics of gathering up available
-// histograms for recording either to disk or for transmission (such as from
-// renderer to browser, or from browser to UMA upload). Since histograms can sit
-// in memory for an extended period of time, and are vulnerable to memory
-// corruption, this class also validates as much redundancy as it can before
-// calling for the marginal change (a.k.a., delta) in a histogram to be
-// recorded.
-class BASE_EXPORT HistogramSnapshotManager final {
- public:
- explicit HistogramSnapshotManager(HistogramFlattener* histogram_flattener);
- ~HistogramSnapshotManager();
-
- // Snapshot all histograms, and ask |histogram_flattener_| to record the
- // delta. |flags_to_set| is used to set flags for each histogram.
- // |required_flags| is used to select histograms to be recorded.
- // Only histograms that have all the flags specified by the argument will be
- // chosen. If all histograms should be recorded, set it to
- // |Histogram::kNoFlags|.
- void PrepareDeltas(const std::vector<HistogramBase*>& histograms,
- HistogramBase::Flags flags_to_set,
- HistogramBase::Flags required_flags);
-
- // When the collection is not so simple as can be done using a single
- // iterator, the steps can be performed separately. Call PerpareDelta()
- // as many times as necessary. PrepareFinalDelta() works like PrepareDelta()
- // except that it does not update the previous logged values and can thus
- // be used with read-only files.
- void PrepareDelta(HistogramBase* histogram);
- void PrepareFinalDelta(const HistogramBase* histogram);
-
- private:
- FRIEND_TEST_ALL_PREFIXES(HistogramSnapshotManagerTest, CheckMerge);
-
- // During a snapshot, samples are acquired and aggregated. This structure
- // contains all the information for a given histogram that persists between
- // collections.
- struct SampleInfo {
- // The set of inconsistencies (flags) already seen for the histogram.
- // See HistogramBase::Inconsistency for values.
- uint32_t inconsistencies = 0;
- };
-
- // Capture and hold samples from a histogram. This does all the heavy
- // lifting for PrepareDelta() and PrepareAbsolute().
- void PrepareSamples(const HistogramBase* histogram,
- std::unique_ptr<HistogramSamples> samples);
-
- // |histogram_flattener_| handles the logistics of recording the histogram
- // deltas.
- HistogramFlattener* const histogram_flattener_; // Weak.
-
- // For histograms, track what has been previously seen, indexed
- // by the hash of the histogram name.
- std::map<uint64_t, SampleInfo> known_histograms_;
-
- // A flag indicating if a thread is currently doing an operation. This is
- // used to check against concurrent access which is not supported. A Thread-
- // Checker is not sufficient because it may be guarded by at outside lock
- // (as is the case with cronet).
- std::atomic<bool> is_active_;
-
- DISALLOW_COPY_AND_ASSIGN(HistogramSnapshotManager);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_
diff --git a/base/metrics/metrics_hashes.cc b/base/metrics/metrics_hashes.cc
deleted file mode 100644
index 5672b06..0000000
--- a/base/metrics/metrics_hashes.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/metrics_hashes.h"
-
-#include "base/logging.h"
-#include "base/md5.h"
-#include "base/sys_byteorder.h"
-
-namespace base {
-
-namespace {
-
-// Converts the 8-byte prefix of an MD5 hash into a uint64_t value.
-inline uint64_t DigestToUInt64(const base::MD5Digest& digest) {
- uint64_t value;
- DCHECK_GE(sizeof(digest.a), sizeof(value));
- memcpy(&value, digest.a, sizeof(value));
- return base::NetToHost64(value);
-}
-
-} // namespace
-
-uint64_t HashMetricName(base::StringPiece name) {
- base::MD5Digest digest;
- base::MD5Sum(name.data(), name.size(), &digest);
- return DigestToUInt64(digest);
-}
-
-} // namespace metrics
diff --git a/base/metrics/metrics_hashes.h b/base/metrics/metrics_hashes.h
deleted file mode 100644
index d05c4ba..0000000
--- a/base/metrics/metrics_hashes.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_METRICS_HASHES_H_
-#define BASE_METRICS_METRICS_HASHES_H_
-
-#include <stdint.h>
-
-#include "base/base_export.h"
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-// Computes a uint64_t hash of a given string based on its MD5 hash. Suitable
-// for metric names.
-BASE_EXPORT uint64_t HashMetricName(base::StringPiece name);
-
-} // namespace metrics
-
-#endif // BASE_METRICS_METRICS_HASHES_H_
diff --git a/base/metrics/persistent_histogram_allocator.cc b/base/metrics/persistent_histogram_allocator.cc
deleted file mode 100644
index bfbb44b..0000000
--- a/base/metrics/persistent_histogram_allocator.cc
+++ /dev/null
@@ -1,1024 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// 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_allocator.h"
-
-#include <memory>
-
-#include "base/atomicops.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/important_file_writer.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/metrics_hashes.h"
-#include "base/metrics/persistent_sample_map.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/pickle.h"
-#include "base/process/process_handle.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-namespace {
-
-// Type identifiers used when storing in persistent memory so they can be
-// identified during extraction; the first 4 bytes of the SHA1 of the name
-// is used as a unique integer. A "version number" is added to the base
-// so that, if the structure of that object changes, stored older versions
-// will be safely ignored.
-enum : uint32_t {
- kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1
- kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1
-};
-
-// The current globally-active persistent allocator for all new histograms.
-// The object held here will obviously not be destructed at process exit
-// but that's best since PersistentMemoryAllocator objects (that underlie
-// GlobalHistogramAllocator objects) are explicitly forbidden from doing
-// anything essential at exit anyway due to the fact that they depend on data
-// managed elsewhere and which could be destructed first. An AtomicWord is
-// used instead of std::atomic because the latter can create global ctors
-// and dtors.
-subtle::AtomicWord g_histogram_allocator = 0;
-
-// Take an array of range boundaries and create a proper BucketRanges object
-// which is returned to the caller. A return of nullptr indicates that the
-// passed boundaries are invalid.
-std::unique_ptr<BucketRanges> CreateRangesFromData(
- HistogramBase::Sample* ranges_data,
- uint32_t ranges_checksum,
- size_t count) {
- // To avoid racy destruction at shutdown, the following may be leaked.
- std::unique_ptr<BucketRanges> ranges(new BucketRanges(count));
- DCHECK_EQ(count, ranges->size());
- for (size_t i = 0; i < count; ++i) {
- if (i > 0 && ranges_data[i] <= ranges_data[i - 1])
- return nullptr;
- ranges->set_range(i, ranges_data[i]);
- }
-
- ranges->ResetChecksum();
- if (ranges->checksum() != ranges_checksum)
- return nullptr;
-
- return ranges;
-}
-
-// Calculate the number of bytes required to store all of a histogram's
-// "counts". This will return zero (0) if |bucket_count| is not valid.
-size_t CalculateRequiredCountsBytes(size_t bucket_count) {
- // 2 because each "sample count" also requires a backup "logged count"
- // used for calculating the delta during snapshot operations.
- const size_t kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount);
-
- // If the |bucket_count| is such that it would overflow the return type,
- // perhaps as the result of a malicious actor, then return zero to
- // indicate the problem to the caller.
- if (bucket_count > std::numeric_limits<size_t>::max() / kBytesPerBucket)
- return 0;
-
- return bucket_count * kBytesPerBucket;
-}
-
-} // namespace
-
-const Feature kPersistentHistogramsFeature{
- "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT
-};
-
-
-PersistentSparseHistogramDataManager::PersistentSparseHistogramDataManager(
- PersistentMemoryAllocator* allocator)
- : allocator_(allocator), record_iterator_(allocator) {}
-
-PersistentSparseHistogramDataManager::~PersistentSparseHistogramDataManager() =
- default;
-
-PersistentSampleMapRecords*
-PersistentSparseHistogramDataManager::UseSampleMapRecords(uint64_t id,
- const void* user) {
- base::AutoLock auto_lock(lock_);
- return GetSampleMapRecordsWhileLocked(id)->Acquire(user);
-}
-
-PersistentSampleMapRecords*
-PersistentSparseHistogramDataManager::GetSampleMapRecordsWhileLocked(
- uint64_t id) {
- lock_.AssertAcquired();
-
- auto found = sample_records_.find(id);
- if (found != sample_records_.end())
- return found->second.get();
-
- std::unique_ptr<PersistentSampleMapRecords>& samples = sample_records_[id];
- samples = std::make_unique<PersistentSampleMapRecords>(this, id);
- return samples.get();
-}
-
-bool PersistentSparseHistogramDataManager::LoadRecords(
- PersistentSampleMapRecords* sample_map_records) {
- // DataManager must be locked in order to access the found_ field of any
- // PersistentSampleMapRecords object.
- base::AutoLock auto_lock(lock_);
- bool found = false;
-
- // If there are already "found" entries for the passed object, move them.
- if (!sample_map_records->found_.empty()) {
- sample_map_records->records_.reserve(sample_map_records->records_.size() +
- sample_map_records->found_.size());
- sample_map_records->records_.insert(sample_map_records->records_.end(),
- sample_map_records->found_.begin(),
- sample_map_records->found_.end());
- sample_map_records->found_.clear();
- found = true;
- }
-
- // Acquiring a lock is a semi-expensive operation so load some records with
- // each call. More than this number may be loaded if it takes longer to
- // find at least one matching record for the passed object.
- const int kMinimumNumberToLoad = 10;
- const uint64_t match_id = sample_map_records->sample_map_id_;
-
- // Loop while no enty is found OR we haven't yet loaded the minimum number.
- // This will continue reading even after a match is found.
- for (int count = 0; !found || count < kMinimumNumberToLoad; ++count) {
- // Get the next sample-record. The iterator will always resume from where
- // it left off even if it previously had nothing further to return.
- uint64_t found_id;
- PersistentMemoryAllocator::Reference ref =
- PersistentSampleMap::GetNextPersistentRecord(record_iterator_,
- &found_id);
-
- // Stop immediately if there are none.
- if (!ref)
- break;
-
- // The sample-record could be for any sparse histogram. Add the reference
- // to the appropriate collection for later use.
- if (found_id == match_id) {
- sample_map_records->records_.push_back(ref);
- found = true;
- } else {
- PersistentSampleMapRecords* samples =
- GetSampleMapRecordsWhileLocked(found_id);
- DCHECK(samples);
- samples->found_.push_back(ref);
- }
- }
-
- return found;
-}
-
-
-PersistentSampleMapRecords::PersistentSampleMapRecords(
- PersistentSparseHistogramDataManager* data_manager,
- uint64_t sample_map_id)
- : data_manager_(data_manager), sample_map_id_(sample_map_id) {}
-
-PersistentSampleMapRecords::~PersistentSampleMapRecords() = default;
-
-PersistentSampleMapRecords* PersistentSampleMapRecords::Acquire(
- const void* user) {
- DCHECK(!user_);
- user_ = user;
- seen_ = 0;
- return this;
-}
-
-void PersistentSampleMapRecords::Release(const void* user) {
- DCHECK_EQ(user_, user);
- user_ = nullptr;
-}
-
-PersistentMemoryAllocator::Reference PersistentSampleMapRecords::GetNext() {
- DCHECK(user_);
-
- // If there are no unseen records, lock and swap in all the found ones.
- if (records_.size() == seen_) {
- if (!data_manager_->LoadRecords(this))
- return false;
- }
-
- // Return the next record. Records *must* be returned in the same order
- // they are found in the persistent memory in order to ensure that all
- // objects using this data always have the same state. Race conditions
- // can cause duplicate records so using the "first found" is the only
- // guarantee that all objects always access the same one.
- DCHECK_LT(seen_, records_.size());
- return records_[seen_++];
-}
-
-PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew(
- HistogramBase::Sample value) {
- return PersistentSampleMap::CreatePersistentRecord(data_manager_->allocator_,
- sample_map_id_, value);
-}
-
-
-// This data will be held in persistent memory in order for processes to
-// locate and use histograms created elsewhere.
-struct PersistentHistogramAllocator::PersistentHistogramData {
- // SHA1(Histogram): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0xF1645910 + 3;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize =
- 40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize;
-
- int32_t histogram_type;
- int32_t flags;
- int32_t minimum;
- int32_t maximum;
- uint32_t bucket_count;
- PersistentMemoryAllocator::Reference ranges_ref;
- uint32_t ranges_checksum;
- subtle::Atomic32 counts_ref; // PersistentMemoryAllocator::Reference
- HistogramSamples::Metadata samples_metadata;
- HistogramSamples::Metadata logged_metadata;
-
- // Space for the histogram name will be added during the actual allocation
- // request. This must be the last field of the structure. A zero-size array
- // or a "flexible" array would be preferred but is not (yet) valid C++.
- char name[sizeof(uint64_t)]; // Force 64-bit alignment on 32-bit builds.
-};
-
-PersistentHistogramAllocator::Iterator::Iterator(
- PersistentHistogramAllocator* allocator)
- : allocator_(allocator), memory_iter_(allocator->memory_allocator()) {}
-
-std::unique_ptr<HistogramBase>
-PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) {
- PersistentMemoryAllocator::Reference ref;
- while ((ref = memory_iter_.GetNextOfType<PersistentHistogramData>()) != 0) {
- if (ref != ignore)
- return allocator_->GetHistogram(ref);
- }
- return nullptr;
-}
-
-
-PersistentHistogramAllocator::PersistentHistogramAllocator(
- std::unique_ptr<PersistentMemoryAllocator> memory)
- : memory_allocator_(std::move(memory)),
- sparse_histogram_data_manager_(memory_allocator_.get()) {}
-
-PersistentHistogramAllocator::~PersistentHistogramAllocator() = default;
-
-std::unique_ptr<HistogramBase> PersistentHistogramAllocator::GetHistogram(
- Reference ref) {
- // Unfortunately, the histogram "pickle" methods cannot be used as part of
- // the persistance because the deserialization methods always create local
- // count data (while these must reference the persistent counts) and always
- // add it to the local list of known histograms (while these may be simple
- // references to histograms in other processes).
- PersistentHistogramData* data =
- memory_allocator_->GetAsObject<PersistentHistogramData>(ref);
- const size_t length = memory_allocator_->GetAllocSize(ref);
-
- // Check that metadata is reasonable: name is null-terminated and non-empty,
- // ID fields have been loaded with a hash of the name (0 is considered
- // unset/invalid).
- if (!data || data->name[0] == '\0' ||
- reinterpret_cast<char*>(data)[length - 1] != '\0' ||
- data->samples_metadata.id == 0 || data->logged_metadata.id == 0 ||
- // Note: Sparse histograms use |id + 1| in |logged_metadata|.
- (data->logged_metadata.id != data->samples_metadata.id &&
- data->logged_metadata.id != data->samples_metadata.id + 1) ||
- // Most non-matching values happen due to truncated names. Ideally, we
- // could just verify the name length based on the overall alloc length,
- // but that doesn't work because the allocated block may have been
- // aligned to the next boundary value.
- HashMetricName(data->name) != data->samples_metadata.id) {
- NOTREACHED();
- return nullptr;
- }
- return CreateHistogram(data);
-}
-
-std::unique_ptr<HistogramBase> PersistentHistogramAllocator::AllocateHistogram(
- HistogramType histogram_type,
- const std::string& name,
- int minimum,
- int maximum,
- const BucketRanges* bucket_ranges,
- int32_t flags,
- Reference* ref_ptr) {
- // If the allocator is corrupt, don't waste time trying anything else.
- // This also allows differentiating on the dashboard between allocations
- // failed due to a corrupt allocator and the number of process instances
- // with one, the latter being idicated by "newly corrupt", below.
- if (memory_allocator_->IsCorrupt())
- return nullptr;
-
- // Create the metadata necessary for a persistent sparse histogram. This
- // is done first because it is a small subset of what is required for
- // other histograms. The type is "under construction" so that a crash
- // during the datafill doesn't leave a bad record around that could cause
- // confusion by another process trying to read it. It will be corrected
- // once histogram construction is complete.
- PersistentHistogramData* histogram_data =
- memory_allocator_->New<PersistentHistogramData>(
- offsetof(PersistentHistogramData, name) + name.length() + 1);
- if (histogram_data) {
- memcpy(histogram_data->name, name.c_str(), name.size() + 1);
- histogram_data->histogram_type = histogram_type;
- histogram_data->flags = flags | HistogramBase::kIsPersistent;
- }
-
- // Create the remaining metadata necessary for regular histograms.
- if (histogram_type != SPARSE_HISTOGRAM) {
- size_t bucket_count = bucket_ranges->bucket_count();
- size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count);
- if (counts_bytes == 0) {
- // |bucket_count| was out-of-range.
- NOTREACHED();
- return nullptr;
- }
-
- // Since the StasticsRecorder keeps a global collection of BucketRanges
- // objects for re-use, it would be dangerous for one to hold a reference
- // from a persistent allocator that is not the global one (which is
- // permanent once set). If this stops being the case, this check can
- // become an "if" condition beside "!ranges_ref" below and before
- // set_persistent_reference() farther down.
- DCHECK_EQ(this, GlobalHistogramAllocator::Get());
-
- // Re-use an existing BucketRanges persistent allocation if one is known;
- // otherwise, create one.
- PersistentMemoryAllocator::Reference ranges_ref =
- bucket_ranges->persistent_reference();
- if (!ranges_ref) {
- size_t ranges_count = bucket_count + 1;
- size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample);
- ranges_ref =
- memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray);
- if (ranges_ref) {
- HistogramBase::Sample* ranges_data =
- memory_allocator_->GetAsArray<HistogramBase::Sample>(
- ranges_ref, kTypeIdRangesArray, ranges_count);
- if (ranges_data) {
- for (size_t i = 0; i < bucket_ranges->size(); ++i)
- ranges_data[i] = bucket_ranges->range(i);
- bucket_ranges->set_persistent_reference(ranges_ref);
- } else {
- // This should never happen but be tolerant if it does.
- NOTREACHED();
- ranges_ref = PersistentMemoryAllocator::kReferenceNull;
- }
- }
- } else {
- DCHECK_EQ(kTypeIdRangesArray, memory_allocator_->GetType(ranges_ref));
- }
-
-
- // Only continue here if all allocations were successful. If they weren't,
- // there is no way to free the space but that's not really a problem since
- // the allocations only fail because the space is full or corrupt and so
- // any future attempts will also fail.
- if (ranges_ref && histogram_data) {
- histogram_data->minimum = minimum;
- histogram_data->maximum = maximum;
- // |bucket_count| must fit within 32-bits or the allocation of the counts
- // array would have failed for being too large; the allocator supports
- // less than 4GB total size.
- histogram_data->bucket_count = static_cast<uint32_t>(bucket_count);
- histogram_data->ranges_ref = ranges_ref;
- histogram_data->ranges_checksum = bucket_ranges->checksum();
- } else {
- histogram_data = nullptr; // Clear this for proper handling below.
- }
- }
-
- if (histogram_data) {
- // Create the histogram using resources in persistent memory. This ends up
- // resolving the "ref" values stored in histogram_data instad of just
- // using what is already known above but avoids duplicating the switch
- // statement here and serves as a double-check that everything is
- // correct before commiting the new histogram to persistent space.
- std::unique_ptr<HistogramBase> histogram = CreateHistogram(histogram_data);
- DCHECK(histogram);
- DCHECK_NE(0U, histogram_data->samples_metadata.id);
- DCHECK_NE(0U, histogram_data->logged_metadata.id);
-
- PersistentMemoryAllocator::Reference histogram_ref =
- memory_allocator_->GetAsReference(histogram_data);
- if (ref_ptr != nullptr)
- *ref_ptr = histogram_ref;
-
- // By storing the reference within the allocator to this histogram, the
- // next import (which will happen before the next histogram creation)
- // will know to skip it.
- // See also the comment in ImportHistogramsToStatisticsRecorder().
- subtle::NoBarrier_Store(&last_created_, histogram_ref);
- return histogram;
- }
-
- if (memory_allocator_->IsCorrupt())
- NOTREACHED() << memory_allocator_->Name() << " is corrupt!";
-
- return nullptr;
-}
-
-void PersistentHistogramAllocator::FinalizeHistogram(Reference ref,
- bool registered) {
- if (registered) {
- // If the created persistent histogram was registered then it needs to
- // be marked as "iterable" in order to be found by other processes. This
- // happens only after the histogram is fully formed so it's impossible for
- // code iterating through the allocator to read a partially created record.
- memory_allocator_->MakeIterable(ref);
- } else {
- // If it wasn't registered then a race condition must have caused two to
- // be created. The allocator does not support releasing the acquired memory
- // so just change the type to be empty.
- memory_allocator_->ChangeType(ref, 0,
- PersistentHistogramData::kPersistentTypeId,
- /*clear=*/false);
- }
-}
-
-void PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder(
- HistogramBase* histogram) {
- DCHECK(histogram);
-
- HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram);
- if (!existing) {
- // The above should never fail but if it does, no real harm is done.
- // The data won't be merged but it also won't be recorded as merged
- // so a future try, if successful, will get what was missed. If it
- // continues to fail, some metric data will be lost but that is better
- // than crashing.
- NOTREACHED();
- return;
- }
-
- // Merge the delta from the passed object to the one in the SR.
- existing->AddSamples(*histogram->SnapshotDelta());
-}
-
-void PersistentHistogramAllocator::MergeHistogramFinalDeltaToStatisticsRecorder(
- const HistogramBase* histogram) {
- DCHECK(histogram);
-
- HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram);
- if (!existing) {
- // The above should never fail but if it does, no real harm is done.
- // Some metric data will be lost but that is better than crashing.
- NOTREACHED();
- return;
- }
-
- // Merge the delta from the passed object to the one in the SR.
- existing->AddSamples(*histogram->SnapshotFinalDelta());
-}
-
-PersistentSampleMapRecords* PersistentHistogramAllocator::UseSampleMapRecords(
- uint64_t id,
- const void* user) {
- return sparse_histogram_data_manager_.UseSampleMapRecords(id, user);
-}
-
-void PersistentHistogramAllocator::CreateTrackingHistograms(StringPiece name) {
- memory_allocator_->CreateTrackingHistograms(name);
-}
-
-void PersistentHistogramAllocator::UpdateTrackingHistograms() {
- memory_allocator_->UpdateTrackingHistograms();
-}
-
-void PersistentHistogramAllocator::ClearLastCreatedReferenceForTesting() {
- subtle::NoBarrier_Store(&last_created_, 0);
-}
-
-std::unique_ptr<HistogramBase> PersistentHistogramAllocator::CreateHistogram(
- PersistentHistogramData* histogram_data_ptr) {
- if (!histogram_data_ptr) {
- NOTREACHED();
- return nullptr;
- }
-
- // Sparse histograms are quite different so handle them as a special case.
- if (histogram_data_ptr->histogram_type == SPARSE_HISTOGRAM) {
- std::unique_ptr<HistogramBase> histogram =
- SparseHistogram::PersistentCreate(this, histogram_data_ptr->name,
- &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- histogram->SetFlags(histogram_data_ptr->flags);
- return histogram;
- }
-
- // Copy the configuration fields from histogram_data_ptr to local storage
- // because anything in persistent memory cannot be trusted as it could be
- // changed at any moment by a malicious actor that shares access. The local
- // values are validated below and then used to create the histogram, knowing
- // they haven't changed between validation and use.
- int32_t histogram_type = histogram_data_ptr->histogram_type;
- int32_t histogram_flags = histogram_data_ptr->flags;
- int32_t histogram_minimum = histogram_data_ptr->minimum;
- int32_t histogram_maximum = histogram_data_ptr->maximum;
- uint32_t histogram_bucket_count = histogram_data_ptr->bucket_count;
- uint32_t histogram_ranges_ref = histogram_data_ptr->ranges_ref;
- uint32_t histogram_ranges_checksum = histogram_data_ptr->ranges_checksum;
-
- HistogramBase::Sample* ranges_data =
- memory_allocator_->GetAsArray<HistogramBase::Sample>(
- histogram_ranges_ref, kTypeIdRangesArray,
- PersistentMemoryAllocator::kSizeAny);
-
- const uint32_t max_buckets =
- std::numeric_limits<uint32_t>::max() / sizeof(HistogramBase::Sample);
- size_t required_bytes =
- (histogram_bucket_count + 1) * sizeof(HistogramBase::Sample);
- size_t allocated_bytes =
- memory_allocator_->GetAllocSize(histogram_ranges_ref);
- if (!ranges_data || histogram_bucket_count < 2 ||
- histogram_bucket_count >= max_buckets ||
- allocated_bytes < required_bytes) {
- NOTREACHED();
- return nullptr;
- }
-
- std::unique_ptr<const BucketRanges> created_ranges = CreateRangesFromData(
- ranges_data, histogram_ranges_checksum, histogram_bucket_count + 1);
- if (!created_ranges) {
- NOTREACHED();
- return nullptr;
- }
- const BucketRanges* ranges =
- StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
- created_ranges.release());
-
- size_t counts_bytes = CalculateRequiredCountsBytes(histogram_bucket_count);
- PersistentMemoryAllocator::Reference counts_ref =
- subtle::Acquire_Load(&histogram_data_ptr->counts_ref);
- if (counts_bytes == 0 ||
- (counts_ref != 0 &&
- memory_allocator_->GetAllocSize(counts_ref) < counts_bytes)) {
- NOTREACHED();
- return nullptr;
- }
-
- // The "counts" data (including both samples and logged samples) is a delayed
- // persistent allocation meaning that though its size and storage for a
- // reference is defined, no space is reserved until actually needed. When
- // it is needed, memory will be allocated from the persistent segment and
- // a reference to it stored at the passed address. Other threads can then
- // notice the valid reference and access the same data.
- DelayedPersistentAllocation counts_data(memory_allocator_.get(),
- &histogram_data_ptr->counts_ref,
- kTypeIdCountsArray, counts_bytes, 0);
-
- // A second delayed allocations is defined using the same reference storage
- // location as the first so the allocation of one will automatically be found
- // by the other. Within the block, the first half of the space is for "counts"
- // and the second half is for "logged counts".
- DelayedPersistentAllocation logged_data(
- memory_allocator_.get(), &histogram_data_ptr->counts_ref,
- kTypeIdCountsArray, counts_bytes, counts_bytes / 2,
- /*make_iterable=*/false);
-
- // Create the right type of histogram.
- const char* name = histogram_data_ptr->name;
- std::unique_ptr<HistogramBase> histogram;
- switch (histogram_type) {
- case HISTOGRAM:
- histogram = Histogram::PersistentCreate(
- name, histogram_minimum, histogram_maximum, ranges, counts_data,
- logged_data, &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- case LINEAR_HISTOGRAM:
- histogram = LinearHistogram::PersistentCreate(
- name, histogram_minimum, histogram_maximum, ranges, counts_data,
- logged_data, &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- case BOOLEAN_HISTOGRAM:
- histogram = BooleanHistogram::PersistentCreate(
- name, ranges, counts_data, logged_data,
- &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- case CUSTOM_HISTOGRAM:
- histogram = CustomHistogram::PersistentCreate(
- name, ranges, counts_data, logged_data,
- &histogram_data_ptr->samples_metadata,
- &histogram_data_ptr->logged_metadata);
- DCHECK(histogram);
- break;
- default:
- NOTREACHED();
- }
-
- if (histogram) {
- DCHECK_EQ(histogram_type, histogram->GetHistogramType());
- histogram->SetFlags(histogram_flags);
- }
-
- return histogram;
-}
-
-HistogramBase*
-PersistentHistogramAllocator::GetOrCreateStatisticsRecorderHistogram(
- const HistogramBase* histogram) {
- // This should never be called on the global histogram allocator as objects
- // created there are already within the global statistics recorder.
- DCHECK_NE(GlobalHistogramAllocator::Get(), this);
- DCHECK(histogram);
-
- HistogramBase* existing =
- StatisticsRecorder::FindHistogram(histogram->histogram_name());
- if (existing)
- return existing;
-
- // Adding the passed histogram to the SR would cause a problem if the
- // allocator that holds it eventually goes away. Instead, create a new
- // one from a serialized version. Deserialization calls the appropriate
- // FactoryGet() which will create the histogram in the global persistent-
- // histogram allocator if such is set.
- base::Pickle pickle;
- histogram->SerializeInfo(&pickle);
- PickleIterator iter(pickle);
- existing = DeserializeHistogramInfo(&iter);
- if (!existing)
- return nullptr;
-
- // Make sure there is no "serialization" flag set.
- DCHECK_EQ(0, existing->flags() & HistogramBase::kIPCSerializationSourceFlag);
- // Record the newly created histogram in the SR.
- return StatisticsRecorder::RegisterOrDeleteDuplicate(existing);
-}
-
-GlobalHistogramAllocator::~GlobalHistogramAllocator() = default;
-
-// static
-void GlobalHistogramAllocator::CreateWithPersistentMemory(
- void* base,
- size_t size,
- size_t page_size,
- uint64_t id,
- StringPiece name) {
- Set(WrapUnique(
- new GlobalHistogramAllocator(std::make_unique<PersistentMemoryAllocator>(
- base, size, page_size, id, name, false))));
-}
-
-// static
-void GlobalHistogramAllocator::CreateWithLocalMemory(
- size_t size,
- uint64_t id,
- StringPiece name) {
- Set(WrapUnique(new GlobalHistogramAllocator(
- std::make_unique<LocalPersistentMemoryAllocator>(size, id, name))));
-}
-
-#if !defined(OS_NACL)
-// static
-bool GlobalHistogramAllocator::CreateWithFile(
- const FilePath& file_path,
- size_t size,
- uint64_t id,
- StringPiece name) {
- bool exists = PathExists(file_path);
- File file(
- file_path, File::FLAG_OPEN_ALWAYS | File::FLAG_SHARE_DELETE |
- File::FLAG_READ | File::FLAG_WRITE);
-
- std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
- if (exists) {
- size = saturated_cast<size_t>(file.GetLength());
- mmfile->Initialize(std::move(file), MemoryMappedFile::READ_WRITE);
- } else {
- mmfile->Initialize(std::move(file), {0, size},
- MemoryMappedFile::READ_WRITE_EXTEND);
- }
- if (!mmfile->IsValid() ||
- !FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
- NOTREACHED() << file_path;
- return false;
- }
-
- Set(WrapUnique(new GlobalHistogramAllocator(
- std::make_unique<FilePersistentMemoryAllocator>(std::move(mmfile), size,
- id, name, false))));
- Get()->SetPersistentLocation(file_path);
- return true;
-}
-
-// static
-bool GlobalHistogramAllocator::CreateWithActiveFile(const FilePath& base_path,
- const FilePath& active_path,
- const FilePath& spare_path,
- size_t size,
- uint64_t id,
- StringPiece name) {
- // Old "active" becomes "base".
- if (!base::ReplaceFile(active_path, base_path, nullptr))
- base::DeleteFile(base_path, /*recursive=*/false);
- DCHECK(!base::PathExists(active_path));
-
- // Move any "spare" into "active". Okay to continue if file doesn't exist.
- if (!spare_path.empty()) {
- base::ReplaceFile(spare_path, active_path, nullptr);
- DCHECK(!base::PathExists(spare_path));
- }
-
- return base::GlobalHistogramAllocator::CreateWithFile(active_path, size, id,
- name);
-}
-
-// static
-bool GlobalHistogramAllocator::CreateWithActiveFileInDir(const FilePath& dir,
- size_t size,
- uint64_t id,
- StringPiece name) {
- FilePath base_path, active_path, spare_path;
- ConstructFilePaths(dir, name, &base_path, &active_path, &spare_path);
- return CreateWithActiveFile(base_path, active_path, spare_path, size, id,
- name);
-}
-
-// static
-FilePath GlobalHistogramAllocator::ConstructFilePath(const FilePath& dir,
- StringPiece name) {
- return dir.AppendASCII(name).AddExtension(
- PersistentMemoryAllocator::kFileExtension);
-}
-
-// static
-FilePath GlobalHistogramAllocator::ConstructFilePathForUploadDir(
- const FilePath& dir,
- StringPiece name,
- base::Time stamp,
- ProcessId pid) {
- return ConstructFilePath(
- dir,
- StringPrintf("%.*s-%lX-%lX", static_cast<int>(name.length()), name.data(),
- static_cast<long>(stamp.ToTimeT()), static_cast<long>(pid)));
-}
-
-// static
-bool GlobalHistogramAllocator::ParseFilePath(const FilePath& path,
- std::string* out_name,
- Time* out_stamp,
- ProcessId* out_pid) {
- std::string filename = path.BaseName().AsUTF8Unsafe();
- std::vector<base::StringPiece> parts = base::SplitStringPiece(
- filename, "-.", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
- if (parts.size() != 4)
- return false;
-
- if (out_name)
- *out_name = parts[0].as_string();
-
- if (out_stamp) {
- int64_t stamp;
- if (!HexStringToInt64(parts[1], &stamp))
- return false;
- *out_stamp = Time::FromTimeT(static_cast<time_t>(stamp));
- }
-
- if (out_pid) {
- int64_t pid;
- if (!HexStringToInt64(parts[2], &pid))
- return false;
- *out_pid = static_cast<ProcessId>(pid);
- }
-
- return true;
-}
-
-// static
-void GlobalHistogramAllocator::ConstructFilePaths(const FilePath& dir,
- StringPiece name,
- FilePath* out_base_path,
- FilePath* out_active_path,
- FilePath* out_spare_path) {
- if (out_base_path)
- *out_base_path = ConstructFilePath(dir, name);
-
- if (out_active_path) {
- *out_active_path =
- ConstructFilePath(dir, name.as_string().append("-active"));
- }
-
- if (out_spare_path) {
- *out_spare_path = ConstructFilePath(dir, name.as_string().append("-spare"));
- }
-}
-
-// static
-void GlobalHistogramAllocator::ConstructFilePathsForUploadDir(
- const FilePath& active_dir,
- const FilePath& upload_dir,
- const std::string& name,
- FilePath* out_upload_path,
- FilePath* out_active_path,
- FilePath* out_spare_path) {
- if (out_upload_path) {
- *out_upload_path = ConstructFilePathForUploadDir(
- upload_dir, name, Time::Now(), GetCurrentProcId());
- }
-
- if (out_active_path) {
- *out_active_path =
- ConstructFilePath(active_dir, name + std::string("-active"));
- }
-
- if (out_spare_path) {
- *out_spare_path =
- ConstructFilePath(active_dir, name + std::string("-spare"));
- }
-}
-
-// static
-bool GlobalHistogramAllocator::CreateSpareFile(const FilePath& spare_path,
- size_t size) {
- FilePath temp_spare_path = spare_path.AddExtension(FILE_PATH_LITERAL(".tmp"));
- bool success = true;
- {
- File spare_file(temp_spare_path, File::FLAG_CREATE_ALWAYS |
- File::FLAG_READ | File::FLAG_WRITE);
- if (!spare_file.IsValid())
- return false;
-
- MemoryMappedFile mmfile;
- mmfile.Initialize(std::move(spare_file), {0, size},
- MemoryMappedFile::READ_WRITE_EXTEND);
- success = mmfile.IsValid();
- }
-
- if (success)
- success = ReplaceFile(temp_spare_path, spare_path, nullptr);
-
- if (!success)
- DeleteFile(temp_spare_path, /*recursive=*/false);
-
- return success;
-}
-
-// static
-bool GlobalHistogramAllocator::CreateSpareFileInDir(const FilePath& dir,
- size_t size,
- StringPiece name) {
- FilePath spare_path;
- ConstructFilePaths(dir, name, nullptr, nullptr, &spare_path);
- return CreateSpareFile(spare_path, size);
-}
-#endif // !defined(OS_NACL)
-
-// static
-void GlobalHistogramAllocator::CreateWithSharedMemoryHandle(
- const SharedMemoryHandle& handle,
- size_t size) {
- std::unique_ptr<SharedMemory> shm(
- new SharedMemory(handle, /*readonly=*/false));
- if (!shm->Map(size) ||
- !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) {
- NOTREACHED();
- return;
- }
-
- Set(WrapUnique(new GlobalHistogramAllocator(
- std::make_unique<SharedPersistentMemoryAllocator>(
- std::move(shm), 0, StringPiece(), /*readonly=*/false))));
-}
-
-// static
-void GlobalHistogramAllocator::Set(
- std::unique_ptr<GlobalHistogramAllocator> allocator) {
- // Releasing or changing an allocator is extremely dangerous because it
- // likely has histograms stored within it. If the backing memory is also
- // also released, future accesses to those histograms will seg-fault.
- CHECK(!subtle::NoBarrier_Load(&g_histogram_allocator));
- subtle::Release_Store(&g_histogram_allocator,
- reinterpret_cast<uintptr_t>(allocator.release()));
- size_t existing = StatisticsRecorder::GetHistogramCount();
-
- DVLOG_IF(1, existing)
- << existing << " histograms were created before persistence was enabled.";
-}
-
-// static
-GlobalHistogramAllocator* GlobalHistogramAllocator::Get() {
- return reinterpret_cast<GlobalHistogramAllocator*>(
- subtle::Acquire_Load(&g_histogram_allocator));
-}
-
-// static
-std::unique_ptr<GlobalHistogramAllocator>
-GlobalHistogramAllocator::ReleaseForTesting() {
- GlobalHistogramAllocator* histogram_allocator = Get();
- if (!histogram_allocator)
- return nullptr;
- PersistentMemoryAllocator* memory_allocator =
- histogram_allocator->memory_allocator();
-
- // Before releasing the memory, it's necessary to have the Statistics-
- // Recorder forget about the histograms contained therein; otherwise,
- // some operations will try to access them and the released memory.
- PersistentMemoryAllocator::Iterator iter(memory_allocator);
- const PersistentHistogramData* data;
- while ((data = iter.GetNextOfObject<PersistentHistogramData>()) != nullptr) {
- StatisticsRecorder::ForgetHistogramForTesting(data->name);
- }
-
- subtle::Release_Store(&g_histogram_allocator, 0);
- return WrapUnique(histogram_allocator);
-};
-
-void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) {
- persistent_location_ = location;
-}
-
-const FilePath& GlobalHistogramAllocator::GetPersistentLocation() const {
- return persistent_location_;
-}
-
-bool GlobalHistogramAllocator::WriteToPersistentLocation() {
-#if defined(OS_NACL)
- // NACL doesn't support file operations, including ImportantFileWriter.
- NOTREACHED();
- return false;
-#else
- // Stop if no destination is set.
- if (persistent_location_.empty()) {
- NOTREACHED() << "Could not write \"" << Name() << "\" persistent histograms"
- << " to file because no location was set.";
- return false;
- }
-
- StringPiece contents(static_cast<const char*>(data()), used());
- if (!ImportantFileWriter::WriteFileAtomically(persistent_location_,
- contents)) {
- LOG(ERROR) << "Could not write \"" << Name() << "\" persistent histograms"
- << " to file: " << persistent_location_.value();
- return false;
- }
-
- return true;
-#endif
-}
-
-void GlobalHistogramAllocator::DeletePersistentLocation() {
- memory_allocator()->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
-
-#if defined(OS_NACL)
- NOTREACHED();
-#else
- if (persistent_location_.empty())
- return;
-
- // Open (with delete) and then immediately close the file by going out of
- // scope. This is the only cross-platform safe way to delete a file that may
- // be open elsewhere. Open handles will continue to operate normally but
- // new opens will not be possible.
- File file(persistent_location_,
- File::FLAG_OPEN | File::FLAG_READ | File::FLAG_DELETE_ON_CLOSE);
-#endif
-}
-
-GlobalHistogramAllocator::GlobalHistogramAllocator(
- std::unique_ptr<PersistentMemoryAllocator> memory)
- : PersistentHistogramAllocator(std::move(memory)),
- import_iterator_(this) {
-}
-
-void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() {
- // Skip the import if it's the histogram that was last created. Should a
- // race condition cause the "last created" to be overwritten before it
- // is recognized here then the histogram will be created and be ignored
- // when it is detected as a duplicate by the statistics-recorder. This
- // simple check reduces the time of creating persistent histograms by
- // about 40%.
- Reference record_to_ignore = last_created();
-
- // There is no lock on this because the iterator is lock-free while still
- // guaranteed to only return each entry only once. The StatisticsRecorder
- // has its own lock so the Register operation is safe.
- while (true) {
- std::unique_ptr<HistogramBase> histogram =
- import_iterator_.GetNextWithIgnore(record_to_ignore);
- if (!histogram)
- break;
- StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release());
- }
-}
-
-} // namespace base
diff --git a/base/metrics/persistent_histogram_allocator.h b/base/metrics/persistent_histogram_allocator.h
deleted file mode 100644
index 395511f..0000000
--- a/base/metrics/persistent_histogram_allocator.h
+++ /dev/null
@@ -1,505 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_HISTOGRAM_PERSISTENCE_H_
-#define BASE_METRICS_HISTOGRAM_PERSISTENCE_H_
-
-#include <map>
-#include <memory>
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/feature_list.h"
-#include "base/memory/shared_memory.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/persistent_memory_allocator.h"
-#include "base/strings/string_piece.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-class BucketRanges;
-class FilePath;
-class PersistentSampleMapRecords;
-class PersistentSparseHistogramDataManager;
-
-// Feature definition for enabling histogram persistence.
-BASE_EXPORT extern const Feature kPersistentHistogramsFeature;
-
-
-// A data manager for sparse histograms so each instance of such doesn't have
-// to separately iterate over the entire memory segment. Though this class
-// will generally be accessed through the PersistentHistogramAllocator above,
-// it can be used independently on any PersistentMemoryAllocator (making it
-// useable for testing). This object supports only one instance of a sparse
-// histogram for a given id. Tests that create multiple identical histograms,
-// perhaps to simulate multiple processes, should create a separate manager
-// for each.
-class BASE_EXPORT PersistentSparseHistogramDataManager {
- public:
- // Constructs the data manager. The allocator must live longer than any
- // managers that reference it.
- explicit PersistentSparseHistogramDataManager(
- PersistentMemoryAllocator* allocator);
-
- ~PersistentSparseHistogramDataManager();
-
- // Returns the object that manages the persistent-sample-map records for a
- // given |id|. Only one |user| of this data is allowed at a time. This does
- // an automatic Acquire() on the records. The user must call Release() on
- // the returned object when it is finished with it. Ownership of the records
- // object stays with this manager.
- PersistentSampleMapRecords* UseSampleMapRecords(uint64_t id,
- const void* user);
-
- // Convenience method that gets the object for a given reference so callers
- // don't have to also keep their own pointer to the appropriate allocator.
- template <typename T>
- T* GetAsObject(PersistentMemoryAllocator::Reference ref) {
- return allocator_->GetAsObject<T>(ref);
- }
-
- private:
- friend class PersistentSampleMapRecords;
-
- // Gets the object holding records for a given sample-map id when |lock_|
- // has already been acquired.
- PersistentSampleMapRecords* GetSampleMapRecordsWhileLocked(uint64_t id);
-
- // Loads sample-map records looking for those belonging to the specified
- // |load_id|. Records found for other sample-maps are held for later use
- // without having to iterate again. This should be called only from a
- // PersistentSampleMapRecords object because those objects have a contract
- // that there are no other threads accessing the internal records_ field
- // of the object that is passed in.
- bool LoadRecords(PersistentSampleMapRecords* sample_map_records);
-
- // Weak-pointer to the allocator used by the sparse histograms.
- PersistentMemoryAllocator* allocator_;
-
- // Iterator within the allocator for finding sample records.
- PersistentMemoryAllocator::Iterator record_iterator_;
-
- // Mapping of sample-map IDs to their sample records.
- std::map<uint64_t, std::unique_ptr<PersistentSampleMapRecords>>
- sample_records_;
-
- // A lock used for synchronizing changes to sample_records_.
- base::Lock lock_;
-
- DISALLOW_COPY_AND_ASSIGN(PersistentSparseHistogramDataManager);
-};
-
-
-// This class manages sample-records used by a PersistentSampleMap container
-// that underlies a persistent SparseHistogram object. It is broken out into a
-// top-level class so that it can be forward-declared in other header files
-// rather than include this entire file as would be necessary if it were
-// declared within the PersistentSparseHistogramDataManager class above.
-class BASE_EXPORT PersistentSampleMapRecords {
- public:
- // Constructs an instance of this class. The manager object must live longer
- // than all instances of this class that reference it, which is not usually
- // a problem since these objects are generally managed from within that
- // manager instance.
- PersistentSampleMapRecords(PersistentSparseHistogramDataManager* data_manager,
- uint64_t sample_map_id);
-
- ~PersistentSampleMapRecords();
-
- // Resets the internal state for a new object using this data. The return
- // value is "this" as a convenience.
- PersistentSampleMapRecords* Acquire(const void* user);
-
- // Indicates that the using object is done with this data.
- void Release(const void* user);
-
- // Gets the next reference to a persistent sample-map record. The type and
- // layout of the data being referenced is defined entirely within the
- // PersistentSampleMap class.
- PersistentMemoryAllocator::Reference GetNext();
-
- // Creates a new persistent sample-map record for sample |value| and returns
- // a reference to it.
- PersistentMemoryAllocator::Reference CreateNew(HistogramBase::Sample value);
-
- // Convenience method that gets the object for a given reference so callers
- // don't have to also keep their own pointer to the appropriate allocator.
- // This is expected to be used with the SampleRecord structure defined inside
- // the persistent_sample_map.cc file but since that isn't exported (for
- // cleanliness of the interface), a template is defined that will be
- // resolved when used inside that file.
- template <typename T>
- T* GetAsObject(PersistentMemoryAllocator::Reference ref) {
- return data_manager_->GetAsObject<T>(ref);
- }
-
- private:
- friend PersistentSparseHistogramDataManager;
-
- // Weak-pointer to the parent data-manager object.
- PersistentSparseHistogramDataManager* data_manager_;
-
- // ID of PersistentSampleMap to which these records apply.
- const uint64_t sample_map_id_;
-
- // The current user of this set of records. It is used to ensure that no
- // more than one object is using these records at a given time.
- const void* user_ = nullptr;
-
- // This is the count of how many "records" have already been read by the
- // owning sample-map.
- size_t seen_ = 0;
-
- // This is the set of records previously found for a sample map. Because
- // there is ever only one object with a given ID (typically a hash of a
- // histogram name) and because the parent SparseHistogram has acquired
- // its own lock before accessing the PersistentSampleMap it controls, this
- // list can be accessed without acquiring any additional lock.
- std::vector<PersistentMemoryAllocator::Reference> records_;
-
- // This is the set of records found during iteration through memory. It
- // is appended in bulk to "records". Access to this vector can be done
- // only while holding the parent manager's lock.
- std::vector<PersistentMemoryAllocator::Reference> found_;
-
- DISALLOW_COPY_AND_ASSIGN(PersistentSampleMapRecords);
-};
-
-
-// This class manages histograms created within a PersistentMemoryAllocator.
-class BASE_EXPORT PersistentHistogramAllocator {
- public:
- // A reference to a histogram. While this is implemented as PMA::Reference,
- // it is not conceptually the same thing. Outside callers should always use
- // a Reference matching the class it is for and not mix the two.
- using Reference = PersistentMemoryAllocator::Reference;
-
- // Iterator used for fetching persistent histograms from an allocator.
- // It is lock-free and thread-safe.
- // See PersistentMemoryAllocator::Iterator for more information.
- class BASE_EXPORT Iterator {
- public:
- // Constructs an iterator on a given |allocator|, starting at the beginning.
- // The allocator must live beyond the lifetime of the iterator.
- explicit Iterator(PersistentHistogramAllocator* allocator);
-
- // Gets the next histogram from persistent memory; returns null if there
- // are no more histograms to be found. This may still be called again
- // later to retrieve any new histograms added in the meantime.
- std::unique_ptr<HistogramBase> GetNext() { return GetNextWithIgnore(0); }
-
- // Gets the next histogram from persistent memory, ignoring one particular
- // reference in the process. Pass |ignore| of zero (0) to ignore nothing.
- std::unique_ptr<HistogramBase> GetNextWithIgnore(Reference ignore);
-
- private:
- // Weak-pointer to histogram allocator being iterated over.
- PersistentHistogramAllocator* allocator_;
-
- // The iterator used for stepping through objects in persistent memory.
- // It is lock-free and thread-safe which is why this class is also such.
- PersistentMemoryAllocator::Iterator memory_iter_;
-
- DISALLOW_COPY_AND_ASSIGN(Iterator);
- };
-
- // A PersistentHistogramAllocator is constructed from a PersistentMemory-
- // Allocator object of which it takes ownership.
- explicit PersistentHistogramAllocator(
- std::unique_ptr<PersistentMemoryAllocator> memory);
- virtual ~PersistentHistogramAllocator();
-
- // Direct access to underlying memory allocator. If the segment is shared
- // across threads or processes, reading data through these values does
- // not guarantee consistency. Use with care. Do not write.
- PersistentMemoryAllocator* memory_allocator() {
- return memory_allocator_.get();
- }
-
- // Implement the "metadata" API of a PersistentMemoryAllocator, forwarding
- // those requests to the real one.
- uint64_t Id() const { return memory_allocator_->Id(); }
- const char* Name() const { return memory_allocator_->Name(); }
- const void* data() const { return memory_allocator_->data(); }
- size_t length() const { return memory_allocator_->length(); }
- size_t size() const { return memory_allocator_->size(); }
- size_t used() const { return memory_allocator_->used(); }
-
- // Recreate a Histogram from data held in persistent memory. Though this
- // object will be local to the current process, the sample data will be
- // shared with all other threads referencing it. This method takes a |ref|
- // to where the top-level histogram data may be found in this allocator.
- // This method will return null if any problem is detected with the data.
- std::unique_ptr<HistogramBase> GetHistogram(Reference ref);
-
- // Allocate a new persistent histogram. The returned histogram will not
- // be able to be located by other allocators until it is "finalized".
- std::unique_ptr<HistogramBase> AllocateHistogram(
- HistogramType histogram_type,
- const std::string& name,
- int minimum,
- int maximum,
- const BucketRanges* bucket_ranges,
- int32_t flags,
- Reference* ref_ptr);
-
- // Finalize the creation of the histogram, making it available to other
- // processes if |registered| (as in: added to the StatisticsRecorder) is
- // True, forgetting it otherwise.
- void FinalizeHistogram(Reference ref, bool registered);
-
- // Merges the data in a persistent histogram with one held globally by the
- // StatisticsRecorder, updating the "logged" samples within the passed
- // object so that repeated merges are allowed. Don't call this on a "global"
- // allocator because histograms created there will already be in the SR.
- void MergeHistogramDeltaToStatisticsRecorder(HistogramBase* histogram);
-
- // As above but merge the "final" delta. No update of "logged" samples is
- // done which means it can operate on read-only objects. It's essential,
- // however, not to call this more than once or those final samples will
- // get recorded again.
- void MergeHistogramFinalDeltaToStatisticsRecorder(
- const HistogramBase* histogram);
-
- // Returns the object that manages the persistent-sample-map records for a
- // given |id|. Only one |user| of this data is allowed at a time. This does
- // an automatic Acquire() on the records. The user must call Release() on
- // the returned object when it is finished with it. Ownership stays with
- // this allocator.
- PersistentSampleMapRecords* UseSampleMapRecords(uint64_t id,
- const void* user);
-
- // Create internal histograms for tracking memory use and allocation sizes
- // for allocator of |name| (which can simply be the result of Name()). This
- // is done seperately from construction for situations such as when the
- // histograms will be backed by memory provided by this very allocator.
- //
- // IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml
- // with the following histograms:
- // UMA.PersistentAllocator.name.Allocs
- // UMA.PersistentAllocator.name.UsedPct
- void CreateTrackingHistograms(StringPiece name);
- void UpdateTrackingHistograms();
-
- // Clears the internal |last_created_| reference so testing can validate
- // operation without that optimization.
- void ClearLastCreatedReferenceForTesting();
-
- protected:
- // The structure used to hold histogram data in persistent memory. It is
- // defined and used entirely within the .cc file.
- struct PersistentHistogramData;
-
- // Gets the reference of the last histogram created, used to avoid
- // trying to import what was just created.
- PersistentHistogramAllocator::Reference last_created() {
- return subtle::NoBarrier_Load(&last_created_);
- }
-
- // Gets the next histogram in persistent data based on iterator while
- // ignoring a particular reference if it is found.
- std::unique_ptr<HistogramBase> GetNextHistogramWithIgnore(Iterator* iter,
- Reference ignore);
-
- private:
- // Create a histogram based on saved (persistent) information about it.
- std::unique_ptr<HistogramBase> CreateHistogram(
- PersistentHistogramData* histogram_data_ptr);
-
- // Gets or creates an object in the global StatisticsRecorder matching
- // the |histogram| passed. Null is returned if one was not found and
- // one could not be created.
- HistogramBase* GetOrCreateStatisticsRecorderHistogram(
- const HistogramBase* histogram);
-
- // The memory allocator that provides the actual histogram storage.
- std::unique_ptr<PersistentMemoryAllocator> memory_allocator_;
-
- // The data-manager used to improve performance of sparse histograms.
- PersistentSparseHistogramDataManager sparse_histogram_data_manager_;
-
- // A reference to the last-created histogram in the allocator, used to avoid
- // trying to import what was just created.
- // TODO(bcwhite): Change this to std::atomic<PMA::Reference> when available.
- subtle::Atomic32 last_created_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(PersistentHistogramAllocator);
-};
-
-
-// A special case of the PersistentHistogramAllocator that operates on a
-// global scale, collecting histograms created through standard macros and
-// the FactoryGet() method.
-class BASE_EXPORT GlobalHistogramAllocator
- : public PersistentHistogramAllocator {
- public:
- ~GlobalHistogramAllocator() override;
-
- // Create a global allocator using the passed-in memory |base|, |size|, and
- // other parameters. Ownership of the memory segment remains with the caller.
- static void CreateWithPersistentMemory(void* base,
- size_t size,
- size_t page_size,
- uint64_t id,
- StringPiece name);
-
- // Create a global allocator using an internal block of memory of the
- // specified |size| taken from the heap.
- static void CreateWithLocalMemory(size_t size, uint64_t id, StringPiece name);
-
-#if !defined(OS_NACL)
- // Create a global allocator by memory-mapping a |file|. If the file does
- // not exist, it will be created with the specified |size|. If the file does
- // exist, the allocator will use and add to its contents, ignoring the passed
- // size in favor of the existing size. Returns whether the global allocator
- // was set.
- static bool CreateWithFile(const FilePath& file_path,
- size_t size,
- uint64_t id,
- StringPiece name);
-
- // Creates a new file at |active_path|. If it already exists, it will first be
- // moved to |base_path|. In all cases, any old file at |base_path| will be
- // removed. If |spare_path| is non-empty and exists, that will be renamed and
- // used as the active file. Otherwise, the file will be created using the
- // given size, id, and name. Returns whether the global allocator was set.
- static bool CreateWithActiveFile(const FilePath& base_path,
- const FilePath& active_path,
- const FilePath& spare_path,
- size_t size,
- uint64_t id,
- StringPiece name);
-
- // Uses ConstructBaseActivePairFilePaths() to build a pair of file names which
- // are then used for CreateWithActiveFile(). |name| is used for both the
- // internal name for the allocator and also for the name of the file inside
- // |dir|.
- static bool CreateWithActiveFileInDir(const FilePath& dir,
- size_t size,
- uint64_t id,
- StringPiece name);
-
- // Constructs a filename using a name.
- static FilePath ConstructFilePath(const FilePath& dir, StringPiece name);
-
- // Like above but with timestamp and pid for use in upload directories.
- static FilePath ConstructFilePathForUploadDir(const FilePath& dir,
- StringPiece name,
- base::Time stamp,
- ProcessId pid);
-
- // Parses a filename to extract name, timestamp, and pid.
- static bool ParseFilePath(const FilePath& path,
- std::string* out_name,
- Time* out_stamp,
- ProcessId* out_pid);
-
- // Constructs a set of names in |dir| based on name that can be used for a
- // base + active persistent memory mapped location for CreateWithActiveFile().
- // The spare path is a file that can be pre-created and moved to be active
- // without any startup penalty that comes from constructing the file. |name|
- // will be used as the basename of the file inside |dir|. |out_base_path|,
- // |out_active_path|, or |out_spare_path| may be null if not needed.
- static void ConstructFilePaths(const FilePath& dir,
- StringPiece name,
- FilePath* out_base_path,
- FilePath* out_active_path,
- FilePath* out_spare_path);
-
- // As above but puts the base files in a different "upload" directory. This
- // is useful when moving all completed files into a single directory for easy
- // upload management.
- static void ConstructFilePathsForUploadDir(const FilePath& active_dir,
- const FilePath& upload_dir,
- const std::string& name,
- FilePath* out_upload_path,
- FilePath* out_active_path,
- FilePath* out_spare_path);
-
- // Create a "spare" file that can later be made the "active" file. This
- // should be done on a background thread if possible.
- static bool CreateSpareFile(const FilePath& spare_path, size_t size);
-
- // Same as above but uses standard names. |name| is the name of the allocator
- // and is also used to create the correct filename.
- static bool CreateSpareFileInDir(const FilePath& dir_path,
- size_t size,
- StringPiece name);
-#endif
-
- // Create a global allocator using a block of shared memory accessed
- // through the given |handle| and |size|. The allocator takes ownership
- // of the handle and closes it upon destruction, though the memory will
- // continue to live if other processes have access to it.
- static void CreateWithSharedMemoryHandle(const SharedMemoryHandle& handle,
- size_t size);
-
- // Sets a GlobalHistogramAllocator for globally storing histograms in
- // a space that can be persisted or shared between processes. There is only
- // ever one allocator for all such histograms created by a single process.
- // This takes ownership of the object and should be called as soon as
- // possible during startup to capture as many histograms as possible and
- // while operating single-threaded so there are no race-conditions.
- static void Set(std::unique_ptr<GlobalHistogramAllocator> allocator);
-
- // Gets a pointer to the global histogram allocator. Returns null if none
- // exists.
- static GlobalHistogramAllocator* Get();
-
- // This access to the persistent allocator is only for testing; it extracts
- // the current allocator completely. This allows easy creation of histograms
- // within persistent memory segments which can then be extracted and used in
- // other ways.
- static std::unique_ptr<GlobalHistogramAllocator> ReleaseForTesting();
-
- // Stores a pathname to which the contents of this allocator should be saved
- // in order to persist the data for a later use.
- void SetPersistentLocation(const FilePath& location);
-
- // Retrieves a previously set pathname to which the contents of this allocator
- // are to be saved.
- const FilePath& GetPersistentLocation() const;
-
- // Writes the internal data to a previously set location. This is generally
- // called when a process is exiting from a section of code that may not know
- // the filesystem. The data is written in an atomic manner. The return value
- // indicates success.
- bool WriteToPersistentLocation();
-
- // If there is a global metrics file being updated on disk, mark it to be
- // deleted when the process exits.
- void DeletePersistentLocation();
-
- private:
- friend class StatisticsRecorder;
-
- // Creates a new global histogram allocator.
- explicit GlobalHistogramAllocator(
- std::unique_ptr<PersistentMemoryAllocator> memory);
-
- // Import new histograms from the global histogram allocator. It's possible
- // for other processes to create histograms in the active memory segment;
- // this adds those to the internal list of known histograms to avoid creating
- // duplicates that would have to be merged during reporting. Every call to
- // this method resumes from the last entry it saw; it costs nothing if
- // nothing new has been added.
- void ImportHistogramsToStatisticsRecorder();
-
- // Builds a FilePath for a metrics file.
- static FilePath MakeMetricsFilePath(const FilePath& dir, StringPiece name);
-
- // Import always continues from where it left off, making use of a single
- // iterator to continue the work.
- Iterator import_iterator_;
-
- // The location to which the data should be persisted.
- FilePath persistent_location_;
-
- DISALLOW_COPY_AND_ASSIGN(GlobalHistogramAllocator);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_HISTOGRAM_PERSISTENCE_H_
diff --git a/base/metrics/persistent_histogram_storage.cc b/base/metrics/persistent_histogram_storage.cc
deleted file mode 100644
index 8f527b0..0000000
--- a/base/metrics/persistent_histogram_storage.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// 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 "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/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "build_config.h"
-
-namespace {
-
-constexpr size_t kAllocSize = 1 << 20; // 1 MiB
-
-} // namespace
-
-namespace base {
-
-PersistentHistogramStorage::PersistentHistogramStorage(
- StringPiece allocator_name,
- StorageDirManagement storage_dir_management)
- : storage_dir_management_(storage_dir_management) {
- DCHECK(!allocator_name.empty());
- DCHECK(IsStringASCII(allocator_name));
-
- GlobalHistogramAllocator::CreateWithLocalMemory(kAllocSize,
- 0, // No identifier.
- allocator_name);
- GlobalHistogramAllocator::Get()->CreateTrackingHistograms(allocator_name);
-}
-
-PersistentHistogramStorage::~PersistentHistogramStorage() {
- PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
- allocator->UpdateTrackingHistograms();
-
- // TODO(chengx): Investigate making early return depend on whethere there are
- // metrics to report at this point or not.
- 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 current time as the filename. The actual filename
- // doesn't matter (so long as it ends with the correct extension) but this
- // works as well as anything.
- Time::Exploded exploded;
- Time::Now().LocalExplode(&exploded);
- const FilePath file_path =
- storage_dir
- .AppendASCII(StringPrintf("%04d%02d%02d%02d%02d%02d", exploded.year,
- exploded.month, exploded.day_of_month,
- exploded.hour, exploded.minute,
- exploded.second))
- .AddExtension(PersistentMemoryAllocator::kFileExtension);
-
- StringPiece contents(static_cast<const char*>(allocator->data()),
- allocator->used());
- if (!ImportantFileWriter::WriteFileAtomically(file_path, contents)) {
- LOG(ERROR) << "Persistent histograms fail to write to file: "
- << file_path.value();
- }
-}
-
-} // namespace base
diff --git a/base/metrics/persistent_histogram_storage.h b/base/metrics/persistent_histogram_storage.h
deleted file mode 100644
index 397236d..0000000
--- a/base/metrics/persistent_histogram_storage.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_PERSISTENT_HISTOGRAM_STORAGE_H_
-#define BASE_METRICS_PERSISTENT_HISTOGRAM_STORAGE_H_
-
-#include "base/base_export.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-// This class creates a fixed sized persistent memory to allow histograms to be
-// stored in it. When a PersistentHistogramStorage is destructed, histograms
-// recorded during its lifetime are persisted in the directory
-// |storage_base_dir_|/|allocator_name| (see the ctor for allocator_name).
-// Histograms are not persisted if the storage directory does not exist on
-// destruction. PersistentHistogramStorage should be instantiated as early as
-// possible in the process lifetime and should never be instantiated again.
-// Persisted histograms will eventually be reported by Chrome.
-class BASE_EXPORT PersistentHistogramStorage {
- public:
- enum class StorageDirManagement { kCreate, kUseExisting };
-
- // Creates a process-wide storage location for histograms that will be written
- // to a file within a directory provided by |set_storage_base_dir()| on
- // destruction.
- // The |allocator_name| is used both as an internal name for the allocator,
- // well as the leaf directory name for the file to which the histograms are
- // persisted. The string must be ASCII.
- // |storage_dir_management| specifies if this instance reuses an existing
- // storage directory, or is responsible for creating one.
- PersistentHistogramStorage(StringPiece allocator_name,
- StorageDirManagement storage_dir_management);
-
- ~PersistentHistogramStorage();
-
- // The storage directory isn't always known during initial construction so
- // it's set separately. The last one wins if there are multiple calls to this
- // method.
- void set_storage_base_dir(const FilePath& storage_base_dir) {
- storage_base_dir_ = storage_base_dir;
- }
-
- // Disables histogram storage.
- void Disable() { disabled_ = true; }
-
- private:
- // Metrics files are written into directory
- // |storage_base_dir_|/|allocator_name| (see the ctor for allocator_name).
- FilePath storage_base_dir_;
-
- // The setting of the storage directory management.
- const StorageDirManagement storage_dir_management_;
-
- // A flag indicating if histogram storage is disabled. It starts with false,
- // but can be set to true by the caller who decides to throw away its
- // histogram data.
- bool disabled_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(PersistentHistogramStorage);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_PERSISTENT_HISTOGRAM_STORAGE_H_
diff --git a/base/metrics/persistent_memory_allocator.cc b/base/metrics/persistent_memory_allocator.cc
deleted file mode 100644
index 7d9c03d..0000000
--- a/base/metrics/persistent_memory_allocator.cc
+++ /dev/null
@@ -1,1204 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/persistent_memory_allocator.h"
-
-#include <assert.h>
-#include <algorithm>
-
-#if defined(OS_WIN)
-#include <windows.h>
-#include "winbase.h"
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <sys/mman.h>
-#endif
-
-#include "base/files/memory_mapped_file.h"
-#include "base/logging.h"
-#include "base/memory/shared_memory.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/sparse_histogram.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/sys_info.h"
-#include "base/threading/thread_restrictions.h"
-#include "build_config.h"
-
-namespace {
-
-// Limit of memory segment size. It has to fit in an unsigned 32-bit number
-// and should be a power of 2 in order to accomodate almost any page size.
-const uint32_t kSegmentMaxSize = 1 << 30; // 1 GiB
-
-// A constant (random) value placed in the shared metadata to identify
-// an already initialized memory segment.
-const uint32_t kGlobalCookie = 0x408305DC;
-
-// The current version of the metadata. If updates are made that change
-// the metadata, the version number can be queried to operate in a backward-
-// compatible manner until the memory segment is completely re-initalized.
-const uint32_t kGlobalVersion = 2;
-
-// Constant values placed in the block headers to indicate its state.
-const uint32_t kBlockCookieFree = 0;
-const uint32_t kBlockCookieQueue = 1;
-const uint32_t kBlockCookieWasted = (uint32_t)-1;
-const uint32_t kBlockCookieAllocated = 0xC8799269;
-
-// TODO(bcwhite): When acceptable, consider moving flags to std::atomic<char>
-// types rather than combined bitfield.
-
-// Flags stored in the flags_ field of the SharedMetadata structure below.
-enum : int {
- kFlagCorrupt = 1 << 0,
- kFlagFull = 1 << 1
-};
-
-// Errors that are logged in "errors" histogram.
-enum AllocatorError : int {
- kMemoryIsCorrupt = 1,
-};
-
-bool CheckFlag(const volatile std::atomic<uint32_t>* flags, int flag) {
- uint32_t loaded_flags = flags->load(std::memory_order_relaxed);
- return (loaded_flags & flag) != 0;
-}
-
-void SetFlag(volatile std::atomic<uint32_t>* flags, int flag) {
- uint32_t loaded_flags = flags->load(std::memory_order_relaxed);
- for (;;) {
- uint32_t new_flags = (loaded_flags & ~flag) | flag;
- // In the failue case, actual "flags" value stored in loaded_flags.
- // These access are "relaxed" because they are completely independent
- // of all other values.
- if (flags->compare_exchange_weak(loaded_flags, new_flags,
- std::memory_order_relaxed,
- std::memory_order_relaxed)) {
- break;
- }
- }
-}
-
-} // namespace
-
-namespace base {
-
-// All allocations and data-structures must be aligned to this byte boundary.
-// Alignment as large as the physical bus between CPU and RAM is _required_
-// for some architectures, is simply more efficient on other CPUs, and
-// generally a Good Idea(tm) for all platforms as it reduces/eliminates the
-// chance that a type will span cache lines. Alignment mustn't be less
-// than 8 to ensure proper alignment for all types. The rest is a balance
-// between reducing spans across multiple cache lines and wasted space spent
-// padding out allocations. An alignment of 16 would ensure that the block
-// header structure always sits in a single cache line. An average of about
-// 1/2 this value will be wasted with every allocation.
-const uint32_t PersistentMemoryAllocator::kAllocAlignment = 8;
-
-// The block-header is placed at the top of every allocation within the
-// segment to describe the data that follows it.
-struct PersistentMemoryAllocator::BlockHeader {
- uint32_t size; // Number of bytes in this block, including header.
- uint32_t cookie; // Constant value indicating completed allocation.
- std::atomic<uint32_t> type_id; // Arbitrary number indicating data type.
- std::atomic<uint32_t> next; // Pointer to the next block when iterating.
-};
-
-// The shared metadata exists once at the top of the memory segment to
-// describe the state of the allocator to all processes. The size of this
-// structure must be a multiple of 64-bits to ensure compatibility between
-// architectures.
-struct PersistentMemoryAllocator::SharedMetadata {
- uint32_t cookie; // Some value that indicates complete initialization.
- uint32_t size; // Total size of memory segment.
- uint32_t page_size; // Paging size within memory segment.
- uint32_t version; // Version code so upgrades don't break.
- uint64_t id; // Arbitrary ID number given by creator.
- uint32_t name; // Reference to stored name string.
- uint32_t padding1; // Pad-out read-only data to 64-bit alignment.
-
- // Above is read-only after first construction. Below may be changed and
- // so must be marked "volatile" to provide correct inter-process behavior.
-
- // State of the memory, plus some padding to keep alignment.
- volatile std::atomic<uint8_t> memory_state; // MemoryState enum values.
- uint8_t padding2[3];
-
- // Bitfield of information flags. Access to this should be done through
- // the CheckFlag() and SetFlag() methods defined above.
- volatile std::atomic<uint32_t> flags;
-
- // Offset/reference to first free space in segment.
- volatile std::atomic<uint32_t> freeptr;
-
- // The "iterable" queue is an M&S Queue as described here, append-only:
- // https://www.research.ibm.com/people/m/michael/podc-1996.pdf
- // |queue| needs to be 64-bit aligned and is itself a multiple of 64 bits.
- volatile std::atomic<uint32_t> tailptr; // Last block of iteration queue.
- volatile BlockHeader queue; // Empty block for linked-list head/tail.
-};
-
-// The "queue" block header is used to detect "last node" so that zero/null
-// can be used to indicate that it hasn't been added at all. It is part of
-// the SharedMetadata structure which itself is always located at offset zero.
-const PersistentMemoryAllocator::Reference
- PersistentMemoryAllocator::kReferenceQueue =
- offsetof(SharedMetadata, queue);
-
-const base::FilePath::CharType PersistentMemoryAllocator::kFileExtension[] =
- FILE_PATH_LITERAL(".pma");
-
-
-PersistentMemoryAllocator::Iterator::Iterator(
- const PersistentMemoryAllocator* allocator)
- : allocator_(allocator), last_record_(kReferenceQueue), record_count_(0) {}
-
-PersistentMemoryAllocator::Iterator::Iterator(
- const PersistentMemoryAllocator* allocator,
- Reference starting_after)
- : allocator_(allocator), last_record_(0), record_count_(0) {
- Reset(starting_after);
-}
-
-void PersistentMemoryAllocator::Iterator::Reset() {
- last_record_.store(kReferenceQueue, std::memory_order_relaxed);
- record_count_.store(0, std::memory_order_relaxed);
-}
-
-void PersistentMemoryAllocator::Iterator::Reset(Reference starting_after) {
- if (starting_after == 0) {
- Reset();
- return;
- }
-
- last_record_.store(starting_after, std::memory_order_relaxed);
- record_count_.store(0, std::memory_order_relaxed);
-
- // Ensure that the starting point is a valid, iterable block (meaning it can
- // be read and has a non-zero "next" pointer).
- const volatile BlockHeader* block =
- allocator_->GetBlock(starting_after, 0, 0, false, false);
- if (!block || block->next.load(std::memory_order_relaxed) == 0) {
- NOTREACHED();
- last_record_.store(kReferenceQueue, std::memory_order_release);
- }
-}
-
-PersistentMemoryAllocator::Reference
-PersistentMemoryAllocator::Iterator::GetLast() {
- Reference last = last_record_.load(std::memory_order_relaxed);
- if (last == kReferenceQueue)
- return kReferenceNull;
- return last;
-}
-
-PersistentMemoryAllocator::Reference
-PersistentMemoryAllocator::Iterator::GetNext(uint32_t* type_return) {
- // Make a copy of the existing count of found-records, acquiring all changes
- // made to the allocator, notably "freeptr" (see comment in loop for why
- // the load of that value cannot be moved above here) that occurred during
- // any previous runs of this method, including those by parallel threads
- // that interrupted it. It pairs with the Release at the end of this method.
- //
- // Otherwise, if the compiler were to arrange the two loads such that
- // "count" was fetched _after_ "freeptr" then it would be possible for
- // this thread to be interrupted between them and other threads perform
- // multiple allocations, make-iterables, and iterations (with the included
- // increment of |record_count_|) culminating in the check at the bottom
- // mistakenly determining that a loop exists. Isn't this stuff fun?
- uint32_t count = record_count_.load(std::memory_order_acquire);
-
- Reference last = last_record_.load(std::memory_order_acquire);
- Reference next;
- while (true) {
- const volatile BlockHeader* block =
- allocator_->GetBlock(last, 0, 0, true, false);
- if (!block) // Invalid iterator state.
- return kReferenceNull;
-
- // The compiler and CPU can freely reorder all memory accesses on which
- // there are no dependencies. It could, for example, move the load of
- // "freeptr" to above this point because there are no explicit dependencies
- // between it and "next". If it did, however, then another block could
- // be queued after that but before the following load meaning there is
- // one more queued block than the future "detect loop by having more
- // blocks that could fit before freeptr" will allow.
- //
- // By "acquiring" the "next" value here, it's synchronized to the enqueue
- // of the node which in turn is synchronized to the allocation (which sets
- // freeptr). Thus, the scenario above cannot happen.
- next = block->next.load(std::memory_order_acquire);
- if (next == kReferenceQueue) // No next allocation in queue.
- return kReferenceNull;
- block = allocator_->GetBlock(next, 0, 0, false, false);
- if (!block) { // Memory is corrupt.
- allocator_->SetCorrupt();
- return kReferenceNull;
- }
-
- // Update the "last_record" pointer to be the reference being returned.
- // If it fails then another thread has already iterated past it so loop
- // again. Failing will also load the existing value into "last" so there
- // is no need to do another such load when the while-loop restarts. A
- // "strong" compare-exchange is used because failing unnecessarily would
- // mean repeating some fairly costly validations above.
- if (last_record_.compare_exchange_strong(
- last, next, std::memory_order_acq_rel, std::memory_order_acquire)) {
- *type_return = block->type_id.load(std::memory_order_relaxed);
- break;
- }
- }
-
- // Memory corruption could cause a loop in the list. Such must be detected
- // so as to not cause an infinite loop in the caller. This is done by simply
- // making sure it doesn't iterate more times than the absolute maximum
- // number of allocations that could have been made. Callers are likely
- // to loop multiple times before it is detected but at least it stops.
- const uint32_t freeptr = std::min(
- allocator_->shared_meta()->freeptr.load(std::memory_order_relaxed),
- allocator_->mem_size_);
- const uint32_t max_records =
- freeptr / (sizeof(BlockHeader) + kAllocAlignment);
- if (count > max_records) {
- allocator_->SetCorrupt();
- return kReferenceNull;
- }
-
- // Increment the count and release the changes made above. It pairs with
- // the Acquire at the top of this method. Note that this operation is not
- // strictly synchonized with fetching of the object to return, which would
- // have to be done inside the loop and is somewhat complicated to achieve.
- // It does not matter if it falls behind temporarily so long as it never
- // gets ahead.
- record_count_.fetch_add(1, std::memory_order_release);
- return next;
-}
-
-PersistentMemoryAllocator::Reference
-PersistentMemoryAllocator::Iterator::GetNextOfType(uint32_t type_match) {
- Reference ref;
- uint32_t type_found;
- while ((ref = GetNext(&type_found)) != 0) {
- if (type_found == type_match)
- return ref;
- }
- return kReferenceNull;
-}
-
-
-// static
-bool PersistentMemoryAllocator::IsMemoryAcceptable(const void* base,
- size_t size,
- size_t page_size,
- bool readonly) {
- return ((base && reinterpret_cast<uintptr_t>(base) % kAllocAlignment == 0) &&
- (size >= sizeof(SharedMetadata) && size <= kSegmentMaxSize) &&
- (size % kAllocAlignment == 0 || readonly) &&
- (page_size == 0 || size % page_size == 0 || readonly));
-}
-
-PersistentMemoryAllocator::PersistentMemoryAllocator(void* base,
- size_t size,
- size_t page_size,
- uint64_t id,
- base::StringPiece name,
- bool readonly)
- : PersistentMemoryAllocator(Memory(base, MEM_EXTERNAL),
- size,
- page_size,
- id,
- name,
- readonly) {}
-
-PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory,
- size_t size,
- size_t page_size,
- uint64_t id,
- base::StringPiece name,
- bool readonly)
- : mem_base_(static_cast<char*>(memory.base)),
- mem_type_(memory.type),
- mem_size_(static_cast<uint32_t>(size)),
- mem_page_(static_cast<uint32_t>((page_size ? page_size : size))),
-#if defined(OS_NACL)
- vm_page_size_(4096U), // SysInfo is not built for NACL.
-#else
- vm_page_size_(SysInfo::VMAllocationGranularity()),
-#endif
- readonly_(readonly),
- corrupt_(0),
- allocs_histogram_(nullptr),
- used_histogram_(nullptr),
- errors_histogram_(nullptr) {
- // These asserts ensure that the structures are 32/64-bit agnostic and meet
- // all the requirements of use within the allocator. They access private
- // definitions and so cannot be moved to the global scope.
- static_assert(sizeof(PersistentMemoryAllocator::BlockHeader) == 16,
- "struct is not portable across different natural word widths");
- static_assert(sizeof(PersistentMemoryAllocator::SharedMetadata) == 64,
- "struct is not portable across different natural word widths");
-
- static_assert(sizeof(BlockHeader) % kAllocAlignment == 0,
- "BlockHeader is not a multiple of kAllocAlignment");
- static_assert(sizeof(SharedMetadata) % kAllocAlignment == 0,
- "SharedMetadata is not a multiple of kAllocAlignment");
- static_assert(kReferenceQueue % kAllocAlignment == 0,
- "\"queue\" is not aligned properly; must be at end of struct");
-
- // Ensure that memory segment is of acceptable size.
- CHECK(IsMemoryAcceptable(memory.base, size, page_size, readonly));
-
- // These atomics operate inter-process and so must be lock-free. The local
- // casts are to make sure it can be evaluated at compile time to a constant.
- CHECK(((SharedMetadata*)nullptr)->freeptr.is_lock_free());
- CHECK(((SharedMetadata*)nullptr)->flags.is_lock_free());
- CHECK(((BlockHeader*)nullptr)->next.is_lock_free());
- CHECK(corrupt_.is_lock_free());
-
- if (shared_meta()->cookie != kGlobalCookie) {
- if (readonly) {
- SetCorrupt();
- return;
- }
-
- // This block is only executed when a completely new memory segment is
- // being initialized. It's unshared and single-threaded...
- volatile BlockHeader* const first_block =
- reinterpret_cast<volatile BlockHeader*>(mem_base_ +
- sizeof(SharedMetadata));
- if (shared_meta()->cookie != 0 ||
- shared_meta()->size != 0 ||
- shared_meta()->version != 0 ||
- shared_meta()->freeptr.load(std::memory_order_relaxed) != 0 ||
- shared_meta()->flags.load(std::memory_order_relaxed) != 0 ||
- shared_meta()->id != 0 ||
- shared_meta()->name != 0 ||
- shared_meta()->tailptr != 0 ||
- shared_meta()->queue.cookie != 0 ||
- shared_meta()->queue.next.load(std::memory_order_relaxed) != 0 ||
- first_block->size != 0 ||
- first_block->cookie != 0 ||
- first_block->type_id.load(std::memory_order_relaxed) != 0 ||
- first_block->next != 0) {
- // ...or something malicious has been playing with the metadata.
- SetCorrupt();
- }
-
- // This is still safe to do even if corruption has been detected.
- shared_meta()->cookie = kGlobalCookie;
- shared_meta()->size = mem_size_;
- shared_meta()->page_size = mem_page_;
- shared_meta()->version = kGlobalVersion;
- shared_meta()->id = id;
- shared_meta()->freeptr.store(sizeof(SharedMetadata),
- std::memory_order_release);
-
- // Set up the queue of iterable allocations.
- shared_meta()->queue.size = sizeof(BlockHeader);
- shared_meta()->queue.cookie = kBlockCookieQueue;
- shared_meta()->queue.next.store(kReferenceQueue, std::memory_order_release);
- shared_meta()->tailptr.store(kReferenceQueue, std::memory_order_release);
-
- // Allocate space for the name so other processes can learn it.
- if (!name.empty()) {
- const size_t name_length = name.length() + 1;
- shared_meta()->name = Allocate(name_length, 0);
- char* name_cstr = GetAsArray<char>(shared_meta()->name, 0, name_length);
- if (name_cstr)
- memcpy(name_cstr, name.data(), name.length());
- }
-
- shared_meta()->memory_state.store(MEMORY_INITIALIZED,
- std::memory_order_release);
- } else {
- if (shared_meta()->size == 0 || shared_meta()->version != kGlobalVersion ||
- shared_meta()->freeptr.load(std::memory_order_relaxed) == 0 ||
- shared_meta()->tailptr == 0 || shared_meta()->queue.cookie == 0 ||
- shared_meta()->queue.next.load(std::memory_order_relaxed) == 0) {
- SetCorrupt();
- }
- if (!readonly) {
- // The allocator is attaching to a previously initialized segment of
- // memory. If the initialization parameters differ, make the best of it
- // by reducing the local construction parameters to match those of
- // the actual memory area. This ensures that the local object never
- // tries to write outside of the original bounds.
- // Because the fields are const to ensure that no code other than the
- // constructor makes changes to them as well as to give optimization
- // hints to the compiler, it's necessary to const-cast them for changes
- // here.
- if (shared_meta()->size < mem_size_)
- *const_cast<uint32_t*>(&mem_size_) = shared_meta()->size;
- if (shared_meta()->page_size < mem_page_)
- *const_cast<uint32_t*>(&mem_page_) = shared_meta()->page_size;
-
- // Ensure that settings are still valid after the above adjustments.
- if (!IsMemoryAcceptable(memory.base, mem_size_, mem_page_, readonly))
- SetCorrupt();
- }
- }
-}
-
-PersistentMemoryAllocator::~PersistentMemoryAllocator() {
- // It's strictly forbidden to do any memory access here in case there is
- // some issue with the underlying memory segment. The "Local" allocator
- // makes use of this to allow deletion of the segment on the heap from
- // within its destructor.
-}
-
-uint64_t PersistentMemoryAllocator::Id() const {
- return shared_meta()->id;
-}
-
-const char* PersistentMemoryAllocator::Name() const {
- Reference name_ref = shared_meta()->name;
- const char* name_cstr =
- GetAsArray<char>(name_ref, 0, PersistentMemoryAllocator::kSizeAny);
- if (!name_cstr)
- return "";
-
- size_t name_length = GetAllocSize(name_ref);
- if (name_cstr[name_length - 1] != '\0') {
- NOTREACHED();
- SetCorrupt();
- return "";
- }
-
- return name_cstr;
-}
-
-void PersistentMemoryAllocator::CreateTrackingHistograms(
- base::StringPiece name) {
- if (name.empty() || readonly_)
- return;
- std::string name_string = name.as_string();
-
-#if 0
- // This histogram wasn't being used so has been disabled. It is left here
- // in case development of a new use of the allocator could benefit from
- // recording (temporarily and locally) the allocation sizes.
- DCHECK(!allocs_histogram_);
- allocs_histogram_ = Histogram::FactoryGet(
- "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50,
- HistogramBase::kUmaTargetedHistogramFlag);
-#endif
-
- DCHECK(!used_histogram_);
- used_histogram_ = LinearHistogram::FactoryGet(
- "UMA.PersistentAllocator." + name_string + ".UsedPct", 1, 101, 21,
- HistogramBase::kUmaTargetedHistogramFlag);
-
- DCHECK(!errors_histogram_);
- errors_histogram_ = SparseHistogram::FactoryGet(
- "UMA.PersistentAllocator." + name_string + ".Errors",
- HistogramBase::kUmaTargetedHistogramFlag);
-}
-
-void PersistentMemoryAllocator::Flush(bool sync) {
- FlushPartial(used(), sync);
-}
-
-void PersistentMemoryAllocator::SetMemoryState(uint8_t memory_state) {
- shared_meta()->memory_state.store(memory_state, std::memory_order_relaxed);
- FlushPartial(sizeof(SharedMetadata), false);
-}
-
-uint8_t PersistentMemoryAllocator::GetMemoryState() const {
- return shared_meta()->memory_state.load(std::memory_order_relaxed);
-}
-
-size_t PersistentMemoryAllocator::used() const {
- return std::min(shared_meta()->freeptr.load(std::memory_order_relaxed),
- mem_size_);
-}
-
-PersistentMemoryAllocator::Reference PersistentMemoryAllocator::GetAsReference(
- const void* memory,
- uint32_t type_id) const {
- uintptr_t address = reinterpret_cast<uintptr_t>(memory);
- if (address < reinterpret_cast<uintptr_t>(mem_base_))
- return kReferenceNull;
-
- uintptr_t offset = address - reinterpret_cast<uintptr_t>(mem_base_);
- if (offset >= mem_size_ || offset < sizeof(BlockHeader))
- return kReferenceNull;
-
- Reference ref = static_cast<Reference>(offset) - sizeof(BlockHeader);
- if (!GetBlockData(ref, type_id, kSizeAny))
- return kReferenceNull;
-
- return ref;
-}
-
-size_t PersistentMemoryAllocator::GetAllocSize(Reference ref) const {
- const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
- if (!block)
- return 0;
- uint32_t size = block->size;
- // Header was verified by GetBlock() but a malicious actor could change
- // the value between there and here. Check it again.
- if (size <= sizeof(BlockHeader) || ref + size > mem_size_) {
- SetCorrupt();
- return 0;
- }
- return size - sizeof(BlockHeader);
-}
-
-uint32_t PersistentMemoryAllocator::GetType(Reference ref) const {
- const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
- if (!block)
- return 0;
- return block->type_id.load(std::memory_order_relaxed);
-}
-
-bool PersistentMemoryAllocator::ChangeType(Reference ref,
- uint32_t to_type_id,
- uint32_t from_type_id,
- bool clear) {
- DCHECK(!readonly_);
- volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false);
- if (!block)
- return false;
-
- // "Strong" exchanges are used below because there is no loop that can retry
- // in the wake of spurious failures possible with "weak" exchanges. It is,
- // in aggregate, an "acquire-release" operation so no memory accesses can be
- // reordered either before or after this method (since changes based on type
- // could happen on either side).
-
- if (clear) {
- // If clearing the memory, first change it to the "transitioning" type so
- // there can be no confusion by other threads. After the memory is cleared,
- // it can be changed to its final type.
- if (!block->type_id.compare_exchange_strong(
- from_type_id, kTypeIdTransitioning, std::memory_order_acquire,
- std::memory_order_acquire)) {
- // Existing type wasn't what was expected: fail (with no changes)
- return false;
- }
-
- // Clear the memory in an atomic manner. Using "release" stores force
- // every write to be done after the ones before it. This is better than
- // using memset because (a) it supports "volatile" and (b) it creates a
- // reliable pattern upon which other threads may rely.
- volatile std::atomic<int>* data =
- reinterpret_cast<volatile std::atomic<int>*>(
- reinterpret_cast<volatile char*>(block) + sizeof(BlockHeader));
- const uint32_t words = (block->size - sizeof(BlockHeader)) / sizeof(int);
- DCHECK_EQ(0U, (block->size - sizeof(BlockHeader)) % sizeof(int));
- for (uint32_t i = 0; i < words; ++i) {
- data->store(0, std::memory_order_release);
- ++data;
- }
-
- // If the destination type is "transitioning" then skip the final exchange.
- if (to_type_id == kTypeIdTransitioning)
- return true;
-
- // Finish the change to the desired type.
- from_type_id = kTypeIdTransitioning; // Exchange needs modifiable original.
- bool success = block->type_id.compare_exchange_strong(
- from_type_id, to_type_id, std::memory_order_release,
- std::memory_order_relaxed);
- DCHECK(success); // Should never fail.
- return success;
- }
-
- // One step change to the new type. Will return false if the existing value
- // doesn't match what is expected.
- return block->type_id.compare_exchange_strong(from_type_id, to_type_id,
- std::memory_order_acq_rel,
- std::memory_order_acquire);
-}
-
-PersistentMemoryAllocator::Reference PersistentMemoryAllocator::Allocate(
- size_t req_size,
- uint32_t type_id) {
- Reference ref = AllocateImpl(req_size, type_id);
- if (ref) {
- // Success: Record this allocation in usage stats (if active).
- if (allocs_histogram_)
- allocs_histogram_->Add(static_cast<HistogramBase::Sample>(req_size));
- } else {
- // Failure: Record an allocation of zero for tracking.
- if (allocs_histogram_)
- allocs_histogram_->Add(0);
- }
- return ref;
-}
-
-PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl(
- size_t req_size,
- uint32_t type_id) {
- DCHECK(!readonly_);
-
- // Validate req_size to ensure it won't overflow when used as 32-bit value.
- if (req_size > kSegmentMaxSize - sizeof(BlockHeader)) {
- NOTREACHED();
- return kReferenceNull;
- }
-
- // Round up the requested size, plus header, to the next allocation alignment.
- uint32_t size = static_cast<uint32_t>(req_size + sizeof(BlockHeader));
- size = (size + (kAllocAlignment - 1)) & ~(kAllocAlignment - 1);
- if (size <= sizeof(BlockHeader) || size > mem_page_) {
- NOTREACHED();
- return kReferenceNull;
- }
-
- // Get the current start of unallocated memory. Other threads may
- // update this at any time and cause us to retry these operations.
- // This value should be treated as "const" to avoid confusion through
- // the code below but recognize that any failed compare-exchange operation
- // involving it will cause it to be loaded with a more recent value. The
- // code should either exit or restart the loop in that case.
- /* const */ uint32_t freeptr =
- shared_meta()->freeptr.load(std::memory_order_acquire);
-
- // Allocation is lockless so we do all our caculation and then, if saving
- // indicates a change has occurred since we started, scrap everything and
- // start over.
- for (;;) {
- if (IsCorrupt())
- return kReferenceNull;
-
- if (freeptr + size > mem_size_) {
- SetFlag(&shared_meta()->flags, kFlagFull);
- return kReferenceNull;
- }
-
- // Get pointer to the "free" block. If something has been allocated since
- // the load of freeptr above, it is still safe as nothing will be written
- // to that location until after the compare-exchange below.
- volatile BlockHeader* const block = GetBlock(freeptr, 0, 0, false, true);
- if (!block) {
- SetCorrupt();
- return kReferenceNull;
- }
-
- // An allocation cannot cross page boundaries. If it would, create a
- // "wasted" block and begin again at the top of the next page. This
- // area could just be left empty but we fill in the block header just
- // for completeness sake.
- const uint32_t page_free = mem_page_ - freeptr % mem_page_;
- if (size > page_free) {
- if (page_free <= sizeof(BlockHeader)) {
- SetCorrupt();
- return kReferenceNull;
- }
- const uint32_t new_freeptr = freeptr + page_free;
- if (shared_meta()->freeptr.compare_exchange_strong(
- freeptr, new_freeptr, std::memory_order_acq_rel,
- std::memory_order_acquire)) {
- block->size = page_free;
- block->cookie = kBlockCookieWasted;
- }
- continue;
- }
-
- // Don't leave a slice at the end of a page too small for anything. This
- // can result in an allocation up to two alignment-sizes greater than the
- // minimum required by requested-size + header + alignment.
- if (page_free - size < sizeof(BlockHeader) + kAllocAlignment)
- size = page_free;
-
- const uint32_t new_freeptr = freeptr + size;
- if (new_freeptr > mem_size_) {
- SetCorrupt();
- return kReferenceNull;
- }
-
- // Save our work. Try again if another thread has completed an allocation
- // while we were processing. A "weak" exchange would be permissable here
- // because the code will just loop and try again but the above processing
- // is significant so make the extra effort of a "strong" exchange.
- if (!shared_meta()->freeptr.compare_exchange_strong(
- freeptr, new_freeptr, std::memory_order_acq_rel,
- std::memory_order_acquire)) {
- continue;
- }
-
- // Given that all memory was zeroed before ever being given to an instance
- // of this class and given that we only allocate in a monotomic fashion
- // going forward, it must be that the newly allocated block is completely
- // full of zeros. If we find anything in the block header that is NOT a
- // zero then something must have previously run amuck through memory,
- // writing beyond the allocated space and into unallocated space.
- if (block->size != 0 ||
- block->cookie != kBlockCookieFree ||
- block->type_id.load(std::memory_order_relaxed) != 0 ||
- block->next.load(std::memory_order_relaxed) != 0) {
- SetCorrupt();
- return kReferenceNull;
- }
-
- // Make sure the memory exists by writing to the first byte of every memory
- // page it touches beyond the one containing the block header itself.
- // As the underlying storage is often memory mapped from disk or shared
- // space, sometimes things go wrong and those address don't actually exist
- // leading to a SIGBUS (or Windows equivalent) at some arbitrary location
- // in the code. This should concentrate all those failures into this
- // location for easy tracking and, eventually, proper handling.
- volatile char* mem_end = reinterpret_cast<volatile char*>(block) + size;
- volatile char* mem_begin = reinterpret_cast<volatile char*>(
- (reinterpret_cast<uintptr_t>(block) + sizeof(BlockHeader) +
- (vm_page_size_ - 1)) &
- ~static_cast<uintptr_t>(vm_page_size_ - 1));
- for (volatile char* memory = mem_begin; memory < mem_end;
- memory += vm_page_size_) {
- // It's required that a memory segment start as all zeros and thus the
- // newly allocated block is all zeros at this point. Thus, writing a
- // zero to it allows testing that the memory exists without actually
- // changing its contents. The compiler doesn't know about the requirement
- // and so cannot optimize-away these writes.
- *memory = 0;
- }
-
- // Load information into the block header. There is no "release" of the
- // data here because this memory can, currently, be seen only by the thread
- // performing the allocation. When it comes time to share this, the thread
- // will call MakeIterable() which does the release operation.
- block->size = size;
- block->cookie = kBlockCookieAllocated;
- block->type_id.store(type_id, std::memory_order_relaxed);
- return freeptr;
- }
-}
-
-void PersistentMemoryAllocator::GetMemoryInfo(MemoryInfo* meminfo) const {
- uint32_t remaining = std::max(
- mem_size_ - shared_meta()->freeptr.load(std::memory_order_relaxed),
- (uint32_t)sizeof(BlockHeader));
- meminfo->total = mem_size_;
- meminfo->free = remaining - sizeof(BlockHeader);
-}
-
-void PersistentMemoryAllocator::MakeIterable(Reference ref) {
- DCHECK(!readonly_);
- if (IsCorrupt())
- return;
- volatile BlockHeader* block = GetBlock(ref, 0, 0, false, false);
- if (!block) // invalid reference
- return;
- if (block->next.load(std::memory_order_acquire) != 0) // Already iterable.
- return;
- block->next.store(kReferenceQueue, std::memory_order_release); // New tail.
-
- // Try to add this block to the tail of the queue. May take multiple tries.
- // If so, tail will be automatically updated with a more recent value during
- // compare-exchange operations.
- uint32_t tail = shared_meta()->tailptr.load(std::memory_order_acquire);
- for (;;) {
- // Acquire the current tail-pointer released by previous call to this
- // method and validate it.
- block = GetBlock(tail, 0, 0, true, false);
- if (!block) {
- SetCorrupt();
- return;
- }
-
- // Try to insert the block at the tail of the queue. The tail node always
- // has an existing value of kReferenceQueue; if that is somehow not the
- // existing value then another thread has acted in the meantime. A "strong"
- // exchange is necessary so the "else" block does not get executed when
- // that is not actually the case (which can happen with a "weak" exchange).
- uint32_t next = kReferenceQueue; // Will get replaced with existing value.
- if (block->next.compare_exchange_strong(next, ref,
- std::memory_order_acq_rel,
- std::memory_order_acquire)) {
- // Update the tail pointer to the new offset. If the "else" clause did
- // not exist, then this could be a simple Release_Store to set the new
- // value but because it does, it's possible that other threads could add
- // one or more nodes at the tail before reaching this point. We don't
- // have to check the return value because it either operates correctly
- // or the exact same operation has already been done (by the "else"
- // clause) on some other thread.
- shared_meta()->tailptr.compare_exchange_strong(tail, ref,
- std::memory_order_release,
- std::memory_order_relaxed);
- return;
- } else {
- // In the unlikely case that a thread crashed or was killed between the
- // update of "next" and the update of "tailptr", it is necessary to
- // perform the operation that would have been done. There's no explicit
- // check for crash/kill which means that this operation may also happen
- // even when the other thread is in perfect working order which is what
- // necessitates the CompareAndSwap above.
- shared_meta()->tailptr.compare_exchange_strong(tail, next,
- std::memory_order_acq_rel,
- std::memory_order_acquire);
- }
- }
-}
-
-// The "corrupted" state is held both locally and globally (shared). The
-// shared flag can't be trusted since a malicious actor could overwrite it.
-// Because corruption can be detected during read-only operations such as
-// iteration, this method may be called by other "const" methods. In this
-// case, it's safe to discard the constness and modify the local flag and
-// maybe even the shared flag if the underlying data isn't actually read-only.
-void PersistentMemoryAllocator::SetCorrupt() const {
- if (!corrupt_.load(std::memory_order_relaxed) &&
- !CheckFlag(
- const_cast<volatile std::atomic<uint32_t>*>(&shared_meta()->flags),
- kFlagCorrupt)) {
- LOG(ERROR) << "Corruption detected in shared-memory segment.";
- RecordError(kMemoryIsCorrupt);
- }
-
- corrupt_.store(true, std::memory_order_relaxed);
- if (!readonly_) {
- SetFlag(const_cast<volatile std::atomic<uint32_t>*>(&shared_meta()->flags),
- kFlagCorrupt);
- }
-}
-
-bool PersistentMemoryAllocator::IsCorrupt() const {
- if (corrupt_.load(std::memory_order_relaxed) ||
- CheckFlag(&shared_meta()->flags, kFlagCorrupt)) {
- SetCorrupt(); // Make sure all indicators are set.
- return true;
- }
- return false;
-}
-
-bool PersistentMemoryAllocator::IsFull() const {
- return CheckFlag(&shared_meta()->flags, kFlagFull);
-}
-
-// Dereference a block |ref| and ensure that it's valid for the desired
-// |type_id| and |size|. |special| indicates that we may try to access block
-// headers not available to callers but still accessed by this module. By
-// having internal dereferences go through this same function, the allocator
-// is hardened against corruption.
-const volatile PersistentMemoryAllocator::BlockHeader*
-PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id,
- uint32_t size, bool queue_ok,
- bool free_ok) const {
- // Handle special cases.
- if (ref == kReferenceQueue && queue_ok)
- return reinterpret_cast<const volatile BlockHeader*>(mem_base_ + ref);
-
- // Validation of parameters.
- if (ref < sizeof(SharedMetadata))
- return nullptr;
- if (ref % kAllocAlignment != 0)
- return nullptr;
- size += sizeof(BlockHeader);
- if (ref + size > mem_size_)
- return nullptr;
-
- // Validation of referenced block-header.
- if (!free_ok) {
- const volatile BlockHeader* const block =
- reinterpret_cast<volatile BlockHeader*>(mem_base_ + ref);
- if (block->cookie != kBlockCookieAllocated)
- return nullptr;
- if (block->size < size)
- return nullptr;
- if (ref + block->size > mem_size_)
- return nullptr;
- if (type_id != 0 &&
- block->type_id.load(std::memory_order_relaxed) != type_id) {
- return nullptr;
- }
- }
-
- // Return pointer to block data.
- return reinterpret_cast<const volatile BlockHeader*>(mem_base_ + ref);
-}
-
-void PersistentMemoryAllocator::FlushPartial(size_t length, bool sync) {
- // Generally there is nothing to do as every write is done through volatile
- // memory with atomic instructions to guarantee consistency. This (virtual)
- // method exists so that derivced classes can do special things, such as
- // tell the OS to write changes to disk now rather than when convenient.
-}
-
-void PersistentMemoryAllocator::RecordError(int error) const {
- if (errors_histogram_)
- errors_histogram_->Add(error);
-}
-
-const volatile void* PersistentMemoryAllocator::GetBlockData(
- Reference ref,
- uint32_t type_id,
- uint32_t size) const {
- DCHECK(size > 0);
- const volatile BlockHeader* block =
- GetBlock(ref, type_id, size, false, false);
- if (!block)
- return nullptr;
- return reinterpret_cast<const volatile char*>(block) + sizeof(BlockHeader);
-}
-
-void PersistentMemoryAllocator::UpdateTrackingHistograms() {
- DCHECK(!readonly_);
- if (used_histogram_) {
- MemoryInfo meminfo;
- GetMemoryInfo(&meminfo);
- HistogramBase::Sample used_percent = static_cast<HistogramBase::Sample>(
- ((meminfo.total - meminfo.free) * 100ULL / meminfo.total));
- used_histogram_->Add(used_percent);
- }
-}
-
-
-//----- LocalPersistentMemoryAllocator -----------------------------------------
-
-LocalPersistentMemoryAllocator::LocalPersistentMemoryAllocator(
- size_t size,
- uint64_t id,
- base::StringPiece name)
- : PersistentMemoryAllocator(AllocateLocalMemory(size),
- size, 0, id, name, false) {}
-
-LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() {
- DeallocateLocalMemory(const_cast<char*>(mem_base_), mem_size_, mem_type_);
-}
-
-// static
-PersistentMemoryAllocator::Memory
-LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) {
- void* address;
-
-#if defined(OS_WIN)
- address =
- ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- if (address)
- return Memory(address, MEM_VIRTUAL);
- UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Win",
- ::GetLastError());
-#elif defined(OS_POSIX) || defined(OS_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 Memory(address, MEM_VIRTUAL);
- UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Posix",
- errno);
-#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 istead of only when first accessed).
- address = malloc(size);
- DPCHECK(address);
- memset(address, 0, size);
- return Memory(address, MEM_MALLOC);
-}
-
-// static
-void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory,
- size_t size,
- MemoryType type) {
- if (type == MEM_MALLOC) {
- free(memory);
- return;
- }
-
- DCHECK_EQ(MEM_VIRTUAL, type);
-#if defined(OS_WIN)
- BOOL success = ::VirtualFree(memory, 0, MEM_DECOMMIT);
- DCHECK(success);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- int result = ::munmap(memory, size);
- DCHECK_EQ(0, result);
-#else
-#error This architecture is not (yet) supported.
-#endif
-}
-
-
-//----- SharedPersistentMemoryAllocator ----------------------------------------
-
-SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator(
- std::unique_ptr<SharedMemory> memory,
- uint64_t id,
- base::StringPiece name,
- bool read_only)
- : PersistentMemoryAllocator(
- Memory(static_cast<uint8_t*>(memory->memory()), MEM_SHARED),
- memory->mapped_size(),
- 0,
- id,
- name,
- read_only),
- shared_memory_(std::move(memory)) {}
-
-SharedPersistentMemoryAllocator::~SharedPersistentMemoryAllocator() = default;
-
-// static
-bool SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(
- const SharedMemory& memory) {
- return IsMemoryAcceptable(memory.memory(), memory.mapped_size(), 0, false);
-}
-
-
-#if !defined(OS_NACL)
-//----- FilePersistentMemoryAllocator ------------------------------------------
-
-FilePersistentMemoryAllocator::FilePersistentMemoryAllocator(
- std::unique_ptr<MemoryMappedFile> file,
- size_t max_size,
- uint64_t id,
- base::StringPiece name,
- bool read_only)
- : PersistentMemoryAllocator(
- Memory(const_cast<uint8_t*>(file->data()), MEM_FILE),
- max_size != 0 ? max_size : file->length(),
- 0,
- id,
- name,
- read_only),
- mapped_file_(std::move(file)) {}
-
-FilePersistentMemoryAllocator::~FilePersistentMemoryAllocator() = default;
-
-// static
-bool FilePersistentMemoryAllocator::IsFileAcceptable(
- const MemoryMappedFile& file,
- bool read_only) {
- return IsMemoryAcceptable(file.data(), file.length(), 0, read_only);
-}
-
-void FilePersistentMemoryAllocator::FlushPartial(size_t length, bool sync) {
- if (sync)
- AssertBlockingAllowed();
- if (IsReadonly())
- return;
-
-#if defined(OS_WIN)
- // Windows doesn't support asynchronous flush.
- AssertBlockingAllowed();
- BOOL success = ::FlushViewOfFile(data(), length);
- DPCHECK(success);
-#elif defined(OS_MACOSX)
- // On OSX, "invalidate" removes all cached pages, forcing a re-read from
- // disk. That's not applicable to "flush" so omit it.
- int result =
- ::msync(const_cast<void*>(data()), length, sync ? MS_SYNC : MS_ASYNC);
- DCHECK_NE(EINVAL, result);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- // On POSIX, "invalidate" forces _other_ processes to recognize what has
- // been written to disk and so is applicable to "flush".
- int result = ::msync(const_cast<void*>(data()), length,
- MS_INVALIDATE | (sync ? MS_SYNC : MS_ASYNC));
- DCHECK_NE(EINVAL, result);
-#else
-#error Unsupported OS.
-#endif
-}
-#endif // !defined(OS_NACL)
-
-//----- DelayedPersistentAllocation --------------------------------------------
-
-// Forwarding constructors.
-DelayedPersistentAllocation::DelayedPersistentAllocation(
- PersistentMemoryAllocator* allocator,
- subtle::Atomic32* ref,
- uint32_t type,
- size_t size,
- bool make_iterable)
- : DelayedPersistentAllocation(
- allocator,
- reinterpret_cast<std::atomic<Reference>*>(ref),
- type,
- size,
- 0,
- make_iterable) {}
-
-DelayedPersistentAllocation::DelayedPersistentAllocation(
- PersistentMemoryAllocator* allocator,
- subtle::Atomic32* ref,
- uint32_t type,
- size_t size,
- size_t offset,
- bool make_iterable)
- : DelayedPersistentAllocation(
- allocator,
- reinterpret_cast<std::atomic<Reference>*>(ref),
- type,
- size,
- offset,
- make_iterable) {}
-
-DelayedPersistentAllocation::DelayedPersistentAllocation(
- PersistentMemoryAllocator* allocator,
- std::atomic<Reference>* ref,
- uint32_t type,
- size_t size,
- bool make_iterable)
- : DelayedPersistentAllocation(allocator,
- ref,
- type,
- size,
- 0,
- make_iterable) {}
-
-// Real constructor.
-DelayedPersistentAllocation::DelayedPersistentAllocation(
- PersistentMemoryAllocator* allocator,
- std::atomic<Reference>* ref,
- uint32_t type,
- size_t size,
- size_t offset,
- bool make_iterable)
- : allocator_(allocator),
- type_(type),
- size_(checked_cast<uint32_t>(size)),
- offset_(checked_cast<uint32_t>(offset)),
- make_iterable_(make_iterable),
- reference_(ref) {
- DCHECK(allocator_);
- DCHECK_NE(0U, type_);
- DCHECK_LT(0U, size_);
- DCHECK(reference_);
-}
-
-DelayedPersistentAllocation::~DelayedPersistentAllocation() = default;
-
-void* DelayedPersistentAllocation::Get() const {
- // Relaxed operations are acceptable here because it's not protecting the
- // contents of the allocation in any way.
- Reference ref = reference_->load(std::memory_order_acquire);
- if (!ref) {
- ref = allocator_->Allocate(size_, type_);
- if (!ref)
- return nullptr;
-
- // Store the new reference in its proper location using compare-and-swap.
- // Use a "strong" exchange to ensure no false-negatives since the operation
- // cannot be retried.
- Reference existing = 0; // Must be mutable; receives actual value.
- if (reference_->compare_exchange_strong(existing, ref,
- std::memory_order_release,
- std::memory_order_relaxed)) {
- if (make_iterable_)
- allocator_->MakeIterable(ref);
- } else {
- // Failure indicates that something else has raced ahead, performed the
- // allocation, and stored its reference. Purge the allocation that was
- // just done and use the other one instead.
- DCHECK_EQ(type_, allocator_->GetType(existing));
- DCHECK_LE(size_, allocator_->GetAllocSize(existing));
- allocator_->ChangeType(ref, 0, type_, /*clear=*/false);
- ref = existing;
- }
- }
-
- char* mem = allocator_->GetAsArray<char>(ref, type_, size_);
- if (!mem) {
- // This should never happen but be tolerant if it does as corruption from
- // the outside is something to guard against.
- NOTREACHED();
- return nullptr;
- }
- return mem + offset_;
-}
-
-} // namespace base
diff --git a/base/metrics/persistent_memory_allocator.h b/base/metrics/persistent_memory_allocator.h
deleted file mode 100644
index 978a362..0000000
--- a/base/metrics/persistent_memory_allocator.h
+++ /dev/null
@@ -1,872 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
-#define BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
-
-#include <stdint.h>
-
-#include <atomic>
-#include <memory>
-#include <type_traits>
-
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/files/file_path.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/strings/string_piece.h"
-
-namespace base {
-
-class HistogramBase;
-class MemoryMappedFile;
-class SharedMemory;
-
-// Simple allocator for pieces of a memory block that may be persistent
-// to some storage or shared across multiple processes. This class resides
-// under base/metrics because it was written for that purpose. It is,
-// however, fully general-purpose and can be freely moved to base/memory
-// if other uses are found.
-//
-// This class provides for thread-secure (i.e. safe against other threads
-// or processes that may be compromised and thus have malicious intent)
-// allocation of memory within a designated block and also a mechanism by
-// which other threads can learn of these allocations.
-//
-// There is (currently) no way to release an allocated block of data because
-// doing so would risk invalidating pointers held by other processes and
-// greatly complicate the allocation algorithm.
-//
-// Construction of this object can accept new, clean (i.e. zeroed) memory
-// or previously initialized memory. In the first case, construction must
-// be allowed to complete before letting other allocators attach to the same
-// segment. In other words, don't share the segment until at least one
-// allocator has been attached to it.
-//
-// Note that memory not in active use is not accessed so it is possible to
-// use virtual memory, including memory-mapped files, as backing storage with
-// the OS "pinning" new (zeroed) physical RAM pages only as they are needed.
-//
-// OBJECTS: Although the allocator can be used in a "malloc" sense, fetching
-// character arrays and manipulating that memory manually, the better way is
-// generally to use the "object" methods to create and manage allocations. In
-// this way the sizing, type-checking, and construction are all automatic. For
-// this to work, however, every type of stored object must define two public
-// "constexpr" values, kPersistentTypeId and kExpectedInstanceSize, as such:
-//
-// struct MyPersistentObjectType {
-// // SHA1(MyPersistentObjectType): Increment this if structure changes!
-// static constexpr uint32_t kPersistentTypeId = 0x3E15F6DE + 1;
-//
-// // Expected size for 32/64-bit check. Update this if structure changes!
-// static constexpr size_t kExpectedInstanceSize = 20;
-//
-// ...
-// };
-//
-// kPersistentTypeId: This value is an arbitrary identifier that allows the
-// identification of these objects in the allocator, including the ability
-// to find them via iteration. The number is arbitrary but using the first
-// four bytes of the SHA1 hash of the type name means that there shouldn't
-// be any conflicts with other types that may also be stored in the memory.
-// The fully qualified name (e.g. base::debug::MyPersistentObjectType) could
-// be used to generate the hash if the type name seems common. Use a command
-// like this to get the hash: echo -n "MyPersistentObjectType" | sha1sum
-// If the structure layout changes, ALWAYS increment this number so that
-// newer versions of the code don't try to interpret persistent data written
-// by older versions with a different layout.
-//
-// kExpectedInstanceSize: This value is the hard-coded number that matches
-// what sizeof(T) would return. By providing it explicitly, the allocator can
-// verify that the structure is compatible between both 32-bit and 64-bit
-// versions of the code.
-//
-// Using New manages the memory and then calls the default constructor for the
-// object. Given that objects are persistent, no destructor is ever called
-// automatically though a caller can explicitly call Delete to destruct it and
-// change the type to something indicating it is no longer in use.
-//
-// Though persistent memory segments are transferrable between programs built
-// for different natural word widths, they CANNOT be exchanged between CPUs
-// of different endianess. Attempts to do so will simply see the existing data
-// as corrupt and refuse to access any of it.
-class BASE_EXPORT PersistentMemoryAllocator {
- public:
- typedef uint32_t Reference;
-
- // These states are used to indicate the overall condition of the memory
- // segment irrespective of what is stored within it. Because the data is
- // often persistent and thus needs to be readable by different versions of
- // a program, these values are fixed and can never change.
- enum MemoryState : uint8_t {
- // Persistent memory starts all zeros and so shows "uninitialized".
- MEMORY_UNINITIALIZED = 0,
-
- // The header has been written and the memory is ready for use.
- MEMORY_INITIALIZED = 1,
-
- // The data should be considered deleted. This would be set when the
- // allocator is being cleaned up. If file-backed, the file is likely
- // to be deleted but since deletion can fail for a variety of reasons,
- // having this extra status means a future reader can realize what
- // should have happened.
- MEMORY_DELETED = 2,
-
- // Outside code can create states starting with this number; these too
- // must also never change between code versions.
- MEMORY_USER_DEFINED = 100,
- };
-
- // Iterator for going through all iterable memory records in an allocator.
- // Like the allocator itself, iterators are lock-free and thread-secure.
- // That means that multiple threads can share an iterator and the same
- // reference will not be returned twice.
- //
- // The order of the items returned by an iterator matches the order in which
- // MakeIterable() was called on them. Once an allocation is made iterable,
- // it is always such so the only possible difference between successive
- // iterations is for more to be added to the end.
- //
- // Iteration, in general, is tolerant of corrupted memory. It will return
- // what it can and stop only when corruption forces it to. Bad corruption
- // could cause the same object to be returned many times but it will
- // eventually quit.
- class BASE_EXPORT Iterator {
- public:
- // Constructs an iterator on a given |allocator|, starting at the beginning.
- // The allocator must live beyond the lifetime of the iterator. This class
- // has read-only access to the allocator (hence "const") but the returned
- // references can be used on a read/write version, too.
- explicit Iterator(const PersistentMemoryAllocator* allocator);
-
- // As above but resuming from the |starting_after| reference. The first call
- // to GetNext() will return the next object found after that reference. The
- // reference must be to an "iterable" object; references to non-iterable
- // objects (those that never had MakeIterable() called for them) will cause
- // a run-time error.
- Iterator(const PersistentMemoryAllocator* allocator,
- Reference starting_after);
-
- // Resets the iterator back to the beginning.
- void Reset();
-
- // Resets the iterator, resuming from the |starting_after| reference.
- void Reset(Reference starting_after);
-
- // Returns the previously retrieved reference, or kReferenceNull if none.
- // If constructor or reset with a starting_after location, this will return
- // that value.
- Reference GetLast();
-
- // Gets the next iterable, storing that type in |type_return|. The actual
- // return value is a reference to the allocation inside the allocator or
- // zero if there are no more. GetNext() may still be called again at a
- // later time to retrieve any new allocations that have been added.
- Reference GetNext(uint32_t* type_return);
-
- // Similar to above but gets the next iterable of a specific |type_match|.
- // This should not be mixed with calls to GetNext() because any allocations
- // skipped here due to a type mis-match will never be returned by later
- // calls to GetNext() meaning it's possible to completely miss entries.
- Reference GetNextOfType(uint32_t type_match);
-
- // As above but works using object type.
- template <typename T>
- Reference GetNextOfType() {
- return GetNextOfType(T::kPersistentTypeId);
- }
-
- // As above but works using objects and returns null if not found.
- template <typename T>
- const T* GetNextOfObject() {
- return GetAsObject<T>(GetNextOfType<T>());
- }
-
- // Converts references to objects. This is a convenience method so that
- // users of the iterator don't need to also have their own pointer to the
- // allocator over which the iterator runs in order to retrieve objects.
- // Because the iterator is not read/write, only "const" objects can be
- // fetched. Non-const objects can be fetched using the reference on a
- // non-const (external) pointer to the same allocator (or use const_cast
- // to remove the qualifier).
- template <typename T>
- const T* GetAsObject(Reference ref) const {
- return allocator_->GetAsObject<T>(ref);
- }
-
- // Similar to GetAsObject() but converts references to arrays of things.
- template <typename T>
- const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const {
- return allocator_->GetAsArray<T>(ref, type_id, count);
- }
-
- // Convert a generic pointer back into a reference. A null reference will
- // be returned if |memory| is not inside the persistent segment or does not
- // point to an object of the specified |type_id|.
- Reference GetAsReference(const void* memory, uint32_t type_id) const {
- return allocator_->GetAsReference(memory, type_id);
- }
-
- // As above but convert an object back into a reference.
- template <typename T>
- Reference GetAsReference(const T* obj) const {
- return allocator_->GetAsReference(obj);
- }
-
- private:
- // Weak-pointer to memory allocator being iterated over.
- const PersistentMemoryAllocator* allocator_;
-
- // The last record that was returned.
- std::atomic<Reference> last_record_;
-
- // The number of records found; used for detecting loops.
- std::atomic<uint32_t> record_count_;
-
- DISALLOW_COPY_AND_ASSIGN(Iterator);
- };
-
- // Returned information about the internal state of the heap.
- struct MemoryInfo {
- size_t total;
- size_t free;
- };
-
- enum : Reference {
- // A common "null" reference value.
- kReferenceNull = 0,
- };
-
- enum : uint32_t {
- // A value that will match any type when doing lookups.
- kTypeIdAny = 0x00000000,
-
- // A value indicating that the type is in transition. Work is being done
- // on the contents to prepare it for a new type to come.
- kTypeIdTransitioning = 0xFFFFFFFF,
- };
-
- enum : size_t {
- kSizeAny = 1 // Constant indicating that any array size is acceptable.
- };
-
- // This is the standard file extension (suitable for being passed to the
- // AddExtension() method of base::FilePath) for dumps of persistent memory.
- static const base::FilePath::CharType kFileExtension[];
-
- // The allocator operates on any arbitrary block of memory. Creation and
- // persisting or sharing of that block with another process is the
- // responsibility of the caller. The allocator needs to know only the
- // block's |base| address, the total |size| of the block, and any internal
- // |page| size (zero if not paged) across which allocations should not span.
- // The |id| is an arbitrary value the caller can use to identify a
- // particular memory segment. It will only be loaded during the initial
- // creation of the segment and can be checked by the caller for consistency.
- // The |name|, if provided, is used to distinguish histograms for this
- // allocator. Only the primary owner of the segment should define this value;
- // other processes can learn it from the shared state. If the underlying
- // memory is |readonly| then no changes will be made to it. The resulting
- // object should be stored as a "const" pointer.
- //
- // PersistentMemoryAllocator does NOT take ownership of the memory block.
- // The caller must manage it and ensure it stays available throughout the
- // lifetime of this object.
- //
- // Memory segments for sharing must have had an allocator attached to them
- // before actually being shared. If the memory segment was just created, it
- // should be zeroed before being passed here. If it was an existing segment,
- // the values here will be compared to copies stored in the shared segment
- // as a guard against corruption.
- //
- // Make sure that the memory segment is acceptable (see IsMemoryAcceptable()
- // method below) before construction if the definition of the segment can
- // vary in any way at run-time. Invalid memory segments will cause a crash.
- PersistentMemoryAllocator(void* base, size_t size, size_t page_size,
- uint64_t id, base::StringPiece name,
- bool readonly);
- virtual ~PersistentMemoryAllocator();
-
- // Check if memory segment is acceptable for creation of an Allocator. This
- // doesn't do any analysis of the data and so doesn't guarantee that the
- // contents are valid, just that the paramaters won't cause the program to
- // abort. The IsCorrupt() method will report detection of data problems
- // found during construction and general operation.
- static bool IsMemoryAcceptable(const void* data, size_t size,
- size_t page_size, bool readonly);
-
- // Get the internal identifier for this persistent memory segment.
- uint64_t Id() const;
-
- // Get the internal name of this allocator (possibly an empty string).
- const char* Name() const;
-
- // Is this segment open only for read?
- bool IsReadonly() const { return readonly_; }
-
- // Manage the saved state of the memory.
- void SetMemoryState(uint8_t memory_state);
- uint8_t GetMemoryState() const;
-
- // Create internal histograms for tracking memory use and allocation sizes
- // for allocator of |name| (which can simply be the result of Name()). This
- // is done seperately from construction for situations such as when the
- // histograms will be backed by memory provided by this very allocator.
- //
- // IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml
- // with the following histograms:
- // UMA.PersistentAllocator.name.Errors
- // UMA.PersistentAllocator.name.UsedPct
- void CreateTrackingHistograms(base::StringPiece name);
-
- // Flushes the persistent memory to any backing store. This typically does
- // nothing but is used by the FilePersistentMemoryAllocator to inform the
- // OS that all the data should be sent to the disk immediately. This is
- // useful in the rare case where something has just been stored that needs
- // to survive a hard shutdown of the machine like from a power failure.
- // The |sync| parameter indicates if this call should block until the flush
- // is complete but is only advisory and may or may not have an effect
- // depending on the capabilities of the OS. Synchronous flushes are allowed
- // only from theads that are allowed to do I/O but since |sync| is only
- // advisory, all flushes should be done on IO-capable threads.
- void Flush(bool sync);
-
- // Direct access to underlying memory segment. If the segment is shared
- // across threads or processes, reading data through these values does
- // not guarantee consistency. Use with care. Do not write.
- const void* data() const { return const_cast<const char*>(mem_base_); }
- size_t length() const { return mem_size_; }
- size_t size() const { return mem_size_; }
- size_t used() const;
-
- // Get an object referenced by a |ref|. For safety reasons, the |type_id|
- // code and size-of(|T|) are compared to ensure the reference is valid
- // and cannot return an object outside of the memory segment. A |type_id| of
- // kTypeIdAny (zero) will match any though the size is still checked. NULL is
- // returned if any problem is detected, such as corrupted storage or incorrect
- // parameters. Callers MUST check that the returned value is not-null EVERY
- // TIME before accessing it or risk crashing! Once dereferenced, the pointer
- // is safe to reuse forever.
- //
- // It is essential that the object be of a fixed size. All fields must be of
- // a defined type that does not change based on the compiler or the CPU
- // natural word size. Acceptable are char, float, double, and (u)intXX_t.
- // Unacceptable are int, bool, and wchar_t which are implementation defined
- // with regards to their size.
- //
- // Alignment must also be consistent. A uint64_t after a uint32_t will pad
- // differently between 32 and 64 bit architectures. Either put the bigger
- // elements first, group smaller elements into blocks the size of larger
- // elements, or manually insert padding fields as appropriate for the
- // largest architecture, including at the end.
- //
- // To protected against mistakes, all objects must have the attribute
- // |kExpectedInstanceSize| (static constexpr size_t) that is a hard-coded
- // numerical value -- NNN, not sizeof(T) -- that can be tested. If the
- // instance size is not fixed, at least one build will fail.
- //
- // If the size of a structure changes, the type-ID used to recognize it
- // should also change so later versions of the code don't try to read
- // incompatible structures from earlier versions.
- //
- // NOTE: Though this method will guarantee that an object of the specified
- // type can be accessed without going outside the bounds of the memory
- // segment, it makes no guarantees of the validity of the data within the
- // object itself. If it is expected that the contents of the segment could
- // be compromised with malicious intent, the object must be hardened as well.
- //
- // Though the persistent data may be "volatile" if it is shared with
- // other processes, such is not necessarily the case. The internal
- // "volatile" designation is discarded so as to not propagate the viral
- // nature of that keyword to the caller. It can add it back, if necessary,
- // based on knowledge of how the allocator is being used.
- template <typename T>
- T* GetAsObject(Reference ref) {
- static_assert(std::is_standard_layout<T>::value, "only standard objects");
- static_assert(!std::is_array<T>::value, "use GetAsArray<>()");
- static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size");
- return const_cast<T*>(reinterpret_cast<volatile T*>(
- GetBlockData(ref, T::kPersistentTypeId, sizeof(T))));
- }
- template <typename T>
- const T* GetAsObject(Reference ref) const {
- static_assert(std::is_standard_layout<T>::value, "only standard objects");
- static_assert(!std::is_array<T>::value, "use GetAsArray<>()");
- static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size");
- return const_cast<const T*>(reinterpret_cast<const volatile T*>(
- GetBlockData(ref, T::kPersistentTypeId, sizeof(T))));
- }
-
- // Like GetAsObject but get an array of simple, fixed-size types.
- //
- // Use a |count| of the required number of array elements, or kSizeAny.
- // GetAllocSize() can be used to calculate the upper bound but isn't reliable
- // because padding can make space for extra elements that were not written.
- //
- // Remember that an array of char is a string but may not be NUL terminated.
- //
- // There are no compile-time or run-time checks to ensure 32/64-bit size
- // compatibilty when using these accessors. Only use fixed-size types such
- // as char, float, double, or (u)intXX_t.
- template <typename T>
- T* GetAsArray(Reference ref, uint32_t type_id, size_t count) {
- static_assert(std::is_fundamental<T>::value, "use GetAsObject<>()");
- return const_cast<T*>(reinterpret_cast<volatile T*>(
- GetBlockData(ref, type_id, count * sizeof(T))));
- }
- template <typename T>
- const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const {
- static_assert(std::is_fundamental<T>::value, "use GetAsObject<>()");
- return const_cast<const char*>(reinterpret_cast<const volatile T*>(
- GetBlockData(ref, type_id, count * sizeof(T))));
- }
-
- // Get the corresponding reference for an object held in persistent memory.
- // If the |memory| is not valid or the type does not match, a kReferenceNull
- // result will be returned.
- Reference GetAsReference(const void* memory, uint32_t type_id) const;
-
- // Get the number of bytes allocated to a block. This is useful when storing
- // arrays in order to validate the ending boundary. The returned value will
- // include any padding added to achieve the required alignment and so could
- // be larger than given in the original Allocate() request.
- size_t GetAllocSize(Reference ref) const;
-
- // Access the internal "type" of an object. This generally isn't necessary
- // but can be used to "clear" the type and so effectively mark it as deleted
- // even though the memory stays valid and allocated. Changing the type is
- // an atomic compare/exchange and so requires knowing the existing value.
- // It will return false if the existing type is not what is expected.
- //
- // Changing the type doesn't mean the data is compatible with the new type.
- // Passing true for |clear| will zero the memory after the type has been
- // changed away from |from_type_id| but before it becomes |to_type_id| meaning
- // that it is done in a manner that is thread-safe. Memory is guaranteed to
- // be zeroed atomically by machine-word in a monotonically increasing order.
- //
- // It will likely be necessary to reconstruct the type before it can be used.
- // Changing the type WILL NOT invalidate existing pointers to the data, either
- // in this process or others, so changing the data structure could have
- // unpredicatable results. USE WITH CARE!
- uint32_t GetType(Reference ref) const;
- bool ChangeType(Reference ref,
- uint32_t to_type_id,
- uint32_t from_type_id,
- bool clear);
-
- // Allocated objects can be added to an internal list that can then be
- // iterated over by other processes. If an allocated object can be found
- // another way, such as by having its reference within a different object
- // that will be made iterable, then this call is not necessary. This always
- // succeeds unless corruption is detected; check IsCorrupted() to find out.
- // Once an object is made iterable, its position in iteration can never
- // change; new iterable objects will always be added after it in the series.
- // Changing the type does not alter its "iterable" status.
- void MakeIterable(Reference ref);
-
- // Get the information about the amount of free space in the allocator. The
- // amount of free space should be treated as approximate due to extras from
- // alignment and metadata. Concurrent allocations from other threads will
- // also make the true amount less than what is reported.
- void GetMemoryInfo(MemoryInfo* meminfo) const;
-
- // If there is some indication that the memory has become corrupted,
- // calling this will attempt to prevent further damage by indicating to
- // all processes that something is not as expected.
- void SetCorrupt() const;
-
- // This can be called to determine if corruption has been detected in the
- // segment, possibly my a malicious actor. Once detected, future allocations
- // will fail and iteration may not locate all objects.
- bool IsCorrupt() const;
-
- // Flag set if an allocation has failed because the memory segment was full.
- bool IsFull() const;
-
- // Update those "tracking" histograms which do not get updates during regular
- // operation, such as how much memory is currently used. This should be
- // called before such information is to be displayed or uploaded.
- void UpdateTrackingHistograms();
-
- // While the above works much like malloc & free, these next methods provide
- // an "object" interface similar to new and delete.
-
- // Reserve space in the memory segment of the desired |size| and |type_id|.
- // A return value of zero indicates the allocation failed, otherwise the
- // returned reference can be used by any process to get a real pointer via
- // the GetAsObject() or GetAsArray calls. The actual allocated size may be
- // larger and will always be a multiple of 8 bytes (64 bits).
- Reference Allocate(size_t size, uint32_t type_id);
-
- // Allocate and construct an object in persistent memory. The type must have
- // both (size_t) kExpectedInstanceSize and (uint32_t) kPersistentTypeId
- // static constexpr fields that are used to ensure compatibility between
- // software versions. An optional size parameter can be specified to force
- // the allocation to be bigger than the size of the object; this is useful
- // when the last field is actually variable length.
- template <typename T>
- T* New(size_t size) {
- if (size < sizeof(T))
- size = sizeof(T);
- Reference ref = Allocate(size, T::kPersistentTypeId);
- void* mem =
- const_cast<void*>(GetBlockData(ref, T::kPersistentTypeId, size));
- if (!mem)
- return nullptr;
- DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (alignof(T) - 1));
- return new (mem) T();
- }
- template <typename T>
- T* New() {
- return New<T>(sizeof(T));
- }
-
- // Similar to New, above, but construct the object out of an existing memory
- // block and of an expected type. If |clear| is true, memory will be zeroed
- // before construction. Though this is not standard object behavior, it
- // is present to match with new allocations that always come from zeroed
- // memory. Anything previously present simply ceases to exist; no destructor
- // is called for it so explicitly Delete() the old object first if need be.
- // Calling this will not invalidate existing pointers to the object, either
- // in this process or others, so changing the object could have unpredictable
- // results. USE WITH CARE!
- template <typename T>
- T* New(Reference ref, uint32_t from_type_id, bool clear) {
- DCHECK_LE(sizeof(T), GetAllocSize(ref)) << "alloc not big enough for obj";
- // Make sure the memory is appropriate. This won't be used until after
- // the type is changed but checking first avoids the possibility of having
- // to change the type back.
- void* mem = const_cast<void*>(GetBlockData(ref, 0, sizeof(T)));
- if (!mem)
- return nullptr;
- // Ensure the allocator's internal alignment is sufficient for this object.
- // This protects against coding errors in the allocator.
- DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(mem) & (alignof(T) - 1));
- // Change the type, clearing the memory if so desired. The new type is
- // "transitioning" so that there is no race condition with the construction
- // of the object should another thread be simultaneously iterating over
- // data. This will "acquire" the memory so no changes get reordered before
- // it.
- if (!ChangeType(ref, kTypeIdTransitioning, from_type_id, clear))
- return nullptr;
- // Construct an object of the desired type on this memory, just as if
- // New() had been called to create it.
- T* obj = new (mem) T();
- // Finally change the type to the desired one. This will "release" all of
- // the changes above and so provide a consistent view to other threads.
- bool success =
- ChangeType(ref, T::kPersistentTypeId, kTypeIdTransitioning, false);
- DCHECK(success);
- return obj;
- }
-
- // Deletes an object by destructing it and then changing the type to a
- // different value (default 0).
- template <typename T>
- void Delete(T* obj, uint32_t new_type) {
- // Get the reference for the object.
- Reference ref = GetAsReference<T>(obj);
- // First change the type to "transitioning" so there is no race condition
- // where another thread could find the object through iteration while it
- // is been destructed. This will "acquire" the memory so no changes get
- // reordered before it. It will fail if |ref| is invalid.
- if (!ChangeType(ref, kTypeIdTransitioning, T::kPersistentTypeId, false))
- return;
- // Destruct the object.
- obj->~T();
- // Finally change the type to the desired value. This will "release" all
- // the changes above.
- bool success = ChangeType(ref, new_type, kTypeIdTransitioning, false);
- DCHECK(success);
- }
- template <typename T>
- void Delete(T* obj) {
- Delete<T>(obj, 0);
- }
-
- // As above but works with objects allocated from persistent memory.
- template <typename T>
- Reference GetAsReference(const T* obj) const {
- return GetAsReference(obj, T::kPersistentTypeId);
- }
-
- // As above but works with an object allocated from persistent memory.
- template <typename T>
- void MakeIterable(const T* obj) {
- MakeIterable(GetAsReference<T>(obj));
- }
-
- protected:
- enum MemoryType {
- MEM_EXTERNAL,
- MEM_MALLOC,
- MEM_VIRTUAL,
- MEM_SHARED,
- MEM_FILE,
- };
-
- struct Memory {
- Memory(void* b, MemoryType t) : base(b), type(t) {}
-
- void* base;
- MemoryType type;
- };
-
- // Constructs the allocator. Everything is the same as the public allocator
- // except |memory| which is a structure with additional information besides
- // the base address.
- PersistentMemoryAllocator(Memory memory, size_t size, size_t page_size,
- uint64_t id, base::StringPiece name,
- bool readonly);
-
- // Implementation of Flush that accepts how much to flush.
- virtual void FlushPartial(size_t length, bool sync);
-
- volatile char* const mem_base_; // Memory base. (char so sizeof guaranteed 1)
- const MemoryType mem_type_; // Type of memory allocation.
- const uint32_t mem_size_; // Size of entire memory segment.
- const uint32_t mem_page_; // Page size allocations shouldn't cross.
-
- private:
- struct SharedMetadata;
- struct BlockHeader;
- static const uint32_t kAllocAlignment;
- static const Reference kReferenceQueue;
-
- // The shared metadata is always located at the top of the memory segment.
- // These convenience functions eliminate constant casting of the base
- // pointer within the code.
- const SharedMetadata* shared_meta() const {
- return reinterpret_cast<const SharedMetadata*>(
- const_cast<const char*>(mem_base_));
- }
- SharedMetadata* shared_meta() {
- return reinterpret_cast<SharedMetadata*>(const_cast<char*>(mem_base_));
- }
-
- // Actual method for doing the allocation.
- Reference AllocateImpl(size_t size, uint32_t type_id);
-
- // Get the block header associated with a specific reference.
- const volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id,
- uint32_t size, bool queue_ok,
- bool free_ok) const;
- volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, uint32_t size,
- bool queue_ok, bool free_ok) {
- return const_cast<volatile BlockHeader*>(
- const_cast<const PersistentMemoryAllocator*>(this)->GetBlock(
- ref, type_id, size, queue_ok, free_ok));
- }
-
- // Get the actual data within a block associated with a specific reference.
- const volatile void* GetBlockData(Reference ref, uint32_t type_id,
- uint32_t size) const;
- volatile void* GetBlockData(Reference ref, uint32_t type_id,
- uint32_t size) {
- return const_cast<volatile void*>(
- const_cast<const PersistentMemoryAllocator*>(this)->GetBlockData(
- ref, type_id, size));
- }
-
- // Record an error in the internal histogram.
- void RecordError(int error) const;
-
- const size_t vm_page_size_; // The page size used by the OS.
- const bool readonly_; // Indicates access to read-only memory.
- mutable std::atomic<bool> corrupt_; // Local version of "corrupted" flag.
-
- HistogramBase* allocs_histogram_; // Histogram recording allocs.
- HistogramBase* used_histogram_; // Histogram recording used space.
- HistogramBase* errors_histogram_; // Histogram recording errors.
-
- friend class PersistentMemoryAllocatorTest;
- FRIEND_TEST_ALL_PREFIXES(PersistentMemoryAllocatorTest, AllocateAndIterate);
- DISALLOW_COPY_AND_ASSIGN(PersistentMemoryAllocator);
-};
-
-
-// This allocator uses a local memory block it allocates from the general
-// heap. It is generally used when some kind of "death rattle" handler will
-// save the contents to persistent storage during process shutdown. It is
-// also useful for testing.
-class BASE_EXPORT LocalPersistentMemoryAllocator
- : public PersistentMemoryAllocator {
- public:
- LocalPersistentMemoryAllocator(size_t size, uint64_t id,
- base::StringPiece name);
- ~LocalPersistentMemoryAllocator() override;
-
- private:
- // Allocates a block of local memory of the specified |size|, ensuring that
- // the memory will not be physically allocated until accessed and will read
- // as zero when that happens.
- static Memory AllocateLocalMemory(size_t size);
-
- // Deallocates a block of local |memory| of the specified |size|.
- static void DeallocateLocalMemory(void* memory, size_t size, MemoryType type);
-
- DISALLOW_COPY_AND_ASSIGN(LocalPersistentMemoryAllocator);
-};
-
-
-// This allocator takes a shared-memory object and performs allocation from
-// it. The memory must be previously mapped via Map() or MapAt(). The allocator
-// takes ownership of the memory object.
-class BASE_EXPORT SharedPersistentMemoryAllocator
- : public PersistentMemoryAllocator {
- public:
- SharedPersistentMemoryAllocator(std::unique_ptr<SharedMemory> memory,
- uint64_t id,
- base::StringPiece name,
- bool read_only);
- ~SharedPersistentMemoryAllocator() override;
-
- SharedMemory* shared_memory() { return shared_memory_.get(); }
-
- // Ensure that the memory isn't so invalid that it would crash when passing it
- // to the allocator. This doesn't guarantee the data is valid, just that it
- // won't cause the program to abort. The existing IsCorrupt() call will handle
- // the rest.
- static bool IsSharedMemoryAcceptable(const SharedMemory& memory);
-
- private:
- std::unique_ptr<SharedMemory> shared_memory_;
-
- DISALLOW_COPY_AND_ASSIGN(SharedPersistentMemoryAllocator);
-};
-
-
-#if !defined(OS_NACL) // NACL doesn't support any kind of file access in build.
-// This allocator takes a memory-mapped file object and performs allocation
-// from it. The allocator takes ownership of the file object.
-class BASE_EXPORT FilePersistentMemoryAllocator
- : public PersistentMemoryAllocator {
- public:
- // A |max_size| of zero will use the length of the file as the maximum
- // size. The |file| object must have been already created with sufficient
- // permissions (read, read/write, or read/write/extend).
- FilePersistentMemoryAllocator(std::unique_ptr<MemoryMappedFile> file,
- size_t max_size,
- uint64_t id,
- base::StringPiece name,
- bool read_only);
- ~FilePersistentMemoryAllocator() override;
-
- // Ensure that the file isn't so invalid that it would crash when passing it
- // to the allocator. This doesn't guarantee the file is valid, just that it
- // won't cause the program to abort. The existing IsCorrupt() call will handle
- // the rest.
- static bool IsFileAcceptable(const MemoryMappedFile& file, bool read_only);
-
- protected:
- // PersistentMemoryAllocator:
- void FlushPartial(size_t length, bool sync) override;
-
- private:
- std::unique_ptr<MemoryMappedFile> mapped_file_;
-
- DISALLOW_COPY_AND_ASSIGN(FilePersistentMemoryAllocator);
-};
-#endif // !defined(OS_NACL)
-
-// An allocation that is defined but not executed until required at a later
-// time. This allows for potential users of an allocation to be decoupled
-// from the logic that defines it. In addition, there can be multiple users
-// of the same allocation or any region thereof that are guaranteed to always
-// use the same space. It's okay to copy/move these objects.
-//
-// This is a top-level class instead of an inner class of the PMA so that it
-// can be forward-declared in other header files without the need to include
-// the full contents of this file.
-class BASE_EXPORT DelayedPersistentAllocation {
- public:
- using Reference = PersistentMemoryAllocator::Reference;
-
- // Creates a delayed allocation using the specified |allocator|. When
- // needed, the memory will be allocated using the specified |type| and
- // |size|. If |offset| is given, the returned pointer will be at that
- // offset into the segment; this allows combining allocations into a
- // single persistent segment to reduce overhead and means an "all or
- // nothing" request. Note that |size| is always the total memory size
- // and |offset| is just indicating the start of a block within it. If
- // |make_iterable| was true, the allocation will made iterable when it
- // is created; already existing allocations are not changed.
- //
- // Once allocated, a reference to the segment will be stored at |ref|.
- // This shared location must be initialized to zero (0); it is checked
- // with every Get() request to see if the allocation has already been
- // done. If reading |ref| outside of this object, be sure to do an
- // "acquire" load. Don't write to it -- leave that to this object.
- //
- // For convenience, methods taking both Atomic32 and std::atomic<Reference>
- // are defined.
- DelayedPersistentAllocation(PersistentMemoryAllocator* allocator,
- subtle::Atomic32* ref,
- uint32_t type,
- size_t size,
- bool make_iterable);
- DelayedPersistentAllocation(PersistentMemoryAllocator* allocator,
- subtle::Atomic32* ref,
- uint32_t type,
- size_t size,
- size_t offset,
- bool make_iterable);
- DelayedPersistentAllocation(PersistentMemoryAllocator* allocator,
- std::atomic<Reference>* ref,
- uint32_t type,
- size_t size,
- bool make_iterable);
- DelayedPersistentAllocation(PersistentMemoryAllocator* allocator,
- std::atomic<Reference>* ref,
- uint32_t type,
- size_t size,
- size_t offset,
- bool make_iterable);
- ~DelayedPersistentAllocation();
-
- // Gets a pointer to the defined allocation. This will realize the request
- // and update the reference provided during construction. The memory will
- // be zeroed the first time it is returned, after that it is shared with
- // all other Get() requests and so shows any changes made to it elsewhere.
- //
- // If the allocation fails for any reason, null will be returned. This works
- // even on "const" objects because the allocation is already defined, just
- // delayed.
- void* Get() const;
-
- // Gets the internal reference value. If this returns a non-zero value then
- // a subsequent call to Get() will do nothing but convert that reference into
- // a memory location -- useful for accessing an existing allocation without
- // creating one unnecessarily.
- Reference reference() const {
- return reference_->load(std::memory_order_relaxed);
- }
-
- private:
- // The underlying object that does the actual allocation of memory. Its
- // lifetime must exceed that of all DelayedPersistentAllocation objects
- // that use it.
- PersistentMemoryAllocator* const allocator_;
-
- // The desired type and size of the allocated segment plus the offset
- // within it for the defined request.
- const uint32_t type_;
- const uint32_t size_;
- const uint32_t offset_;
-
- // Flag indicating if allocation should be made iterable when done.
- const bool make_iterable_;
-
- // The location at which a reference to the allocated segment is to be
- // stored once the allocation is complete. If multiple delayed allocations
- // share the same pointer then an allocation on one will amount to an
- // allocation for all.
- volatile std::atomic<Reference>* const reference_;
-
- // No DISALLOW_COPY_AND_ASSIGN as it's okay to copy/move these objects.
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_
diff --git a/base/metrics/persistent_sample_map.cc b/base/metrics/persistent_sample_map.cc
deleted file mode 100644
index f38b9d1..0000000
--- a/base/metrics/persistent_sample_map.cc
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/persistent_sample_map.h"
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/persistent_histogram_allocator.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/stl_util.h"
-
-namespace base {
-
-typedef HistogramBase::Count Count;
-typedef HistogramBase::Sample Sample;
-
-namespace {
-
-// An iterator for going through a PersistentSampleMap. The logic here is
-// identical to that of SampleMapIterator but with different data structures.
-// Changes here likely need to be duplicated there.
-class PersistentSampleMapIterator : public SampleCountIterator {
- public:
- typedef std::map<HistogramBase::Sample, HistogramBase::Count*>
- SampleToCountMap;
-
- explicit PersistentSampleMapIterator(const SampleToCountMap& sample_counts);
- ~PersistentSampleMapIterator() override;
-
- // SampleCountIterator:
- bool Done() const override;
- void Next() override;
- void Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const override;
-
- private:
- void SkipEmptyBuckets();
-
- SampleToCountMap::const_iterator iter_;
- const SampleToCountMap::const_iterator end_;
-};
-
-PersistentSampleMapIterator::PersistentSampleMapIterator(
- const SampleToCountMap& sample_counts)
- : iter_(sample_counts.begin()),
- end_(sample_counts.end()) {
- SkipEmptyBuckets();
-}
-
-PersistentSampleMapIterator::~PersistentSampleMapIterator() = default;
-
-bool PersistentSampleMapIterator::Done() const {
- return iter_ == end_;
-}
-
-void PersistentSampleMapIterator::Next() {
- DCHECK(!Done());
- ++iter_;
- SkipEmptyBuckets();
-}
-
-void PersistentSampleMapIterator::Get(Sample* min,
- int64_t* max,
- Count* count) const {
- DCHECK(!Done());
- if (min)
- *min = iter_->first;
- if (max)
- *max = strict_cast<int64_t>(iter_->first) + 1;
- if (count)
- *count = *iter_->second;
-}
-
-void PersistentSampleMapIterator::SkipEmptyBuckets() {
- while (!Done() && *iter_->second == 0) {
- ++iter_;
- }
-}
-
-// This structure holds an entry for a PersistentSampleMap within a persistent
-// memory allocator. The "id" must be unique across all maps held by an
-// allocator or they will get attached to the wrong sample map.
-struct SampleRecord {
- // SHA1(SampleRecord): Increment this if structure changes!
- static constexpr uint32_t kPersistentTypeId = 0x8FE6A69F + 1;
-
- // Expected size for 32/64-bit check.
- static constexpr size_t kExpectedInstanceSize = 16;
-
- uint64_t id; // Unique identifier of owner.
- Sample value; // The value for which this record holds a count.
- Count count; // The count associated with the above value.
-};
-
-} // namespace
-
-PersistentSampleMap::PersistentSampleMap(
- uint64_t id,
- PersistentHistogramAllocator* allocator,
- Metadata* meta)
- : HistogramSamples(id, meta), allocator_(allocator) {}
-
-PersistentSampleMap::~PersistentSampleMap() {
- if (records_)
- records_->Release(this);
-}
-
-void PersistentSampleMap::Accumulate(Sample value, Count count) {
-#if 0 // TODO(bcwhite) Re-enable efficient version after crbug.com/682680.
- *GetOrCreateSampleCountStorage(value) += count;
-#else
- Count* local_count_ptr = GetOrCreateSampleCountStorage(value);
- if (count < 0) {
- if (*local_count_ptr < -count)
- RecordNegativeSample(SAMPLES_ACCUMULATE_WENT_NEGATIVE, -count);
- else
- RecordNegativeSample(SAMPLES_ACCUMULATE_NEGATIVE_COUNT, -count);
- *local_count_ptr += count;
- } else {
- Sample old_value = *local_count_ptr;
- Sample new_value = old_value + count;
- *local_count_ptr = new_value;
- if ((new_value >= 0) != (old_value >= 0))
- RecordNegativeSample(SAMPLES_ACCUMULATE_OVERFLOW, count);
- }
-#endif
- IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
-}
-
-Count PersistentSampleMap::GetCount(Sample value) const {
- // Have to override "const" to make sure all samples have been loaded before
- // being able to know what value to return.
- Count* count_pointer =
- const_cast<PersistentSampleMap*>(this)->GetSampleCountStorage(value);
- return count_pointer ? *count_pointer : 0;
-}
-
-Count PersistentSampleMap::TotalCount() const {
- // Have to override "const" in order to make sure all samples have been
- // loaded before trying to iterate over the map.
- const_cast<PersistentSampleMap*>(this)->ImportSamples(-1, true);
-
- Count count = 0;
- for (const auto& entry : sample_counts_) {
- count += *entry.second;
- }
- return count;
-}
-
-std::unique_ptr<SampleCountIterator> PersistentSampleMap::Iterator() const {
- // Have to override "const" in order to make sure all samples have been
- // loaded before trying to iterate over the map.
- const_cast<PersistentSampleMap*>(this)->ImportSamples(-1, true);
- return WrapUnique(new PersistentSampleMapIterator(sample_counts_));
-}
-
-// static
-PersistentMemoryAllocator::Reference
-PersistentSampleMap::GetNextPersistentRecord(
- PersistentMemoryAllocator::Iterator& iterator,
- uint64_t* sample_map_id) {
- const SampleRecord* record = iterator.GetNextOfObject<SampleRecord>();
- if (!record)
- return 0;
-
- *sample_map_id = record->id;
- return iterator.GetAsReference(record);
-}
-
-// static
-PersistentMemoryAllocator::Reference
-PersistentSampleMap::CreatePersistentRecord(
- PersistentMemoryAllocator* allocator,
- uint64_t sample_map_id,
- Sample value) {
- SampleRecord* record = allocator->New<SampleRecord>();
- if (!record) {
- NOTREACHED() << "full=" << allocator->IsFull()
- << ", corrupt=" << allocator->IsCorrupt();
- return 0;
- }
-
- record->id = sample_map_id;
- record->value = value;
- record->count = 0;
-
- PersistentMemoryAllocator::Reference ref = allocator->GetAsReference(record);
- allocator->MakeIterable(ref);
- return ref;
-}
-
-bool PersistentSampleMap::AddSubtractImpl(SampleCountIterator* iter,
- Operator op) {
- Sample min;
- int64_t max;
- Count count;
- for (; !iter->Done(); iter->Next()) {
- iter->Get(&min, &max, &count);
- if (count == 0)
- continue;
- if (strict_cast<int64_t>(min) + 1 != max)
- return false; // SparseHistogram only supports bucket with size 1.
- *GetOrCreateSampleCountStorage(min) +=
- (op == HistogramSamples::ADD) ? count : -count;
- }
- return true;
-}
-
-Count* PersistentSampleMap::GetSampleCountStorage(Sample value) {
- // If |value| is already in the map, just return that.
- auto it = sample_counts_.find(value);
- if (it != sample_counts_.end())
- return it->second;
-
- // Import any new samples from persistent memory looking for the value.
- return ImportSamples(value, false);
-}
-
-Count* PersistentSampleMap::GetOrCreateSampleCountStorage(Sample value) {
- // Get any existing count storage.
- Count* count_pointer = GetSampleCountStorage(value);
- if (count_pointer)
- return count_pointer;
-
- // Create a new record in persistent memory for the value. |records_| will
- // have been initialized by the GetSampleCountStorage() call above.
- DCHECK(records_);
- PersistentMemoryAllocator::Reference ref = records_->CreateNew(value);
- if (!ref) {
- // If a new record could not be created then the underlying allocator is
- // full or corrupt. Instead, allocate the counter from the heap. This
- // sample will not be persistent, will not be shared, and will leak...
- // but it's better than crashing.
- count_pointer = new Count(0);
- sample_counts_[value] = count_pointer;
- return count_pointer;
- }
-
- // A race condition between two independent processes (i.e. two independent
- // histogram objects sharing the same sample data) could cause two of the
- // above records to be created. The allocator, however, forces a strict
- // ordering on iterable objects so use the import method to actually add the
- // just-created record. This ensures that all PersistentSampleMap objects
- // will always use the same record, whichever was first made iterable.
- // Thread-safety within a process where multiple threads use the same
- // histogram object is delegated to the controlling histogram object which,
- // for sparse histograms, is a lock object.
- count_pointer = ImportSamples(value, false);
- DCHECK(count_pointer);
- return count_pointer;
-}
-
-PersistentSampleMapRecords* PersistentSampleMap::GetRecords() {
- // The |records_| pointer is lazily fetched from the |allocator_| only on
- // first use. Sometimes duplicate histograms are created by race conditions
- // and if both were to grab the records object, there would be a conflict.
- // Use of a histogram, and thus a call to this method, won't occur until
- // after the histogram has been de-dup'd.
- if (!records_)
- records_ = allocator_->UseSampleMapRecords(id(), this);
- return records_;
-}
-
-Count* PersistentSampleMap::ImportSamples(Sample until_value,
- bool import_everything) {
- Count* found_count = nullptr;
- PersistentMemoryAllocator::Reference ref;
- PersistentSampleMapRecords* records = GetRecords();
- while ((ref = records->GetNext()) != 0) {
- SampleRecord* record = records->GetAsObject<SampleRecord>(ref);
- if (!record)
- continue;
-
- DCHECK_EQ(id(), record->id);
-
- // Check if the record's value is already known.
- if (!ContainsKey(sample_counts_, record->value)) {
- // No: Add it to map of known values.
- sample_counts_[record->value] = &record->count;
- } else {
- // Yes: Ignore it; it's a duplicate caused by a race condition -- see
- // code & comment in GetOrCreateSampleCountStorage() for details.
- // Check that nothing ever operated on the duplicate record.
- DCHECK_EQ(0, record->count);
- }
-
- // Check if it's the value being searched for and, if so, keep a pointer
- // to return later. Stop here unless everything is being imported.
- // Because race conditions can cause multiple records for a single value,
- // be sure to return the first one found.
- if (record->value == until_value) {
- if (!found_count)
- found_count = &record->count;
- if (!import_everything)
- break;
- }
- }
-
- return found_count;
-}
-
-} // namespace base
diff --git a/base/metrics/persistent_sample_map.h b/base/metrics/persistent_sample_map.h
deleted file mode 100644
index 853f862..0000000
--- a/base/metrics/persistent_sample_map.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// PersistentSampleMap implements HistogramSamples interface. It is used
-// by the SparseHistogram class to store samples in persistent memory which
-// allows it to be shared between processes or live across restarts.
-
-#ifndef BASE_METRICS_PERSISTENT_SAMPLE_MAP_H_
-#define BASE_METRICS_PERSISTENT_SAMPLE_MAP_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/persistent_memory_allocator.h"
-
-namespace base {
-
-class PersistentHistogramAllocator;
-class PersistentSampleMapRecords;
-
-// The logic here is similar to that of SampleMap but with different data
-// structures. Changes here likely need to be duplicated there.
-class BASE_EXPORT PersistentSampleMap : public HistogramSamples {
- public:
- // Constructs a persistent sample map using a PersistentHistogramAllocator
- // as the data source for persistent records.
- PersistentSampleMap(uint64_t id,
- PersistentHistogramAllocator* allocator,
- Metadata* meta);
-
- ~PersistentSampleMap() override;
-
- // HistogramSamples:
- void Accumulate(HistogramBase::Sample value,
- HistogramBase::Count count) override;
- HistogramBase::Count GetCount(HistogramBase::Sample value) const override;
- HistogramBase::Count TotalCount() const override;
- std::unique_ptr<SampleCountIterator> Iterator() const override;
-
- // Uses a persistent-memory |iterator| to locate and return information about
- // the next record holding information for a PersistentSampleMap. The record
- // could be for any Map so return the |sample_map_id| as well.
- static PersistentMemoryAllocator::Reference GetNextPersistentRecord(
- PersistentMemoryAllocator::Iterator& iterator,
- uint64_t* sample_map_id);
-
- // Creates a new record in an |allocator| storing count information for a
- // specific sample |value| of a histogram with the given |sample_map_id|.
- static PersistentMemoryAllocator::Reference CreatePersistentRecord(
- PersistentMemoryAllocator* allocator,
- uint64_t sample_map_id,
- HistogramBase::Sample value);
-
- protected:
- // Performs arithemetic. |op| is ADD or SUBTRACT.
- bool AddSubtractImpl(SampleCountIterator* iter, Operator op) override;
-
- // Gets a pointer to a "count" corresponding to a given |value|. Returns NULL
- // if sample does not exist.
- HistogramBase::Count* GetSampleCountStorage(HistogramBase::Sample value);
-
- // Gets a pointer to a "count" corresponding to a given |value|, creating
- // the sample (initialized to zero) if it does not already exists.
- HistogramBase::Count* GetOrCreateSampleCountStorage(
- HistogramBase::Sample value);
-
- private:
- // Gets the object that manages persistent records. This returns the
- // |records_| member after first initializing it if necessary.
- PersistentSampleMapRecords* GetRecords();
-
- // Imports samples from persistent memory by iterating over all sample
- // records found therein, adding them to the sample_counts_ map. If a
- // count for the sample |until_value| is found, stop the import and return
- // a pointer to that counter. If that value is not found, null will be
- // returned after all currently available samples have been loaded. Pass
- // true for |import_everything| to force the importing of all available
- // samples even if a match is found.
- HistogramBase::Count* ImportSamples(HistogramBase::Sample until_value,
- bool import_everything);
-
- // All created/loaded sample values and their associated counts. The storage
- // for the actual Count numbers is owned by the |records_| object and its
- // underlying allocator.
- std::map<HistogramBase::Sample, HistogramBase::Count*> sample_counts_;
-
- // The allocator that manages histograms inside persistent memory. This is
- // owned externally and is expected to live beyond the life of this object.
- PersistentHistogramAllocator* allocator_;
-
- // The object that manages sample records inside persistent memory. This is
- // owned by the |allocator_| object (above) and so, like it, is expected to
- // live beyond the life of this object. This value is lazily-initialized on
- // first use via the GetRecords() accessor method.
- PersistentSampleMapRecords* records_ = nullptr;
-
- DISALLOW_COPY_AND_ASSIGN(PersistentSampleMap);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_PERSISTENT_SAMPLE_MAP_H_
diff --git a/base/metrics/record_histogram_checker.h b/base/metrics/record_histogram_checker.h
deleted file mode 100644
index 75bc336..0000000
--- a/base/metrics/record_histogram_checker.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_RECORD_HISTOGRAM_CHECKER_H_
-#define BASE_METRICS_RECORD_HISTOGRAM_CHECKER_H_
-
-#include <stdint.h>
-
-#include "base/base_export.h"
-
-namespace base {
-
-// RecordHistogramChecker provides an interface for checking whether
-// the given histogram should be recorded.
-class BASE_EXPORT RecordHistogramChecker {
- public:
- virtual ~RecordHistogramChecker() = default;
-
- // Returns true iff the given histogram should be recorded.
- // This method may be called on any thread, so it should not mutate any state.
- virtual bool ShouldRecord(uint64_t histogram_hash) const = 0;
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_RECORD_HISTOGRAM_CHECKER_H_
diff --git a/base/metrics/sample_map.cc b/base/metrics/sample_map.cc
deleted file mode 100644
index c6dce29..0000000
--- a/base/metrics/sample_map.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/sample_map.h"
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/stl_util.h"
-
-namespace base {
-
-typedef HistogramBase::Count Count;
-typedef HistogramBase::Sample Sample;
-
-namespace {
-
-// An iterator for going through a SampleMap. The logic here is identical
-// to that of PersistentSampleMapIterator but with different data structures.
-// Changes here likely need to be duplicated there.
-class SampleMapIterator : public SampleCountIterator {
- public:
- typedef std::map<HistogramBase::Sample, HistogramBase::Count>
- SampleToCountMap;
-
- explicit SampleMapIterator(const SampleToCountMap& sample_counts);
- ~SampleMapIterator() override;
-
- // SampleCountIterator:
- bool Done() const override;
- void Next() override;
- void Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const override;
-
- private:
- void SkipEmptyBuckets();
-
- SampleToCountMap::const_iterator iter_;
- const SampleToCountMap::const_iterator end_;
-};
-
-SampleMapIterator::SampleMapIterator(const SampleToCountMap& sample_counts)
- : iter_(sample_counts.begin()),
- end_(sample_counts.end()) {
- SkipEmptyBuckets();
-}
-
-SampleMapIterator::~SampleMapIterator() = default;
-
-bool SampleMapIterator::Done() const {
- return iter_ == end_;
-}
-
-void SampleMapIterator::Next() {
- DCHECK(!Done());
- ++iter_;
- SkipEmptyBuckets();
-}
-
-void SampleMapIterator::Get(Sample* min, int64_t* max, Count* count) const {
- DCHECK(!Done());
- if (min)
- *min = iter_->first;
- if (max)
- *max = strict_cast<int64_t>(iter_->first) + 1;
- if (count)
- *count = iter_->second;
-}
-
-void SampleMapIterator::SkipEmptyBuckets() {
- while (!Done() && iter_->second == 0) {
- ++iter_;
- }
-}
-
-} // namespace
-
-SampleMap::SampleMap() : SampleMap(0) {}
-
-SampleMap::SampleMap(uint64_t id) : HistogramSamples(id, new LocalMetadata()) {}
-
-SampleMap::~SampleMap() {
- delete static_cast<LocalMetadata*>(meta());
-}
-
-void SampleMap::Accumulate(Sample value, Count count) {
- sample_counts_[value] += count;
- IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
-}
-
-Count SampleMap::GetCount(Sample value) const {
- std::map<Sample, Count>::const_iterator it = sample_counts_.find(value);
- if (it == sample_counts_.end())
- return 0;
- return it->second;
-}
-
-Count SampleMap::TotalCount() const {
- Count count = 0;
- for (const auto& entry : sample_counts_) {
- count += entry.second;
- }
- return count;
-}
-
-std::unique_ptr<SampleCountIterator> SampleMap::Iterator() const {
- return WrapUnique(new SampleMapIterator(sample_counts_));
-}
-
-bool SampleMap::AddSubtractImpl(SampleCountIterator* iter, Operator op) {
- Sample min;
- int64_t max;
- Count count;
- for (; !iter->Done(); iter->Next()) {
- iter->Get(&min, &max, &count);
- if (strict_cast<int64_t>(min) + 1 != max)
- return false; // SparseHistogram only supports bucket with size 1.
-
- sample_counts_[min] += (op == HistogramSamples::ADD) ? count : -count;
- }
- return true;
-}
-
-} // namespace base
diff --git a/base/metrics/sample_map.h b/base/metrics/sample_map.h
deleted file mode 100644
index 7458e05..0000000
--- a/base/metrics/sample_map.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// SampleMap implements HistogramSamples interface. It is used by the
-// SparseHistogram class to store samples.
-
-#ifndef BASE_METRICS_SAMPLE_MAP_H_
-#define BASE_METRICS_SAMPLE_MAP_H_
-
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-
-namespace base {
-
-// The logic here is similar to that of PersistentSampleMap but with different
-// data structures. Changes here likely need to be duplicated there.
-class BASE_EXPORT SampleMap : public HistogramSamples {
- public:
- SampleMap();
- explicit SampleMap(uint64_t id);
- ~SampleMap() override;
-
- // HistogramSamples:
- void Accumulate(HistogramBase::Sample value,
- HistogramBase::Count count) override;
- HistogramBase::Count GetCount(HistogramBase::Sample value) const override;
- HistogramBase::Count TotalCount() const override;
- std::unique_ptr<SampleCountIterator> Iterator() const override;
-
- protected:
- // Performs arithemetic. |op| is ADD or SUBTRACT.
- bool AddSubtractImpl(SampleCountIterator* iter, Operator op) override;
-
- private:
- std::map<HistogramBase::Sample, HistogramBase::Count> sample_counts_;
-
- DISALLOW_COPY_AND_ASSIGN(SampleMap);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_SAMPLE_MAP_H_
diff --git a/base/metrics/sample_vector.cc b/base/metrics/sample_vector.cc
deleted file mode 100644
index cf8634e..0000000
--- a/base/metrics/sample_vector.cc
+++ /dev/null
@@ -1,429 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/sample_vector.h"
-
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/persistent_memory_allocator.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/platform_thread.h"
-
-// This SampleVector makes use of the single-sample embedded in the base
-// HistogramSamples class. If the count is non-zero then there is guaranteed
-// (within the bounds of "eventual consistency") to be no allocated external
-// storage. Once the full counts storage is allocated, the single-sample must
-// be extracted and disabled.
-
-namespace base {
-
-typedef HistogramBase::Count Count;
-typedef HistogramBase::Sample Sample;
-
-SampleVectorBase::SampleVectorBase(uint64_t id,
- Metadata* meta,
- const BucketRanges* bucket_ranges)
- : HistogramSamples(id, meta), bucket_ranges_(bucket_ranges) {
- CHECK_GE(bucket_ranges_->bucket_count(), 1u);
-}
-
-SampleVectorBase::~SampleVectorBase() = default;
-
-void SampleVectorBase::Accumulate(Sample value, Count count) {
- const size_t bucket_index = GetBucketIndex(value);
-
- // Handle the single-sample case.
- if (!counts()) {
- // Try to accumulate the parameters into the single-count entry.
- if (AccumulateSingleSample(value, count, bucket_index)) {
- // A race condition could lead to a new single-sample being accumulated
- // above just after another thread executed the MountCountsStorage below.
- // Since it is mounted, it could be mounted elsewhere and have values
- // written to it. It's not allowed to have both a single-sample and
- // entries in the counts array so move the single-sample.
- if (counts())
- MoveSingleSampleToCounts();
- return;
- }
-
- // Need real storage to store both what was in the single-sample plus the
- // parameter information.
- MountCountsStorageAndMoveSingleSample();
- }
-
- // Handle the multi-sample case.
- Count new_value =
- subtle::NoBarrier_AtomicIncrement(&counts()[bucket_index], count);
- IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
-
- // TODO(bcwhite) Remove after crbug.com/682680.
- Count old_value = new_value - count;
- if ((new_value >= 0) != (old_value >= 0) && count > 0)
- RecordNegativeSample(SAMPLES_ACCUMULATE_OVERFLOW, count);
-}
-
-Count SampleVectorBase::GetCount(Sample value) const {
- return GetCountAtIndex(GetBucketIndex(value));
-}
-
-Count SampleVectorBase::TotalCount() const {
- // Handle the single-sample case.
- SingleSample sample = single_sample().Load();
- if (sample.count != 0)
- return sample.count;
-
- // Handle the multi-sample case.
- if (counts() || MountExistingCountsStorage()) {
- Count count = 0;
- size_t size = counts_size();
- const HistogramBase::AtomicCount* counts_array = counts();
- for (size_t i = 0; i < size; ++i) {
- count += subtle::NoBarrier_Load(&counts_array[i]);
- }
- return count;
- }
-
- // And the no-value case.
- return 0;
-}
-
-Count SampleVectorBase::GetCountAtIndex(size_t bucket_index) const {
- DCHECK(bucket_index < counts_size());
-
- // Handle the single-sample case.
- SingleSample sample = single_sample().Load();
- if (sample.count != 0)
- return sample.bucket == bucket_index ? sample.count : 0;
-
- // Handle the multi-sample case.
- if (counts() || MountExistingCountsStorage())
- return subtle::NoBarrier_Load(&counts()[bucket_index]);
-
- // And the no-value case.
- return 0;
-}
-
-std::unique_ptr<SampleCountIterator> SampleVectorBase::Iterator() const {
- // Handle the single-sample case.
- SingleSample sample = single_sample().Load();
- if (sample.count != 0) {
- return std::make_unique<SingleSampleIterator>(
- bucket_ranges_->range(sample.bucket),
- bucket_ranges_->range(sample.bucket + 1), sample.count, sample.bucket);
- }
-
- // Handle the multi-sample case.
- if (counts() || MountExistingCountsStorage()) {
- return std::make_unique<SampleVectorIterator>(counts(), counts_size(),
- bucket_ranges_);
- }
-
- // And the no-value case.
- return std::make_unique<SampleVectorIterator>(nullptr, 0, bucket_ranges_);
-}
-
-bool SampleVectorBase::AddSubtractImpl(SampleCountIterator* iter,
- HistogramSamples::Operator op) {
- // Stop now if there's nothing to do.
- if (iter->Done())
- return true;
-
- // Get the first value and its index.
- HistogramBase::Sample min;
- int64_t max;
- HistogramBase::Count count;
- iter->Get(&min, &max, &count);
- size_t dest_index = GetBucketIndex(min);
-
- // The destination must be a superset of the source meaning that though the
- // incoming ranges will find an exact match, the incoming bucket-index, if
- // it exists, may be offset from the destination bucket-index. Calculate
- // that offset of the passed iterator; there are are no overflow checks
- // because 2's compliment math will work it out in the end.
- //
- // Because GetBucketIndex() always returns the same true or false result for
- // a given iterator object, |index_offset| is either set here and used below,
- // or never set and never used. The compiler doesn't know this, though, which
- // is why it's necessary to initialize it to something.
- size_t index_offset = 0;
- size_t iter_index;
- if (iter->GetBucketIndex(&iter_index))
- index_offset = dest_index - iter_index;
- if (dest_index >= counts_size())
- return false;
-
- // Post-increment. Information about the current sample is not available
- // after this point.
- iter->Next();
-
- // Single-value storage is possible if there is no counts storage and the
- // retrieved entry is the only one in the iterator.
- if (!counts()) {
- if (iter->Done()) {
- // Don't call AccumulateSingleSample because that updates sum and count
- // which was already done by the caller of this method.
- if (single_sample().Accumulate(
- dest_index, op == HistogramSamples::ADD ? count : -count)) {
- // Handle race-condition that mounted counts storage between above and
- // here.
- if (counts())
- MoveSingleSampleToCounts();
- return true;
- }
- }
-
- // The counts storage will be needed to hold the multiple incoming values.
- MountCountsStorageAndMoveSingleSample();
- }
-
- // Go through the iterator and add the counts into correct bucket.
- while (true) {
- // Ensure that the sample's min/max match the ranges min/max.
- if (min != bucket_ranges_->range(dest_index) ||
- max != bucket_ranges_->range(dest_index + 1)) {
- NOTREACHED() << "sample=" << min << "," << max
- << "; range=" << bucket_ranges_->range(dest_index) << ","
- << bucket_ranges_->range(dest_index + 1);
- return false;
- }
-
- // Sample's bucket matches exactly. Adjust count.
- subtle::NoBarrier_AtomicIncrement(
- &counts()[dest_index], op == HistogramSamples::ADD ? count : -count);
-
- // Advance to the next iterable sample. See comments above for how
- // everything works.
- if (iter->Done())
- return true;
- iter->Get(&min, &max, &count);
- if (iter->GetBucketIndex(&iter_index)) {
- // Destination bucket is a known offset from the source bucket.
- dest_index = iter_index + index_offset;
- } else {
- // Destination bucket has to be determined anew each time.
- dest_index = GetBucketIndex(min);
- }
- if (dest_index >= counts_size())
- return false;
- iter->Next();
- }
-}
-
-// Use simple binary search. This is very general, but there are better
-// approaches if we knew that the buckets were linearly distributed.
-size_t SampleVectorBase::GetBucketIndex(Sample value) const {
- size_t bucket_count = bucket_ranges_->bucket_count();
- CHECK_GE(bucket_count, 1u);
- CHECK_GE(value, bucket_ranges_->range(0));
- CHECK_LT(value, bucket_ranges_->range(bucket_count));
-
- size_t under = 0;
- size_t over = bucket_count;
- size_t mid;
- do {
- DCHECK_GE(over, under);
- mid = under + (over - under)/2;
- if (mid == under)
- break;
- if (bucket_ranges_->range(mid) <= value)
- under = mid;
- else
- over = mid;
- } while (true);
-
- DCHECK_LE(bucket_ranges_->range(mid), value);
- CHECK_GT(bucket_ranges_->range(mid + 1), value);
- return mid;
-}
-
-void SampleVectorBase::MoveSingleSampleToCounts() {
- DCHECK(counts());
-
- // Disable the single-sample since there is now counts storage for the data.
- SingleSample sample = single_sample().Extract(/*disable=*/true);
-
- // Stop here if there is no "count" as trying to find the bucket index of
- // an invalid (including zero) "value" will crash.
- if (sample.count == 0)
- return;
-
- // Move the value into storage. Sum and redundant-count already account
- // for this entry so no need to call IncreaseSumAndCount().
- subtle::NoBarrier_AtomicIncrement(&counts()[sample.bucket], sample.count);
-}
-
-void SampleVectorBase::MountCountsStorageAndMoveSingleSample() {
- // There are many SampleVector objects and the lock is needed very
- // infrequently (just when advancing from single-sample to multi-sample) so
- // define a single, global lock that all can use. This lock only prevents
- // concurrent entry into the code below; access and updates to |counts_|
- // still requires atomic operations.
- static LazyInstance<Lock>::Leaky counts_lock = LAZY_INSTANCE_INITIALIZER;
- if (subtle::NoBarrier_Load(&counts_) == 0) {
- AutoLock lock(counts_lock.Get());
- if (subtle::NoBarrier_Load(&counts_) == 0) {
- // Create the actual counts storage while the above lock is acquired.
- HistogramBase::Count* counts = CreateCountsStorageWhileLocked();
- DCHECK(counts);
-
- // Point |counts_| to the newly created storage. This is done while
- // locked to prevent possible concurrent calls to CreateCountsStorage
- // but, between that call and here, other threads could notice the
- // existence of the storage and race with this to set_counts(). That's
- // okay because (a) it's atomic and (b) it always writes the same value.
- set_counts(counts);
- }
- }
-
- // Move any single-sample into the newly mounted storage.
- MoveSingleSampleToCounts();
-}
-
-SampleVector::SampleVector(const BucketRanges* bucket_ranges)
- : SampleVector(0, bucket_ranges) {}
-
-SampleVector::SampleVector(uint64_t id, const BucketRanges* bucket_ranges)
- : SampleVectorBase(id, new LocalMetadata(), bucket_ranges) {}
-
-SampleVector::~SampleVector() {
- delete static_cast<LocalMetadata*>(meta());
-}
-
-bool SampleVector::MountExistingCountsStorage() const {
- // There is never any existing storage other than what is already in use.
- return counts() != nullptr;
-}
-
-HistogramBase::AtomicCount* SampleVector::CreateCountsStorageWhileLocked() {
- local_counts_.resize(counts_size());
- return &local_counts_[0];
-}
-
-PersistentSampleVector::PersistentSampleVector(
- uint64_t id,
- const BucketRanges* bucket_ranges,
- Metadata* meta,
- const DelayedPersistentAllocation& counts)
- : SampleVectorBase(id, meta, bucket_ranges), persistent_counts_(counts) {
- // Only mount the full storage if the single-sample has been disabled.
- // Otherwise, it is possible for this object instance to start using (empty)
- // storage that was created incidentally while another instance continues to
- // update to the single sample. This "incidental creation" can happen because
- // the memory is a DelayedPersistentAllocation which allows multiple memory
- // blocks within it and applies an all-or-nothing approach to the allocation.
- // Thus, a request elsewhere for one of the _other_ blocks would make _this_
- // block available even though nothing has explicitly requested it.
- //
- // Note that it's not possible for the ctor to mount existing storage and
- // move any single-sample to it because sometimes the persistent memory is
- // read-only. Only non-const methods (which assume that memory is read/write)
- // can do that.
- if (single_sample().IsDisabled()) {
- bool success = MountExistingCountsStorage();
- DCHECK(success);
- }
-}
-
-PersistentSampleVector::~PersistentSampleVector() = default;
-
-bool PersistentSampleVector::MountExistingCountsStorage() const {
- // There is no early exit if counts is not yet mounted because, given that
- // this is a virtual function, it's more efficient to do that at the call-
- // site. There is no danger, however, should this get called anyway (perhaps
- // because of a race condition) because at worst the |counts_| value would
- // be over-written (in an atomic manner) with the exact same address.
-
- if (!persistent_counts_.reference())
- return false; // Nothing to mount.
-
- // Mount the counts array in position.
- set_counts(
- static_cast<HistogramBase::AtomicCount*>(persistent_counts_.Get()));
-
- // The above shouldn't fail but can if the data is corrupt or incomplete.
- return counts() != nullptr;
-}
-
-HistogramBase::AtomicCount*
-PersistentSampleVector::CreateCountsStorageWhileLocked() {
- void* mem = persistent_counts_.Get();
- if (!mem) {
- // The above shouldn't fail but can if Bad Things(tm) are occurring in the
- // persistent allocator. Crashing isn't a good option so instead just
- // allocate something from the heap and return that. There will be no
- // sharing or persistence but worse things are already happening.
- return new HistogramBase::AtomicCount[counts_size()];
- }
-
- return static_cast<HistogramBase::AtomicCount*>(mem);
-}
-
-SampleVectorIterator::SampleVectorIterator(
- const std::vector<HistogramBase::AtomicCount>* counts,
- const BucketRanges* bucket_ranges)
- : counts_(&(*counts)[0]),
- counts_size_(counts->size()),
- bucket_ranges_(bucket_ranges),
- index_(0) {
- DCHECK_GE(bucket_ranges_->bucket_count(), counts_size_);
- SkipEmptyBuckets();
-}
-
-SampleVectorIterator::SampleVectorIterator(
- const HistogramBase::AtomicCount* counts,
- size_t counts_size,
- const BucketRanges* bucket_ranges)
- : counts_(counts),
- counts_size_(counts_size),
- bucket_ranges_(bucket_ranges),
- index_(0) {
- DCHECK_GE(bucket_ranges_->bucket_count(), counts_size_);
- SkipEmptyBuckets();
-}
-
-SampleVectorIterator::~SampleVectorIterator() = default;
-
-bool SampleVectorIterator::Done() const {
- return index_ >= counts_size_;
-}
-
-void SampleVectorIterator::Next() {
- DCHECK(!Done());
- index_++;
- SkipEmptyBuckets();
-}
-
-void SampleVectorIterator::Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const {
- DCHECK(!Done());
- if (min != nullptr)
- *min = bucket_ranges_->range(index_);
- if (max != nullptr)
- *max = strict_cast<int64_t>(bucket_ranges_->range(index_ + 1));
- if (count != nullptr)
- *count = subtle::NoBarrier_Load(&counts_[index_]);
-}
-
-bool SampleVectorIterator::GetBucketIndex(size_t* index) const {
- DCHECK(!Done());
- if (index != nullptr)
- *index = index_;
- return true;
-}
-
-void SampleVectorIterator::SkipEmptyBuckets() {
- if (Done())
- return;
-
- while (index_ < counts_size_) {
- if (subtle::NoBarrier_Load(&counts_[index_]) != 0)
- return;
- index_++;
- }
-}
-
-} // namespace base
diff --git a/base/metrics/sample_vector.h b/base/metrics/sample_vector.h
deleted file mode 100644
index 278272d..0000000
--- a/base/metrics/sample_vector.h
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// SampleVector implements HistogramSamples interface. It is used by all
-// Histogram based classes to store samples.
-
-#ifndef BASE_METRICS_SAMPLE_VECTOR_H_
-#define BASE_METRICS_SAMPLE_VECTOR_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/atomicops.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/metrics/bucket_ranges.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/persistent_memory_allocator.h"
-
-namespace base {
-
-class BucketRanges;
-
-class BASE_EXPORT SampleVectorBase : public HistogramSamples {
- public:
- SampleVectorBase(uint64_t id,
- Metadata* meta,
- const BucketRanges* bucket_ranges);
- ~SampleVectorBase() override;
-
- // HistogramSamples:
- void Accumulate(HistogramBase::Sample value,
- HistogramBase::Count count) override;
- HistogramBase::Count GetCount(HistogramBase::Sample value) const override;
- HistogramBase::Count TotalCount() const override;
- std::unique_ptr<SampleCountIterator> Iterator() const override;
-
- // Get count of a specific bucket.
- HistogramBase::Count GetCountAtIndex(size_t bucket_index) const;
-
- // Access the bucket ranges held externally.
- const BucketRanges* bucket_ranges() const { return bucket_ranges_; }
-
- protected:
- bool AddSubtractImpl(
- SampleCountIterator* iter,
- HistogramSamples::Operator op) override; // |op| is ADD or SUBTRACT.
-
- virtual size_t GetBucketIndex(HistogramBase::Sample value) const;
-
- // Moves the single-sample value to a mounted "counts" array.
- void MoveSingleSampleToCounts();
-
- // Mounts (creating if necessary) an array of "counts" for multi-value
- // storage.
- void MountCountsStorageAndMoveSingleSample();
-
- // Mounts "counts" storage that already exists. This does not attempt to move
- // any single-sample information to that storage as that would violate the
- // "const" restriction that is often used to indicate read-only memory.
- virtual bool MountExistingCountsStorage() const = 0;
-
- // Creates "counts" storage and returns a pointer to it. Ownership of the
- // array remains with the called method but will never change. This must be
- // called while some sort of lock is held to prevent reentry.
- virtual HistogramBase::Count* CreateCountsStorageWhileLocked() = 0;
-
- HistogramBase::AtomicCount* counts() {
- return reinterpret_cast<HistogramBase::AtomicCount*>(
- subtle::Acquire_Load(&counts_));
- }
-
- const HistogramBase::AtomicCount* counts() const {
- return reinterpret_cast<HistogramBase::AtomicCount*>(
- subtle::Acquire_Load(&counts_));
- }
-
- void set_counts(const HistogramBase::AtomicCount* counts) const {
- subtle::Release_Store(&counts_, reinterpret_cast<uintptr_t>(counts));
- }
-
- size_t counts_size() const { return bucket_ranges_->bucket_count(); }
-
- private:
- friend class SampleVectorTest;
- FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
- FRIEND_TEST_ALL_PREFIXES(SharedHistogramTest, CorruptSampleCounts);
-
- // |counts_| is actually a pointer to a HistogramBase::AtomicCount array but
- // is held as an AtomicWord for concurrency reasons. When combined with the
- // single_sample held in the metadata, there are four possible states:
- // 1) single_sample == zero, counts_ == null
- // 2) single_sample != zero, counts_ == null
- // 3) single_sample != zero, counts_ != null BUT IS EMPTY
- // 4) single_sample == zero, counts_ != null and may have data
- // Once |counts_| is set, it can never revert and any existing single-sample
- // must be moved to this storage. It is mutable because changing it doesn't
- // change the (const) data but must adapt if a non-const object causes the
- // storage to be allocated and updated.
- mutable subtle::AtomicWord counts_ = 0;
-
- // Shares the same BucketRanges with Histogram object.
- const BucketRanges* const bucket_ranges_;
-
- DISALLOW_COPY_AND_ASSIGN(SampleVectorBase);
-};
-
-// A sample vector that uses local memory for the counts array.
-class BASE_EXPORT SampleVector : public SampleVectorBase {
- public:
- explicit SampleVector(const BucketRanges* bucket_ranges);
- SampleVector(uint64_t id, const BucketRanges* bucket_ranges);
- ~SampleVector() override;
-
- private:
- // SampleVectorBase:
- bool MountExistingCountsStorage() const override;
- HistogramBase::Count* CreateCountsStorageWhileLocked() override;
-
- // Simple local storage for counts.
- mutable std::vector<HistogramBase::AtomicCount> local_counts_;
-
- DISALLOW_COPY_AND_ASSIGN(SampleVector);
-};
-
-// A sample vector that uses persistent memory for the counts array.
-class BASE_EXPORT PersistentSampleVector : public SampleVectorBase {
- public:
- PersistentSampleVector(uint64_t id,
- const BucketRanges* bucket_ranges,
- Metadata* meta,
- const DelayedPersistentAllocation& counts);
- ~PersistentSampleVector() override;
-
- private:
- // SampleVectorBase:
- bool MountExistingCountsStorage() const override;
- HistogramBase::Count* CreateCountsStorageWhileLocked() override;
-
- // Persistent storage for counts.
- DelayedPersistentAllocation persistent_counts_;
-
- DISALLOW_COPY_AND_ASSIGN(PersistentSampleVector);
-};
-
-// An iterator for sample vectors. This could be defined privately in the .cc
-// file but is here for easy testing.
-class BASE_EXPORT SampleVectorIterator : public SampleCountIterator {
- public:
- SampleVectorIterator(const std::vector<HistogramBase::AtomicCount>* counts,
- const BucketRanges* bucket_ranges);
- SampleVectorIterator(const HistogramBase::AtomicCount* counts,
- size_t counts_size,
- const BucketRanges* bucket_ranges);
- ~SampleVectorIterator() override;
-
- // SampleCountIterator implementation:
- bool Done() const override;
- void Next() override;
- void Get(HistogramBase::Sample* min,
- int64_t* max,
- HistogramBase::Count* count) const override;
-
- // SampleVector uses predefined buckets, so iterator can return bucket index.
- bool GetBucketIndex(size_t* index) const override;
-
- private:
- void SkipEmptyBuckets();
-
- const HistogramBase::AtomicCount* counts_;
- size_t counts_size_;
- const BucketRanges* bucket_ranges_;
-
- size_t index_;
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_SAMPLE_VECTOR_H_
diff --git a/base/metrics/single_sample_metrics.cc b/base/metrics/single_sample_metrics.cc
deleted file mode 100644
index 57c1c8f..0000000
--- a/base/metrics/single_sample_metrics.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/single_sample_metrics.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
-
-namespace base {
-
-static SingleSampleMetricsFactory* g_factory = nullptr;
-
-// static
-SingleSampleMetricsFactory* SingleSampleMetricsFactory::Get() {
- if (!g_factory)
- g_factory = new DefaultSingleSampleMetricsFactory();
-
- return g_factory;
-}
-
-// static
-void SingleSampleMetricsFactory::SetFactory(
- std::unique_ptr<SingleSampleMetricsFactory> factory) {
- DCHECK(!g_factory);
- g_factory = factory.release();
-}
-
-// static
-void SingleSampleMetricsFactory::DeleteFactoryForTesting() {
- DCHECK(g_factory);
- delete g_factory;
- g_factory = nullptr;
-}
-
-std::unique_ptr<SingleSampleMetric>
-DefaultSingleSampleMetricsFactory::CreateCustomCountsMetric(
- const std::string& histogram_name,
- HistogramBase::Sample min,
- HistogramBase::Sample max,
- uint32_t bucket_count) {
- return std::make_unique<DefaultSingleSampleMetric>(
- histogram_name, min, max, bucket_count,
- HistogramBase::kUmaTargetedHistogramFlag);
-}
-
-DefaultSingleSampleMetric::DefaultSingleSampleMetric(
- const std::string& histogram_name,
- HistogramBase::Sample min,
- HistogramBase::Sample max,
- uint32_t bucket_count,
- int32_t flags)
- : histogram_(Histogram::FactoryGet(histogram_name,
- min,
- max,
- bucket_count,
- flags)) {
- // Bad construction parameters may lead to |histogram_| being null; DCHECK to
- // find accidental errors in production. We must still handle the nullptr in
- // destruction though since this construction may come from another untrusted
- // process.
- DCHECK(histogram_);
-}
-
-DefaultSingleSampleMetric::~DefaultSingleSampleMetric() {
- // |histogram_| may be nullptr if bad construction parameters are given.
- if (sample_ < 0 || !histogram_)
- return;
- histogram_->Add(sample_);
-}
-
-void DefaultSingleSampleMetric::SetSample(HistogramBase::Sample sample) {
- DCHECK_GE(sample, 0);
- sample_ = sample;
-}
-
-} // namespace base
diff --git a/base/metrics/single_sample_metrics.h b/base/metrics/single_sample_metrics.h
deleted file mode 100644
index b966cb1..0000000
--- a/base/metrics/single_sample_metrics.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_SINGLE_SAMPLE_METRICS_H_
-#define BASE_METRICS_SINGLE_SAMPLE_METRICS_H_
-
-#include <string>
-
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
-
-namespace base {
-
-// See base/metrics/histograms.h for parameter definitions. Must only be used
-// and destroyed from the same thread as construction.
-class BASE_EXPORT SingleSampleMetric {
- public:
- virtual ~SingleSampleMetric() = default;
-
- virtual void SetSample(HistogramBase::Sample sample) = 0;
-};
-
-// Factory for creating single sample metrics. A single sample metric only
-// reports its sample once at destruction time. The sample may be changed prior
-// to destruction using the SetSample() method as many times as desired.
-//
-// The metric creation methods are safe to call from any thread, however the
-// returned class must only be used and destroyed from the same thread as
-// construction.
-//
-// See base/metrics/histogram_macros.h for usage recommendations and
-// base/metrics/histogram.h for full parameter definitions.
-class BASE_EXPORT SingleSampleMetricsFactory {
- public:
- virtual ~SingleSampleMetricsFactory() = default;
-
- // Returns the factory provided by SetFactory(), or if no factory has been set
- // a default factory will be provided (future calls to SetFactory() will fail
- // if the default factory is ever vended).
- static SingleSampleMetricsFactory* Get();
- static void SetFactory(std::unique_ptr<SingleSampleMetricsFactory> factory);
-
- // The factory normally persists until process shutdown, but in testing we
- // should avoid leaking it since it sets a global.
- static void DeleteFactoryForTesting();
-
- // The methods below return a single sample metric for counts histograms; see
- // method comments for the corresponding histogram macro.
-
- // UMA_HISTOGRAM_CUSTOM_COUNTS()
- virtual std::unique_ptr<SingleSampleMetric> CreateCustomCountsMetric(
- const std::string& histogram_name,
- HistogramBase::Sample min,
- HistogramBase::Sample max,
- uint32_t bucket_count) = 0;
-};
-
-// Default implementation for when no factory has been provided to the process.
-// Samples are only recorded within the current process in this case, so samples
-// will be lost in the event of sudden process termination.
-class BASE_EXPORT DefaultSingleSampleMetricsFactory
- : public SingleSampleMetricsFactory {
- public:
- DefaultSingleSampleMetricsFactory() = default;
- ~DefaultSingleSampleMetricsFactory() override = default;
-
- // SingleSampleMetricsFactory:
- std::unique_ptr<SingleSampleMetric> CreateCustomCountsMetric(
- const std::string& histogram_name,
- HistogramBase::Sample min,
- HistogramBase::Sample max,
- uint32_t bucket_count) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DefaultSingleSampleMetricsFactory);
-};
-
-class BASE_EXPORT DefaultSingleSampleMetric : public SingleSampleMetric {
- public:
- DefaultSingleSampleMetric(const std::string& histogram_name,
- HistogramBase::Sample min,
- HistogramBase::Sample max,
- uint32_t bucket_count,
- int32_t flags);
- ~DefaultSingleSampleMetric() override;
-
- // SingleSampleMetric:
- void SetSample(HistogramBase::Sample sample) override;
-
- private:
- HistogramBase* const histogram_;
-
- // The last sample provided to SetSample(). We use -1 as a sentinel value to
- // indicate no sample has been set.
- HistogramBase::Sample sample_ = -1;
-
- DISALLOW_COPY_AND_ASSIGN(DefaultSingleSampleMetric);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_SINGLE_SAMPLE_METRICS_H_
diff --git a/base/metrics/sparse_histogram.cc b/base/metrics/sparse_histogram.cc
deleted file mode 100644
index 30175a0..0000000
--- a/base/metrics/sparse_histogram.cc
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/sparse_histogram.h"
-
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/dummy_histogram.h"
-#include "base/metrics/metrics_hashes.h"
-#include "base/metrics/persistent_histogram_allocator.h"
-#include "base/metrics/persistent_sample_map.h"
-#include "base/metrics/sample_map.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/pickle.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-typedef HistogramBase::Count Count;
-typedef HistogramBase::Sample Sample;
-
-// static
-HistogramBase* SparseHistogram::FactoryGet(const std::string& name,
- int32_t flags) {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (!histogram) {
- // TODO(gayane): |HashMetricName| is called again in Histogram constructor.
- // Refactor code to avoid the additional call.
- bool should_record =
- StatisticsRecorder::ShouldRecordHistogram(HashMetricName(name));
- if (!should_record)
- return DummyHistogram::GetInstance();
- // Try to create the histogram using a "persistent" allocator. As of
- // 2016-02-25, the availability of such is controlled by a base::Feature
- // that is off by default. If the allocator doesn't exist or if
- // allocating from it fails, code below will allocate the histogram from
- // the process heap.
- PersistentMemoryAllocator::Reference histogram_ref = 0;
- std::unique_ptr<HistogramBase> tentative_histogram;
- PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
- if (allocator) {
- tentative_histogram = allocator->AllocateHistogram(
- SPARSE_HISTOGRAM, name, 0, 0, nullptr, flags, &histogram_ref);
- }
-
- // Handle the case where no persistent allocator is present or the
- // persistent allocation fails (perhaps because it is full).
- if (!tentative_histogram) {
- DCHECK(!histogram_ref); // Should never have been set.
- DCHECK(!allocator); // Shouldn't have failed.
- flags &= ~HistogramBase::kIsPersistent;
- tentative_histogram.reset(new SparseHistogram(GetPermanentName(name)));
- tentative_histogram->SetFlags(flags);
- }
-
- // Register this histogram with the StatisticsRecorder. Keep a copy of
- // the pointer value to tell later whether the locally created histogram
- // was registered or deleted. The type is "void" because it could point
- // to released memory after the following line.
- const void* tentative_histogram_ptr = tentative_histogram.get();
- histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
- tentative_histogram.release());
-
- // Persistent histograms need some follow-up processing.
- if (histogram_ref) {
- allocator->FinalizeHistogram(histogram_ref,
- histogram == tentative_histogram_ptr);
- }
- }
-
- CHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType());
- return histogram;
-}
-
-// static
-std::unique_ptr<HistogramBase> SparseHistogram::PersistentCreate(
- PersistentHistogramAllocator* allocator,
- const char* name,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta) {
- return WrapUnique(
- new SparseHistogram(allocator, name, meta, logged_meta));
-}
-
-SparseHistogram::~SparseHistogram() = default;
-
-uint64_t SparseHistogram::name_hash() const {
- return unlogged_samples_->id();
-}
-
-HistogramType SparseHistogram::GetHistogramType() const {
- return SPARSE_HISTOGRAM;
-}
-
-bool SparseHistogram::HasConstructionArguments(
- Sample expected_minimum,
- Sample expected_maximum,
- uint32_t expected_bucket_count) const {
- // SparseHistogram never has min/max/bucket_count limit.
- return false;
-}
-
-void SparseHistogram::Add(Sample value) {
- AddCount(value, 1);
-}
-
-void SparseHistogram::AddCount(Sample value, int count) {
- if (count <= 0) {
- NOTREACHED();
- return;
- }
- {
- base::AutoLock auto_lock(lock_);
- unlogged_samples_->Accumulate(value, count);
- }
-
- FindAndRunCallback(value);
-}
-
-std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const {
- std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
-
- base::AutoLock auto_lock(lock_);
- snapshot->Add(*unlogged_samples_);
- snapshot->Add(*logged_samples_);
- return std::move(snapshot);
-}
-
-std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotDelta() {
- DCHECK(!final_delta_created_);
-
- std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
- base::AutoLock auto_lock(lock_);
- snapshot->Add(*unlogged_samples_);
-
- unlogged_samples_->Subtract(*snapshot);
- logged_samples_->Add(*snapshot);
- return std::move(snapshot);
-}
-
-std::unique_ptr<HistogramSamples> SparseHistogram::SnapshotFinalDelta() const {
- DCHECK(!final_delta_created_);
- final_delta_created_ = true;
-
- std::unique_ptr<SampleMap> snapshot(new SampleMap(name_hash()));
- base::AutoLock auto_lock(lock_);
- snapshot->Add(*unlogged_samples_);
-
- return std::move(snapshot);
-}
-
-void SparseHistogram::AddSamples(const HistogramSamples& samples) {
- base::AutoLock auto_lock(lock_);
- unlogged_samples_->Add(samples);
-}
-
-bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) {
- base::AutoLock auto_lock(lock_);
- return unlogged_samples_->AddFromPickle(iter);
-}
-
-void SparseHistogram::WriteHTMLGraph(std::string* output) const {
- output->append("<PRE>");
- WriteAsciiImpl(true, "<br>", output);
- output->append("</PRE>");
-}
-
-void SparseHistogram::WriteAscii(std::string* output) const {
- WriteAsciiImpl(true, "\n", output);
-}
-
-void SparseHistogram::SerializeInfoImpl(Pickle* pickle) const {
- pickle->WriteString(histogram_name());
- pickle->WriteInt(flags());
-}
-
-SparseHistogram::SparseHistogram(const char* name)
- : HistogramBase(name),
- unlogged_samples_(new SampleMap(HashMetricName(name))),
- logged_samples_(new SampleMap(unlogged_samples_->id())) {}
-
-SparseHistogram::SparseHistogram(PersistentHistogramAllocator* allocator,
- const char* name,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta)
- : HistogramBase(name),
- // While other histogram types maintain a static vector of values with
- // sufficient space for both "active" and "logged" samples, with each
- // SampleVector being given the appropriate half, sparse histograms
- // have no such initial allocation. Each sample has its own record
- // attached to a single PersistentSampleMap by a common 64-bit identifier.
- // Since a sparse histogram has two sample maps (active and logged),
- // there must be two sets of sample records with diffent IDs. The
- // "active" samples use, for convenience purposes, an ID matching
- // that of the histogram while the "logged" samples use that number
- // plus 1.
- unlogged_samples_(
- new PersistentSampleMap(HashMetricName(name), allocator, meta)),
- logged_samples_(new PersistentSampleMap(unlogged_samples_->id() + 1,
- allocator,
- logged_meta)) {}
-
-HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) {
- std::string histogram_name;
- int flags;
- if (!iter->ReadString(&histogram_name) || !iter->ReadInt(&flags)) {
- DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
- return nullptr;
- }
-
- flags &= ~HistogramBase::kIPCSerializationSourceFlag;
-
- return SparseHistogram::FactoryGet(histogram_name, flags);
-}
-
-void SparseHistogram::GetParameters(DictionaryValue* params) const {
- // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
-}
-
-void SparseHistogram::GetCountAndBucketData(Count* count,
- int64_t* sum,
- ListValue* buckets) const {
- // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
-}
-
-void SparseHistogram::WriteAsciiImpl(bool graph_it,
- const std::string& newline,
- std::string* output) const {
- // Get a local copy of the data so we are consistent.
- std::unique_ptr<HistogramSamples> snapshot = SnapshotSamples();
- Count total_count = snapshot->TotalCount();
- double scaled_total_count = total_count / 100.0;
-
- WriteAsciiHeader(total_count, output);
- output->append(newline);
-
- // Determine how wide the largest bucket range is (how many digits to print),
- // so that we'll be able to right-align starts for the graphical bars.
- // Determine which bucket has the largest sample count so that we can
- // normalize the graphical bar-width relative to that sample count.
- Count largest_count = 0;
- Sample largest_sample = 0;
- std::unique_ptr<SampleCountIterator> it = snapshot->Iterator();
- while (!it->Done()) {
- Sample min;
- int64_t max;
- Count count;
- it->Get(&min, &max, &count);
- if (min > largest_sample)
- largest_sample = min;
- if (count > largest_count)
- largest_count = count;
- it->Next();
- }
- size_t print_width = GetSimpleAsciiBucketRange(largest_sample).size() + 1;
-
- // iterate over each item and display them
- it = snapshot->Iterator();
- while (!it->Done()) {
- Sample min;
- int64_t max;
- Count count;
- it->Get(&min, &max, &count);
-
- // value is min, so display it
- std::string range = GetSimpleAsciiBucketRange(min);
- output->append(range);
- for (size_t j = 0; range.size() + j < print_width + 1; ++j)
- output->push_back(' ');
-
- if (graph_it)
- WriteAsciiBucketGraph(count, largest_count, output);
- WriteAsciiBucketValue(count, scaled_total_count, output);
- output->append(newline);
- it->Next();
- }
-}
-
-void SparseHistogram::WriteAsciiHeader(const Count total_count,
- std::string* output) const {
- StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name(),
- total_count);
- if (flags())
- StringAppendF(output, " (flags = 0x%x)", flags());
-}
-
-} // namespace base
diff --git a/base/metrics/sparse_histogram.h b/base/metrics/sparse_histogram.h
deleted file mode 100644
index 913762c..0000000
--- a/base/metrics/sparse_histogram.h
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_SPARSE_HISTOGRAM_H_
-#define BASE_METRICS_SPARSE_HISTOGRAM_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-class HistogramSamples;
-class PersistentHistogramAllocator;
-class Pickle;
-class PickleIterator;
-
-class BASE_EXPORT SparseHistogram : public HistogramBase {
- public:
- // If there's one with same name, return the existing one. If not, create a
- // new one.
- static HistogramBase* FactoryGet(const std::string& name, int32_t flags);
-
- // Create a histogram using data in persistent storage. The allocator must
- // live longer than the created sparse histogram.
- static std::unique_ptr<HistogramBase> PersistentCreate(
- PersistentHistogramAllocator* allocator,
- const char* name,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- ~SparseHistogram() override;
-
- // HistogramBase implementation:
- uint64_t name_hash() const override;
- HistogramType GetHistogramType() const override;
- bool HasConstructionArguments(Sample expected_minimum,
- Sample expected_maximum,
- uint32_t expected_bucket_count) const override;
- void Add(Sample value) override;
- void AddCount(Sample value, int count) override;
- void AddSamples(const HistogramSamples& samples) override;
- bool AddSamplesFromPickle(base::PickleIterator* iter) override;
- std::unique_ptr<HistogramSamples> SnapshotSamples() const override;
- std::unique_ptr<HistogramSamples> SnapshotDelta() override;
- std::unique_ptr<HistogramSamples> SnapshotFinalDelta() const override;
- void WriteHTMLGraph(std::string* output) const override;
- void WriteAscii(std::string* output) const override;
-
- protected:
- // HistogramBase implementation:
- void SerializeInfoImpl(base::Pickle* pickle) const override;
-
- private:
- // Clients should always use FactoryGet to create SparseHistogram.
- explicit SparseHistogram(const char* name);
-
- SparseHistogram(PersistentHistogramAllocator* allocator,
- const char* name,
- HistogramSamples::Metadata* meta,
- HistogramSamples::Metadata* logged_meta);
-
- friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
- base::PickleIterator* iter);
- static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter);
-
- void GetParameters(DictionaryValue* params) const override;
- void GetCountAndBucketData(Count* count,
- int64_t* sum,
- ListValue* buckets) const override;
-
- // Helpers for emitting Ascii graphic. Each method appends data to output.
- void WriteAsciiImpl(bool graph_it,
- const std::string& newline,
- std::string* output) const;
-
- // Write a common header message describing this histogram.
- void WriteAsciiHeader(const Count total_count,
- std::string* output) const;
-
- // For constuctor calling.
- friend class SparseHistogramTest;
-
- // Protects access to |samples_|.
- mutable base::Lock lock_;
-
- // Flag to indicate if PrepareFinalDelta has been previously called.
- mutable bool final_delta_created_ = false;
-
- std::unique_ptr<HistogramSamples> unlogged_samples_;
- std::unique_ptr<HistogramSamples> logged_samples_;
-
- DISALLOW_COPY_AND_ASSIGN(SparseHistogram);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_SPARSE_HISTOGRAM_H_
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
deleted file mode 100644
index 28773a1..0000000
--- a/base/metrics/statistics_recorder.cc
+++ /dev/null
@@ -1,416 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/statistics_recorder.h"
-
-#include <memory>
-
-#include "base/at_exit.h"
-#include "base/debug/leak_annotations.h"
-#include "base/json/string_escape.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_snapshot_manager.h"
-#include "base/metrics/metrics_hashes.h"
-#include "base/metrics/persistent_histogram_allocator.h"
-#include "base/metrics/record_histogram_checker.h"
-#include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/values.h"
-
-namespace base {
-namespace {
-
-bool HistogramNameLesser(const base::HistogramBase* a,
- const base::HistogramBase* b) {
- return strcmp(a->histogram_name(), b->histogram_name()) < 0;
-}
-
-} // namespace
-
-// static
-LazyInstance<Lock>::Leaky StatisticsRecorder::lock_;
-
-// static
-StatisticsRecorder* StatisticsRecorder::top_ = nullptr;
-
-// static
-bool StatisticsRecorder::is_vlog_initialized_ = false;
-
-size_t StatisticsRecorder::BucketRangesHash::operator()(
- const BucketRanges* const a) const {
- return a->checksum();
-}
-
-bool StatisticsRecorder::BucketRangesEqual::operator()(
- const BucketRanges* const a,
- const BucketRanges* const b) const {
- return a->Equals(b);
-}
-
-StatisticsRecorder::~StatisticsRecorder() {
- const AutoLock auto_lock(lock_.Get());
- DCHECK_EQ(this, top_);
- top_ = previous_;
-}
-
-// static
-void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() {
- lock_.Get().AssertAcquired();
- if (top_)
- return;
-
- const StatisticsRecorder* const p = new StatisticsRecorder;
- // The global recorder is never deleted.
- ANNOTATE_LEAKING_OBJECT_PTR(p);
- DCHECK_EQ(p, top_);
-}
-
-// static
-void StatisticsRecorder::RegisterHistogramProvider(
- const WeakPtr<HistogramProvider>& provider) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- top_->providers_.push_back(provider);
-}
-
-// static
-HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
- HistogramBase* histogram) {
- // Declared before |auto_lock| to ensure correct destruction order.
- std::unique_ptr<HistogramBase> histogram_deleter;
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
-
- const char* const name = histogram->histogram_name();
- HistogramBase*& registered = top_->histograms_[name];
-
- if (!registered) {
- // |name| is guaranteed to never change or be deallocated so long
- // as the histogram is alive (which is forever).
- registered = histogram;
- ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
- // If there are callbacks for this histogram, we set the kCallbackExists
- // flag.
- const auto callback_iterator = top_->callbacks_.find(name);
- if (callback_iterator != top_->callbacks_.end()) {
- if (!callback_iterator->second.is_null())
- histogram->SetFlags(HistogramBase::kCallbackExists);
- else
- histogram->ClearFlags(HistogramBase::kCallbackExists);
- }
- return histogram;
- }
-
- if (histogram == registered) {
- // The histogram was registered before.
- return histogram;
- }
-
- // We already have one histogram with this name.
- histogram_deleter.reset(histogram);
- return registered;
-}
-
-// static
-const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
- const BucketRanges* ranges) {
- DCHECK(ranges->HasValidChecksum());
-
- // Declared before |auto_lock| to ensure correct destruction order.
- std::unique_ptr<const BucketRanges> ranges_deleter;
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
-
- const BucketRanges* const registered = *top_->ranges_.insert(ranges).first;
- if (registered == ranges) {
- ANNOTATE_LEAKING_OBJECT_PTR(ranges);
- } else {
- ranges_deleter.reset(ranges);
- }
-
- return registered;
-}
-
-// static
-void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
- std::string* output) {
- for (const HistogramBase* const histogram :
- Sort(WithName(GetHistograms(), query))) {
- histogram->WriteHTMLGraph(output);
- *output += "<br><hr><br>";
- }
-}
-
-// static
-void StatisticsRecorder::WriteGraph(const std::string& query,
- std::string* output) {
- if (query.length())
- StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
- else
- output->append("Collections of all histograms\n");
-
- for (const HistogramBase* const histogram :
- Sort(WithName(GetHistograms(), query))) {
- histogram->WriteAscii(output);
- output->append("\n");
- }
-}
-
-// static
-std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) {
- std::string output = "{\"histograms\":[";
- const char* sep = "";
- for (const HistogramBase* const histogram : Sort(GetHistograms())) {
- output += sep;
- sep = ",";
- std::string json;
- histogram->WriteJSON(&json, verbosity_level);
- output += json;
- }
- output += "]}";
- return output;
-}
-
-// static
-std::vector<const BucketRanges*> StatisticsRecorder::GetBucketRanges() {
- std::vector<const BucketRanges*> out;
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- out.reserve(top_->ranges_.size());
- out.assign(top_->ranges_.begin(), top_->ranges_.end());
- return out;
-}
-
-// static
-HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
- // This must be called *before* the lock is acquired below because it will
- // call back into this object to register histograms. Those called methods
- // will acquire the lock at that time.
- ImportGlobalPersistentHistograms();
-
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
-
- const HistogramMap::const_iterator it = top_->histograms_.find(name);
- return it != top_->histograms_.end() ? it->second : nullptr;
-}
-
-// static
-StatisticsRecorder::HistogramProviders
-StatisticsRecorder::GetHistogramProviders() {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- return top_->providers_;
-}
-
-// static
-void StatisticsRecorder::ImportProvidedHistograms() {
- // Merge histogram data from each provider in turn.
- for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) {
- // Weak-pointer may be invalid if the provider was destructed, though they
- // generally never are.
- if (provider)
- provider->MergeHistogramDeltas();
- }
-}
-
-// static
-void StatisticsRecorder::PrepareDeltas(
- bool include_persistent,
- HistogramBase::Flags flags_to_set,
- HistogramBase::Flags required_flags,
- HistogramSnapshotManager* snapshot_manager) {
- Histograms histograms = GetHistograms();
- if (!include_persistent)
- histograms = NonPersistent(std::move(histograms));
- snapshot_manager->PrepareDeltas(Sort(std::move(histograms)), flags_to_set,
- required_flags);
-}
-
-// static
-void StatisticsRecorder::InitLogOnShutdown() {
- const AutoLock auto_lock(lock_.Get());
- InitLogOnShutdownWhileLocked();
-}
-
-// static
-bool StatisticsRecorder::SetCallback(
- const std::string& name,
- const StatisticsRecorder::OnSampleCallback& cb) {
- DCHECK(!cb.is_null());
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
-
- if (!top_->callbacks_.insert({name, cb}).second)
- return false;
-
- const HistogramMap::const_iterator it = top_->histograms_.find(name);
- if (it != top_->histograms_.end())
- it->second->SetFlags(HistogramBase::kCallbackExists);
-
- return true;
-}
-
-// static
-void StatisticsRecorder::ClearCallback(const std::string& name) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
-
- top_->callbacks_.erase(name);
-
- // We also clear the flag from the histogram (if it exists).
- const HistogramMap::const_iterator it = top_->histograms_.find(name);
- if (it != top_->histograms_.end())
- it->second->ClearFlags(HistogramBase::kCallbackExists);
-}
-
-// static
-StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
- const std::string& name) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- const auto it = top_->callbacks_.find(name);
- return it != top_->callbacks_.end() ? it->second : OnSampleCallback();
-}
-
-// static
-size_t StatisticsRecorder::GetHistogramCount() {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- return top_->histograms_.size();
-}
-
-// static
-void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
-
- const HistogramMap::iterator found = top_->histograms_.find(name);
- if (found == top_->histograms_.end())
- return;
-
- HistogramBase* const base = found->second;
- if (base->GetHistogramType() != SPARSE_HISTOGRAM) {
- // When forgetting a histogram, it's likely that other information is
- // also becoming invalid. Clear the persistent reference that may no
- // longer be valid. There's no danger in this as, at worst, duplicates
- // will be created in persistent memory.
- static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0);
- }
-
- top_->histograms_.erase(found);
-}
-
-// static
-std::unique_ptr<StatisticsRecorder>
-StatisticsRecorder::CreateTemporaryForTesting() {
- const AutoLock auto_lock(lock_.Get());
- return WrapUnique(new StatisticsRecorder());
-}
-
-// static
-void StatisticsRecorder::SetRecordChecker(
- std::unique_ptr<RecordHistogramChecker> record_checker) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- top_->record_checker_ = std::move(record_checker);
-}
-
-// static
-bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- return !top_->record_checker_ ||
- top_->record_checker_->ShouldRecord(histogram_hash);
-}
-
-// static
-StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms() {
- // This must be called *before* the lock is acquired below because it will
- // call back into this object to register histograms. Those called methods
- // will acquire the lock at that time.
- ImportGlobalPersistentHistograms();
-
- Histograms out;
-
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
-
- out.reserve(top_->histograms_.size());
- for (const auto& entry : top_->histograms_)
- out.push_back(entry.second);
-
- return out;
-}
-
-// static
-StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) {
- std::sort(histograms.begin(), histograms.end(), &HistogramNameLesser);
- return histograms;
-}
-
-// static
-StatisticsRecorder::Histograms StatisticsRecorder::WithName(
- Histograms histograms,
- const std::string& query) {
- // Need a C-string query for comparisons against C-string histogram name.
- const char* const query_string = query.c_str();
- histograms.erase(std::remove_if(histograms.begin(), histograms.end(),
- [query_string](const HistogramBase* const h) {
- return !strstr(h->histogram_name(),
- query_string);
- }),
- histograms.end());
- return histograms;
-}
-
-// static
-StatisticsRecorder::Histograms StatisticsRecorder::NonPersistent(
- Histograms histograms) {
- histograms.erase(
- std::remove_if(histograms.begin(), histograms.end(),
- [](const HistogramBase* const h) {
- return (h->flags() & HistogramBase::kIsPersistent) != 0;
- }),
- histograms.end());
- return histograms;
-}
-
-// static
-void StatisticsRecorder::ImportGlobalPersistentHistograms() {
- // Import histograms from known persistent storage. Histograms could have been
- // added by other processes and they must be fetched and recognized locally.
- // If the persistent memory segment is not shared between processes, this call
- // does nothing.
- if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get())
- allocator->ImportHistogramsToStatisticsRecorder();
-}
-
-// This singleton instance should be started during the single threaded portion
-// of main(), and hence it is not thread safe. It initializes globals to provide
-// support for all future calls.
-StatisticsRecorder::StatisticsRecorder() {
- lock_.Get().AssertAcquired();
- previous_ = top_;
- top_ = this;
- InitLogOnShutdownWhileLocked();
-}
-
-// static
-void StatisticsRecorder::InitLogOnShutdownWhileLocked() {
- lock_.Get().AssertAcquired();
- if (!is_vlog_initialized_ && VLOG_IS_ON(1)) {
- is_vlog_initialized_ = true;
- const auto dump_to_vlog = [](void*) {
- std::string output;
- WriteGraph("", &output);
- VLOG(1) << output;
- };
- AtExitManager::RegisterCallback(dump_to_vlog, nullptr);
- }
-}
-
-} // namespace base
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h
deleted file mode 100644
index 87a9311..0000000
--- a/base/metrics/statistics_recorder.h
+++ /dev/null
@@ -1,302 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// StatisticsRecorder holds all Histograms and BucketRanges that are used by
-// Histograms in the system. It provides a general place for
-// Histograms/BucketRanges to register, and supports a global API for accessing
-// (i.e., dumping, or graphing) the data.
-
-#ifndef BASE_METRICS_STATISTICS_RECORDER_H_
-#define BASE_METRICS_STATISTICS_RECORDER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "base/base_export.h"
-#include "base/callback.h"
-#include "base/gtest_prod_util.h"
-#include "base/lazy_instance.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/record_histogram_checker.h"
-#include "base/strings/string_piece.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-
-class BucketRanges;
-class HistogramSnapshotManager;
-
-// In-memory recorder of usage statistics (aka metrics, aka histograms).
-//
-// All the public methods are static and act on a global recorder. This global
-// recorder is internally synchronized and all the static methods are thread
-// safe.
-//
-// StatisticsRecorder doesn't have any public constructor. For testing purpose,
-// you can create a temporary recorder using the factory method
-// CreateTemporaryForTesting(). This temporary recorder becomes the global one
-// until deleted. When this temporary recorder is deleted, it restores the
-// previous global one.
-class BASE_EXPORT StatisticsRecorder {
- public:
- // An interface class that allows the StatisticsRecorder to forcibly merge
- // histograms from providers when necessary.
- class HistogramProvider {
- public:
- // Merges all histogram information into the global versions.
- virtual void MergeHistogramDeltas() = 0;
- };
-
- typedef std::vector<HistogramBase*> Histograms;
-
- // Restores the previous global recorder.
- //
- // When several temporary recorders are created using
- // CreateTemporaryForTesting(), these recorders must be deleted in reverse
- // order of creation.
- //
- // This method is thread safe.
- //
- // Precondition: The recorder being deleted is the current global recorder.
- ~StatisticsRecorder();
-
- // Registers a provider of histograms that can be called to merge those into
- // the global recorder. Calls to ImportProvidedHistograms() will fetch from
- // registered providers.
- //
- // This method is thread safe.
- static void RegisterHistogramProvider(
- const WeakPtr<HistogramProvider>& provider);
-
- // Registers or adds a new histogram to the collection of statistics. If an
- // identically named histogram is already registered, then the argument
- // |histogram| will be deleted. The returned value is always the registered
- // histogram (either the argument, or the pre-existing registered histogram).
- //
- // This method is thread safe.
- static HistogramBase* RegisterOrDeleteDuplicate(HistogramBase* histogram);
-
- // Registers or adds a new BucketRanges. If an equivalent BucketRanges is
- // already registered, then the argument |ranges| will be deleted. The
- // returned value is always the registered BucketRanges (either the argument,
- // or the pre-existing one).
- //
- // This method is thread safe.
- static const BucketRanges* RegisterOrDeleteDuplicateRanges(
- const BucketRanges* ranges);
-
- // Methods for appending histogram data to a string. Only histograms which
- // have |query| as a substring are written to |output| (an empty string will
- // process all registered histograms).
- //
- // These methods are thread safe.
- static void WriteHTMLGraph(const std::string& query, std::string* output);
- static void WriteGraph(const std::string& query, std::string* output);
-
- // Returns the histograms with |verbosity_level| as the serialization
- // verbosity.
- //
- // This method is thread safe.
- static std::string ToJSON(JSONVerbosityLevel verbosity_level);
-
- // Gets existing histograms.
- //
- // The order of returned histograms is not guaranteed.
- //
- // Ownership of the individual histograms remains with the StatisticsRecorder.
- //
- // This method is thread safe.
- static Histograms GetHistograms();
-
- // Gets BucketRanges used by all histograms registered. The order of returned
- // BucketRanges is not guaranteed.
- //
- // This method is thread safe.
- static std::vector<const BucketRanges*> GetBucketRanges();
-
- // Finds a histogram by name. Matches the exact name. Returns a null pointer
- // if a matching histogram is not found.
- //
- // This method is thread safe.
- static HistogramBase* FindHistogram(base::StringPiece name);
-
- // Imports histograms from providers.
- //
- // This method must be called on the UI thread.
- static void ImportProvidedHistograms();
-
- // Snapshots all histograms via |snapshot_manager|. |flags_to_set| is used to
- // set flags for each histogram. |required_flags| is used to select
- // histograms to be recorded. Only histograms that have all the flags
- // specified by the argument will be chosen. If all histograms should be
- // recorded, set it to |Histogram::kNoFlags|.
- static void PrepareDeltas(bool include_persistent,
- HistogramBase::Flags flags_to_set,
- HistogramBase::Flags required_flags,
- HistogramSnapshotManager* snapshot_manager);
-
- typedef base::Callback<void(HistogramBase::Sample)> OnSampleCallback;
-
- // Sets the callback to notify when a new sample is recorded on the histogram
- // referred to by |histogram_name|. Can be called before or after the
- // histogram is created. Returns whether the callback was successfully set.
- //
- // This method is thread safe.
- static bool SetCallback(const std::string& histogram_name,
- const OnSampleCallback& callback);
-
- // Clears any callback set on the histogram referred to by |histogram_name|.
- //
- // This method is thread safe.
- static void ClearCallback(const std::string& histogram_name);
-
- // Retrieves the callback for the histogram referred to by |histogram_name|,
- // or a null callback if no callback exists for this histogram.
- //
- // This method is thread safe.
- static OnSampleCallback FindCallback(const std::string& histogram_name);
-
- // Returns the number of known histograms.
- //
- // This method is thread safe.
- static size_t GetHistogramCount();
-
- // Initializes logging histograms with --v=1. Safe to call multiple times.
- // Is called from ctor but for browser it seems that it is more useful to
- // start logging after statistics recorder, so we need to init log-on-shutdown
- // later.
- //
- // This method is thread safe.
- static void InitLogOnShutdown();
-
- // Removes a histogram from the internal set of known ones. This can be
- // necessary during testing persistent histograms where the underlying
- // memory is being released.
- //
- // This method is thread safe.
- static void ForgetHistogramForTesting(base::StringPiece name);
-
- // Creates a temporary StatisticsRecorder object for testing purposes. All new
- // histograms will be registered in it until it is destructed or pushed aside
- // for the lifetime of yet another StatisticsRecorder object. The destruction
- // of the returned object will re-activate the previous one.
- // StatisticsRecorder objects must be deleted in the opposite order to which
- // they're created.
- //
- // This method is thread safe.
- static std::unique_ptr<StatisticsRecorder> CreateTemporaryForTesting()
- WARN_UNUSED_RESULT;
-
- // Sets the record checker for determining if a histogram should be recorded.
- // Record checker doesn't affect any already recorded histograms, so this
- // method must be called very early, before any threads have started.
- // Record checker methods can be called on any thread, so they shouldn't
- // mutate any state.
- static void SetRecordChecker(
- std::unique_ptr<RecordHistogramChecker> record_checker);
-
- // Checks if the given histogram should be recorded based on the
- // ShouldRecord() method of the record checker. If the record checker is not
- // set, returns true.
- //
- // This method is thread safe.
- static bool ShouldRecordHistogram(uint64_t histogram_hash);
-
- // Sorts histograms by name.
- static Histograms Sort(Histograms histograms);
-
- // Filters histograms by name. Only histograms which have |query| as a
- // substring in their name are kept. An empty query keeps all histograms.
- static Histograms WithName(Histograms histograms, const std::string& query);
-
- // Filters histograms by persistency. Only non-persistent histograms are kept.
- static Histograms NonPersistent(Histograms histograms);
-
- private:
- typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders;
-
- typedef std::unordered_map<StringPiece, HistogramBase*, StringPieceHash>
- HistogramMap;
-
- // We keep a map of callbacks to histograms, so that as histograms are
- // created, we can set the callback properly.
- typedef std::unordered_map<std::string, OnSampleCallback> CallbackMap;
-
- struct BucketRangesHash {
- size_t operator()(const BucketRanges* a) const;
- };
-
- struct BucketRangesEqual {
- bool operator()(const BucketRanges* a, const BucketRanges* b) const;
- };
-
- typedef std::
- unordered_set<const BucketRanges*, BucketRangesHash, BucketRangesEqual>
- RangesMap;
-
- friend class StatisticsRecorderTest;
- FRIEND_TEST_ALL_PREFIXES(StatisticsRecorderTest, IterationTest);
-
- // Initializes the global recorder if it doesn't already exist. Safe to call
- // multiple times.
- //
- // Precondition: The global lock is already acquired.
- static void EnsureGlobalRecorderWhileLocked();
-
- // Gets histogram providers.
- //
- // This method is thread safe.
- static HistogramProviders GetHistogramProviders();
-
- // Imports histograms from global persistent memory.
- //
- // Precondition: The global lock must not be held during this call.
- static void ImportGlobalPersistentHistograms();
-
- // Constructs a new StatisticsRecorder and sets it as the current global
- // recorder.
- //
- // Precondition: The global lock is already acquired.
- StatisticsRecorder();
-
- // Initialize implementation but without lock. Caller should guard
- // StatisticsRecorder by itself if needed (it isn't in unit tests).
- //
- // Precondition: The global lock is already acquired.
- static void InitLogOnShutdownWhileLocked();
-
- HistogramMap histograms_;
- CallbackMap callbacks_;
- RangesMap ranges_;
- HistogramProviders providers_;
- std::unique_ptr<RecordHistogramChecker> record_checker_;
-
- // Previous global recorder that existed when this one was created.
- StatisticsRecorder* previous_ = nullptr;
-
- // Global lock for internal synchronization.
- static LazyInstance<Lock>::Leaky lock_;
-
- // Current global recorder. This recorder is used by static methods. When a
- // new global recorder is created by CreateTemporaryForTesting(), then the
- // previous global recorder is referenced by top_->previous_.
- static StatisticsRecorder* top_;
-
- // Tracks whether InitLogOnShutdownWhileLocked() has registered a logging
- // function that will be called when the program finishes.
- static bool is_vlog_initialized_;
-
- DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_STATISTICS_RECORDER_H_
diff --git a/base/metrics/user_metrics.cc b/base/metrics/user_metrics.cc
deleted file mode 100644
index 9fcc9e8..0000000
--- a/base/metrics/user_metrics.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/user_metrics.h"
-
-#include <stddef.h>
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/lazy_instance.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/threading/thread_checker.h"
-
-namespace base {
-namespace {
-
-LazyInstance<std::vector<ActionCallback>>::DestructorAtExit g_callbacks =
- LAZY_INSTANCE_INITIALIZER;
-LazyInstance<scoped_refptr<SingleThreadTaskRunner>>::DestructorAtExit
- g_task_runner = LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-void RecordAction(const UserMetricsAction& action) {
- RecordComputedAction(action.str_);
-}
-
-void RecordComputedAction(const std::string& action) {
- if (!g_task_runner.Get()) {
- DCHECK(g_callbacks.Get().empty());
- return;
- }
-
- if (!g_task_runner.Get()->BelongsToCurrentThread()) {
- g_task_runner.Get()->PostTask(FROM_HERE,
- BindOnce(&RecordComputedAction, action));
- return;
- }
-
- for (const ActionCallback& callback : g_callbacks.Get()) {
- callback.Run(action);
- }
-}
-
-void AddActionCallback(const ActionCallback& callback) {
- // Only allow adding a callback if the task runner is set.
- DCHECK(g_task_runner.Get());
- DCHECK(g_task_runner.Get()->BelongsToCurrentThread());
- g_callbacks.Get().push_back(callback);
-}
-
-void RemoveActionCallback(const ActionCallback& callback) {
- DCHECK(g_task_runner.Get());
- DCHECK(g_task_runner.Get()->BelongsToCurrentThread());
- std::vector<ActionCallback>* callbacks = g_callbacks.Pointer();
- for (size_t i = 0; i < callbacks->size(); ++i) {
- if ((*callbacks)[i].Equals(callback)) {
- callbacks->erase(callbacks->begin() + i);
- return;
- }
- }
-}
-
-void SetRecordActionTaskRunner(
- scoped_refptr<SingleThreadTaskRunner> task_runner) {
- DCHECK(task_runner->BelongsToCurrentThread());
- DCHECK(!g_task_runner.Get() || g_task_runner.Get()->BelongsToCurrentThread());
- g_task_runner.Get() = task_runner;
-}
-
-} // namespace base
diff --git a/base/metrics/user_metrics.h b/base/metrics/user_metrics.h
deleted file mode 100644
index 87fbd9c..0000000
--- a/base/metrics/user_metrics.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_USER_METRICS_H_
-#define BASE_METRICS_USER_METRICS_H_
-
-#include <string>
-
-#include "base/base_export.h"
-#include "base/callback.h"
-#include "base/metrics/user_metrics_action.h"
-#include "base/single_thread_task_runner.h"
-
-namespace base {
-
-// This module provides some helper functions for logging actions tracked by
-// the user metrics system.
-
-// For best practices on deciding when to emit a user action, see
-// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/actions/README.md
-
-// Record that the user performed an action.
-// This function must be called after the task runner has been set with
-// SetRecordActionTaskRunner().
-//
-// "Action" here means a user-generated event:
-// good: "Reload", "CloseTab", and "IMEInvoked"
-// not good: "SSLDialogShown", "PageLoaded", "DiskFull"
-// We use this to gather anonymized information about how users are
-// interacting with the browser.
-// WARNING: In calls to this function, UserMetricsAction should be followed by a
-// string literal parameter and not a variable e.g.
-// RecordAction(UserMetricsAction("my action name"));
-// This ensures that our processing scripts can associate this action's hash
-// with its metric name. Therefore, it will be possible to retrieve the metric
-// name from the hash later on.
-//
-// Once a new recorded action is added, run
-// tools/metrics/actions/extract_actions.py
-// to add the metric to actions.xml, then update the <owner>s and <description>
-// sections. Make sure to include the actions.xml file when you upload your code
-// for review!
-//
-// For more complicated situations (like when there are many different
-// possible actions), see RecordComputedAction().
-BASE_EXPORT void RecordAction(const UserMetricsAction& action);
-
-// This function has identical input and behavior to RecordAction(), but is
-// not automatically found by the action-processing scripts. It can be used
-// when it's a pain to enumerate all possible actions, but if you use this
-// you need to also update the rules for extracting known actions in
-// tools/metrics/actions/extract_actions.py.
-// This function must be called after the task runner has been set with
-// SetRecordActionTaskRunner().
-BASE_EXPORT void RecordComputedAction(const std::string& action);
-
-// Called with the action string.
-typedef Callback<void(const std::string&)> ActionCallback;
-
-// Add/remove action callbacks (see above).
-// These functions must be called after the task runner has been set with
-// SetRecordActionTaskRunner().
-BASE_EXPORT void AddActionCallback(const ActionCallback& callback);
-BASE_EXPORT void RemoveActionCallback(const ActionCallback& callback);
-
-// Set the task runner on which to record actions.
-BASE_EXPORT void SetRecordActionTaskRunner(
- scoped_refptr<SingleThreadTaskRunner> task_runner);
-
-} // namespace base
-
-#endif // BASE_METRICS_USER_METRICS_H_
diff --git a/base/metrics/user_metrics_action.h b/base/metrics/user_metrics_action.h
deleted file mode 100644
index 454ed83..0000000
--- a/base/metrics/user_metrics_action.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_METRICS_USER_METRICS_ACTION_H_
-#define BASE_METRICS_USER_METRICS_ACTION_H_
-
-namespace base {
-
-// UserMetricsAction exists purely to standardize on the parameters passed to
-// UserMetrics. That way, our toolset can scan the source code reliable for
-// constructors and extract the associated string constants.
-// WARNING: When using UserMetricsAction you should use a string literal
-// parameter e.g.
-// RecordAction(UserMetricsAction("my action name"));
-// This ensures that our processing scripts can associate this action's hash
-// with its metric name. Therefore, it will be possible to retrieve the metric
-// name from the hash later on.
-// Please see tools/metrics/actions/extract_actions.py for details.
-struct UserMetricsAction {
- const char* str_;
- explicit constexpr UserMetricsAction(const char* str) noexcept : str_(str) {}
-};
-
-} // namespace base
-
-#endif // BASE_METRICS_USER_METRICS_ACTION_H_
diff --git a/base/process/kill_posix.cc b/base/process/kill_posix.cc
index 5159c19..7d75095 100644
--- a/base/process/kill_posix.cc
+++ b/base/process/kill_posix.cc
@@ -10,7 +10,6 @@
#include <sys/wait.h>
#include <unistd.h>
-#include "base/debug/activity_tracker.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
diff --git a/base/process/process.h b/base/process/process.h
index 479e24d..2826be3 100644
--- a/base/process/process.h
+++ b/base/process/process.h
@@ -20,16 +20,11 @@
#endif
#if defined(OS_MACOSX)
-#include "base/feature_list.h"
#include "base/process/port_provider_mac.h"
#endif
namespace base {
-#if defined(OS_MACOSX)
-extern const Feature kMacAllowBackgroundingProcesses;
-#endif
-
// Provides a move-only encapsulation of a process.
//
// This object is not tied to the lifetime of the underlying process: the
diff --git a/base/process/process_mac.cc b/base/process/process_mac.cc
index 70bc4c2..cd47c62 100644
--- a/base/process/process_mac.cc
+++ b/base/process/process_mac.cc
@@ -6,17 +6,12 @@
#include <mach/mach.h>
-#include "base/feature_list.h"
#include "base/mac/mach_logging.h"
namespace base {
-// Enables backgrounding hidden renderers on Mac.
-const Feature kMacAllowBackgroundingProcesses{"MacAllowBackgroundingProcesses",
- FEATURE_DISABLED_BY_DEFAULT};
-
bool Process::CanBackgroundProcesses() {
- return FeatureList::IsEnabled(kMacAllowBackgroundingProcesses);
+ return false;
}
bool Process::IsProcessBackgrounded(PortProvider* port_provider) const {
diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc
index 51b57e1..6b758a2 100644
--- a/base/process/process_posix.cc
+++ b/base/process/process_posix.cc
@@ -10,7 +10,6 @@
#include <sys/resource.h>
#include <sys/wait.h>
-#include "base/debug/activity_tracker.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
@@ -339,9 +338,6 @@
if (!timeout.is_zero())
internal::AssertBaseSyncPrimitivesAllowed();
- // Record the event that this thread is blocking upon (for hang diagnosis).
- base::debug::ScopedProcessWaitActivity process_activity(this);
-
int local_exit_code;
bool exited = WaitForExitWithTimeoutImpl(Handle(), &local_exit_code, timeout);
if (exited) {
diff --git a/base/synchronization/lock_impl_posix.cc b/base/synchronization/lock_impl_posix.cc
index 1cfa88a..3812fe2 100644
--- a/base/synchronization/lock_impl_posix.cc
+++ b/base/synchronization/lock_impl_posix.cc
@@ -6,7 +6,6 @@
#include <string>
-#include "base/debug/activity_tracker.h"
#include "base/logging.h"
#include "base/posix/safe_strerror.h"
#include "base/strings/stringprintf.h"
@@ -86,18 +85,6 @@
}
void LockImpl::Lock() {
- // The ScopedLockAcquireActivity below is relatively expensive and so its
- // actions can become significant due to the very large number of locks
- // that tend to be used throughout the build. To avoid this cost in the
- // vast majority of the calls, simply "try" the lock first and only do the
- // (tracked) blocking call if that fails. Since "try" itself is a system
- // call, and thus also somewhat expensive, don't bother with it unless
- // tracking is actually enabled.
- if (base::debug::GlobalActivityTracker::IsEnabled())
- if (Try())
- return;
-
- base::debug::ScopedLockAcquireActivity lock_activity(this);
int rv = pthread_mutex_lock(&native_handle_);
DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
}
diff --git a/base/synchronization/waitable_event_mac.cc b/base/synchronization/waitable_event_mac.cc
index 56e6cb3..7979553 100644
--- a/base/synchronization/waitable_event_mac.cc
+++ b/base/synchronization/waitable_event_mac.cc
@@ -8,7 +8,7 @@
#include <mach/mach.h>
#include <sys/event.h>
-#include "base/debug/activity_tracker.h"
+#include "base/callback.h"
#include "base/files/scoped_file.h"
#include "base/mac/dispatch_source_mach.h"
#include "base/mac/mac_util.h"
@@ -113,8 +113,6 @@
bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
internal::AssertBaseSyncPrimitivesAllowed();
ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
- // Record the event that this thread is blocking upon (for hang diagnosis).
- debug::ScopedEventWaitActivity event_activity(this);
TimeDelta wait_time = end_time - TimeTicks::Now();
if (wait_time < TimeDelta()) {
@@ -169,8 +167,6 @@
internal::AssertBaseSyncPrimitivesAllowed();
DCHECK(count) << "Cannot wait on no events";
ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
- // Record an event (the first) that this thread is blocking upon.
- debug::ScopedEventWaitActivity event_activity(raw_waitables[0]);
// On macOS 10.11+, using Mach port sets may cause system instability, per
// https://crbug.com/756102. On macOS 10.12+, a kqueue can be used
diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc
index 9799e7d..34d54c7 100644
--- a/base/synchronization/waitable_event_posix.cc
+++ b/base/synchronization/waitable_event_posix.cc
@@ -8,7 +8,6 @@
#include <limits>
#include <vector>
-#include "base/debug/activity_tracker.h"
#include "base/logging.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
@@ -164,8 +163,6 @@
bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
internal::AssertBaseSyncPrimitivesAllowed();
ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
- // Record the event that this thread is blocking upon (for hang diagnosis).
- base::debug::ScopedEventWaitActivity event_activity(this);
const bool finite_time = !end_time.is_max();
@@ -240,8 +237,6 @@
internal::AssertBaseSyncPrimitivesAllowed();
DCHECK(count) << "Cannot wait on no events";
ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
- // Record an event (the first) that this thread is blocking upon.
- base::debug::ScopedEventWaitActivity event_activity(raw_waitables[0]);
// We need to acquire the locks in a globally consistent order. Thus we sort
// the array of waitables by address. We actually sort a pairs so that we can
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.cc b/base/task_scheduler/scheduler_worker_pool_impl.cc
index 0e0c107..9e7e892 100644
--- a/base/task_scheduler/scheduler_worker_pool_impl.cc
+++ b/base/task_scheduler/scheduler_worker_pool_impl.cc
@@ -16,7 +16,6 @@
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
#include "base/sequence_token.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
@@ -41,13 +40,6 @@
namespace {
-constexpr char kPoolNameSuffix[] = "Pool";
-constexpr char kDetachDurationHistogramPrefix[] =
- "TaskScheduler.DetachDuration.";
-constexpr char kNumTasksBeforeDetachHistogramPrefix[] =
- "TaskScheduler.NumTasksBeforeDetach.";
-constexpr char kNumTasksBetweenWaitsHistogramPrefix[] =
- "TaskScheduler.NumTasksBetweenWaits.";
constexpr size_t kMaxNumberOfWorkers = 256;
// Only used in DCHECKs.
@@ -168,38 +160,6 @@
priority_hint_(priority_hint),
lock_(shared_priority_queue_.container_lock()),
idle_workers_stack_cv_for_testing_(lock_.CreateConditionVariable()),
- // Mimics the UMA_HISTOGRAM_LONG_TIMES macro.
- detach_duration_histogram_(Histogram::FactoryTimeGet(
- JoinString({kDetachDurationHistogramPrefix, histogram_label,
- kPoolNameSuffix},
- ""),
- TimeDelta::FromMilliseconds(1),
- TimeDelta::FromHours(1),
- 50,
- HistogramBase::kUmaTargetedHistogramFlag)),
- // Mimics the UMA_HISTOGRAM_COUNTS_1000 macro. When a worker runs more
- // than 1000 tasks before detaching, there is no need to know the exact
- // number of tasks that ran.
- num_tasks_before_detach_histogram_(Histogram::FactoryGet(
- JoinString({kNumTasksBeforeDetachHistogramPrefix, histogram_label,
- kPoolNameSuffix},
- ""),
- 1,
- 1000,
- 50,
- HistogramBase::kUmaTargetedHistogramFlag)),
- // Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A SchedulerWorker is
- // expected to run between zero and a few tens of tasks between waits.
- // When it runs more than 100 tasks, there is no need to know the exact
- // number of tasks that ran.
- num_tasks_between_waits_histogram_(Histogram::FactoryGet(
- JoinString({kNumTasksBetweenWaitsHistogramPrefix, histogram_label,
- kPoolNameSuffix},
- ""),
- 1,
- 100,
- 50,
- HistogramBase::kUmaTargetedHistogramFlag)),
tracked_ref_factory_(this) {
DCHECK(!histogram_label.empty());
DCHECK(!pool_label_.empty());
@@ -271,12 +231,6 @@
WakeUpOneWorker();
}
-void SchedulerWorkerPoolImpl::GetHistograms(
- std::vector<const HistogramBase*>* histograms) const {
- histograms->push_back(detach_duration_histogram_);
- histograms->push_back(num_tasks_between_waits_histogram_);
-}
-
int SchedulerWorkerPoolImpl::GetMaxConcurrentNonBlockedTasksDeprecated() const {
#if DCHECK_IS_ON()
AutoSchedulerLock auto_lock(lock_);
@@ -521,7 +475,6 @@
DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
outer_->lock_.AssertAcquired();
- outer_->num_tasks_before_detach_histogram_->Add(num_tasks_since_last_detach_);
outer_->cleanup_timestamps_.push(TimeTicks::Now());
worker->Cleanup();
outer_->RemoveFromIdleWorkersStackLockRequired(worker);
@@ -546,7 +499,6 @@
// returns nullptr, the SchedulerWorker will perform a wait on its
// WaitableEvent, so we record how many tasks were ran since the last wait
// here.
- outer_->num_tasks_between_waits_histogram_->Add(num_tasks_since_last_wait_);
num_tasks_since_last_wait_ = 0;
outer_->AddToIdleWorkersStackLockRequired(worker);
SetIsOnIdleWorkersStackLockRequired(worker);
@@ -843,8 +795,6 @@
DCHECK_LE(workers_.size(), worker_capacity_);
if (!cleanup_timestamps_.empty()) {
- detach_duration_histogram_->AddTime(TimeTicks::Now() -
- cleanup_timestamps_.top());
cleanup_timestamps_.pop();
}
return worker.get();
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.h b/base/task_scheduler/scheduler_worker_pool_impl.h
index 997fcc9..d331d3c 100644
--- a/base/task_scheduler/scheduler_worker_pool_impl.h
+++ b/base/task_scheduler/scheduler_worker_pool_impl.h
@@ -34,7 +34,6 @@
namespace base {
-class HistogramBase;
class SchedulerWorkerObserver;
class SchedulerWorkerPoolParams;
@@ -95,16 +94,6 @@
// SchedulerWorkerPool:
void JoinForTesting() override;
- const HistogramBase* num_tasks_before_detach_histogram() const {
- return num_tasks_before_detach_histogram_;
- }
-
- const HistogramBase* num_tasks_between_waits_histogram() const {
- return num_tasks_between_waits_histogram_;
- }
-
- void GetHistograms(std::vector<const HistogramBase*>* histograms) const;
-
// Returns the maximum number of non-blocked tasks that can run concurrently
// in this pool.
//
@@ -306,18 +295,6 @@
AtomicFlag join_for_testing_started_;
#endif
- // TaskScheduler.DetachDuration.[worker pool name] histogram. Intentionally
- // leaked.
- HistogramBase* const detach_duration_histogram_;
-
- // TaskScheduler.NumTasksBeforeDetach.[worker pool name] histogram.
- // Intentionally leaked.
- HistogramBase* const num_tasks_before_detach_histogram_;
-
- // TaskScheduler.NumTasksBetweenWaits.[worker pool name] histogram.
- // Intentionally leaked.
- HistogramBase* const num_tasks_between_waits_histogram_;
-
scoped_refptr<TaskRunner> service_thread_task_runner_;
// Optional observer notified when a worker enters and exits its main
diff --git a/base/task_scheduler/service_thread.cc b/base/task_scheduler/service_thread.cc
index 40f217f..ce8bf4d 100644
--- a/base/task_scheduler/service_thread.cc
+++ b/base/task_scheduler/service_thread.cc
@@ -13,17 +13,9 @@
namespace base {
namespace internal {
-ServiceThread::ServiceThread(const TaskTracker* task_tracker)
- : Thread("TaskSchedulerServiceThread"), task_tracker_(task_tracker) {}
+ServiceThread::ServiceThread() : Thread("TaskSchedulerServiceThread") {}
-void ServiceThread::Init() {
- if (task_tracker_) {
- heartbeat_latency_timer_.Start(
- FROM_HERE, TimeDelta::FromSeconds(5),
- BindRepeating(&ServiceThread::PerformHeartbeatLatencyReport,
- Unretained(this)));
- }
-}
+void ServiceThread::Init() {}
NOINLINE void ServiceThread::Run(RunLoop* run_loop) {
const int line_number = __LINE__;
@@ -31,23 +23,5 @@
base::debug::Alias(&line_number);
}
-void ServiceThread::PerformHeartbeatLatencyReport() const {
- static constexpr TaskTraits kReportedTraits[] = {
- {TaskPriority::BACKGROUND}, {TaskPriority::BACKGROUND, MayBlock()},
- {TaskPriority::USER_VISIBLE}, {TaskPriority::USER_VISIBLE, MayBlock()},
- {TaskPriority::USER_BLOCKING}, {TaskPriority::USER_BLOCKING, MayBlock()}};
-
- for (auto& traits : kReportedTraits) {
- // Post through the static API to time the full stack. Use a new Now() for
- // every set of traits in case PostTaskWithTraits() itself is slow.
- base::PostTaskWithTraits(
- FROM_HERE, traits,
- BindOnce(&TaskTracker::RecordLatencyHistogram,
- Unretained(task_tracker_),
- TaskTracker::LatencyHistogramType::HEARTBEAT_LATENCY, traits,
- TimeTicks::Now()));
- }
-}
-
} // namespace internal
} // namespace base
diff --git a/base/task_scheduler/service_thread.h b/base/task_scheduler/service_thread.h
index f9b23fa..14ccd76 100644
--- a/base/task_scheduler/service_thread.h
+++ b/base/task_scheduler/service_thread.h
@@ -27,24 +27,13 @@
// |task_tracker| if non-null. In that case, this ServiceThread will assume a
// registered TaskScheduler instance and that |task_tracker| will outlive this
// ServiceThread.
- explicit ServiceThread(const TaskTracker* task_tracker);
+ ServiceThread();
private:
// Thread:
void Init() override;
void Run(RunLoop* run_loop) override;
- // Kicks off async tasks which will record a histogram on the latency of
- // various traits.
- void PerformHeartbeatLatencyReport() const;
-
- const TaskTracker* const task_tracker_;
-
- // Fires a recurring heartbeat task to record latency histograms which are
- // independent from any execution sequence. This is done on the service thread
- // to avoid all external dependencies (even main thread).
- base::RepeatingTimer heartbeat_latency_timer_;
-
DISALLOW_COPY_AND_ASSIGN(ServiceThread);
};
diff --git a/base/task_scheduler/task_scheduler.h b/base/task_scheduler/task_scheduler.h
index 8881028..3135f8c 100644
--- a/base/task_scheduler/task_scheduler.h
+++ b/base/task_scheduler/task_scheduler.h
@@ -136,9 +136,6 @@
SingleThreadTaskRunnerThreadMode thread_mode) = 0;
#endif // defined(OS_WIN)
- // Returns a vector of all histograms available in this task scheduler.
- virtual std::vector<const HistogramBase*> GetHistograms() const = 0;
-
// Synchronously shuts down the scheduler. Once this is called, only tasks
// posted with the BLOCK_SHUTDOWN behavior will be run. When this returns:
// - All SKIP_ON_SHUTDOWN tasks that were already running have completed their
diff --git a/base/task_scheduler/task_scheduler_impl.cc b/base/task_scheduler/task_scheduler_impl.cc
index a5ab06c..88250c2 100644
--- a/base/task_scheduler/task_scheduler_impl.cc
+++ b/base/task_scheduler/task_scheduler_impl.cc
@@ -9,7 +9,6 @@
#include "base/compiler_specific.h"
#include "base/message_loop/message_loop.h"
-#include "base/metrics/field_trial_params.h"
#include "base/strings/string_util.h"
#include "base/task_scheduler/delayed_task_manager.h"
#include "base/task_scheduler/environment_config.h"
@@ -32,7 +31,7 @@
StringPiece histogram_label,
std::unique_ptr<TaskTrackerImpl> task_tracker)
: task_tracker_(std::move(task_tracker)),
- service_thread_(std::make_unique<ServiceThread>(task_tracker_.get())),
+ service_thread_(std::make_unique<ServiceThread>()),
single_thread_task_runner_manager_(task_tracker_->GetTrackedRef(),
&delayed_task_manager_) {
DCHECK(!histogram_label.empty());
@@ -64,13 +63,6 @@
void TaskSchedulerImpl::Start(
const TaskScheduler::InitParams& init_params,
SchedulerWorkerObserver* scheduler_worker_observer) {
- // This is set in Start() and not in the constructor because variation params
- // are usually not ready when TaskSchedulerImpl is instantiated in a process.
- if (base::GetFieldTrialParamValue("BrowserScheduler",
- "AllTasksUserBlocking") == "true") {
- all_tasks_user_blocking_.Set();
- }
-
// Start the service thread. On platforms that support it (POSIX except NaCL
// SFI), the service thread runs a MessageLoopForIO which is used to support
// FileDescriptorWatcher in the scope in which tasks run.
@@ -174,14 +166,6 @@
}
#endif // defined(OS_WIN)
-std::vector<const HistogramBase*> TaskSchedulerImpl::GetHistograms() const {
- std::vector<const HistogramBase*> histograms;
- for (const auto& worker_pool : worker_pools_)
- worker_pool->GetHistograms(&histograms);
-
- return histograms;
-}
-
int TaskSchedulerImpl::GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
const TaskTraits& traits) const {
return GetWorkerPoolForTraits(traits)
@@ -225,9 +209,7 @@
TaskTraits TaskSchedulerImpl::SetUserBlockingPriorityIfNeeded(
const TaskTraits& traits) const {
- return all_tasks_user_blocking_.IsSet()
- ? TaskTraits::Override(traits, {TaskPriority::USER_BLOCKING})
- : traits;
+ return traits;
}
} // namespace internal
diff --git a/base/task_scheduler/task_scheduler_impl.h b/base/task_scheduler/task_scheduler_impl.h
index 4ad7fc2..f409dc5 100644
--- a/base/task_scheduler/task_scheduler_impl.h
+++ b/base/task_scheduler/task_scheduler_impl.h
@@ -79,7 +79,6 @@
const TaskTraits& traits,
SingleThreadTaskRunnerThreadMode thread_mode) override;
#endif // defined(OS_WIN)
- std::vector<const HistogramBase*> GetHistograms() const override;
int GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
const TaskTraits& traits) const override;
void Shutdown() override;
@@ -101,14 +100,6 @@
DelayedTaskManager delayed_task_manager_;
SchedulerSingleThreadTaskRunnerManager single_thread_task_runner_manager_;
- // Indicates that all tasks are handled as if they had been posted with
- // TaskPriority::USER_BLOCKING. Since this is set in Start(), it doesn't apply
- // to tasks posted before Start() or to tasks posted to TaskRunners created
- // before Start().
- //
- // TODO(fdoray): Remove after experiment. https://crbug.com/757022
- AtomicFlag all_tasks_user_blocking_;
-
// There are 4 SchedulerWorkerPoolImpl in this array to match the 4
// SchedulerWorkerPoolParams in TaskScheduler::InitParams.
std::unique_ptr<SchedulerWorkerPoolImpl> worker_pools_[4];
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index 4fd2356..33424bb 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -13,7 +13,6 @@
#include "base/command_line.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
#include "base/sequence_token.h"
#include "base/strings/string_util.h"
#include "base/synchronization/condition_variable.h"
@@ -52,45 +51,6 @@
DISALLOW_COPY_AND_ASSIGN(TaskTracingInfo);
};
-// These name conveys that a Task is posted to/run by the task scheduler without
-// revealing its implementation details.
-constexpr char kQueueFunctionName[] = "TaskScheduler PostTask";
-constexpr char kRunFunctionName[] = "TaskScheduler RunTask";
-
-// Constructs a histogram to track latency which is logging to
-// "TaskScheduler.{histogram_name}.{histogram_label}.{task_type_suffix}".
-HistogramBase* GetLatencyHistogram(StringPiece histogram_name,
- StringPiece histogram_label,
- StringPiece task_type_suffix) {
- DCHECK(!histogram_name.empty());
- DCHECK(!histogram_label.empty());
- DCHECK(!task_type_suffix.empty());
- // Mimics the UMA_HISTOGRAM_HIGH_RESOLUTION_CUSTOM_TIMES macro. The minimums
- // and maximums were chosen to place the 1ms mark at around the 70% range
- // coverage for buckets giving us good info for tasks that have a latency
- // below 1ms (most of them) and enough info to assess how bad the latency is
- // for tasks that exceed this threshold.
- const std::string histogram = JoinString(
- {"TaskScheduler", histogram_name, histogram_label, task_type_suffix},
- ".");
- return Histogram::FactoryMicrosecondsTimeGet(
- histogram, TimeDelta::FromMicroseconds(1),
- TimeDelta::FromMilliseconds(20), 50,
- HistogramBase::kUmaTargetedHistogramFlag);
-}
-
-// Upper bound for the
-// TaskScheduler.BlockShutdownTasksPostedDuringShutdown histogram.
-constexpr HistogramBase::Sample kMaxBlockShutdownTasksPostedDuringShutdown =
- 1000;
-
-void RecordNumBlockShutdownTasksPostedDuringShutdown(
- HistogramBase::Sample value) {
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "TaskScheduler.BlockShutdownTasksPostedDuringShutdown", value, 1,
- kMaxBlockShutdownTasksPostedDuringShutdown, 50);
-}
-
// Returns the maximum number of TaskPriority::BACKGROUND sequences that can be
// scheduled concurrently based on command line flags.
int GetMaxNumScheduledBackgroundSequences() {
@@ -235,49 +195,7 @@
shutdown_lock_(&flush_lock_),
max_num_scheduled_background_sequences_(
max_num_scheduled_background_sequences),
- task_latency_histograms_{
- {GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority"),
- GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority_MayBlock")},
- {GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority"),
- GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority_MayBlock")},
- {GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority"),
- GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority_MayBlock")}},
- heartbeat_latency_histograms_{
- {GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority"),
- GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority_MayBlock")},
- {GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority"),
- GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority_MayBlock")},
- {GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority"),
- GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority_MayBlock")}},
tracked_ref_factory_(this) {
- // Confirm that all |task_latency_histograms_| have been initialized above.
- DCHECK(*(&task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) +
- 1][0] -
- 1));
}
TaskTracker::~TaskTracker() = default;
@@ -419,22 +337,9 @@
state_->StartShutdown();
}
-void TaskTracker::RecordLatencyHistogram(
- LatencyHistogramType latency_histogram_type,
- TaskTraits task_traits,
- TimeTicks posted_time) const {
- const TimeDelta task_latency = TimeTicks::Now() - posted_time;
-
- DCHECK(latency_histogram_type == LatencyHistogramType::TASK_LATENCY ||
- latency_histogram_type == LatencyHistogramType::HEARTBEAT_LATENCY);
-}
-
void TaskTracker::RunOrSkipTask(Task task,
Sequence* sequence,
bool can_run_task) {
- RecordLatencyHistogram(LatencyHistogramType::TASK_LATENCY, task.traits,
- task.sequenced_time);
-
const bool previous_singleton_allowed =
ThreadRestrictions::SetSingletonAllowed(
task.traits.shutdown_behavior() !=
@@ -488,7 +393,6 @@
// This method can only be called once.
DCHECK(!shutdown_event_);
- DCHECK(!num_block_shutdown_tasks_posted_during_shutdown_);
DCHECK(!state_->HasShutdownStarted());
shutdown_event_.reset(
@@ -523,20 +427,6 @@
base::ThreadRestrictions::ScopedAllowWait allow_wait;
shutdown_event_->Wait();
}
-
- {
- AutoSchedulerLock auto_lock(shutdown_lock_);
-
- // Record TaskScheduler.BlockShutdownTasksPostedDuringShutdown if less than
- // |kMaxBlockShutdownTasksPostedDuringShutdown| BLOCK_SHUTDOWN tasks were
- // posted during shutdown. Otherwise, the histogram has already been
- // recorded in BeforePostTask().
- if (num_block_shutdown_tasks_posted_during_shutdown_ <
- kMaxBlockShutdownTasksPostedDuringShutdown) {
- RecordNumBlockShutdownTasksPostedDuringShutdown(
- num_block_shutdown_tasks_posted_during_shutdown_);
- }
- }
}
void TaskTracker::SetMaxNumScheduledBackgroundSequences(
@@ -626,18 +516,6 @@
state_->DecrementNumTasksBlockingShutdown();
return false;
}
-
- ++num_block_shutdown_tasks_posted_during_shutdown_;
-
- if (num_block_shutdown_tasks_posted_during_shutdown_ ==
- kMaxBlockShutdownTasksPostedDuringShutdown) {
- // Record the TaskScheduler.BlockShutdownTasksPostedDuringShutdown
- // histogram as soon as its upper bound is hit. That way, a value will
- // be recorded even if an infinite number of BLOCK_SHUTDOWN tasks are
- // posted, preventing shutdown to complete.
- RecordNumBlockShutdownTasksPostedDuringShutdown(
- num_block_shutdown_tasks_posted_during_shutdown_);
- }
}
return true;
diff --git a/base/task_scheduler/task_tracker.h b/base/task_scheduler/task_tracker.h
index 760a8f7..ae484ce 100644
--- a/base/task_scheduler/task_tracker.h
+++ b/base/task_scheduler/task_tracker.h
@@ -15,7 +15,6 @@
#include "base/debug/task_annotator.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/metrics/histogram_base.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/waitable_event.h"
#include "base/task_scheduler/can_schedule_sequence_observer.h"
@@ -28,7 +27,6 @@
namespace base {
class ConditionVariable;
-class HistogramBase;
namespace internal {
@@ -160,27 +158,11 @@
// Returns true if shutdown has completed (Shutdown() has returned).
bool IsShutdownComplete() const;
- enum class LatencyHistogramType {
- // Records the latency of each individual task posted through TaskTracker.
- TASK_LATENCY,
- // Records the latency of heartbeat tasks which are independent of current
- // workload. These avoid a bias towards TASK_LATENCY reporting that high-
- // priority tasks are "slower" than regular tasks because high-priority
- // tasks tend to be correlated with heavy workloads.
- HEARTBEAT_LATENCY,
- };
-
// Causes HasShutdownStarted() to return true. Unlike when Shutdown() returns,
// IsShutdownComplete() won't return true after this returns. Shutdown()
// cannot be called after this.
void SetHasShutdownStartedForTesting();
- // Records |Now() - posted_time| to the appropriate |latency_histogram_type|
- // based on |task_traits|.
- void RecordLatencyHistogram(LatencyHistogramType latency_histogram_type,
- TaskTraits task_traits,
- TimeTicks posted_time) const;
-
TrackedRef<TaskTracker> GetTrackedRef() {
return tracked_ref_factory_.GetTrackedRef();
}
@@ -331,20 +313,6 @@
// Number of currently scheduled background sequences.
int num_scheduled_background_sequences_ = 0;
- // TaskScheduler.TaskLatencyMicroseconds.* and
- // TaskScheduler.HeartbeatLatencyMicroseconds.* histograms. The first index is
- // a TaskPriority. The second index is 0 for non-blocking tasks, 1 for
- // blocking tasks. Intentionally leaked.
- // TODO(scheduler-dev): Consider using STATIC_HISTOGRAM_POINTER_GROUP for
- // these.
- static constexpr int kNumTaskPriorities =
- static_cast<int>(TaskPriority::HIGHEST) + 1;
- HistogramBase* const task_latency_histograms_[kNumTaskPriorities][2];
- HistogramBase* const heartbeat_latency_histograms_[kNumTaskPriorities][2];
-
- // Number of BLOCK_SHUTDOWN tasks posted during shutdown.
- HistogramBase::Sample num_block_shutdown_tasks_posted_during_shutdown_ = 0;
-
// Ensures all state (e.g. dangling cleaned up workers) is coalesced before
// destroying the TaskTracker (e.g. in test environments).
// Ref. https://crbug.com/827615.
diff --git a/base/test/histogram_tester.cc b/base/test/histogram_tester.cc
deleted file mode 100644
index 2a63b8c..0000000
--- a/base/test/histogram_tester.cc
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/histogram_tester.h"
-
-#include <stddef.h>
-
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_samples.h"
-#include "base/metrics/metrics_hashes.h"
-#include "base/metrics/sample_map.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/strings/string_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-HistogramTester::HistogramTester() {
- // Record any histogram data that exists when the object is created so it can
- // be subtracted later.
- for (const auto* const histogram : StatisticsRecorder::GetHistograms()) {
- histograms_snapshot_[histogram->histogram_name()] =
- histogram->SnapshotSamples();
- }
-}
-
-HistogramTester::~HistogramTester() = default;
-
-void HistogramTester::ExpectUniqueSample(
- const std::string& name,
- HistogramBase::Sample sample,
- HistogramBase::Count expected_count) const {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- EXPECT_NE(nullptr, histogram) << "Histogram \"" << name
- << "\" does not exist.";
-
- if (histogram) {
- std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
- CheckBucketCount(name, sample, expected_count, *samples);
- CheckTotalCount(name, expected_count, *samples);
- }
-}
-
-void HistogramTester::ExpectBucketCount(
- const std::string& name,
- HistogramBase::Sample sample,
- HistogramBase::Count expected_count) const {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- EXPECT_NE(nullptr, histogram) << "Histogram \"" << name
- << "\" does not exist.";
-
- if (histogram) {
- std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
- CheckBucketCount(name, sample, expected_count, *samples);
- }
-}
-
-void HistogramTester::ExpectTotalCount(const std::string& name,
- HistogramBase::Count count) const {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (histogram) {
- std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
- CheckTotalCount(name, count, *samples);
- } else {
- // No histogram means there were zero samples.
- EXPECT_EQ(count, 0) << "Histogram \"" << name << "\" does not exist.";
- }
-}
-
-void HistogramTester::ExpectTimeBucketCount(const std::string& name,
- TimeDelta sample,
- HistogramBase::Count count) const {
- ExpectBucketCount(name, sample.InMilliseconds(), count);
-}
-
-std::vector<Bucket> HistogramTester::GetAllSamples(
- const std::string& name) const {
- std::vector<Bucket> samples;
- std::unique_ptr<HistogramSamples> snapshot =
- GetHistogramSamplesSinceCreation(name);
- if (snapshot) {
- for (auto it = snapshot->Iterator(); !it->Done(); it->Next()) {
- HistogramBase::Sample sample;
- HistogramBase::Count count;
- it->Get(&sample, nullptr, &count);
- samples.push_back(Bucket(sample, count));
- }
- }
- return samples;
-}
-
-HistogramBase::Count HistogramTester::GetBucketCount(
- const std::string& name,
- HistogramBase::Sample sample) const {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- EXPECT_NE(nullptr, histogram)
- << "Histogram \"" << name << "\" does not exist.";
- HistogramBase::Count count = 0;
- if (histogram) {
- std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
- GetBucketCountForSamples(name, sample, *samples, &count);
- }
- return count;
-}
-
-void HistogramTester::GetBucketCountForSamples(
- const std::string& name,
- HistogramBase::Sample sample,
- const HistogramSamples& samples,
- HistogramBase::Count* count) const {
- *count = samples.GetCount(sample);
- auto histogram_data = histograms_snapshot_.find(name);
- if (histogram_data != histograms_snapshot_.end())
- *count -= histogram_data->second->GetCount(sample);
-}
-
-HistogramTester::CountsMap HistogramTester::GetTotalCountsForPrefix(
- const std::string& prefix) const {
- EXPECT_TRUE(prefix.find('.') != std::string::npos)
- << "|prefix| ought to contain at least one period, to avoid matching too"
- << " many histograms.";
-
- CountsMap result;
-
- // Find candidate matches by using the logic built into GetSnapshot().
- for (const HistogramBase* histogram : StatisticsRecorder::GetHistograms()) {
- if (!StartsWith(histogram->histogram_name(), prefix,
- CompareCase::SENSITIVE)) {
- continue;
- }
- std::unique_ptr<HistogramSamples> new_samples =
- GetHistogramSamplesSinceCreation(histogram->histogram_name());
- // Omit unchanged histograms from the result.
- if (new_samples->TotalCount()) {
- result[histogram->histogram_name()] = new_samples->TotalCount();
- }
- }
- return result;
-}
-
-std::unique_ptr<HistogramSamples>
-HistogramTester::GetHistogramSamplesSinceCreation(
- const std::string& histogram_name) const {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(histogram_name);
- // Whether the histogram exists or not may not depend on the current test
- // calling this method, but rather on which tests ran before and possibly
- // generated a histogram or not (see http://crbug.com/473689). To provide a
- // response which is independent of the previously run tests, this method
- // creates empty samples in the absence of the histogram, rather than
- // returning null.
- if (!histogram) {
- return std::unique_ptr<HistogramSamples>(
- new SampleMap(HashMetricName(histogram_name)));
- }
- std::unique_ptr<HistogramSamples> named_samples =
- histogram->SnapshotSamples();
- auto original_samples_it = histograms_snapshot_.find(histogram_name);
- if (original_samples_it != histograms_snapshot_.end())
- named_samples->Subtract(*original_samples_it->second.get());
- return named_samples;
-}
-
-void HistogramTester::CheckBucketCount(const std::string& name,
- HistogramBase::Sample sample,
- HistogramBase::Count expected_count,
- const HistogramSamples& samples) const {
- int actual_count;
- GetBucketCountForSamples(name, sample, samples, &actual_count);
-
- EXPECT_EQ(expected_count, actual_count)
- << "Histogram \"" << name
- << "\" does not have the right number of samples (" << expected_count
- << ") in the expected bucket (" << sample << "). It has (" << actual_count
- << ").";
-}
-
-void HistogramTester::CheckTotalCount(const std::string& name,
- HistogramBase::Count expected_count,
- const HistogramSamples& samples) const {
- int actual_count = samples.TotalCount();
- auto histogram_data = histograms_snapshot_.find(name);
- if (histogram_data != histograms_snapshot_.end())
- actual_count -= histogram_data->second->TotalCount();
-
- EXPECT_EQ(expected_count, actual_count)
- << "Histogram \"" << name
- << "\" does not have the right total number of samples ("
- << expected_count << "). It has (" << actual_count << ").";
-}
-
-bool Bucket::operator==(const Bucket& other) const {
- return min == other.min && count == other.count;
-}
-
-void PrintTo(const Bucket& bucket, std::ostream* os) {
- *os << "Bucket " << bucket.min << ": " << bucket.count;
-}
-
-} // namespace base
diff --git a/base/test/histogram_tester.h b/base/test/histogram_tester.h
deleted file mode 100644
index 8019931..0000000
--- a/base/test/histogram_tester.h
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_HISTOGRAM_TESTER_H_
-#define BASE_TEST_HISTOGRAM_TESTER_H_
-
-#include <map>
-#include <memory>
-#include <ostream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_base.h"
-#include "base/time/time.h"
-
-namespace base {
-
-struct Bucket;
-class HistogramSamples;
-
-// HistogramTester provides a simple interface for examining histograms, UMA
-// or otherwise. Tests can use this interface to verify that histogram data is
-// getting logged as intended.
-//
-// Note: When using this class from a browser test, one might have to call
-// SubprocessMetricsProvider::MergeHistogramDeltasForTesting() to sync the
-// histogram data between the renderer and browser processes. If it is in a
-// content browser test, then content::FetchHistogramsFromChildProcesses()
-// should be used to achieve that.
-class HistogramTester {
- public:
- using CountsMap = std::map<std::string, HistogramBase::Count>;
-
- // Takes a snapshot of all current histograms counts.
- HistogramTester();
- ~HistogramTester();
-
- // We know the exact number of samples in a bucket, and that no other bucket
- // should have samples. Measures the diff from the snapshot taken when this
- // object was constructed.
- void ExpectUniqueSample(const std::string& name,
- HistogramBase::Sample sample,
- HistogramBase::Count expected_count) const;
- template <typename T>
- void ExpectUniqueSample(const std::string& name,
- T sample,
- HistogramBase::Count expected_count) const {
- ExpectUniqueSample(name, static_cast<HistogramBase::Sample>(sample),
- expected_count);
- }
-
- // We know the exact number of samples in a bucket, but other buckets may
- // have samples as well. Measures the diff from the snapshot taken when this
- // object was constructed.
- void ExpectBucketCount(const std::string& name,
- HistogramBase::Sample sample,
- HistogramBase::Count expected_count) const;
- template <typename T>
- void ExpectBucketCount(const std::string& name,
- T sample,
- HistogramBase::Count expected_count) const {
- ExpectBucketCount(name, static_cast<HistogramBase::Sample>(sample),
- expected_count);
- }
-
- // We don't know the values of the samples, but we know how many there are.
- // This measures the diff from the snapshot taken when this object was
- // constructed.
- void ExpectTotalCount(const std::string& name,
- HistogramBase::Count count) const;
-
- // We know exact number of samples for buckets corresponding to a time
- // interval. Other intervals may have samples too.
- void ExpectTimeBucketCount(const std::string& name,
- TimeDelta sample,
- HistogramBase::Count count) const;
-
- // Returns a list of all of the buckets recorded since creation of this
- // object, as vector<Bucket>, where the Bucket represents the min boundary of
- // the bucket and the count of samples recorded to that bucket since creation.
- //
- // Example usage, using gMock:
- // EXPECT_THAT(histogram_tester.GetAllSamples("HistogramName"),
- // ElementsAre(Bucket(1, 5), Bucket(2, 10), Bucket(3, 5)));
- //
- // If you build the expected list programmatically, you can use ContainerEq:
- // EXPECT_THAT(histogram_tester.GetAllSamples("HistogramName"),
- // ContainerEq(expected_buckets));
- //
- // or EXPECT_EQ if you prefer not to depend on gMock, at the expense of a
- // slightly less helpful failure message:
- // EXPECT_EQ(expected_buckets,
- // histogram_tester.GetAllSamples("HistogramName"));
- std::vector<Bucket> GetAllSamples(const std::string& name) const;
-
- // Returns the value of the |sample| bucket for ths histogram |name|.
- HistogramBase::Count GetBucketCount(const std::string& name,
- HistogramBase::Sample sample) const;
-
- // Finds histograms whose names start with |prefix|, and returns them along
- // with the counts of any samples added since the creation of this object.
- // Histograms that are unchanged are omitted from the result. The return value
- // is a map whose keys are the histogram name, and whose values are the sample
- // count.
- //
- // This is useful for cases where the code under test is choosing among a
- // family of related histograms and incrementing one of them. Typically you
- // should pass the result of this function directly to EXPECT_THAT.
- //
- // Example usage, using gmock (which produces better failure messages):
- // #include "testing/gmock/include/gmock/gmock.h"
- // ...
- // base::HistogramTester::CountsMap expected_counts;
- // expected_counts["MyMetric.A"] = 1;
- // expected_counts["MyMetric.B"] = 1;
- // EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("MyMetric."),
- // testing::ContainerEq(expected_counts));
- CountsMap GetTotalCountsForPrefix(const std::string& prefix) const;
-
- // Access a modified HistogramSamples containing only what has been logged
- // to the histogram since the creation of this object.
- std::unique_ptr<HistogramSamples> GetHistogramSamplesSinceCreation(
- const std::string& histogram_name) const;
-
- private:
- // Verifies and asserts that value in the |sample| bucket matches the
- // |expected_count|. The bucket's current value is determined from |samples|
- // and is modified based on the snapshot stored for histogram |name|.
- void CheckBucketCount(const std::string& name,
- HistogramBase::Sample sample,
- Histogram::Count expected_count,
- const HistogramSamples& samples) const;
-
- // Verifies that the total number of values recorded for the histogram |name|
- // is |expected_count|. This is checked against |samples| minus the snapshot
- // that was taken for |name|.
- void CheckTotalCount(const std::string& name,
- Histogram::Count expected_count,
- const HistogramSamples& samples) const;
-
- // Sets the value for |count| to be the value in the |sample| bucket. The
- // bucket's current value is determined from |samples| and is modified based
- // on the snapshot stored for histogram |name|.
- void GetBucketCountForSamples(const std::string& name,
- HistogramBase::Sample sample,
- const HistogramSamples& samples,
- HistogramBase::Count* count) const;
-
- // Used to determine the histogram changes made during this instance's
- // lifecycle.
- std::map<std::string, std::unique_ptr<HistogramSamples>> histograms_snapshot_;
-
- DISALLOW_COPY_AND_ASSIGN(HistogramTester);
-};
-
-struct Bucket {
- Bucket(HistogramBase::Sample min, HistogramBase::Count count)
- : min(min), count(count) {}
-
- bool operator==(const Bucket& other) const;
-
- HistogramBase::Sample min;
- HistogramBase::Count count;
-};
-
-void PrintTo(const Bucket& value, std::ostream* os);
-
-} // namespace base
-
-#endif // BASE_TEST_HISTOGRAM_TESTER_H_
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index a5ddb2e..c3a071b 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -15,7 +15,6 @@
#include <memory>
-#include "base/debug/activity_tracker.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/threading/platform_thread_internal_posix.h"
@@ -220,9 +219,6 @@
// static
void PlatformThread::Join(PlatformThreadHandle thread_handle) {
- // Record the event that this thread is blocking upon (for hang diagnosis).
- base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
-
// Joining another thread may block the current thread for a long time, since
// the thread referred to by |thread_handle| may still be running long-lived /
// blocking tasks.
diff --git a/build/gen.py b/build/gen.py
index 988cc58..37592d4 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -237,7 +237,6 @@
'base/callback_helpers.cc',
'base/callback_internal.cc',
'base/command_line.cc',
- 'base/debug/activity_tracker.cc',
'base/debug/alias.cc',
'base/debug/crash_logging.cc',
'base/debug/dump_without_crashing.cc',
@@ -245,14 +244,12 @@
'base/debug/task_annotator.cc',
'base/debug/thread_heap_usage_tracker.cc',
'base/environment.cc',
- 'base/feature_list.cc',
'base/files/file.cc',
'base/files/file_enumerator.cc',
'base/files/file_path.cc',
'base/files/file_path_constants.cc',
'base/files/file_tracing.cc',
'base/files/file_util.cc',
- 'base/files/important_file_writer.cc',
'base/files/memory_mapped_file.cc',
'base/files/scoped_file.cc',
'base/files/scoped_temp_dir.cc',
@@ -281,24 +278,6 @@
'base/message_loop/message_pump.cc',
'base/message_loop/message_pump_default.cc',
'base/message_loop/watchable_io_message_pump_posix.cc',
- 'base/metrics/bucket_ranges.cc',
- 'base/metrics/dummy_histogram.cc',
- 'base/metrics/field_trial.cc',
- 'base/metrics/field_trial_param_associator.cc',
- 'base/metrics/field_trial_params.cc',
- 'base/metrics/histogram.cc',
- 'base/metrics/histogram_base.cc',
- 'base/metrics/histogram_functions.cc',
- 'base/metrics/histogram_samples.cc',
- 'base/metrics/histogram_snapshot_manager.cc',
- 'base/metrics/metrics_hashes.cc',
- 'base/metrics/persistent_histogram_allocator.cc',
- 'base/metrics/persistent_memory_allocator.cc',
- 'base/metrics/persistent_sample_map.cc',
- 'base/metrics/sample_map.cc',
- 'base/metrics/sample_vector.cc',
- 'base/metrics/sparse_histogram.cc',
- 'base/metrics/statistics_recorder.cc',
'base/observer_list_threadsafe.cc',
'base/path_service.cc',
'base/pending_task.cc',