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