Index: base/metrics/stats_table.cc |
diff --git a/base/metrics/stats_table.cc b/base/metrics/stats_table.cc |
deleted file mode 100644 |
index 0986395dd9841d87ddb1230715ffbc6adcf66227..0000000000000000000000000000000000000000 |
--- a/base/metrics/stats_table.cc |
+++ /dev/null |
@@ -1,617 +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/metrics/stats_table.h" |
- |
-#include "base/logging.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/memory/shared_memory.h" |
-#include "base/process/process_handle.h" |
-#include "base/strings/string_piece.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "base/threading/platform_thread.h" |
-#include "base/threading/thread_local_storage.h" |
- |
-namespace base { |
- |
-// The StatsTable uses a shared memory segment that is laid out as follows |
-// |
-// +-------------------------------------------+ |
-// | Version | Size | MaxCounters | MaxThreads | |
-// +-------------------------------------------+ |
-// | Thread names table | |
-// +-------------------------------------------+ |
-// | Thread TID table | |
-// +-------------------------------------------+ |
-// | Thread PID table | |
-// +-------------------------------------------+ |
-// | Counter names table | |
-// +-------------------------------------------+ |
-// | Data | |
-// +-------------------------------------------+ |
-// |
-// The data layout is a grid, where the columns are the thread_ids and the |
-// rows are the counter_ids. |
-// |
-// If the first character of the thread_name is '\0', then that column is |
-// empty. |
-// If the first character of the counter_name is '\0', then that row is |
-// empty. |
-// |
-// About Locking: |
-// This class is designed to be both multi-thread and multi-process safe. |
-// Aside from initialization, this is done by partitioning the data which |
-// each thread uses so that no locking is required. However, to allocate |
-// the rows and columns of the table to particular threads, locking is |
-// required. |
-// |
-// At the shared-memory level, we have a lock. This lock protects the |
-// shared-memory table only, and is used when we create new counters (e.g. |
-// use rows) or when we register new threads (e.g. use columns). Reading |
-// data from the table does not require any locking at the shared memory |
-// level. |
-// |
-// Each process which accesses the table will create a StatsTable object. |
-// The StatsTable maintains a hash table of the existing counters in the |
-// table for faster lookup. Since the hash table is process specific, |
-// each process maintains its own cache. We avoid complexity here by never |
-// de-allocating from the hash table. (Counters are dynamically added, |
-// but not dynamically removed). |
- |
-// In order for external viewers to be able to read our shared memory, |
-// we all need to use the same size ints. |
-COMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints); |
- |
-namespace { |
- |
-// An internal version in case we ever change the format of this |
-// file, and so that we can identify our table. |
-const int kTableVersion = 0x13131313; |
- |
-// The name for un-named counters and threads in the table. |
-const char kUnknownName[] = "<unknown>"; |
- |
-// Calculates delta to align an offset to the size of an int |
-inline int AlignOffset(int offset) { |
- return (sizeof(int) - (offset % sizeof(int))) % sizeof(int); |
-} |
- |
-inline int AlignedSize(int size) { |
- return size + AlignOffset(size); |
-} |
- |
-} // namespace |
- |
-// The StatsTable::Internal maintains convenience pointers into the |
-// shared memory segment. Use this class to keep the data structure |
-// clean and accessible. |
-class StatsTable::Internal { |
- public: |
- // Various header information contained in the memory mapped segment. |
- struct TableHeader { |
- int version; |
- int size; |
- int max_counters; |
- int max_threads; |
- }; |
- |
- // Construct a new Internal based on expected size parameters, or |
- // return NULL on failure. |
- static Internal* New(const StatsTable::TableIdentifier& table, |
- int size, |
- int max_threads, |
- int max_counters); |
- |
- SharedMemory* shared_memory() { return shared_memory_.get(); } |
- |
- // Accessors for our header pointers |
- TableHeader* table_header() const { return table_header_; } |
- int version() const { return table_header_->version; } |
- int size() const { return table_header_->size; } |
- int max_counters() const { return table_header_->max_counters; } |
- int max_threads() const { return table_header_->max_threads; } |
- |
- // Accessors for our tables |
- char* thread_name(int slot_id) const { |
- return &thread_names_table_[ |
- (slot_id-1) * (StatsTable::kMaxThreadNameLength)]; |
- } |
- PlatformThreadId* thread_tid(int slot_id) const { |
- return &(thread_tid_table_[slot_id-1]); |
- } |
- int* thread_pid(int slot_id) const { |
- return &(thread_pid_table_[slot_id-1]); |
- } |
- char* counter_name(int counter_id) const { |
- return &counter_names_table_[ |
- (counter_id-1) * (StatsTable::kMaxCounterNameLength)]; |
- } |
- int* row(int counter_id) const { |
- return &data_table_[(counter_id-1) * max_threads()]; |
- } |
- |
- private: |
- // Constructor is private because you should use New() instead. |
- explicit Internal(SharedMemory* shared_memory) |
- : shared_memory_(shared_memory), |
- table_header_(NULL), |
- thread_names_table_(NULL), |
- thread_tid_table_(NULL), |
- thread_pid_table_(NULL), |
- counter_names_table_(NULL), |
- data_table_(NULL) { |
- } |
- |
- // Create or open the SharedMemory used by the stats table. |
- static SharedMemory* CreateSharedMemory( |
- const StatsTable::TableIdentifier& table, |
- int size); |
- |
- // Initializes the table on first access. Sets header values |
- // appropriately and zeroes all counters. |
- void InitializeTable(void* memory, int size, int max_counters, |
- int max_threads); |
- |
- // Initializes our in-memory pointers into a pre-created StatsTable. |
- void ComputeMappedPointers(void* memory); |
- |
- scoped_ptr<SharedMemory> shared_memory_; |
- TableHeader* table_header_; |
- char* thread_names_table_; |
- PlatformThreadId* thread_tid_table_; |
- int* thread_pid_table_; |
- char* counter_names_table_; |
- int* data_table_; |
- |
- DISALLOW_COPY_AND_ASSIGN(Internal); |
-}; |
- |
-// static |
-StatsTable::Internal* StatsTable::Internal::New( |
- const StatsTable::TableIdentifier& table, |
- int size, |
- int max_threads, |
- int max_counters) { |
- scoped_ptr<SharedMemory> shared_memory(CreateSharedMemory(table, size)); |
- if (!shared_memory.get()) |
- return NULL; |
- if (!shared_memory->Map(size)) |
- return NULL; |
- void* memory = shared_memory->memory(); |
- |
- scoped_ptr<Internal> internal(new Internal(shared_memory.release())); |
- TableHeader* header = static_cast<TableHeader*>(memory); |
- |
- // If the version does not match, then assume the table needs |
- // to be initialized. |
- if (header->version != kTableVersion) |
- internal->InitializeTable(memory, size, max_counters, max_threads); |
- |
- // We have a valid table, so compute our pointers. |
- internal->ComputeMappedPointers(memory); |
- |
- return internal.release(); |
-} |
- |
-// static |
-SharedMemory* StatsTable::Internal::CreateSharedMemory( |
- const StatsTable::TableIdentifier& table, |
- int size) { |
-#if defined(OS_POSIX) |
- // Check for existing table. |
- if (table.fd != -1) |
- return new SharedMemory(table, false); |
- |
- // Otherwise we need to create it. |
- scoped_ptr<SharedMemory> shared_memory(new SharedMemory()); |
- if (!shared_memory->CreateAnonymous(size)) |
- return NULL; |
- return shared_memory.release(); |
-#elif defined(OS_WIN) |
- scoped_ptr<SharedMemory> shared_memory(new SharedMemory()); |
- if (table.empty()) { |
- // Create an anonymous table. |
- if (!shared_memory->CreateAnonymous(size)) |
- return NULL; |
- } else { |
- // Create a named table for sharing between processes. |
- if (!shared_memory->CreateNamedDeprecated(table, true, size)) |
- return NULL; |
- } |
- return shared_memory.release(); |
-#endif |
-} |
- |
-void StatsTable::Internal::InitializeTable(void* memory, int size, |
- int max_counters, |
- int max_threads) { |
- // Zero everything. |
- memset(memory, 0, size); |
- |
- // Initialize the header. |
- TableHeader* header = static_cast<TableHeader*>(memory); |
- header->version = kTableVersion; |
- header->size = size; |
- header->max_counters = max_counters; |
- header->max_threads = max_threads; |
-} |
- |
-void StatsTable::Internal::ComputeMappedPointers(void* memory) { |
- char* data = static_cast<char*>(memory); |
- int offset = 0; |
- |
- table_header_ = reinterpret_cast<TableHeader*>(data); |
- offset += sizeof(*table_header_); |
- offset += AlignOffset(offset); |
- |
- // Verify we're looking at a valid StatsTable. |
- DCHECK_EQ(table_header_->version, kTableVersion); |
- |
- thread_names_table_ = reinterpret_cast<char*>(data + offset); |
- offset += sizeof(char) * |
- max_threads() * StatsTable::kMaxThreadNameLength; |
- offset += AlignOffset(offset); |
- |
- thread_tid_table_ = reinterpret_cast<PlatformThreadId*>(data + offset); |
- offset += sizeof(int) * max_threads(); |
- offset += AlignOffset(offset); |
- |
- thread_pid_table_ = reinterpret_cast<int*>(data + offset); |
- offset += sizeof(int) * max_threads(); |
- offset += AlignOffset(offset); |
- |
- counter_names_table_ = reinterpret_cast<char*>(data + offset); |
- offset += sizeof(char) * |
- max_counters() * StatsTable::kMaxCounterNameLength; |
- offset += AlignOffset(offset); |
- |
- data_table_ = reinterpret_cast<int*>(data + offset); |
- offset += sizeof(int) * max_threads() * max_counters(); |
- |
- DCHECK_EQ(offset, size()); |
-} |
- |
-// TLSData carries the data stored in the TLS slots for the |
-// StatsTable. This is used so that we can properly cleanup when the |
-// thread exits and return the table slot. |
-// |
-// Each thread that calls RegisterThread in the StatsTable will have |
-// a TLSData stored in its TLS. |
-struct StatsTable::TLSData { |
- StatsTable* table; |
- int slot; |
-}; |
- |
-// We keep a singleton table which can be easily accessed. |
-StatsTable* global_table = NULL; |
- |
-StatsTable::StatsTable(const TableIdentifier& table, |
- int max_threads, |
- int max_counters) |
- : internal_(NULL), |
- tls_index_(SlotReturnFunction) { |
- int table_size = |
- AlignedSize(sizeof(Internal::TableHeader)) + |
- AlignedSize((max_counters * sizeof(char) * kMaxCounterNameLength)) + |
- AlignedSize((max_threads * sizeof(char) * kMaxThreadNameLength)) + |
- AlignedSize(max_threads * sizeof(int)) + |
- AlignedSize(max_threads * sizeof(int)) + |
- AlignedSize((sizeof(int) * (max_counters * max_threads))); |
- |
- internal_ = Internal::New(table, table_size, max_threads, max_counters); |
- |
- if (!internal_) |
- DPLOG(ERROR) << "StatsTable did not initialize"; |
-} |
- |
-StatsTable::~StatsTable() { |
- // Before we tear down our copy of the table, be sure to |
- // unregister our thread. |
- UnregisterThread(); |
- |
- // Return ThreadLocalStorage. At this point, if any registered threads |
- // still exist, they cannot Unregister. |
- tls_index_.Free(); |
- |
- // Cleanup our shared memory. |
- delete internal_; |
- |
- // If we are the global table, unregister ourselves. |
- if (global_table == this) |
- global_table = NULL; |
-} |
- |
-StatsTable* StatsTable::current() { |
- return global_table; |
-} |
- |
-void StatsTable::set_current(StatsTable* value) { |
- global_table = value; |
-} |
- |
-int StatsTable::GetSlot() const { |
- TLSData* data = GetTLSData(); |
- if (!data) |
- return 0; |
- return data->slot; |
-} |
- |
-int StatsTable::RegisterThread(const std::string& name) { |
- int slot = 0; |
- if (!internal_) |
- return 0; |
- |
- // Registering a thread requires that we lock the shared memory |
- // so that two threads don't grab the same slot. Fortunately, |
- // thread creation shouldn't happen in inner loops. |
- // TODO(viettrungluu): crbug.com/345734: Use a different locking mechanism. |
- { |
- SharedMemoryAutoLockDeprecated lock(internal_->shared_memory()); |
- slot = FindEmptyThread(); |
- if (!slot) { |
- return 0; |
- } |
- |
- // We have space, so consume a column in the table. |
- std::string thread_name = name; |
- if (name.empty()) |
- thread_name = kUnknownName; |
- strlcpy(internal_->thread_name(slot), thread_name.c_str(), |
- kMaxThreadNameLength); |
- *(internal_->thread_tid(slot)) = PlatformThread::CurrentId(); |
- *(internal_->thread_pid(slot)) = GetCurrentProcId(); |
- } |
- |
- // Set our thread local storage. |
- TLSData* data = new TLSData; |
- data->table = this; |
- data->slot = slot; |
- tls_index_.Set(data); |
- return slot; |
-} |
- |
-int StatsTable::CountThreadsRegistered() const { |
- if (!internal_) |
- return 0; |
- |
- // Loop through the shared memory and count the threads that are active. |
- // We intentionally do not lock the table during the operation. |
- int count = 0; |
- for (int index = 1; index <= internal_->max_threads(); index++) { |
- char* name = internal_->thread_name(index); |
- if (*name != '\0') |
- count++; |
- } |
- return count; |
-} |
- |
-int StatsTable::FindCounter(const std::string& name) { |
- // Note: the API returns counters numbered from 1..N, although |
- // internally, the array is 0..N-1. This is so that we can return |
- // zero as "not found". |
- if (!internal_) |
- return 0; |
- |
- // Create a scope for our auto-lock. |
- { |
- AutoLock scoped_lock(counters_lock_); |
- |
- // Attempt to find the counter. |
- CountersMap::const_iterator iter; |
- iter = counters_.find(name); |
- if (iter != counters_.end()) |
- return iter->second; |
- } |
- |
- // Counter does not exist, so add it. |
- return AddCounter(name); |
-} |
- |
-int* StatsTable::GetLocation(int counter_id, int slot_id) const { |
- if (!internal_) |
- return NULL; |
- if (slot_id > internal_->max_threads()) |
- return NULL; |
- |
- int* row = internal_->row(counter_id); |
- return &(row[slot_id-1]); |
-} |
- |
-const char* StatsTable::GetRowName(int index) const { |
- if (!internal_) |
- return NULL; |
- |
- return internal_->counter_name(index); |
-} |
- |
-int StatsTable::GetRowValue(int index) const { |
- return GetRowValue(index, 0); |
-} |
- |
-int StatsTable::GetRowValue(int index, int pid) const { |
- if (!internal_) |
- return 0; |
- |
- int rv = 0; |
- int* row = internal_->row(index); |
- for (int slot_id = 1; slot_id <= internal_->max_threads(); slot_id++) { |
- if (pid == 0 || *internal_->thread_pid(slot_id) == pid) |
- rv += row[slot_id-1]; |
- } |
- return rv; |
-} |
- |
-int StatsTable::GetCounterValue(const std::string& name) { |
- return GetCounterValue(name, 0); |
-} |
- |
-int StatsTable::GetCounterValue(const std::string& name, int pid) { |
- if (!internal_) |
- return 0; |
- |
- int row = FindCounter(name); |
- if (!row) |
- return 0; |
- return GetRowValue(row, pid); |
-} |
- |
-int StatsTable::GetMaxCounters() const { |
- if (!internal_) |
- return 0; |
- return internal_->max_counters(); |
-} |
- |
-int StatsTable::GetMaxThreads() const { |
- if (!internal_) |
- return 0; |
- return internal_->max_threads(); |
-} |
- |
-int* StatsTable::FindLocation(const char* name) { |
- // Get the static StatsTable |
- StatsTable *table = StatsTable::current(); |
- if (!table) |
- return NULL; |
- |
- // Get the slot for this thread. Try to register |
- // it if none exists. |
- int slot = table->GetSlot(); |
- if (!slot) |
- slot = table->RegisterThread(std::string()); |
- if (!slot) |
- return NULL; |
- |
- // Find the counter id for the counter. |
- std::string str_name(name); |
- int counter = table->FindCounter(str_name); |
- |
- // Now we can find the location in the table. |
- return table->GetLocation(counter, slot); |
-} |
- |
-void StatsTable::UnregisterThread() { |
- UnregisterThread(GetTLSData()); |
-} |
- |
-void StatsTable::UnregisterThread(TLSData* data) { |
- if (!data) |
- return; |
- DCHECK(internal_); |
- |
- // Mark the slot free by zeroing out the thread name. |
- char* name = internal_->thread_name(data->slot); |
- *name = '\0'; |
- |
- // Remove the calling thread's TLS so that it cannot use the slot. |
- tls_index_.Set(NULL); |
- delete data; |
-} |
- |
-void StatsTable::SlotReturnFunction(void* data) { |
- // This is called by the TLS destructor, which on some platforms has |
- // already cleared the TLS info, so use the tls_data argument |
- // rather than trying to fetch it ourselves. |
- TLSData* tls_data = static_cast<TLSData*>(data); |
- if (tls_data) { |
- DCHECK(tls_data->table); |
- tls_data->table->UnregisterThread(tls_data); |
- } |
-} |
- |
-int StatsTable::FindEmptyThread() const { |
- // Note: the API returns slots numbered from 1..N, although |
- // internally, the array is 0..N-1. This is so that we can return |
- // zero as "not found". |
- // |
- // The reason for doing this is because the thread 'slot' is stored |
- // in TLS, which is always initialized to zero, not -1. If 0 were |
- // returned as a valid slot number, it would be confused with the |
- // uninitialized state. |
- if (!internal_) |
- return 0; |
- |
- int index = 1; |
- for (; index <= internal_->max_threads(); index++) { |
- char* name = internal_->thread_name(index); |
- if (!*name) |
- break; |
- } |
- if (index > internal_->max_threads()) |
- return 0; // The table is full. |
- return index; |
-} |
- |
-int StatsTable::FindCounterOrEmptyRow(const std::string& name) const { |
- // Note: the API returns slots numbered from 1..N, although |
- // internally, the array is 0..N-1. This is so that we can return |
- // zero as "not found". |
- // |
- // There isn't much reason for this other than to be consistent |
- // with the way we track columns for thread slots. (See comments |
- // in FindEmptyThread for why it is done this way). |
- if (!internal_) |
- return 0; |
- |
- int free_slot = 0; |
- for (int index = 1; index <= internal_->max_counters(); index++) { |
- char* row_name = internal_->counter_name(index); |
- if (!*row_name && !free_slot) |
- free_slot = index; // save that we found a free slot |
- else if (!strncmp(row_name, name.c_str(), kMaxCounterNameLength)) |
- return index; |
- } |
- return free_slot; |
-} |
- |
-int StatsTable::AddCounter(const std::string& name) { |
- if (!internal_) |
- return 0; |
- |
- int counter_id = 0; |
- { |
- // To add a counter to the shared memory, we need the |
- // shared memory lock. |
- SharedMemoryAutoLockDeprecated lock(internal_->shared_memory()); |
- |
- // We have space, so create a new counter. |
- counter_id = FindCounterOrEmptyRow(name); |
- if (!counter_id) |
- return 0; |
- |
- std::string counter_name = name; |
- if (name.empty()) |
- counter_name = kUnknownName; |
- strlcpy(internal_->counter_name(counter_id), counter_name.c_str(), |
- kMaxCounterNameLength); |
- } |
- |
- // now add to our in-memory cache |
- { |
- AutoLock lock(counters_lock_); |
- counters_[name] = counter_id; |
- } |
- return counter_id; |
-} |
- |
-StatsTable::TLSData* StatsTable::GetTLSData() const { |
- TLSData* data = |
- static_cast<TLSData*>(tls_index_.Get()); |
- if (!data) |
- return NULL; |
- |
- DCHECK(data->slot); |
- DCHECK_EQ(data->table, this); |
- return data; |
-} |
- |
-#if defined(OS_POSIX) |
-SharedMemoryHandle StatsTable::GetSharedMemoryHandle() const { |
- if (!internal_) |
- return SharedMemory::NULLHandle(); |
- return internal_->shared_memory()->handle(); |
-} |
-#endif |
- |
-} // namespace base |