LCOV - code coverage report
Current view: directory - ipc/chromium/src/base - stats_table.cc (source / functions) Found Hit Coverage
Test: app.info Lines: 233 0 0.0 %
Date: 2012-06-02 Functions: 38 0 0.0 %

       1                 : // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
       2                 : // Use of this source code is governed by a BSD-style license that can be
       3                 : // found in the LICENSE file.
       4                 : 
       5                 : #include "base/stats_table.h"
       6                 : 
       7                 : #include "base/logging.h"
       8                 : #include "base/platform_thread.h"
       9                 : #include "base/process_util.h"
      10                 : #include "base/scoped_ptr.h"
      11                 : #include "base/shared_memory.h"
      12                 : #include "base/string_piece.h"
      13                 : #include "base/string_util.h"
      14                 : #include "base/sys_string_conversions.h"
      15                 : #include "base/thread_local_storage.h"
      16                 : 
      17                 : #if defined(OS_POSIX)
      18                 : #include "errno.h"
      19                 : #endif
      20                 : 
      21                 : // The StatsTable uses a shared memory segment that is laid out as follows
      22                 : //
      23                 : // +-------------------------------------------+
      24                 : // | Version | Size | MaxCounters | MaxThreads |
      25                 : // +-------------------------------------------+
      26                 : // | Thread names table                        |
      27                 : // +-------------------------------------------+
      28                 : // | Thread TID table                          |
      29                 : // +-------------------------------------------+
      30                 : // | Thread PID table                          |
      31                 : // +-------------------------------------------+
      32                 : // | Counter names table                       |
      33                 : // +-------------------------------------------+
      34                 : // | Data                                      |
      35                 : // +-------------------------------------------+
      36                 : //
      37                 : // The data layout is a grid, where the columns are the thread_ids and the
      38                 : // rows are the counter_ids.
      39                 : //
      40                 : // If the first character of the thread_name is '\0', then that column is
      41                 : // empty.
      42                 : // If the first character of the counter_name is '\0', then that row is
      43                 : // empty.
      44                 : //
      45                 : // About Locking:
      46                 : // This class is designed to be both multi-thread and multi-process safe.
      47                 : // Aside from initialization, this is done by partitioning the data which
      48                 : // each thread uses so that no locking is required.  However, to allocate
      49                 : // the rows and columns of the table to particular threads, locking is
      50                 : // required.
      51                 : //
      52                 : // At the shared-memory level, we have a lock.  This lock protects the
      53                 : // shared-memory table only, and is used when we create new counters (e.g.
      54                 : // use rows) or when we register new threads (e.g. use columns).  Reading
      55                 : // data from the table does not require any locking at the shared memory
      56                 : // level.
      57                 : //
      58                 : // Each process which accesses the table will create a StatsTable object.
      59                 : // The StatsTable maintains a hash table of the existing counters in the
      60                 : // table for faster lookup.  Since the hash table is process specific,
      61                 : // each process maintains its own cache.  We avoid complexity here by never
      62                 : // de-allocating from the hash table.  (Counters are dynamically added,
      63                 : // but not dynamically removed).
      64                 : 
      65                 : // In order for external viewers to be able to read our shared memory,
      66                 : // we all need to use the same size ints.
      67                 : COMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints);
      68                 : 
      69                 : namespace {
      70                 : 
      71                 : // An internal version in case we ever change the format of this
      72                 : // file, and so that we can identify our table.
      73                 : const int kTableVersion = 0x13131313;
      74                 : 
      75                 : // The name for un-named counters and threads in the table.
      76                 : const char kUnknownName[] = "<unknown>";
      77                 : 
      78                 : // Calculates delta to align an offset to the size of an int
      79               0 : inline int AlignOffset(int offset) {
      80               0 :   return (sizeof(int) - (offset % sizeof(int))) % sizeof(int);
      81                 : }
      82                 : 
      83               0 : inline int AlignedSize(int size) {
      84               0 :   return size + AlignOffset(size);
      85                 : }
      86                 : 
      87                 : // StatsTableTLSData carries the data stored in the TLS slots for the
      88                 : // StatsTable.  This is used so that we can properly cleanup when the
      89                 : // thread exits and return the table slot.
      90                 : //
      91                 : // Each thread that calls RegisterThread in the StatsTable will have
      92                 : // a StatsTableTLSData stored in its TLS.
      93                 : struct StatsTableTLSData {
      94                 :   StatsTable* table;
      95                 :   int slot;
      96                 : };
      97                 : 
      98                 : }  // namespace
      99                 : 
     100                 : // The StatsTablePrivate maintains convenience pointers into the
     101                 : // shared memory segment.  Use this class to keep the data structure
     102                 : // clean and accessible.
     103               0 : class StatsTablePrivate {
     104                 :  public:
     105                 :   // Various header information contained in the memory mapped segment.
     106                 :   struct TableHeader {
     107                 :     int version;
     108                 :     int size;
     109                 :     int max_counters;
     110                 :     int max_threads;
     111                 :   };
     112                 : 
     113                 :   // Construct a new StatsTablePrivate based on expected size parameters, or
     114                 :   // return NULL on failure.
     115                 :   static StatsTablePrivate* New(const std::string& name, int size,
     116                 :                                 int max_threads, int max_counters);
     117                 : 
     118               0 :   base::SharedMemory* shared_memory() { return &shared_memory_; }
     119                 : 
     120                 :   // Accessors for our header pointers
     121                 :   TableHeader* table_header() const { return table_header_; }
     122                 :   int version() const { return table_header_->version; }
     123               0 :   int size() const { return table_header_->size; }
     124               0 :   int max_counters() const { return table_header_->max_counters; }
     125               0 :   int max_threads() const { return table_header_->max_threads; }
     126                 : 
     127                 :   // Accessors for our tables
     128               0 :   char* thread_name(int slot_id) const {
     129                 :     return &thread_names_table_[
     130               0 :       (slot_id-1) * (StatsTable::kMaxThreadNameLength)];
     131                 :   }
     132               0 :   PlatformThreadId* thread_tid(int slot_id) const {
     133               0 :     return &(thread_tid_table_[slot_id-1]);
     134                 :   }
     135               0 :   int* thread_pid(int slot_id) const {
     136               0 :     return &(thread_pid_table_[slot_id-1]);
     137                 :   }
     138               0 :   char* counter_name(int counter_id) const {
     139                 :     return &counter_names_table_[
     140               0 :       (counter_id-1) * (StatsTable::kMaxCounterNameLength)];
     141                 :   }
     142               0 :   int* row(int counter_id) const {
     143               0 :     return &data_table_[(counter_id-1) * max_threads()];
     144                 :   }
     145                 : 
     146                 :  private:
     147                 :   // Constructor is private because you should use New() instead.
     148               0 :   StatsTablePrivate() {}
     149                 : 
     150                 :   // Initializes the table on first access.  Sets header values
     151                 :   // appropriately and zeroes all counters.
     152                 :   void InitializeTable(void* memory, int size, int max_counters,
     153                 :                        int max_threads);
     154                 : 
     155                 :   // Initializes our in-memory pointers into a pre-created StatsTable.
     156                 :   void ComputeMappedPointers(void* memory);
     157                 : 
     158                 :   base::SharedMemory shared_memory_;
     159                 :   TableHeader* table_header_;
     160                 :   char* thread_names_table_;
     161                 :   PlatformThreadId* thread_tid_table_;
     162                 :   int* thread_pid_table_;
     163                 :   char* counter_names_table_;
     164                 :   int* data_table_;
     165                 : };
     166                 : 
     167                 : // static
     168               0 : StatsTablePrivate* StatsTablePrivate::New(const std::string& name,
     169                 :                                           int size,
     170                 :                                           int max_threads,
     171                 :                                           int max_counters) {
     172               0 :   scoped_ptr<StatsTablePrivate> priv(new StatsTablePrivate());
     173               0 :   if (!priv->shared_memory_.Create(name, false, true, size))
     174               0 :     return NULL;
     175               0 :   if (!priv->shared_memory_.Map(size))
     176               0 :     return NULL;
     177               0 :   void* memory = priv->shared_memory_.memory();
     178                 : 
     179               0 :   TableHeader* header = static_cast<TableHeader*>(memory);
     180                 : 
     181                 :   // If the version does not match, then assume the table needs
     182                 :   // to be initialized.
     183               0 :   if (header->version != kTableVersion)
     184               0 :     priv->InitializeTable(memory, size, max_counters, max_threads);
     185                 : 
     186                 :   // We have a valid table, so compute our pointers.
     187               0 :   priv->ComputeMappedPointers(memory);
     188                 : 
     189               0 :   return priv.release();
     190                 : }
     191                 : 
     192               0 : void StatsTablePrivate::InitializeTable(void* memory, int size,
     193                 :                                         int max_counters,
     194                 :                                         int max_threads) {
     195                 :   // Zero everything.
     196               0 :   memset(memory, 0, size);
     197                 : 
     198                 :   // Initialize the header.
     199               0 :   TableHeader* header = static_cast<TableHeader*>(memory);
     200               0 :   header->version = kTableVersion;
     201               0 :   header->size = size;
     202               0 :   header->max_counters = max_counters;
     203               0 :   header->max_threads = max_threads;
     204               0 : }
     205                 : 
     206               0 : void StatsTablePrivate::ComputeMappedPointers(void* memory) {
     207               0 :   char* data = static_cast<char*>(memory);
     208               0 :   int offset = 0;
     209                 : 
     210               0 :   table_header_ = reinterpret_cast<TableHeader*>(data);
     211               0 :   offset += sizeof(*table_header_);
     212               0 :   offset += AlignOffset(offset);
     213                 : 
     214                 :   // Verify we're looking at a valid StatsTable.
     215               0 :   DCHECK_EQ(table_header_->version, kTableVersion);
     216                 : 
     217               0 :   thread_names_table_ = reinterpret_cast<char*>(data + offset);
     218                 :   offset += sizeof(char) *
     219               0 :             max_threads() * StatsTable::kMaxThreadNameLength;
     220               0 :   offset += AlignOffset(offset);
     221                 : 
     222               0 :   thread_tid_table_ = reinterpret_cast<PlatformThreadId*>(data + offset);
     223               0 :   offset += sizeof(int) * max_threads();
     224               0 :   offset += AlignOffset(offset);
     225                 : 
     226               0 :   thread_pid_table_ = reinterpret_cast<int*>(data + offset);
     227               0 :   offset += sizeof(int) * max_threads();
     228               0 :   offset += AlignOffset(offset);
     229                 : 
     230               0 :   counter_names_table_ = reinterpret_cast<char*>(data + offset);
     231                 :   offset += sizeof(char) *
     232               0 :             max_counters() * StatsTable::kMaxCounterNameLength;
     233               0 :   offset += AlignOffset(offset);
     234                 : 
     235               0 :   data_table_ = reinterpret_cast<int*>(data + offset);
     236               0 :   offset += sizeof(int) * max_threads() * max_counters();
     237                 : 
     238               0 :   DCHECK_EQ(offset, size());
     239               0 : }
     240                 : 
     241                 : 
     242                 : 
     243                 : // We keep a singleton table which can be easily accessed.
     244                 : StatsTable* StatsTable::global_table_ = NULL;
     245                 : 
     246               0 : StatsTable::StatsTable(const std::string& name, int max_threads,
     247                 :                        int max_counters)
     248                 :     : impl_(NULL),
     249               0 :       tls_index_(SlotReturnFunction) {
     250                 :   int table_size =
     251               0 :     AlignedSize(sizeof(StatsTablePrivate::TableHeader)) +
     252               0 :     AlignedSize((max_counters * sizeof(char) * kMaxCounterNameLength)) +
     253               0 :     AlignedSize((max_threads * sizeof(char) * kMaxThreadNameLength)) +
     254               0 :     AlignedSize(max_threads * sizeof(int)) +
     255               0 :     AlignedSize(max_threads * sizeof(int)) +
     256               0 :     AlignedSize((sizeof(int) * (max_counters * max_threads)));
     257                 : 
     258               0 :   impl_ = StatsTablePrivate::New(name, table_size, max_threads, max_counters);
     259                 : 
     260                 :   // TODO(port): clean up this error reporting.
     261                 : #if defined(OS_WIN)
     262                 :   if (!impl_)
     263                 :     LOG(ERROR) << "StatsTable did not initialize:" << GetLastError();
     264                 : #elif defined(OS_POSIX)
     265               0 :   if (!impl_)
     266               0 :     LOG(ERROR) << "StatsTable did not initialize:" << strerror(errno);
     267                 : #endif
     268               0 : }
     269                 : 
     270               0 : StatsTable::~StatsTable() {
     271                 :   // Before we tear down our copy of the table, be sure to
     272                 :   // unregister our thread.
     273               0 :   UnregisterThread();
     274                 : 
     275                 :   // Return ThreadLocalStorage.  At this point, if any registered threads
     276                 :   // still exist, they cannot Unregister.
     277               0 :   tls_index_.Free();
     278                 : 
     279                 :   // Cleanup our shared memory.
     280               0 :   delete impl_;
     281                 : 
     282                 :   // If we are the global table, unregister ourselves.
     283               0 :   if (global_table_ == this)
     284               0 :     global_table_ = NULL;
     285               0 : }
     286                 : 
     287               0 : int StatsTable::RegisterThread(const std::string& name) {
     288               0 :   int slot = 0;
     289                 : 
     290                 :   // Registering a thread requires that we lock the shared memory
     291                 :   // so that two threads don't grab the same slot.  Fortunately,
     292                 :   // thread creation shouldn't happen in inner loops.
     293                 :   {
     294               0 :     base::SharedMemoryAutoLock lock(impl_->shared_memory());
     295               0 :     slot = FindEmptyThread();
     296               0 :     if (!slot) {
     297               0 :       return 0;
     298                 :     }
     299                 : 
     300               0 :     DCHECK(impl_);
     301                 : 
     302                 :     // We have space, so consume a column in the table.
     303               0 :     std::string thread_name = name;
     304               0 :     if (name.empty())
     305               0 :       thread_name = kUnknownName;
     306                 :     base::strlcpy(impl_->thread_name(slot), thread_name.c_str(),
     307               0 :                   kMaxThreadNameLength);
     308               0 :     *(impl_->thread_tid(slot)) = PlatformThread::CurrentId();
     309               0 :     *(impl_->thread_pid(slot)) = base::GetCurrentProcId();
     310                 :   }
     311                 : 
     312                 :   // Set our thread local storage.
     313               0 :   StatsTableTLSData* data = new StatsTableTLSData;
     314               0 :   data->table = this;
     315               0 :   data->slot = slot;
     316               0 :   tls_index_.Set(data);
     317               0 :   return slot;
     318                 : }
     319                 : 
     320               0 : StatsTableTLSData* StatsTable::GetTLSData() const {
     321                 :   StatsTableTLSData* data =
     322               0 :     static_cast<StatsTableTLSData*>(tls_index_.Get());
     323               0 :   if (!data)
     324               0 :     return NULL;
     325                 : 
     326               0 :   DCHECK(data->slot);
     327               0 :   DCHECK_EQ(data->table, this);
     328               0 :   return data;
     329                 : }
     330                 : 
     331               0 : void StatsTable::UnregisterThread() {
     332               0 :   UnregisterThread(GetTLSData());
     333               0 : }
     334                 : 
     335               0 : void StatsTable::UnregisterThread(StatsTableTLSData* data) {
     336               0 :   if (!data)
     337               0 :     return;
     338               0 :   DCHECK(impl_);
     339                 : 
     340                 :   // Mark the slot free by zeroing out the thread name.
     341               0 :   char* name = impl_->thread_name(data->slot);
     342               0 :   *name = '\0';
     343                 : 
     344                 :   // Remove the calling thread's TLS so that it cannot use the slot.
     345               0 :   tls_index_.Set(NULL);
     346                 :   delete data;
     347                 : }
     348                 : 
     349               0 : void StatsTable::SlotReturnFunction(void* data) {
     350                 :   // This is called by the TLS destructor, which on some platforms has
     351                 :   // already cleared the TLS info, so use the tls_data argument
     352                 :   // rather than trying to fetch it ourselves.
     353               0 :   StatsTableTLSData* tls_data = static_cast<StatsTableTLSData*>(data);
     354               0 :   if (tls_data) {
     355               0 :     DCHECK(tls_data->table);
     356               0 :     tls_data->table->UnregisterThread(tls_data);
     357                 :   }
     358               0 : }
     359                 : 
     360               0 : int StatsTable::CountThreadsRegistered() const {
     361               0 :   if (!impl_)
     362               0 :     return 0;
     363                 : 
     364                 :   // Loop through the shared memory and count the threads that are active.
     365                 :   // We intentionally do not lock the table during the operation.
     366               0 :   int count = 0;
     367               0 :   for (int index = 1; index <= impl_->max_threads(); index++) {
     368               0 :     char* name = impl_->thread_name(index);
     369               0 :     if (*name != '\0')
     370               0 :       count++;
     371                 :   }
     372               0 :   return count;
     373                 : }
     374                 : 
     375               0 : int StatsTable::GetSlot() const {
     376               0 :   StatsTableTLSData* data = GetTLSData();
     377               0 :   if (!data)
     378               0 :     return 0;
     379               0 :   return data->slot;
     380                 : }
     381                 : 
     382               0 : int StatsTable::FindEmptyThread() const {
     383                 :   // Note: the API returns slots numbered from 1..N, although
     384                 :   // internally, the array is 0..N-1.  This is so that we can return
     385                 :   // zero as "not found".
     386                 :   //
     387                 :   // The reason for doing this is because the thread 'slot' is stored
     388                 :   // in TLS, which is always initialized to zero, not -1.  If 0 were
     389                 :   // returned as a valid slot number, it would be confused with the
     390                 :   // uninitialized state.
     391               0 :   if (!impl_)
     392               0 :     return 0;
     393                 : 
     394               0 :   int index = 1;
     395               0 :   for (; index <= impl_->max_threads(); index++) {
     396               0 :     char* name = impl_->thread_name(index);
     397               0 :     if (!*name)
     398               0 :       break;
     399                 :   }
     400               0 :   if (index > impl_->max_threads())
     401               0 :     return 0;  // The table is full.
     402               0 :   return index;
     403                 : }
     404                 : 
     405               0 : int StatsTable::FindCounterOrEmptyRow(const std::string& name) const {
     406                 :   // Note: the API returns slots numbered from 1..N, although
     407                 :   // internally, the array is 0..N-1.  This is so that we can return
     408                 :   // zero as "not found".
     409                 :   //
     410                 :   // There isn't much reason for this other than to be consistent
     411                 :   // with the way we track columns for thread slots.  (See comments
     412                 :   // in FindEmptyThread for why it is done this way).
     413               0 :   if (!impl_)
     414               0 :     return 0;
     415                 : 
     416               0 :   int free_slot = 0;
     417               0 :   for (int index = 1; index <= impl_->max_counters(); index++) {
     418               0 :     char* row_name = impl_->counter_name(index);
     419               0 :     if (!*row_name && !free_slot)
     420               0 :       free_slot = index;  // save that we found a free slot
     421               0 :     else if (!strncmp(row_name, name.c_str(), kMaxCounterNameLength))
     422               0 :       return index;
     423                 :   }
     424               0 :   return free_slot;
     425                 : }
     426                 : 
     427               0 : int StatsTable::FindCounter(const std::string& name) {
     428                 :   // Note: the API returns counters numbered from 1..N, although
     429                 :   // internally, the array is 0..N-1.  This is so that we can return
     430                 :   // zero as "not found".
     431               0 :   if (!impl_)
     432               0 :     return 0;
     433                 : 
     434                 :   // Create a scope for our auto-lock.
     435                 :   {
     436               0 :     AutoLock scoped_lock(counters_lock_);
     437                 : 
     438                 :     // Attempt to find the counter.
     439               0 :     CountersMap::const_iterator iter;
     440               0 :     iter = counters_.find(name);
     441               0 :     if (iter != counters_.end())
     442               0 :       return iter->second;
     443                 :   }
     444                 : 
     445                 :   // Counter does not exist, so add it.
     446               0 :   return AddCounter(name);
     447                 : }
     448                 : 
     449               0 : int StatsTable::AddCounter(const std::string& name) {
     450               0 :   DCHECK(impl_);
     451                 : 
     452               0 :   if (!impl_)
     453               0 :     return 0;
     454                 : 
     455               0 :   int counter_id = 0;
     456                 :   {
     457                 :     // To add a counter to the shared memory, we need the
     458                 :     // shared memory lock.
     459               0 :     base::SharedMemoryAutoLock lock(impl_->shared_memory());
     460                 : 
     461                 :     // We have space, so create a new counter.
     462               0 :     counter_id = FindCounterOrEmptyRow(name);
     463               0 :     if (!counter_id)
     464               0 :       return 0;
     465                 : 
     466               0 :     std::string counter_name = name;
     467               0 :     if (name.empty())
     468               0 :       counter_name = kUnknownName;
     469                 :     base::strlcpy(impl_->counter_name(counter_id), counter_name.c_str(),
     470               0 :                   kMaxCounterNameLength);
     471                 :   }
     472                 : 
     473                 :   // now add to our in-memory cache
     474                 :   {
     475               0 :     AutoLock lock(counters_lock_);
     476               0 :     counters_[name] = counter_id;
     477                 :   }
     478               0 :   return counter_id;
     479                 : }
     480                 : 
     481               0 : int* StatsTable::GetLocation(int counter_id, int slot_id) const {
     482               0 :   if (!impl_)
     483               0 :     return NULL;
     484               0 :   if (slot_id > impl_->max_threads())
     485               0 :     return NULL;
     486                 : 
     487               0 :   int* row = impl_->row(counter_id);
     488               0 :   return &(row[slot_id-1]);
     489                 : }
     490                 : 
     491               0 : const char* StatsTable::GetRowName(int index) const {
     492               0 :   if (!impl_)
     493               0 :     return NULL;
     494                 : 
     495               0 :   return impl_->counter_name(index);
     496                 : }
     497                 : 
     498               0 : int StatsTable::GetRowValue(int index, int pid) const {
     499               0 :   if (!impl_)
     500               0 :     return 0;
     501                 : 
     502               0 :   int rv = 0;
     503               0 :   int* row = impl_->row(index);
     504               0 :   for (int slot_id = 0; slot_id < impl_->max_threads(); slot_id++) {
     505               0 :     if (pid == 0 || *impl_->thread_pid(slot_id) == pid)
     506               0 :       rv += row[slot_id];
     507                 :   }
     508               0 :   return rv;
     509                 : }
     510                 : 
     511               0 : int StatsTable::GetRowValue(int index) const {
     512               0 :   return GetRowValue(index, 0);
     513                 : }
     514                 : 
     515               0 : int StatsTable::GetCounterValue(const std::string& name, int pid) {
     516               0 :   if (!impl_)
     517               0 :     return 0;
     518                 : 
     519               0 :   int row = FindCounter(name);
     520               0 :   if (!row)
     521               0 :     return 0;
     522               0 :   return GetRowValue(row, pid);
     523                 : }
     524                 : 
     525               0 : int StatsTable::GetCounterValue(const std::string& name) {
     526               0 :   return GetCounterValue(name, 0);
     527                 : }
     528                 : 
     529               0 : int StatsTable::GetMaxCounters() const {
     530               0 :   if (!impl_)
     531               0 :     return 0;
     532               0 :   return impl_->max_counters();
     533                 : }
     534                 : 
     535               0 : int StatsTable::GetMaxThreads() const {
     536               0 :   if (!impl_)
     537               0 :     return 0;
     538               0 :   return impl_->max_threads();
     539                 : }
     540                 : 
     541               0 : int* StatsTable::FindLocation(const char* name) {
     542                 :   // Get the static StatsTable
     543               0 :   StatsTable *table = StatsTable::current();
     544               0 :   if (!table)
     545               0 :     return NULL;
     546                 : 
     547                 :   // Get the slot for this thread.  Try to register
     548                 :   // it if none exists.
     549               0 :   int slot = table->GetSlot();
     550               0 :   if (!slot && !(slot = table->RegisterThread("")))
     551               0 :       return NULL;
     552                 : 
     553                 :   // Find the counter id for the counter.
     554               0 :   std::string str_name(name);
     555               0 :   int counter = table->FindCounter(str_name);
     556                 : 
     557                 :   // Now we can find the location in the table.
     558               0 :   return table->GetLocation(counter, slot);
     559                 : }

Generated by: LCOV version 1.7