blob: f8541346a25122b7a1e07186367eb4c76c110547 [file] [log] [blame]
fischman@chromium.org998561e2012-01-24 07:56:41 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botf003cfe2008-08-24 09:55:55 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
5#include "base/tracked_objects.h"
6
darin@google.comee6fa722008-08-13 08:25:43 +09007#include <math.h>
vapier@chromium.org58b69ff2012-03-20 06:46:27 +09008#include <stdlib.h>
darin@google.comee6fa722008-08-13 08:25:43 +09009
evan@chromium.org3f6b2602009-11-20 15:53:28 +090010#include "base/format_macros.h"
jar@chromium.org4626ccb2012-02-16 08:05:01 +090011#include "base/profiler/alternate_timer.h"
brettw@chromium.org11f89b02010-08-18 08:05:28 +090012#include "base/stringprintf.h"
rnk@chromium.org1a280df2011-12-05 06:14:05 +090013#include "base/third_party/valgrind/memcheck.h"
brettw@chromium.org5b5f5e02011-01-01 10:01:06 +090014#include "base/threading/thread_restrictions.h"
jar@chromium.org4be2cb02011-11-01 07:36:21 +090015#include "build/build_config.h"
jar@chromium.org10ef9902011-11-19 02:03:33 +090016#include "base/port.h"
initial.commit3f4a7322008-07-27 06:49:38 +090017
dsh@google.com0f8dd262008-10-28 05:43:33 +090018using base::TimeDelta;
19
initial.commit3f4a7322008-07-27 06:49:38 +090020namespace tracked_objects {
21
jar@chromium.org4be2cb02011-11-01 07:36:21 +090022namespace {
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090023
jar@chromium.org4be2cb02011-11-01 07:36:21 +090024// Flag to compile out almost all of the task tracking code.
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090025const bool kTrackAllTaskObjects = true;
joth@chromium.orgc8b867c2011-11-01 00:32:08 +090026
jar@chromium.orgb5c974b2011-12-14 10:36:48 +090027// Flag to compile out parent-child link recording.
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090028const bool kTrackParentChildLinks = false;
jar@chromium.orgb5c974b2011-12-14 10:36:48 +090029
jar@chromium.org4be2cb02011-11-01 07:36:21 +090030// When ThreadData is first initialized, should we start in an ACTIVE state to
31// record all of the startup-time tasks, or should we start up DEACTIVATED, so
32// that we only record after parsing the command line flag --enable-tracking.
33// Note that the flag may force either state, so this really controls only the
34// period of time up until that flag is parsed. If there is no flag seen, then
35// this state may prevail for much or all of the process lifetime.
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090036const ThreadData::Status kInitialStartupState =
jar@chromium.orgb5c974b2011-12-14 10:36:48 +090037 ThreadData::PROFILING_CHILDREN_ACTIVE;
jhawkins@chromium.org88fbaf62012-01-28 09:34:40 +090038
jar@chromium.org4626ccb2012-02-16 08:05:01 +090039// Control whether an alternate time source (Now() function) is supported by
40// the ThreadData class. This compile time flag should be set to true if we
41// want other modules (such as a memory allocator, or a thread-specific CPU time
42// clock) to be able to provide a thread-specific Now() function. Without this
43// compile-time flag, the code will only support the wall-clock time. This flag
44// can be flipped to efficiently disable this path (if there is a performance
45// problem with its presence).
46static const bool kAllowAlternateTimeSourceHandling = true;
jar@chromium.orgb5c974b2011-12-14 10:36:48 +090047} // namespace
jar@chromium.org79a58c32011-10-16 08:52:45 +090048
initial.commit3f4a7322008-07-27 06:49:38 +090049//------------------------------------------------------------------------------
jar@chromium.org0d46f3b2011-11-04 09:23:27 +090050// DeathData tallies durations when a death takes place.
initial.commit3f4a7322008-07-27 06:49:38 +090051
jar@chromium.orga0260412011-12-04 16:19:10 +090052DeathData::DeathData() {
53 Clear();
54}
55
56DeathData::DeathData(int count) {
57 Clear();
58 count_ = count;
59}
60
jar@chromium.org26abc1f2011-12-09 12:41:04 +090061// TODO(jar): I need to see if this macro to optimize branching is worth using.
jar@chromium.orga0260412011-12-04 16:19:10 +090062//
63// This macro has no branching, so it is surely fast, and is equivalent to:
64// if (assign_it)
65// target = source;
66// We use a macro rather than a template to force this to inline.
67// Related code for calculating max is discussed on the web.
68#define CONDITIONAL_ASSIGN(assign_it, target, source) \
69 ((target) ^= ((target) ^ (source)) & -static_cast<DurationInt>(assign_it))
70
71void DeathData::RecordDeath(const DurationInt queue_duration,
72 const DurationInt run_duration,
73 int32 random_number) {
jar@chromium.org26abc1f2011-12-09 12:41:04 +090074 ++count_;
jar@chromium.orga0260412011-12-04 16:19:10 +090075 queue_duration_sum_ += queue_duration;
76 run_duration_sum_ += run_duration;
jar@chromium.org26abc1f2011-12-09 12:41:04 +090077
78 if (queue_duration_max_ < queue_duration)
79 queue_duration_max_ = queue_duration;
80 if (run_duration_max_ < run_duration)
81 run_duration_max_ = run_duration;
jar@chromium.orga0260412011-12-04 16:19:10 +090082
83 // Take a uniformly distributed sample over all durations ever supplied.
84 // The probability that we (instead) use this new sample is 1/count_. This
85 // results in a completely uniform selection of the sample.
86 // We ignore the fact that we correlated our selection of a sample of run
87 // and queue times.
jar@chromium.org26abc1f2011-12-09 12:41:04 +090088 if (0 == (random_number % count_)) {
89 queue_duration_sample_ = queue_duration;
90 run_duration_sample_ = run_duration;
91 }
initial.commit3f4a7322008-07-27 06:49:38 +090092}
93
jar@chromium.orga0260412011-12-04 16:19:10 +090094int DeathData::count() const { return count_; }
95
96DurationInt DeathData::run_duration_sum() const { return run_duration_sum_; }
97
98DurationInt DeathData::run_duration_max() const { return run_duration_max_; }
99
100DurationInt DeathData::run_duration_sample() const {
101 return run_duration_sample_;
initial.commit3f4a7322008-07-27 06:49:38 +0900102}
103
jar@chromium.orga0260412011-12-04 16:19:10 +0900104DurationInt DeathData::queue_duration_sum() const {
105 return queue_duration_sum_;
initial.commit3f4a7322008-07-27 06:49:38 +0900106}
107
jar@chromium.orga0260412011-12-04 16:19:10 +0900108DurationInt DeathData::queue_duration_max() const {
109 return queue_duration_max_;
initial.commit3f4a7322008-07-27 06:49:38 +0900110}
111
jar@chromium.orga0260412011-12-04 16:19:10 +0900112DurationInt DeathData::queue_duration_sample() const {
113 return queue_duration_sample_;
114}
115
116
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900117base::DictionaryValue* DeathData::ToValue() const {
118 base::DictionaryValue* dictionary = new base::DictionaryValue;
119 dictionary->Set("count", base::Value::CreateIntegerValue(count_));
120 dictionary->Set("run_ms",
jar@chromium.orga0260412011-12-04 16:19:10 +0900121 base::Value::CreateIntegerValue(run_duration_sum()));
jar@chromium.org0d46f3b2011-11-04 09:23:27 +0900122 dictionary->Set("run_ms_max",
jar@chromium.orga0260412011-12-04 16:19:10 +0900123 base::Value::CreateIntegerValue(run_duration_max()));
124 dictionary->Set("run_ms_sample",
125 base::Value::CreateIntegerValue(run_duration_sample()));
126 dictionary->Set("queue_ms",
127 base::Value::CreateIntegerValue(queue_duration_sum()));
jar@chromium.org0d46f3b2011-11-04 09:23:27 +0900128 dictionary->Set("queue_ms_max",
jar@chromium.orga0260412011-12-04 16:19:10 +0900129 base::Value::CreateIntegerValue(queue_duration_max()));
130 dictionary->Set("queue_ms_sample",
131 base::Value::CreateIntegerValue(queue_duration_sample()));
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900132 return dictionary;
133}
134
jar@chromium.orga0260412011-12-04 16:19:10 +0900135void DeathData::ResetMax() {
136 run_duration_max_ = 0;
137 queue_duration_max_ = 0;
138}
139
initial.commit3f4a7322008-07-27 06:49:38 +0900140void DeathData::Clear() {
141 count_ = 0;
jar@chromium.orga0260412011-12-04 16:19:10 +0900142 run_duration_sum_ = 0;
143 run_duration_max_ = 0;
144 run_duration_sample_ = 0;
145 queue_duration_sum_ = 0;
146 queue_duration_max_ = 0;
147 queue_duration_sample_ = 0;
initial.commit3f4a7322008-07-27 06:49:38 +0900148}
149
150//------------------------------------------------------------------------------
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900151BirthOnThread::BirthOnThread(const Location& location,
152 const ThreadData& current)
initial.commit3f4a7322008-07-27 06:49:38 +0900153 : location_(location),
jar@chromium.orga0260412011-12-04 16:19:10 +0900154 birth_thread_(&current) {
155}
156
157const Location BirthOnThread::location() const { return location_; }
158const ThreadData* BirthOnThread::birth_thread() const { return birth_thread_; }
initial.commit3f4a7322008-07-27 06:49:38 +0900159
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900160void BirthOnThread::ToValue(const std::string& prefix,
161 base::DictionaryValue* dictionary) const {
162 dictionary->Set(prefix + "_location", location_.ToValue());
163 dictionary->Set(prefix + "_thread",
164 base::Value::CreateStringValue(birth_thread_->thread_name()));
165}
166
initial.commit3f4a7322008-07-27 06:49:38 +0900167//------------------------------------------------------------------------------
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900168Births::Births(const Location& location, const ThreadData& current)
169 : BirthOnThread(location, current),
jar@chromium.orgfebe0e42009-12-30 16:31:45 +0900170 birth_count_(1) { }
initial.commit3f4a7322008-07-27 06:49:38 +0900171
jar@chromium.orga0260412011-12-04 16:19:10 +0900172int Births::birth_count() const { return birth_count_; }
173
174void Births::RecordBirth() { ++birth_count_; }
175
176void Births::ForgetBirth() { --birth_count_; }
177
178void Births::Clear() { birth_count_ = 0; }
179
initial.commit3f4a7322008-07-27 06:49:38 +0900180//------------------------------------------------------------------------------
jar@chromium.orga0260412011-12-04 16:19:10 +0900181// ThreadData maintains the central data for all births and deaths on a single
182// thread.
initial.commit3f4a7322008-07-27 06:49:38 +0900183
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900184// TODO(jar): We should pull all these static vars together, into a struct, and
185// optimize layout so that we benefit from locality of reference during accesses
186// to them.
187
jar@chromium.org4626ccb2012-02-16 08:05:01 +0900188// static
189NowFunction* ThreadData::now_function_ = NULL;
190
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900191// A TLS slot which points to the ThreadData instance for the current thread. We
192// do a fake initialization here (zeroing out data), and then the real in-place
193// construction happens when we call tls_index_.Initialize().
194// static
thakis@chromium.org82306bf2012-01-31 01:52:09 +0900195base::ThreadLocalStorage::StaticSlot ThreadData::tls_index_ = TLS_INITIALIZER;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900196
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900197// static
jar@chromium.org92703d52011-11-24 09:00:31 +0900198int ThreadData::worker_thread_data_creation_count_ = 0;
199
200// static
201int ThreadData::cleanup_count_ = 0;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900202
203// static
204int ThreadData::incarnation_counter_ = 0;
205
initial.commit3f4a7322008-07-27 06:49:38 +0900206// static
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900207ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
208
209// static
jar@chromium.org50c48db2011-11-20 13:17:07 +0900210ThreadData* ThreadData::first_retired_worker_ = NULL;
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900211
initial.commit3f4a7322008-07-27 06:49:38 +0900212// static
fischman@chromium.org998561e2012-01-24 07:56:41 +0900213base::LazyInstance<base::Lock>::Leaky
joth@chromium.orgb24883c2011-11-15 22:31:49 +0900214 ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER;
initial.commit3f4a7322008-07-27 06:49:38 +0900215
216// static
217ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
218
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900219ThreadData::ThreadData(const std::string& suggested_name)
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900220 : next_(NULL),
jar@chromium.org50c48db2011-11-20 13:17:07 +0900221 next_retired_worker_(NULL),
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900222 worker_thread_number_(0),
223 incarnation_count_for_pool_(-1) {
jar@chromium.org79a58c32011-10-16 08:52:45 +0900224 DCHECK_GE(suggested_name.size(), 0u);
225 thread_name_ = suggested_name;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900226 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
jar@chromium.org79a58c32011-10-16 08:52:45 +0900227}
228
jar@chromium.org50c48db2011-11-20 13:17:07 +0900229ThreadData::ThreadData(int thread_number)
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900230 : next_(NULL),
jar@chromium.org50c48db2011-11-20 13:17:07 +0900231 next_retired_worker_(NULL),
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900232 worker_thread_number_(thread_number),
233 incarnation_count_for_pool_(-1) {
jar@chromium.org50c48db2011-11-20 13:17:07 +0900234 CHECK_GT(thread_number, 0);
235 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
jar@chromium.org0d46f3b2011-11-04 09:23:27 +0900236 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
willchan@chromium.org25726ef2010-11-20 05:34:18 +0900237}
initial.commit3f4a7322008-07-27 06:49:38 +0900238
erg@google.com71915232010-09-29 07:54:58 +0900239ThreadData::~ThreadData() {}
240
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900241void ThreadData::PushToHeadOfList() {
jar@chromium.orga0260412011-12-04 16:19:10 +0900242 // Toss in a hint of randomness (atop the uniniitalized value).
rnk@chromium.org69708412011-12-06 00:24:28 +0900243 (void)VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(&random_number_,
rnk@chromium.org1a280df2011-12-05 06:14:05 +0900244 sizeof(random_number_));
jar@chromium.orga0260412011-12-04 16:19:10 +0900245 random_number_ += static_cast<int32>(this - static_cast<ThreadData*>(0));
246 random_number_ ^= (Now() - TrackedTime()).InMilliseconds();
247
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900248 DCHECK(!next_);
jar@chromium.orgb2845c82011-11-15 05:36:46 +0900249 base::AutoLock lock(*list_lock_.Pointer());
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900250 incarnation_count_for_pool_ = incarnation_counter_;
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900251 next_ = all_thread_data_list_head_;
252 all_thread_data_list_head_ = this;
253}
254
initial.commit3f4a7322008-07-27 06:49:38 +0900255// static
jar@chromium.orga0260412011-12-04 16:19:10 +0900256ThreadData* ThreadData::first() {
257 base::AutoLock lock(*list_lock_.Pointer());
258 return all_thread_data_list_head_;
259}
260
261ThreadData* ThreadData::next() const { return next_; }
262
263// static
jar@chromium.org79a58c32011-10-16 08:52:45 +0900264void ThreadData::InitializeThreadContext(const std::string& suggested_name) {
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900265 if (!Initialize()) // Always initialize if needed.
266 return;
267 ThreadData* current_thread_data =
268 reinterpret_cast<ThreadData*>(tls_index_.Get());
269 if (current_thread_data)
270 return; // Browser tests instigate this.
271 current_thread_data = new ThreadData(suggested_name);
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900272 tls_index_.Set(current_thread_data);
jar@chromium.org79a58c32011-10-16 08:52:45 +0900273}
initial.commit3f4a7322008-07-27 06:49:38 +0900274
jar@chromium.org79a58c32011-10-16 08:52:45 +0900275// static
276ThreadData* ThreadData::Get() {
277 if (!tls_index_.initialized())
278 return NULL; // For unittests only.
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900279 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get());
280 if (registered)
281 return registered;
282
283 // We must be a worker thread, since we didn't pre-register.
284 ThreadData* worker_thread_data = NULL;
jar@chromium.org92703d52011-11-24 09:00:31 +0900285 int worker_thread_number = 0;
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900286 {
jar@chromium.orgb2845c82011-11-15 05:36:46 +0900287 base::AutoLock lock(*list_lock_.Pointer());
jar@chromium.org50c48db2011-11-20 13:17:07 +0900288 if (first_retired_worker_) {
289 worker_thread_data = first_retired_worker_;
290 first_retired_worker_ = first_retired_worker_->next_retired_worker_;
291 worker_thread_data->next_retired_worker_ = NULL;
jar@chromium.org10ef9902011-11-19 02:03:33 +0900292 } else {
jar@chromium.org92703d52011-11-24 09:00:31 +0900293 worker_thread_number = ++worker_thread_data_creation_count_;
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900294 }
initial.commit3f4a7322008-07-27 06:49:38 +0900295 }
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900296
297 // If we can't find a previously used instance, then we have to create one.
jar@chromium.org92703d52011-11-24 09:00:31 +0900298 if (!worker_thread_data) {
299 DCHECK_GT(worker_thread_number, 0);
300 worker_thread_data = new ThreadData(worker_thread_number);
301 }
jar@chromium.org50c48db2011-11-20 13:17:07 +0900302 DCHECK_GT(worker_thread_data->worker_thread_number_, 0);
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900303
304 tls_index_.Set(worker_thread_data);
305 return worker_thread_data;
jar@chromium.org79a58c32011-10-16 08:52:45 +0900306}
307
308// static
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900309void ThreadData::OnThreadTermination(void* thread_data) {
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900310 DCHECK(thread_data); // TLS should *never* call us with a NULL.
jar@chromium.org92703d52011-11-24 09:00:31 +0900311 // We must NOT do any allocations during this callback. There is a chance
312 // that the allocator is no longer active on this thread.
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900313 if (!kTrackAllTaskObjects)
314 return; // Not compiled in.
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900315 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup();
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900316}
317
jar@chromium.org50c48db2011-11-20 13:17:07 +0900318void ThreadData::OnThreadTerminationCleanup() {
jar@chromium.org92703d52011-11-24 09:00:31 +0900319 // The list_lock_ was created when we registered the callback, so it won't be
320 // allocated here despite the lazy reference.
jar@chromium.orgb2845c82011-11-15 05:36:46 +0900321 base::AutoLock lock(*list_lock_.Pointer());
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900322 if (incarnation_counter_ != incarnation_count_for_pool_)
323 return; // ThreadData was constructed in an earlier unit test.
jar@chromium.org92703d52011-11-24 09:00:31 +0900324 ++cleanup_count_;
325 // Only worker threads need to be retired and reused.
326 if (!worker_thread_number_) {
327 return;
328 }
jar@chromium.org50c48db2011-11-20 13:17:07 +0900329 // We must NOT do any allocations during this callback.
330 // Using the simple linked lists avoids all allocations.
331 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL));
332 this->next_retired_worker_ = first_retired_worker_;
333 first_retired_worker_ = this;
initial.commit3f4a7322008-07-27 06:49:38 +0900334}
335
initial.commit3f4a7322008-07-27 06:49:38 +0900336// static
jar@chromium.orga0260412011-12-04 16:19:10 +0900337base::DictionaryValue* ThreadData::ToValue(bool reset_max) {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900338 DataCollector collected_data; // Gather data.
jar@chromium.orga0260412011-12-04 16:19:10 +0900339 // Request multiple calls to collected_data.Append() for all threads.
340 SendAllMaps(reset_max, &collected_data);
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900341 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900342 base::DictionaryValue* dictionary = new base::DictionaryValue();
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900343 collected_data.ToValue(dictionary);
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900344 return dictionary;
345}
346
jar@chromium.orgfebe0e42009-12-30 16:31:45 +0900347Births* ThreadData::TallyABirth(const Location& location) {
initial.commit3f4a7322008-07-27 06:49:38 +0900348 BirthMap::iterator it = birth_map_.find(location);
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900349 Births* child;
jar@chromium.orgfebe0e42009-12-30 16:31:45 +0900350 if (it != birth_map_.end()) {
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900351 child = it->second;
352 child->RecordBirth();
353 } else {
354 child = new Births(location, *this); // Leak this.
355 // Lock since the map may get relocated now, and other threads sometimes
356 // snapshot it (but they lock before copying it).
357 base::AutoLock lock(map_lock_);
358 birth_map_[location] = child;
jar@chromium.orgfebe0e42009-12-30 16:31:45 +0900359 }
initial.commit3f4a7322008-07-27 06:49:38 +0900360
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900361 if (kTrackParentChildLinks && status_ > PROFILING_ACTIVE &&
362 !parent_stack_.empty()) {
363 const Births* parent = parent_stack_.top();
364 ParentChildPair pair(parent, child);
365 if (parent_child_set_.find(pair) == parent_child_set_.end()) {
366 // Lock since the map may get relocated now, and other threads sometimes
367 // snapshot it (but they lock before copying it).
368 base::AutoLock lock(map_lock_);
369 parent_child_set_.insert(pair);
370 }
371 }
372
373 return child;
initial.commit3f4a7322008-07-27 06:49:38 +0900374}
375
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900376void ThreadData::TallyADeath(const Births& birth,
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900377 DurationInt queue_duration,
378 DurationInt run_duration) {
jar@chromium.orga0260412011-12-04 16:19:10 +0900379 // Stir in some randomness, plus add constant in case durations are zero.
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900380 const DurationInt kSomePrimeNumber = 2147483647;
jar@chromium.orga0260412011-12-04 16:19:10 +0900381 random_number_ += queue_duration + run_duration + kSomePrimeNumber;
382 // An address is going to have some randomness to it as well ;-).
383 random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0));
384
jar@chromium.org4626ccb2012-02-16 08:05:01 +0900385 // We don't have queue durations without OS timer. OS timer is automatically
386 // used for task-post-timing, so the use of an alternate timer implies all
387 // queue times are invalid.
388 if (kAllowAlternateTimeSourceHandling && now_function_)
389 queue_duration = 0;
390
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900391 DeathMap::iterator it = death_map_.find(&birth);
392 DeathData* death_data;
initial.commit3f4a7322008-07-27 06:49:38 +0900393 if (it != death_map_.end()) {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900394 death_data = &it->second;
395 } else {
jar@chromium.org92703d52011-11-24 09:00:31 +0900396 base::AutoLock lock(map_lock_); // Lock as the map may get relocated now.
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900397 death_data = &death_map_[&birth];
398 } // Release lock ASAP.
jar@chromium.orga0260412011-12-04 16:19:10 +0900399 death_data->RecordDeath(queue_duration, run_duration, random_number_);
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900400
401 if (!kTrackParentChildLinks)
402 return;
403 if (!parent_stack_.empty()) { // We might get turned off.
404 DCHECK_EQ(parent_stack_.top(), &birth);
405 parent_stack_.pop();
406 }
initial.commit3f4a7322008-07-27 06:49:38 +0900407}
408
409// static
ajwong@chromium.org12fa0922011-07-27 03:25:16 +0900410Births* ThreadData::TallyABirthIfActive(const Location& location) {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900411 if (!kTrackAllTaskObjects)
412 return NULL; // Not compiled in.
413
jar@chromium.org23b00722012-02-11 04:43:42 +0900414 if (!TrackingStatus())
jar@chromium.org79a58c32011-10-16 08:52:45 +0900415 return NULL;
416 ThreadData* current_thread_data = Get();
417 if (!current_thread_data)
418 return NULL;
419 return current_thread_data->TallyABirth(location);
ajwong@chromium.org12fa0922011-07-27 03:25:16 +0900420}
421
422// static
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900423void ThreadData::TallyRunOnNamedThreadIfTracking(
424 const base::TrackingInfo& completed_task,
425 const TrackedTime& start_of_run,
426 const TrackedTime& end_of_run) {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900427 if (!kTrackAllTaskObjects)
428 return; // Not compiled in.
429
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900430 // Even if we have been DEACTIVATED, we will process any pending births so
431 // that our data structures (which counted the outstanding births) remain
432 // consistent.
433 const Births* birth = completed_task.birth_tally;
434 if (!birth)
jar@chromium.org79a58c32011-10-16 08:52:45 +0900435 return;
436 ThreadData* current_thread_data = Get();
437 if (!current_thread_data)
438 return;
439
440 // To avoid conflating our stats with the delay duration in a PostDelayedTask,
441 // we identify such tasks, and replace their post_time with the time they
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900442 // were scheduled (requested?) to emerge from the delayed task queue. This
jar@chromium.org79a58c32011-10-16 08:52:45 +0900443 // means that queueing delay for such tasks will show how long they went
444 // unserviced, after they *could* be serviced. This is the same stat as we
445 // have for non-delayed tasks, and we consistently call it queueing delay.
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900446 TrackedTime effective_post_time = completed_task.delayed_run_time.is_null()
447 ? tracked_objects::TrackedTime(completed_task.time_posted)
448 : tracked_objects::TrackedTime(completed_task.delayed_run_time);
449
450 // Watch out for a race where status_ is changing, and hence one or both
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900451 // of start_of_run or end_of_run is zero. In that case, we didn't bother to
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900452 // get a time value since we "weren't tracking" and we were trying to be
453 // efficient by not calling for a genuine time value. For simplicity, we'll
454 // use a default zero duration when we can't calculate a true value.
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900455 DurationInt queue_duration = 0;
456 DurationInt run_duration = 0;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900457 if (!start_of_run.is_null()) {
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900458 queue_duration = (start_of_run - effective_post_time).InMilliseconds();
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900459 if (!end_of_run.is_null())
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900460 run_duration = (end_of_run - start_of_run).InMilliseconds();
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900461 }
462 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
463}
464
465// static
466void ThreadData::TallyRunOnWorkerThreadIfTracking(
467 const Births* birth,
468 const TrackedTime& time_posted,
469 const TrackedTime& start_of_run,
470 const TrackedTime& end_of_run) {
471 if (!kTrackAllTaskObjects)
472 return; // Not compiled in.
473
474 // Even if we have been DEACTIVATED, we will process any pending births so
475 // that our data structures (which counted the outstanding births) remain
476 // consistent.
477 if (!birth)
478 return;
479
480 // TODO(jar): Support the option to coalesce all worker-thread activity under
481 // one ThreadData instance that uses locks to protect *all* access. This will
482 // reduce memory (making it provably bounded), but run incrementally slower
483 // (since we'll use locks on TallyBirth and TallyDeath). The good news is
484 // that the locks on TallyDeath will be *after* the worker thread has run, and
485 // hence nothing will be waiting for the completion (... besides some other
486 // thread that might like to run). Also, the worker threads tasks are
487 // generally longer, and hence the cost of the lock may perchance be amortized
488 // over the long task's lifetime.
489 ThreadData* current_thread_data = Get();
490 if (!current_thread_data)
491 return;
492
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900493 DurationInt queue_duration = 0;
494 DurationInt run_duration = 0;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900495 if (!start_of_run.is_null()) {
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900496 queue_duration = (start_of_run - time_posted).InMilliseconds();
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900497 if (!end_of_run.is_null())
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900498 run_duration = (end_of_run - start_of_run).InMilliseconds();
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900499 }
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900500 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
ajwong@chromium.org12fa0922011-07-27 03:25:16 +0900501}
502
503// static
jar@chromium.org27cf2972011-11-09 02:09:21 +0900504void ThreadData::TallyRunInAScopedRegionIfTracking(
505 const Births* birth,
506 const TrackedTime& start_of_run,
507 const TrackedTime& end_of_run) {
508 if (!kTrackAllTaskObjects)
509 return; // Not compiled in.
510
511 // Even if we have been DEACTIVATED, we will process any pending births so
512 // that our data structures (which counted the outstanding births) remain
513 // consistent.
514 if (!birth)
515 return;
516
517 ThreadData* current_thread_data = Get();
518 if (!current_thread_data)
519 return;
520
jar@chromium.org51c8dfb2011-11-12 07:40:27 +0900521 DurationInt queue_duration = 0;
jar@chromium.org69800902011-11-16 08:59:36 +0900522 DurationInt run_duration = 0;
523 if (!start_of_run.is_null() && !end_of_run.is_null())
524 run_duration = (end_of_run - start_of_run).InMilliseconds();
jar@chromium.org27cf2972011-11-09 02:09:21 +0900525 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
526}
527
jar@chromium.orga0260412011-12-04 16:19:10 +0900528const std::string ThreadData::thread_name() const { return thread_name_; }
initial.commit3f4a7322008-07-27 06:49:38 +0900529
initial.commit3f4a7322008-07-27 06:49:38 +0900530// This may be called from another thread.
jar@chromium.orga0260412011-12-04 16:19:10 +0900531void ThreadData::SnapshotMaps(bool reset_max,
532 BirthMap* birth_map,
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900533 DeathMap* death_map,
534 ParentChildSet* parent_child_set) {
jar@chromium.org92703d52011-11-24 09:00:31 +0900535 base::AutoLock lock(map_lock_);
initial.commit3f4a7322008-07-27 06:49:38 +0900536 for (BirthMap::const_iterator it = birth_map_.begin();
537 it != birth_map_.end(); ++it)
jar@chromium.orga0260412011-12-04 16:19:10 +0900538 (*birth_map)[it->first] = it->second;
539 for (DeathMap::iterator it = death_map_.begin();
540 it != death_map_.end(); ++it) {
541 (*death_map)[it->first] = it->second;
542 if (reset_max)
543 it->second.ResetMax();
544 }
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900545
546 if (!kTrackParentChildLinks)
547 return;
548
549 for (ParentChildSet::iterator it = parent_child_set_.begin();
550 it != parent_child_set_.end(); ++it)
551 parent_child_set->insert(*it);
initial.commit3f4a7322008-07-27 06:49:38 +0900552}
553
jar@chromium.orga0260412011-12-04 16:19:10 +0900554// static
555void ThreadData::SendAllMaps(bool reset_max, class DataCollector* target) {
556 if (!kTrackAllTaskObjects)
557 return; // Not compiled in.
558 // Get an unchanging copy of a ThreadData list.
559 ThreadData* my_list = ThreadData::first();
560
561 // Gather data serially.
562 // This hackish approach *can* get some slighly corrupt tallies, as we are
563 // grabbing values without the protection of a lock, but it has the advantage
564 // of working even with threads that don't have message loops. If a user
565 // sees any strangeness, they can always just run their stats gathering a
566 // second time.
567 for (ThreadData* thread_data = my_list;
568 thread_data;
569 thread_data = thread_data->next()) {
570 // Get copy of data.
571 ThreadData::BirthMap birth_map;
572 ThreadData::DeathMap death_map;
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900573 ThreadData::ParentChildSet parent_child_set;
574 thread_data->SnapshotMaps(reset_max, &birth_map, &death_map,
575 &parent_child_set);
576 target->Append(*thread_data, birth_map, death_map, parent_child_set);
jar@chromium.orga0260412011-12-04 16:19:10 +0900577 }
initial.commit3f4a7322008-07-27 06:49:38 +0900578}
579
jar@chromium.orgfebe0e42009-12-30 16:31:45 +0900580// static
581void ThreadData::ResetAllThreadData() {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900582 ThreadData* my_list = first();
jar@chromium.orgfebe0e42009-12-30 16:31:45 +0900583
584 for (ThreadData* thread_data = my_list;
585 thread_data;
586 thread_data = thread_data->next())
587 thread_data->Reset();
588}
589
590void ThreadData::Reset() {
jar@chromium.org92703d52011-11-24 09:00:31 +0900591 base::AutoLock lock(map_lock_);
jar@chromium.orgfebe0e42009-12-30 16:31:45 +0900592 for (DeathMap::iterator it = death_map_.begin();
593 it != death_map_.end(); ++it)
594 it->second.Clear();
595 for (BirthMap::iterator it = birth_map_.begin();
596 it != birth_map_.end(); ++it)
597 it->second->Clear();
598}
599
jar@chromium.org4626ccb2012-02-16 08:05:01 +0900600static void OptionallyInitializeAlternateTimer() {
601 char* alternate_selector = getenv(kAlternateProfilerTime);
602 if (!alternate_selector)
603 return;
604 switch (*alternate_selector) {
605 case '0': // This is the default value, and uses the wall clock time.
606 break;
607 case '1': {
608 // Use the TCMalloc allocations-on-thread as a pseudo-time.
609 ThreadData::SetAlternateTimeSource(GetAlternateTimeSource());
610 break;
611 }
612 default:
613 NOTREACHED();
614 break;
615 }
616}
617
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900618bool ThreadData::Initialize() {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900619 if (!kTrackAllTaskObjects)
620 return false; // Not compiled in.
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900621 if (status_ >= DEACTIVATED)
jar@chromium.org340a7c52011-11-16 06:50:36 +0900622 return true; // Someone else did the initialization.
623 // Due to racy lazy initialization in tests, we'll need to recheck status_
624 // after we acquire the lock.
625
626 // Ensure that we don't double initialize tls. We are called when single
627 // threaded in the product, but some tests may be racy and lazy about our
628 // initialization.
629 base::AutoLock lock(*list_lock_.Pointer());
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900630 if (status_ >= DEACTIVATED)
jar@chromium.org340a7c52011-11-16 06:50:36 +0900631 return true; // Someone raced in here and beat us.
632
jar@chromium.org4626ccb2012-02-16 08:05:01 +0900633 // Put an alternate timer in place if the environment calls for it, such as
634 // for tracking TCMalloc allocations. This insertion is idempotent, so we
635 // don't mind if there is a race, and we'd prefer not to be in a lock while
636 // doing this work.
637 if (kAllowAlternateTimeSourceHandling)
638 OptionallyInitializeAlternateTimer();
639
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900640 // Perform the "real" TLS initialization now, and leave it intact through
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900641 // process termination.
jar@chromium.org340a7c52011-11-16 06:50:36 +0900642 if (!tls_index_.initialized()) { // Testing may have initialized this.
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900643 DCHECK_EQ(status_, UNINITIALIZED);
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900644 tls_index_.Initialize(&ThreadData::OnThreadTermination);
jar@chromium.org340a7c52011-11-16 06:50:36 +0900645 if (!tls_index_.initialized())
646 return false;
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900647 } else {
648 // TLS was initialzed for us earlier.
649 DCHECK_EQ(status_, DORMANT_DURING_TESTS);
jar@chromium.org340a7c52011-11-16 06:50:36 +0900650 }
joth@chromium.orgc8b867c2011-11-01 00:32:08 +0900651
jar@chromium.org340a7c52011-11-16 06:50:36 +0900652 // Incarnation counter is only significant to testing, as it otherwise will
653 // never again change in this process.
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900654 ++incarnation_counter_;
jar@chromium.org340a7c52011-11-16 06:50:36 +0900655
656 // The lock is not critical for setting status_, but it doesn't hurt. It also
657 // ensures that if we have a racy initialization, that we'll bail as soon as
658 // we get the lock earlier in this method.
659 status_ = kInitialStartupState;
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900660 if (!kTrackParentChildLinks &&
661 kInitialStartupState == PROFILING_CHILDREN_ACTIVE)
662 status_ = PROFILING_ACTIVE;
jar@chromium.org340a7c52011-11-16 06:50:36 +0900663 DCHECK(status_ != UNINITIALIZED);
initial.commit3f4a7322008-07-27 06:49:38 +0900664 return true;
665}
666
667// static
jar@chromium.org23b00722012-02-11 04:43:42 +0900668bool ThreadData::InitializeAndSetTrackingStatus(Status status) {
669 DCHECK_GE(status, DEACTIVATED);
670 DCHECK_LE(status, PROFILING_CHILDREN_ACTIVE);
671
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900672 if (!Initialize()) // No-op if already initialized.
673 return false; // Not compiled in.
674
jar@chromium.org23b00722012-02-11 04:43:42 +0900675 if (!kTrackParentChildLinks && status > DEACTIVATED)
676 status = PROFILING_ACTIVE;
677 status_ = status;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900678 return true;
679}
680
681// static
jar@chromium.org23b00722012-02-11 04:43:42 +0900682ThreadData::Status ThreadData::status() {
683 return status_;
684}
685
686// static
687bool ThreadData::TrackingStatus() {
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900688 return status_ > DEACTIVATED;
initial.commit3f4a7322008-07-27 06:49:38 +0900689}
690
691// static
jar@chromium.org23b00722012-02-11 04:43:42 +0900692bool ThreadData::TrackingParentChildStatus() {
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900693 return status_ >= PROFILING_CHILDREN_ACTIVE;
694}
695
696// static
697TrackedTime ThreadData::NowForStartOfRun(const Births* parent) {
698 if (kTrackParentChildLinks && parent && status_ > PROFILING_ACTIVE) {
699 ThreadData* current_thread_data = Get();
700 if (current_thread_data)
701 current_thread_data->parent_stack_.push(parent);
702 }
jar@chromium.orgb536eef2011-11-14 14:24:07 +0900703 return Now();
704}
705
706// static
707TrackedTime ThreadData::NowForEndOfRun() {
708 return Now();
709}
710
711// static
jar@chromium.org4626ccb2012-02-16 08:05:01 +0900712void ThreadData::SetAlternateTimeSource(NowFunction* now_function) {
713 DCHECK(now_function);
714 if (kAllowAlternateTimeSourceHandling)
715 now_function_ = now_function;
716}
717
718// static
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900719TrackedTime ThreadData::Now() {
jar@chromium.org4626ccb2012-02-16 08:05:01 +0900720 if (kAllowAlternateTimeSourceHandling && now_function_)
721 return TrackedTime::FromMilliseconds((*now_function_)());
jar@chromium.org23b00722012-02-11 04:43:42 +0900722 if (kTrackAllTaskObjects && TrackingStatus())
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900723 return TrackedTime::Now();
724 return TrackedTime(); // Super fast when disabled, or not compiled.
jar@chromium.org79a58c32011-10-16 08:52:45 +0900725}
initial.commit3f4a7322008-07-27 06:49:38 +0900726
727// static
jar@chromium.org92703d52011-11-24 09:00:31 +0900728void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) {
729 base::AutoLock lock(*list_lock_.Pointer());
730 if (worker_thread_data_creation_count_ == 0)
731 return; // We haven't really run much, and couldn't have leaked.
732 // Verify that we've at least shutdown/cleanup the major namesd threads. The
733 // caller should tell us how many thread shutdowns should have taken place by
734 // now.
735 return; // TODO(jar): until this is working on XP, don't run the real test.
736 CHECK_GT(cleanup_count_, major_threads_shutdown_count);
737}
738
739// static
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900740void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900741 // This is only called from test code, where we need to cleanup so that
742 // additional tests can be run.
initial.commit3f4a7322008-07-27 06:49:38 +0900743 // We must be single threaded... but be careful anyway.
jar@chromium.org23b00722012-02-11 04:43:42 +0900744 if (!InitializeAndSetTrackingStatus(DEACTIVATED))
initial.commit3f4a7322008-07-27 06:49:38 +0900745 return;
746 ThreadData* thread_data_list;
747 {
jar@chromium.orgb2845c82011-11-15 05:36:46 +0900748 base::AutoLock lock(*list_lock_.Pointer());
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900749 thread_data_list = all_thread_data_list_head_;
750 all_thread_data_list_head_ = NULL;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900751 ++incarnation_counter_;
jar@chromium.org50c48db2011-11-20 13:17:07 +0900752 // To be clean, break apart the retired worker list (though we leak them).
jar@chromium.orga0260412011-12-04 16:19:10 +0900753 while (first_retired_worker_) {
jar@chromium.org50c48db2011-11-20 13:17:07 +0900754 ThreadData* worker = first_retired_worker_;
755 CHECK_GT(worker->worker_thread_number_, 0);
756 first_retired_worker_ = worker->next_retired_worker_;
757 worker->next_retired_worker_ = NULL;
758 }
initial.commit3f4a7322008-07-27 06:49:38 +0900759 }
760
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900761 // Put most global static back in pristine shape.
jar@chromium.org92703d52011-11-24 09:00:31 +0900762 worker_thread_data_creation_count_ = 0;
763 cleanup_count_ = 0;
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900764 tls_index_.Set(NULL);
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900765 status_ = DORMANT_DURING_TESTS; // Almost UNINITIALIZED.
jar@chromium.org4be2cb02011-11-01 07:36:21 +0900766
767 // To avoid any chance of racing in unit tests, which is the only place we
768 // call this function, we may sometimes leak all the data structures we
769 // recovered, as they may still be in use on threads from prior tests!
770 if (leak)
771 return;
772
773 // When we want to cleanup (on a single thread), here is what we do.
774
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900775 // Do actual recursive delete in all ThreadData instances.
initial.commit3f4a7322008-07-27 06:49:38 +0900776 while (thread_data_list) {
777 ThreadData* next_thread_data = thread_data_list;
778 thread_data_list = thread_data_list->next();
779
780 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
781 next_thread_data->birth_map_.end() != it; ++it)
782 delete it->second; // Delete the Birth Records.
initial.commit3f4a7322008-07-27 06:49:38 +0900783 delete next_thread_data; // Includes all Death Records.
784 }
initial.commit3f4a7322008-07-27 06:49:38 +0900785}
786
initial.commit3f4a7322008-07-27 06:49:38 +0900787//------------------------------------------------------------------------------
788// Individual 3-tuple of birth (place and thread) along with death thread, and
789// the accumulated stats for instances (DeathData).
790
791Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
792 const ThreadData& death_thread,
793 const DeathData& death_data)
794 : birth_(&birth_on_thread),
795 death_thread_(&death_thread),
796 death_data_(death_data) {
797}
798
799Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
800 : birth_(&birth_on_thread),
801 death_thread_(NULL),
802 death_data_(DeathData(count)) {
803}
804
805const std::string Snapshot::DeathThreadName() const {
806 if (death_thread_)
jar@chromium.org79a58c32011-10-16 08:52:45 +0900807 return death_thread_->thread_name();
initial.commit3f4a7322008-07-27 06:49:38 +0900808 return "Still_Alive";
809}
810
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900811base::DictionaryValue* Snapshot::ToValue() const {
812 base::DictionaryValue* dictionary = new base::DictionaryValue;
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900813 // TODO(jar): Switch the next two lines to:
814 // birth_->ToValue("birth", dictionary);
815 // ...but that will require fixing unit tests, and JS to take
816 // "birth_location" rather than "location"
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900817 dictionary->Set("birth_thread",
818 base::Value::CreateStringValue(birth_->birth_thread()->thread_name()));
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900819 dictionary->Set("location", birth_->location().ToValue());
820
821 dictionary->Set("death_data", death_data_.ToValue());
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900822 dictionary->Set("death_thread",
823 base::Value::CreateStringValue(DeathThreadName()));
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900824 return dictionary;
825}
826
initial.commit3f4a7322008-07-27 06:49:38 +0900827//------------------------------------------------------------------------------
828// DataCollector
829
jar@chromium.orga0260412011-12-04 16:19:10 +0900830DataCollector::DataCollector() {}
initial.commit3f4a7322008-07-27 06:49:38 +0900831
erg@google.com71915232010-09-29 07:54:58 +0900832DataCollector::~DataCollector() {
833}
834
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900835void DataCollector::Append(const ThreadData& thread_data,
836 const ThreadData::BirthMap& birth_map,
837 const ThreadData::DeathMap& death_map,
838 const ThreadData::ParentChildSet& parent_child_set) {
initial.commit3f4a7322008-07-27 06:49:38 +0900839 for (ThreadData::DeathMap::const_iterator it = death_map.begin();
840 it != death_map.end(); ++it) {
841 collection_.push_back(Snapshot(*it->first, thread_data, it->second));
842 global_birth_count_[it->first] -= it->first->birth_count();
843 }
844
845 for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
846 it != birth_map.end(); ++it) {
847 global_birth_count_[it->second] += it->second->birth_count();
848 }
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900849
850 if (!kTrackParentChildLinks)
851 return;
852
853 for (ThreadData::ParentChildSet::const_iterator it = parent_child_set.begin();
854 it != parent_child_set.end(); ++it) {
855 parent_child_set_.insert(*it);
856 }
initial.commit3f4a7322008-07-27 06:49:38 +0900857}
858
859DataCollector::Collection* DataCollector::collection() {
initial.commit3f4a7322008-07-27 06:49:38 +0900860 return &collection_;
861}
862
863void DataCollector::AddListOfLivingObjects() {
initial.commit3f4a7322008-07-27 06:49:38 +0900864 for (BirthCount::iterator it = global_birth_count_.begin();
865 it != global_birth_count_.end(); ++it) {
866 if (it->second > 0)
867 collection_.push_back(Snapshot(*it->first, it->second));
868 }
869}
870
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900871void DataCollector::ToValue(base::DictionaryValue* dictionary) const {
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900872 base::ListValue* list = new base::ListValue;
873 for (size_t i = 0; i < collection_.size(); ++i) {
874 list->Append(collection_[i].ToValue());
875 }
jar@chromium.orgb5c974b2011-12-14 10:36:48 +0900876 dictionary->Set("list", list);
877
878 base::ListValue* descendants = new base::ListValue;
879 for (ThreadData::ParentChildSet::const_iterator it =
880 parent_child_set_.begin();
881 it != parent_child_set_.end();
882 ++it) {
883 base::DictionaryValue* parent_child = new base::DictionaryValue;
884 it->first->ToValue("parent", parent_child);
885 it->second->ToValue("child", parent_child);
886 descendants->Append(parent_child);
887 }
888 dictionary->Set("descendants", descendants);
jar@chromium.org666ef9c2011-10-25 03:55:16 +0900889}
890
initial.commit3f4a7322008-07-27 06:49:38 +0900891} // namespace tracked_objects