stats_table.cc revision 2255f234e3edbf06b52de2ab4329d1564ef8efb0
1837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh// Use of this source code is governed by a BSD-style license that can be 3837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// found in the LICENSE file. 4837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 5837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/metrics/stats_table.h" 6837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 7837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/logging.h" 8837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/platform_thread.h" 9837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/process_util.h" 10837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/scoped_ptr.h" 11837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/shared_memory.h" 12837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/string_piece.h" 13837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/string_util.h" 14837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/thread_local_storage.h" 15837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "base/utf_string_conversions.h" 16837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 17837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#if defined(OS_POSIX) 18837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh#include "errno.h" 19bd5fa3c99638830d3fa1ae5b4fc4988de5ee0f4dChia-chi Yeh#endif 20837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 21f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yehnamespace base { 2266d08f4f81c446015c03113bd86d1ef5121bab36Chia-chi Yeh 23f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh// The StatsTable uses a shared memory segment that is laid out as follows 24837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// 25837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// +-------------------------------------------+ 26837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// | Version | Size | MaxCounters | MaxThreads | 27bd5fa3c99638830d3fa1ae5b4fc4988de5ee0f4dChia-chi Yeh// +-------------------------------------------+ 28837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// | Thread names table | 29458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// +-------------------------------------------+ 30458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// | Thread TID table | 31c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root// +-------------------------------------------+ 32c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root// | Thread PID table | 331070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// +-------------------------------------------+ 341070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// | Counter names table | 351070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// +-------------------------------------------+ 361070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// | Data | 371070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// +-------------------------------------------+ 381070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// 391070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// The data layout is a grid, where the columns are the thread_ids and the 401070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// rows are the counter_ids. 411070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// 421070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// If the first character of the thread_name is '\0', then that column is 431070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// empty. 441070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// If the first character of the counter_name is '\0', then that row is 451070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// empty. 461070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh// 47cadace43df20e28cc98c2068b327ec3ebe119418Chia-chi Yeh// About Locking: 48cadace43df20e28cc98c2068b327ec3ebe119418Chia-chi Yeh// This class is designed to be both multi-thread and multi-process safe. 49cadace43df20e28cc98c2068b327ec3ebe119418Chia-chi Yeh// Aside from initialization, this is done by partitioning the data which 50cadace43df20e28cc98c2068b327ec3ebe119418Chia-chi Yeh// each thread uses so that no locking is required. However, to allocate 51cadace43df20e28cc98c2068b327ec3ebe119418Chia-chi Yeh// the rows and columns of the table to particular threads, locking is 524dd8f6be6496fc7cb7b7351c79f6a90be7be8991Chia-chi Yeh// required. 53458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// 54c9ac7d2fae3a233f928fd3f643ffa20b6ea602d3Chia-chi Yeh// At the shared-memory level, we have a lock. This lock protects the 55458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// shared-memory table only, and is used when we create new counters (e.g. 56458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// use rows) or when we register new threads (e.g. use columns). Reading 57458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// data from the table does not require any locking at the shared memory 58cadace43df20e28cc98c2068b327ec3ebe119418Chia-chi Yeh// level. 59cadace43df20e28cc98c2068b327ec3ebe119418Chia-chi Yeh// 60458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// Each process which accesses the table will create a StatsTable object. 61e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh// The StatsTable maintains a hash table of the existing counters in the 62458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// table for faster lookup. Since the hash table is process specific, 63458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// each process maintains its own cache. We avoid complexity here by never 64458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// de-allocating from the hash table. (Counters are dynamically added, 65458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// but not dynamically removed). 66f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh 67458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// In order for external viewers to be able to read our shared memory, 68458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// we all need to use the same size ints. 693724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi YehCOMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints); 70458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 71458fe1ef88671dfe580c488973d5573194839087Chia-chi Yehnamespace { 72c9ac7d2fae3a233f928fd3f643ffa20b6ea602d3Chia-chi Yeh 73c9ac7d2fae3a233f928fd3f643ffa20b6ea602d3Chia-chi Yeh// An internal version in case we ever change the format of this 743724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi Yeh// file, and so that we can identify our table. 753724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi Yehconst int kTableVersion = 0x13131313; 76458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 77f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh// The name for un-named counters and threads in the table. 78458fe1ef88671dfe580c488973d5573194839087Chia-chi Yehconst char kUnknownName[] = "<unknown>"; 793724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi Yeh 80458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// Calculates delta to align an offset to the size of an int 81c9ac7d2fae3a233f928fd3f643ffa20b6ea602d3Chia-chi Yehinline int AlignOffset(int offset) { 823724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi Yeh return (sizeof(int) - (offset % sizeof(int))) % sizeof(int); 833724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi Yeh} 843724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi Yeh 85458fe1ef88671dfe580c488973d5573194839087Chia-chi Yehinline int AlignedSize(int size) { 86458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh return size + AlignOffset(size); 87458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh} 88458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 89458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh} // namespace 90458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 91458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// The StatsTable::Private maintains convenience pointers into the 92f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh// shared memory segment. Use this class to keep the data structure 93458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// clean and accessible. 94458fe1ef88671dfe580c488973d5573194839087Chia-chi Yehclass StatsTable::Private { 95458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh public: 96458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh // Various header information contained in the memory mapped segment. 97458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh struct TableHeader { 98458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh int version; 99458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh int size; 100458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh int max_counters; 101458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh int max_threads; 102e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh }; 103c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh 104c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh // Construct a new Private based on expected size parameters, or 105a9a07aca7cd1e611f2d73582f20623cd62b917baChia-chi Yeh // return NULL on failure. 1061070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh static Private* New(const std::string& name, int size, 1071070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh int max_threads, int max_counters); 108dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh 1091070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh SharedMemory* shared_memory() { return &shared_memory_; } 110a9a07aca7cd1e611f2d73582f20623cd62b917baChia-chi Yeh 111dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh // Accessors for our header pointers 112dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh TableHeader* table_header() const { return table_header_; } 113dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh int version() const { return table_header_->version; } 114dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh int size() const { return table_header_->size; } 115dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh int max_counters() const { return table_header_->max_counters; } 116dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh int max_threads() const { return table_header_->max_threads; } 117dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh 1181070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh // Accessors for our tables 1191070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh char* thread_name(int slot_id) const { 1201070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh return &thread_names_table_[ 1211070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh (slot_id-1) * (StatsTable::kMaxThreadNameLength)]; 122dc6f5b944434891dabd1aed297676349b58cb893Chia-chi Yeh } 123a9a07aca7cd1e611f2d73582f20623cd62b917baChia-chi Yeh PlatformThreadId* thread_tid(int slot_id) const { 1241070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh return &(thread_tid_table_[slot_id-1]); 1251070097bb11002f8b5e289982cee9e324ea2f153Chia-chi Yeh } 126458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh int* thread_pid(int slot_id) const { 127458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh return &(thread_pid_table_[slot_id-1]); 128c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh } 129f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh char* counter_name(int counter_id) const { 130c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh return &counter_names_table_[ 131c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh (counter_id-1) * (StatsTable::kMaxCounterNameLength)]; 132f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh } 133c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh int* row(int counter_id) const { 134c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh return &data_table_[(counter_id-1) * max_threads()]; 135c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh } 136c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh 137c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh private: 138c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh // Constructor is private because you should use New() instead. 139c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh Private() {} 140c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh 141c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh // Initializes the table on first access. Sets header values 142c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh // appropriately and zeroes all counters. 143c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh void InitializeTable(void* memory, int size, int max_counters, 144c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh int max_threads); 145c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi Yeh 146c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh // Initializes our in-memory pointers into a pre-created StatsTable. 147c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh void ComputeMappedPointers(void* memory); 148c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh 149c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh SharedMemory shared_memory_; 150c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh TableHeader* table_header_; 151c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh char* thread_names_table_; 152c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh PlatformThreadId* thread_tid_table_; 153c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh int* thread_pid_table_; 154c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh char* counter_names_table_; 155c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh int* data_table_; 156c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh}; 157c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh 158837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// static 159837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi YehStatsTable::Private* StatsTable::Private::New(const std::string& name, 160e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh int size, 1614dd8f6be6496fc7cb7b7351c79f6a90be7be8991Chia-chi Yeh int max_threads, 162c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root int max_counters) { 163e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh#ifdef ANDROID 1648f3b38855d8849959825acc45dd11144adc7d862Chia-chi Yeh return NULL; 165c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh#else 166c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root scoped_ptr<Private> priv(new Private()); 167c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root if (!priv->shared_memory_.Create(name, false, true, size)) 168c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root return NULL; 169c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root if (!priv->shared_memory_.Map(size)) 170c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root return NULL; 171c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root void* memory = priv->shared_memory_.memory(); 172c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root 173e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh TableHeader* header = static_cast<TableHeader*>(memory); 174e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh 175e9fc376dc7e9ee22358b872c3eb2808fa42160f0Chia-chi Yeh // If the version does not match, then assume the table needs 176c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh // to be initialized. 177837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh if (header->version != kTableVersion) 1787197eb77ef21feeedc5a47de31ded3a19c2af021Chia-chi Yeh priv->InitializeTable(memory, size, max_counters, max_threads); 1797197eb77ef21feeedc5a47de31ded3a19c2af021Chia-chi Yeh 1807197eb77ef21feeedc5a47de31ded3a19c2af021Chia-chi Yeh // We have a valid table, so compute our pointers. 181837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh priv->ComputeMappedPointers(memory); 1829d271b685df5830e92a789119fe9b908da2f6c78Chia-chi Yeh 183837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh return priv.release(); 18412f449335c62c731f6eb33db7e27ce331f423f71Chia-chi Yeh#endif 18512f449335c62c731f6eb33db7e27ce331f423f71Chia-chi Yeh} 18612f449335c62c731f6eb33db7e27ce331f423f71Chia-chi Yeh 1873724e61c7fb7a792d36c4dbec826e06b1aabd039Chia-chi Yehvoid StatsTable::Private::InitializeTable(void* memory, int size, 18812f449335c62c731f6eb33db7e27ce331f423f71Chia-chi Yeh int max_counters, 18912f449335c62c731f6eb33db7e27ce331f423f71Chia-chi Yeh int max_threads) { 19012f449335c62c731f6eb33db7e27ce331f423f71Chia-chi Yeh // Zero everything. 191837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh memset(memory, 0, size); 192f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh 193f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh // Initialize the header. 194f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh TableHeader* header = static_cast<TableHeader*>(memory); 195c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh header->version = kTableVersion; 196f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh header->size = size; 197c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh header->max_counters = max_counters; 198f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh header->max_threads = max_threads; 19966d08f4f81c446015c03113bd86d1ef5121bab36Chia-chi Yeh} 20066d08f4f81c446015c03113bd86d1ef5121bab36Chia-chi Yeh 20166d08f4f81c446015c03113bd86d1ef5121bab36Chia-chi Yehvoid StatsTable::Private::ComputeMappedPointers(void* memory) { 202f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh char* data = static_cast<char*>(memory); 203f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh int offset = 0; 204f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh 205c91307af2622f6625525f3c1f9c954376df950adChia-chi Yeh table_header_ = reinterpret_cast<TableHeader*>(data); 206f8a6a7636d53a5730c58ae041e4e09ae12e1657cChia-chi Yeh offset += sizeof(*table_header_); 207837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh offset += AlignOffset(offset); 208837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 209837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh // Verify we're looking at a valid StatsTable. 210c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root DCHECK_EQ(table_header_->version, kTableVersion); 211c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root 212c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root thread_names_table_ = reinterpret_cast<char*>(data + offset); 213c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root offset += sizeof(char) * 214c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root max_threads() * StatsTable::kMaxThreadNameLength; 215c1b51d45a7363d6fa58b59bf6f12182993a7c1d0Kenny Root offset += AlignOffset(offset); 216837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 217837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh thread_tid_table_ = reinterpret_cast<PlatformThreadId*>(data + offset); 218837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh offset += sizeof(int) * max_threads(); 219837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh offset += AlignOffset(offset); 220837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 221837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh thread_pid_table_ = reinterpret_cast<int*>(data + offset); 222837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh offset += sizeof(int) * max_threads(); 223458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh offset += AlignOffset(offset); 224458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 225458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh counter_names_table_ = reinterpret_cast<char*>(data + offset); 226458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh offset += sizeof(char) * 227458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh max_counters() * StatsTable::kMaxCounterNameLength; 228458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh offset += AlignOffset(offset); 229458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 230458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh data_table_ = reinterpret_cast<int*>(data + offset); 231458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh offset += sizeof(int) * max_threads() * max_counters(); 232458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 233458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh DCHECK_EQ(offset, size()); 234458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh} 235458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh 236458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// TLSData carries the data stored in the TLS slots for the 237458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// StatsTable. This is used so that we can properly cleanup when the 238458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// thread exits and return the table slot. 239458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// 240458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// Each thread that calls RegisterThread in the StatsTable will have 241458fe1ef88671dfe580c488973d5573194839087Chia-chi Yeh// a TLSData stored in its TLS. 242837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yehstruct StatsTable::TLSData { 243837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh StatsTable* table; 244837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh int slot; 245837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh}; 246837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 247837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh// We keep a singleton table which can be easily accessed. 248837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi YehStatsTable* StatsTable::global_table_ = NULL; 249837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh 250c454954382b81262dc81ac54e147f4dc7fc0af75Chia-chi YehStatsTable::StatsTable(const std::string& name, int max_threads, 251837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh int max_counters) 252837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh : impl_(NULL), 253837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh tls_index_(SlotReturnFunction) { 254837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh int table_size = 255837a1c77bab77bd62cccb33a15163a962f8dfb97Chia-chi Yeh AlignedSize(sizeof(Private::TableHeader)) + 256 AlignedSize((max_counters * sizeof(char) * kMaxCounterNameLength)) + 257 AlignedSize((max_threads * sizeof(char) * kMaxThreadNameLength)) + 258 AlignedSize(max_threads * sizeof(int)) + 259 AlignedSize(max_threads * sizeof(int)) + 260 AlignedSize((sizeof(int) * (max_counters * max_threads))); 261 262 impl_ = Private::New(name, table_size, max_threads, max_counters); 263 264 if (!impl_) 265 PLOG(ERROR) << "StatsTable did not initialize"; 266} 267 268StatsTable::~StatsTable() { 269 // Before we tear down our copy of the table, be sure to 270 // unregister our thread. 271 UnregisterThread(); 272 273 // Return ThreadLocalStorage. At this point, if any registered threads 274 // still exist, they cannot Unregister. 275 tls_index_.Free(); 276 277 // Cleanup our shared memory. 278 delete impl_; 279 280 // If we are the global table, unregister ourselves. 281 if (global_table_ == this) 282 global_table_ = NULL; 283} 284 285int StatsTable::RegisterThread(const std::string& name) { 286#ifdef ANDROID 287 return 0; 288#else 289 int slot = 0; 290 if (!impl_) 291 return 0; 292 293 // Registering a thread requires that we lock the shared memory 294 // so that two threads don't grab the same slot. Fortunately, 295 // thread creation shouldn't happen in inner loops. 296 { 297 SharedMemoryAutoLock lock(impl_->shared_memory()); 298 slot = FindEmptyThread(); 299 if (!slot) { 300 return 0; 301 } 302 303 // We have space, so consume a column in the table. 304 std::string thread_name = name; 305 if (name.empty()) 306 thread_name = kUnknownName; 307 strlcpy(impl_->thread_name(slot), thread_name.c_str(), 308 kMaxThreadNameLength); 309 *(impl_->thread_tid(slot)) = PlatformThread::CurrentId(); 310 *(impl_->thread_pid(slot)) = GetCurrentProcId(); 311 } 312 313 // Set our thread local storage. 314 TLSData* data = new TLSData; 315 data->table = this; 316 data->slot = slot; 317 tls_index_.Set(data); 318 return slot; 319#endif 320} 321 322StatsTable::TLSData* StatsTable::GetTLSData() const { 323 TLSData* data = 324 static_cast<TLSData*>(tls_index_.Get()); 325 if (!data) 326 return NULL; 327 328 DCHECK(data->slot); 329 DCHECK_EQ(data->table, this); 330 return data; 331} 332 333void StatsTable::UnregisterThread() { 334 UnregisterThread(GetTLSData()); 335} 336 337void StatsTable::UnregisterThread(TLSData* data) { 338 if (!data) 339 return; 340 DCHECK(impl_); 341 342 // Mark the slot free by zeroing out the thread name. 343 char* name = impl_->thread_name(data->slot); 344 *name = '\0'; 345 346 // Remove the calling thread's TLS so that it cannot use the slot. 347 tls_index_.Set(NULL); 348 delete data; 349} 350 351void StatsTable::SlotReturnFunction(void* data) { 352 // This is called by the TLS destructor, which on some platforms has 353 // already cleared the TLS info, so use the tls_data argument 354 // rather than trying to fetch it ourselves. 355 TLSData* tls_data = static_cast<TLSData*>(data); 356 if (tls_data) { 357 DCHECK(tls_data->table); 358 tls_data->table->UnregisterThread(tls_data); 359 } 360} 361 362int StatsTable::CountThreadsRegistered() const { 363 if (!impl_) 364 return 0; 365 366 // Loop through the shared memory and count the threads that are active. 367 // We intentionally do not lock the table during the operation. 368 int count = 0; 369 for (int index = 1; index <= impl_->max_threads(); index++) { 370 char* name = impl_->thread_name(index); 371 if (*name != '\0') 372 count++; 373 } 374 return count; 375} 376 377int StatsTable::GetSlot() const { 378 TLSData* data = GetTLSData(); 379 if (!data) 380 return 0; 381 return data->slot; 382} 383 384int StatsTable::FindEmptyThread() const { 385 // Note: the API returns slots numbered from 1..N, although 386 // internally, the array is 0..N-1. This is so that we can return 387 // zero as "not found". 388 // 389 // The reason for doing this is because the thread 'slot' is stored 390 // in TLS, which is always initialized to zero, not -1. If 0 were 391 // returned as a valid slot number, it would be confused with the 392 // uninitialized state. 393 if (!impl_) 394 return 0; 395 396 int index = 1; 397 for (; index <= impl_->max_threads(); index++) { 398 char* name = impl_->thread_name(index); 399 if (!*name) 400 break; 401 } 402 if (index > impl_->max_threads()) 403 return 0; // The table is full. 404 return index; 405} 406 407int StatsTable::FindCounterOrEmptyRow(const std::string& name) const { 408 // Note: the API returns slots numbered from 1..N, although 409 // internally, the array is 0..N-1. This is so that we can return 410 // zero as "not found". 411 // 412 // There isn't much reason for this other than to be consistent 413 // with the way we track columns for thread slots. (See comments 414 // in FindEmptyThread for why it is done this way). 415 if (!impl_) 416 return 0; 417 418 int free_slot = 0; 419 for (int index = 1; index <= impl_->max_counters(); index++) { 420 char* row_name = impl_->counter_name(index); 421 if (!*row_name && !free_slot) 422 free_slot = index; // save that we found a free slot 423 else if (!strncmp(row_name, name.c_str(), kMaxCounterNameLength)) 424 return index; 425 } 426 return free_slot; 427} 428 429int StatsTable::FindCounter(const std::string& name) { 430 // Note: the API returns counters numbered from 1..N, although 431 // internally, the array is 0..N-1. This is so that we can return 432 // zero as "not found". 433 if (!impl_) 434 return 0; 435 436 // Create a scope for our auto-lock. 437 { 438 AutoLock scoped_lock(counters_lock_); 439 440 // Attempt to find the counter. 441 CountersMap::const_iterator iter; 442 iter = counters_.find(name); 443 if (iter != counters_.end()) 444 return iter->second; 445 } 446 447 // Counter does not exist, so add it. 448 return AddCounter(name); 449} 450 451int StatsTable::AddCounter(const std::string& name) { 452#ifdef ANDROID 453 return 0; 454#else 455 456 if (!impl_) 457 return 0; 458 459 int counter_id = 0; 460 { 461 // To add a counter to the shared memory, we need the 462 // shared memory lock. 463 SharedMemoryAutoLock lock(impl_->shared_memory()); 464 465 // We have space, so create a new counter. 466 counter_id = FindCounterOrEmptyRow(name); 467 if (!counter_id) 468 return 0; 469 470 std::string counter_name = name; 471 if (name.empty()) 472 counter_name = kUnknownName; 473 strlcpy(impl_->counter_name(counter_id), counter_name.c_str(), 474 kMaxCounterNameLength); 475 } 476 477 // now add to our in-memory cache 478 { 479 AutoLock lock(counters_lock_); 480 counters_[name] = counter_id; 481 } 482 return counter_id; 483#endif 484} 485 486int* StatsTable::GetLocation(int counter_id, int slot_id) const { 487 if (!impl_) 488 return NULL; 489 if (slot_id > impl_->max_threads()) 490 return NULL; 491 492 int* row = impl_->row(counter_id); 493 return &(row[slot_id-1]); 494} 495 496const char* StatsTable::GetRowName(int index) const { 497 if (!impl_) 498 return NULL; 499 500 return impl_->counter_name(index); 501} 502 503int StatsTable::GetRowValue(int index, int pid) const { 504 if (!impl_) 505 return 0; 506 507 int rv = 0; 508 int* row = impl_->row(index); 509 for (int slot_id = 0; slot_id < impl_->max_threads(); slot_id++) { 510 if (pid == 0 || *impl_->thread_pid(slot_id) == pid) 511 rv += row[slot_id]; 512 } 513 return rv; 514} 515 516int StatsTable::GetRowValue(int index) const { 517 return GetRowValue(index, 0); 518} 519 520int StatsTable::GetCounterValue(const std::string& name, int pid) { 521 if (!impl_) 522 return 0; 523 524 int row = FindCounter(name); 525 if (!row) 526 return 0; 527 return GetRowValue(row, pid); 528} 529 530int StatsTable::GetCounterValue(const std::string& name) { 531 return GetCounterValue(name, 0); 532} 533 534int StatsTable::GetMaxCounters() const { 535 if (!impl_) 536 return 0; 537 return impl_->max_counters(); 538} 539 540int StatsTable::GetMaxThreads() const { 541 if (!impl_) 542 return 0; 543 return impl_->max_threads(); 544} 545 546int* StatsTable::FindLocation(const char* name) { 547 // Get the static StatsTable 548 StatsTable *table = StatsTable::current(); 549 if (!table) 550 return NULL; 551 552 // Get the slot for this thread. Try to register 553 // it if none exists. 554 int slot = table->GetSlot(); 555 if (!slot && !(slot = table->RegisterThread(""))) 556 return NULL; 557 558 // Find the counter id for the counter. 559 std::string str_name(name); 560 int counter = table->FindCounter(str_name); 561 562 // Now we can find the location in the table. 563 return table->GetLocation(counter, slot); 564} 565 566} // namespace base 567