michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: // A StatsTable is a table of statistics. It can be used across multiple michael@0: // processes and threads, maintaining cheap statistics counters without michael@0: // locking. michael@0: // michael@0: // The goal is to make it very cheap and easy for developers to add michael@0: // counters to code, without having to build one-off utilities or mechanisms michael@0: // to track the counters, and also to allow a single "view" to display michael@0: // the contents of all counters. michael@0: // michael@0: // To achieve this, StatsTable creates a shared memory segment to store michael@0: // the data for the counters. Upon creation, it has a specific size michael@0: // which governs the maximum number of counters and concurrent michael@0: // threads/processes which can use it. michael@0: // michael@0: michael@0: #ifndef BASE_STATS_TABLE_H__ michael@0: #define BASE_STATS_TABLE_H__ michael@0: michael@0: #include michael@0: #include "base/basictypes.h" michael@0: #include "base/hash_tables.h" michael@0: #include "base/lock.h" michael@0: #include "base/thread_local_storage.h" michael@0: michael@0: class StatsTablePrivate; michael@0: michael@0: namespace { michael@0: struct StatsTableTLSData; michael@0: } michael@0: michael@0: class StatsTable { michael@0: public: michael@0: // Create a new StatsTable. michael@0: // If a StatsTable already exists with the specified name, this StatsTable michael@0: // will use the same shared memory segment as the original. Otherwise, michael@0: // a new StatsTable is created and all counters are zeroed. michael@0: // michael@0: // name is the name of the StatsTable to use. michael@0: // michael@0: // max_threads is the maximum number of threads the table will support. michael@0: // If the StatsTable already exists, this number is ignored. michael@0: // michael@0: // max_counters is the maximum number of counters the table will support. michael@0: // If the StatsTable already exists, this number is ignored. michael@0: StatsTable(const std::string& name, int max_threads, int max_counters); michael@0: michael@0: // Destroys the StatsTable. When the last StatsTable is destroyed michael@0: // (across all processes), the StatsTable is removed from disk. michael@0: ~StatsTable(); michael@0: michael@0: // For convenience, we create a static table. This is generally michael@0: // used automatically by the counters. michael@0: static StatsTable* current() { return global_table_; } michael@0: michael@0: // Set the global table for use in this process. michael@0: static void set_current(StatsTable* value) { global_table_ = value; } michael@0: michael@0: // Get the slot id for the calling thread. Returns 0 if no michael@0: // slot is assigned. michael@0: int GetSlot() const; michael@0: michael@0: // All threads that contribute data to the table must register with the michael@0: // table first. This function will set thread local storage for the michael@0: // thread containing the location in the table where this thread will michael@0: // write its counter data. michael@0: // michael@0: // name is just a debugging tag to label the thread, and it does not michael@0: // need to be unique. It will be truncated to kMaxThreadNameLength-1 michael@0: // characters. michael@0: // michael@0: // On success, returns the slot id for this thread. On failure, michael@0: // returns 0. michael@0: int RegisterThread(const std::string& name); michael@0: michael@0: // Returns the number of threads currently registered. This is really not michael@0: // useful except for diagnostics and debugging. michael@0: int CountThreadsRegistered() const; michael@0: michael@0: // Find a counter in the StatsTable. michael@0: // michael@0: // Returns an id for the counter which can be used to call GetLocation(). michael@0: // If the counter does not exist, attempts to create a row for the new michael@0: // counter. If there is no space in the table for the new counter, michael@0: // returns 0. michael@0: int FindCounter(const std::string& name); michael@0: michael@0: // TODO(mbelshe): implement RemoveCounter. michael@0: michael@0: // Gets the location of a particular value in the table based on michael@0: // the counter id and slot id. michael@0: int* GetLocation(int counter_id, int slot_id) const; michael@0: michael@0: // Gets the counter name at a particular row. If the row is empty, michael@0: // returns NULL. michael@0: const char* GetRowName(int index) const; michael@0: michael@0: // Gets the sum of the values for a particular row. michael@0: int GetRowValue(int index) const; michael@0: michael@0: // Gets the sum of the values for a particular row for a given pid. michael@0: int GetRowValue(int index, int pid) const; michael@0: michael@0: // Gets the sum of the values for a particular counter. If the counter michael@0: // does not exist, creates the counter. michael@0: int GetCounterValue(const std::string& name); michael@0: michael@0: // Gets the sum of the values for a particular counter for a given pid. michael@0: // If the counter does not exist, creates the counter. michael@0: int GetCounterValue(const std::string& name, int pid); michael@0: michael@0: // The maxinum number of counters/rows in the table. michael@0: int GetMaxCounters() const; michael@0: michael@0: // The maxinum number of threads/columns in the table. michael@0: int GetMaxThreads() const; michael@0: michael@0: // The maximum length (in characters) of a Thread's name including michael@0: // null terminator, as stored in the shared memory. michael@0: static const int kMaxThreadNameLength = 32; michael@0: michael@0: // The maximum length (in characters) of a Counter's name including michael@0: // null terminator, as stored in the shared memory. michael@0: static const int kMaxCounterNameLength = 32; michael@0: michael@0: // Convenience function to lookup a counter location for a michael@0: // counter by name for the calling thread. Will register michael@0: // the thread if it is not already registered. michael@0: static int* FindLocation(const char *name); michael@0: michael@0: private: michael@0: // Returns the space occupied by a thread in the table. Generally used michael@0: // if a thread terminates but the process continues. This function michael@0: // does not zero out the thread's counters. michael@0: // Cannot be used inside a posix tls destructor. michael@0: void UnregisterThread(); michael@0: michael@0: // This variant expects the tls data to be passed in, so it is safe to michael@0: // call from inside a posix tls destructor (see doc for pthread_key_create). michael@0: void UnregisterThread(StatsTableTLSData* tls_data); michael@0: michael@0: // The SlotReturnFunction is called at thread exit for each thread michael@0: // which used the StatsTable. michael@0: static void SlotReturnFunction(void* data); michael@0: michael@0: // Locates a free slot in the table. Returns a number > 0 on success, michael@0: // or 0 on failure. The caller must hold the shared_memory lock when michael@0: // calling this function. michael@0: int FindEmptyThread() const; michael@0: michael@0: // Locates a counter in the table or finds an empty row. Returns a michael@0: // number > 0 on success, or 0 on failure. The caller must hold the michael@0: // shared_memory_lock when calling this function. michael@0: int FindCounterOrEmptyRow(const std::string& name) const; michael@0: michael@0: // Internal function to add a counter to the StatsTable. Assumes that michael@0: // the counter does not already exist in the table. michael@0: // michael@0: // name is a unique identifier for this counter, and will be truncated michael@0: // to kMaxCounterNameLength-1 characters. michael@0: // michael@0: // On success, returns the counter_id for the newly added counter. michael@0: // On failure, returns 0. michael@0: int AddCounter(const std::string& name); michael@0: michael@0: // Get the TLS data for the calling thread. Returns NULL if none is michael@0: // initialized. michael@0: StatsTableTLSData* GetTLSData() const; michael@0: michael@0: typedef base::hash_map CountersMap; michael@0: michael@0: StatsTablePrivate* impl_; michael@0: // The counters_lock_ protects the counters_ hash table. michael@0: Lock counters_lock_; michael@0: // The counters_ hash map is an in-memory hash of the counters. michael@0: // It is used for quick lookup of counters, but is cannot be used michael@0: // as a substitute for what is in the shared memory. Even though michael@0: // we don't have a counter in our hash table, another process may michael@0: // have created it. michael@0: CountersMap counters_; michael@0: TLSSlot tls_index_; michael@0: michael@0: static StatsTable* global_table_; michael@0: michael@0: DISALLOW_EVIL_CONSTRUCTORS(StatsTable); michael@0: }; michael@0: michael@0: #endif // BASE_STATS_TABLE_H__