Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: base/tracked_objects.cc

Issue 8429009: Enable tracking of objects by default (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/tracked_objects.h ('k') | base/tracked_objects_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/tracked_objects.h" 5 #include "base/tracked_objects.h"
6 6
7 #include <math.h> 7 #include <math.h>
8 8
9 #include "base/format_macros.h" 9 #include "base/format_macros.h"
10 #include "base/message_loop.h" 10 #include "base/message_loop.h"
11 #include "base/string_util.h" 11 #include "base/string_util.h"
12 #include "base/stringprintf.h" 12 #include "base/stringprintf.h"
13 #include "base/threading/thread_restrictions.h" 13 #include "base/threading/thread_restrictions.h"
14 #include "build/build_config.h"
14 15
15 using base::TimeDelta; 16 using base::TimeDelta;
16 17
17 namespace tracked_objects { 18 namespace tracked_objects {
18 19
19 20 namespace {
20 #if defined(TRACK_ALL_TASK_OBJECTS) 21 // Flag to compile out almost all of the task tracking code.
22 #if defined(NDEBUG) && defined(OS_MAC)
23 // Avoid problems with base_unittest crashes in Mac for now.
24 static const bool kTrackAllTaskObjects = false;
25 #else
21 static const bool kTrackAllTaskObjects = true; 26 static const bool kTrackAllTaskObjects = true;
22 #else
23 static const bool kTrackAllTaskObjects = false;
24 #endif 27 #endif
25 28
26 // Can we count on thread termination to call for thread cleanup? If not, then 29 // When ThreadData is first initialized, should we start in an ACTIVE state to
27 // we can't risk putting references to ThreadData in TLS, as it will leak on 30 // record all of the startup-time tasks, or should we start up DEACTIVATED, so
28 // worker thread termination. 31 // that we only record after parsing the command line flag --enable-tracking.
29 static const bool kWorkerThreadCleanupSupported = true; 32 // Note that the flag may force either state, so this really controls only the
30 33 // period of time up until that flag is parsed. If there is no flag seen, then
31 // A TLS slot which points to the ThreadData instance for the current thread. We 34 // this state may prevail for much or all of the process lifetime.
32 // do a fake initialization here (zeroing out data), and then the real in-place 35 static const ThreadData::Status kInitialStartupState = ThreadData::ACTIVE;
33 // construction happens when we call tls_index_.Initialize(). 36 } // anonymous namespace.
34 // static
35 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED);
36
37 // A global state variable to prevent repeated initialization during tests.
38 // static
39 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun;
40
41 // A locked protected counter to assign sequence number to threads.
42 // static
43 int ThreadData::thread_number_counter_ = 0;
44 37
45 //------------------------------------------------------------------------------ 38 //------------------------------------------------------------------------------
46 // Death data tallies durations when a death takes place. 39 // Death data tallies durations when a death takes place.
47 40
48 void DeathData::RecordDeath(const TimeDelta& queue_duration, 41 void DeathData::RecordDeath(const Duration& queue_duration,
49 const TimeDelta& run_duration) { 42 const Duration& run_duration) {
50 ++count_; 43 ++count_;
51 queue_duration_ += queue_duration; 44 queue_duration_ += queue_duration;
52 run_duration_ += run_duration; 45 run_duration_ += run_duration;
53 } 46 }
54 47
55 int DeathData::AverageMsRunDuration() const { 48 int DeathData::AverageMsRunDuration() const {
56 if (run_duration_ == base::TimeDelta()) 49 if (run_duration_ == Duration() || !count_)
57 return 0; 50 return 0;
58 return static_cast<int>(run_duration_.InMilliseconds() / count_); 51 // Add half of denominator to achieve rounding.
52 return static_cast<int>(run_duration_.InMilliseconds() + count_ / 2) /
53 count_;
59 } 54 }
60 55
61 int DeathData::AverageMsQueueDuration() const { 56 int DeathData::AverageMsQueueDuration() const {
62 if (queue_duration_ == base::TimeDelta()) 57 if (queue_duration_ == Duration() || !count_)
63 return 0; 58 return 0;
64 return static_cast<int>(queue_duration_.InMilliseconds() / count_); 59 // Add half of denominator to achieve rounding.
60 return (static_cast<int>(queue_duration_.InMilliseconds() + count_ / 2) /
61 count_);
65 } 62 }
66 63
67 void DeathData::AddDeathData(const DeathData& other) { 64 void DeathData::AddDeathData(const DeathData& other) {
68 count_ += other.count_; 65 count_ += other.count_;
69 queue_duration_ += other.queue_duration_; 66 queue_duration_ += other.queue_duration_;
70 run_duration_ += other.run_duration_; 67 run_duration_ += other.run_duration_;
71 } 68 }
72 69
73 void DeathData::WriteHTML(std::string* output) const { 70 void DeathData::WriteHTML(std::string* output) const {
74 if (!count_) 71 if (!count_)
75 return; 72 return;
76 base::StringAppendF(output, "%s:%d, ", 73 base::StringAppendF(output, "%s:%d, ",
77 (count_ == 1) ? "Life" : "Lives", count_); 74 (count_ == 1) ? "Life" : "Lives", count_);
78 base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ", 75 // Be careful to leave static_casts intact, as the type returned by
79 run_duration_.InMilliseconds(), 76 // InMilliseconds() may not always be an int, even if it can generally fit
77 // into an int.
78 base::StringAppendF(output, "Run:%dms(%dms/life) ",
79 static_cast<int>(run_duration_.InMilliseconds()),
80 AverageMsRunDuration()); 80 AverageMsRunDuration());
81 base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ", 81 base::StringAppendF(output, "Queue:%dms(%dms/life) ",
82 queue_duration_.InMilliseconds(), 82 static_cast<int>(queue_duration_.InMilliseconds()),
83 AverageMsQueueDuration()); 83 AverageMsQueueDuration());
84 } 84 }
85 85
86 base::DictionaryValue* DeathData::ToValue() const { 86 base::DictionaryValue* DeathData::ToValue() const {
87 base::DictionaryValue* dictionary = new base::DictionaryValue; 87 base::DictionaryValue* dictionary = new base::DictionaryValue;
88 dictionary->Set("count", base::Value::CreateIntegerValue(count_)); 88 dictionary->Set("count", base::Value::CreateIntegerValue(count_));
89 dictionary->Set("run_ms", 89 dictionary->Set("run_ms",
90 base::Value::CreateIntegerValue(run_duration_.InMilliseconds())); 90 base::Value::CreateIntegerValue(run_duration_.InMilliseconds()));
91 dictionary->Set("queue_ms", 91 dictionary->Set("queue_ms",
92 base::Value::CreateIntegerValue(queue_duration_.InMilliseconds())); 92 base::Value::CreateIntegerValue(queue_duration_.InMilliseconds()));
93 return dictionary; 93 return dictionary;
94 } 94 }
95 95
96 void DeathData::Clear() { 96 void DeathData::Clear() {
97 count_ = 0; 97 count_ = 0;
98 queue_duration_ = TimeDelta(); 98 queue_duration_ = Duration();
99 run_duration_ = TimeDelta(); 99 run_duration_ = Duration();
100 } 100 }
101 101
102 //------------------------------------------------------------------------------ 102 //------------------------------------------------------------------------------
103 BirthOnThread::BirthOnThread(const Location& location, 103 BirthOnThread::BirthOnThread(const Location& location,
104 const ThreadData& current) 104 const ThreadData& current)
105 : location_(location), 105 : location_(location),
106 birth_thread_(&current) {} 106 birth_thread_(&current) {}
107 107
108 //------------------------------------------------------------------------------ 108 //------------------------------------------------------------------------------
109 Births::Births(const Location& location, const ThreadData& current) 109 Births::Births(const Location& location, const ThreadData& current)
110 : BirthOnThread(location, current), 110 : BirthOnThread(location, current),
111 birth_count_(1) { } 111 birth_count_(1) { }
112 112
113 //------------------------------------------------------------------------------ 113 //------------------------------------------------------------------------------
114 // ThreadData maintains the central data for all births and deaths. 114 // ThreadData maintains the central data for all births and deaths.
115 115
116 // TODO(jar): We should pull all these static vars together, into a struct, and
117 // optimize layout so that we benefit from locality of reference during accesses
118 // to them.
119
120 // A TLS slot which points to the ThreadData instance for the current thread. We
121 // do a fake initialization here (zeroing out data), and then the real in-place
122 // construction happens when we call tls_index_.Initialize().
123 // static
124 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED);
125
126 // A lock-protected counter to assign sequence number to threads.
127 // static
128 int ThreadData::thread_number_counter_ = 0;
129
130 // static
131 int ThreadData::incarnation_counter_ = 0;
132
116 // static 133 // static
117 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; 134 ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
118 135
119 // static 136 // static
120 ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL; 137 ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL;
121 138
122 // static 139 // static
123 base::Lock ThreadData::list_lock_; 140 base::Lock* ThreadData::list_lock_;
124 141
125 // static 142 // static
126 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; 143 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
127 144
128 ThreadData::ThreadData(const std::string& suggested_name) 145 ThreadData::ThreadData(const std::string& suggested_name)
129 : next_(NULL), 146 : incarnation_count_for_pool_(-1),
147 next_(NULL),
130 is_a_worker_thread_(false) { 148 is_a_worker_thread_(false) {
131 DCHECK_GE(suggested_name.size(), 0u); 149 DCHECK_GE(suggested_name.size(), 0u);
132 thread_name_ = suggested_name; 150 thread_name_ = suggested_name;
133 PushToHeadOfList(); 151 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
134 } 152 }
135 153
136 ThreadData::ThreadData() : next_(NULL), is_a_worker_thread_(true) { 154 ThreadData::ThreadData()
155 : incarnation_count_for_pool_(-1),
156 next_(NULL),
157 is_a_worker_thread_(true) {
137 int thread_number; 158 int thread_number;
138 { 159 {
139 base::AutoLock lock(list_lock_); 160 base::AutoLock lock(*list_lock_);
140 thread_number = ++thread_number_counter_; 161 thread_number = ++thread_number_counter_;
141 } 162 }
142 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); 163 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
143 PushToHeadOfList(); 164 PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
144 } 165 }
145 166
146 ThreadData::~ThreadData() {} 167 ThreadData::~ThreadData() {}
147 168
148 void ThreadData::PushToHeadOfList() { 169 void ThreadData::PushToHeadOfList() {
149 DCHECK(!next_); 170 DCHECK(!next_);
150 base::AutoLock lock(list_lock_); 171 base::AutoLock lock(*list_lock_);
172 incarnation_count_for_pool_ = incarnation_counter_;
151 next_ = all_thread_data_list_head_; 173 next_ = all_thread_data_list_head_;
152 all_thread_data_list_head_ = this; 174 all_thread_data_list_head_ = this;
153 } 175 }
154 176
155 // static 177 // static
156 void ThreadData::InitializeThreadContext(const std::string& suggested_name) { 178 void ThreadData::InitializeThreadContext(const std::string& suggested_name) {
157 if (!tls_index_.initialized()) 179 if (!Initialize()) // Always initialize if needed.
158 return; // For unittests only. 180 return;
159 DCHECK_EQ(tls_index_.Get(), reinterpret_cast<void*>(NULL)); 181 ThreadData* current_thread_data =
160 ThreadData* current_thread_data = new ThreadData(suggested_name); 182 reinterpret_cast<ThreadData*>(tls_index_.Get());
183 if (current_thread_data)
184 return; // Browser tests instigate this.
185 current_thread_data = new ThreadData(suggested_name);
161 tls_index_.Set(current_thread_data); 186 tls_index_.Set(current_thread_data);
162 } 187 }
163 188
164 // static 189 // static
165 ThreadData* ThreadData::Get() { 190 ThreadData* ThreadData::Get() {
166 if (!tls_index_.initialized()) 191 if (!tls_index_.initialized())
167 return NULL; // For unittests only. 192 return NULL; // For unittests only.
168 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); 193 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get());
169 if (registered) 194 if (registered)
170 return registered; 195 return registered;
171 196
172 // We must be a worker thread, since we didn't pre-register. 197 // We must be a worker thread, since we didn't pre-register.
173 ThreadData* worker_thread_data = NULL; 198 ThreadData* worker_thread_data = NULL;
174 { 199 {
175 base::AutoLock lock(list_lock_); 200 base::AutoLock lock(*list_lock_);
176 if (!unregistered_thread_data_pool_->empty()) { 201 if (!unregistered_thread_data_pool_->empty()) {
177 worker_thread_data = 202 worker_thread_data =
178 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); 203 const_cast<ThreadData*>(unregistered_thread_data_pool_->top());
179 unregistered_thread_data_pool_->pop(); 204 unregistered_thread_data_pool_->pop();
180 } 205 }
181 } 206 }
182 207
183 // If we can't find a previously used instance, then we have to create one. 208 // If we can't find a previously used instance, then we have to create one.
184 if (!worker_thread_data) 209 if (!worker_thread_data)
185 worker_thread_data = new ThreadData(); 210 worker_thread_data = new ThreadData();
186 211
187 tls_index_.Set(worker_thread_data); 212 tls_index_.Set(worker_thread_data);
188 return worker_thread_data; 213 return worker_thread_data;
189 } 214 }
190 215
191 // static 216 // static
192 void ThreadData::OnThreadTermination(void* thread_data) { 217 void ThreadData::OnThreadTermination(void* thread_data) {
193 if (!kTrackAllTaskObjects) 218 if (!kTrackAllTaskObjects)
194 return; // Not compiled in. 219 return; // Not compiled in.
195 DCHECK(tls_index_.initialized());
196 if (!thread_data) 220 if (!thread_data)
197 return; 221 return;
198 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); 222 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup();
199 DCHECK_EQ(tls_index_.Get(), reinterpret_cast<ThreadData*>(NULL));
200 } 223 }
201 224
202 void ThreadData::OnThreadTerminationCleanup() const { 225 void ThreadData::OnThreadTerminationCleanup() const {
203 tls_index_.Set(NULL);
204 if (!is_a_worker_thread_) 226 if (!is_a_worker_thread_)
205 return; 227 return;
206 base::AutoLock lock(list_lock_); 228 base::AutoLock lock(*list_lock_);
207 unregistered_thread_data_pool_->push(this); 229 if (incarnation_counter_ != incarnation_count_for_pool_)
230 return; // ThreadData was constructed in an earlier unit test.
231
232 // Handle case where we are in unit tests, and have become UNINITIALIZED.
233 // In that case, the pool might be NULL. We really should detect this via the
234 // incarnation_counter_, but this call is rarely made, so we can afford to
235 // code defensively.
236 if (unregistered_thread_data_pool_)
237 unregistered_thread_data_pool_->push(this);
208 } 238 }
209 239
210 // static 240 // static
211 void ThreadData::WriteHTML(const std::string& query, std::string* output) { 241 void ThreadData::WriteHTML(const std::string& query, std::string* output) {
212 if (!ThreadData::IsActive()) 242 if (status_ == UNINITIALIZED)
213 return; // Not yet initialized. 243 return; // Not yet initialized.
214 244
215 DataCollector collected_data; // Gather data. 245 DataCollector collected_data; // Gather data.
216 collected_data.AddListOfLivingObjects(); // Add births that are still alive. 246 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
217 247
218 // Data Gathering is complete. Now to sort/process/render. 248 // Data Gathering is complete. Now to sort/process/render.
219 DataCollector::Collection* collection = collected_data.collection(); 249 DataCollector::Collection* collection = collected_data.collection();
220 250
221 // Create filtering and sort comparison object. 251 // Create filtering and sort comparison object.
222 Comparator comparator; 252 Comparator comparator;
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 // Print aggregate stats for the group. 339 // Print aggregate stats for the group.
310 output->append("<br>"); 340 output->append("<br>");
311 subtotals.WriteHTML(output); 341 subtotals.WriteHTML(output);
312 output->append("<br><hr><br>"); 342 output->append("<br><hr><br>");
313 subtotals.Clear(); 343 subtotals.Clear();
314 } 344 }
315 } 345 }
316 } 346 }
317 347
318 // static 348 // static
319 base::Value* ThreadData::ToValue(int process_type) { 349 base::DictionaryValue* ThreadData::ToValue() {
320 DataCollector collected_data; // Gather data. 350 DataCollector collected_data; // Gather data.
321 collected_data.AddListOfLivingObjects(); // Add births that are still alive. 351 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
322 base::ListValue* list = collected_data.ToValue(); 352 base::ListValue* list = collected_data.ToValue();
323 base::DictionaryValue* dictionary = new base::DictionaryValue(); 353 base::DictionaryValue* dictionary = new base::DictionaryValue();
324 dictionary->Set("list", list); 354 dictionary->Set("list", list);
325 dictionary->SetInteger("process", process_type);
326 return dictionary; 355 return dictionary;
327 } 356 }
328 357
329 Births* ThreadData::TallyABirth(const Location& location) { 358 Births* ThreadData::TallyABirth(const Location& location) {
330 BirthMap::iterator it = birth_map_.find(location); 359 BirthMap::iterator it = birth_map_.find(location);
331 if (it != birth_map_.end()) { 360 if (it != birth_map_.end()) {
332 it->second->RecordBirth(); 361 it->second->RecordBirth();
333 return it->second; 362 return it->second;
334 } 363 }
335 364
336 Births* tracker = new Births(location, *this); 365 Births* tracker = new Births(location, *this);
337 // Lock since the map may get relocated now, and other threads sometimes 366 // Lock since the map may get relocated now, and other threads sometimes
338 // snapshot it (but they lock before copying it). 367 // snapshot it (but they lock before copying it).
339 base::AutoLock lock(lock_); 368 base::AutoLock lock(lock_);
340 birth_map_[location] = tracker; 369 birth_map_[location] = tracker;
341 return tracker; 370 return tracker;
342 } 371 }
343 372
344 void ThreadData::TallyADeath(const Births& birth, 373 void ThreadData::TallyADeath(const Births& birth,
345 const TimeDelta& queue_duration, 374 const Duration& queue_duration,
346 const TimeDelta& run_duration) { 375 const Duration& run_duration) {
347 DeathMap::iterator it = death_map_.find(&birth); 376 DeathMap::iterator it = death_map_.find(&birth);
348 DeathData* death_data; 377 DeathData* death_data;
349 if (it != death_map_.end()) { 378 if (it != death_map_.end()) {
350 death_data = &it->second; 379 death_data = &it->second;
351 } else { 380 } else {
352 base::AutoLock lock(lock_); // Lock since the map may get relocated now. 381 base::AutoLock lock(lock_); // Lock since the map may get relocated now.
353 death_data = &death_map_[&birth]; 382 death_data = &death_map_[&birth];
354 } // Release lock ASAP. 383 } // Release lock ASAP.
355 death_data->RecordDeath(queue_duration, run_duration); 384 death_data->RecordDeath(queue_duration, run_duration);
356 } 385 }
357 386
358 // static 387 // static
359 Births* ThreadData::TallyABirthIfActive(const Location& location) { 388 Births* ThreadData::TallyABirthIfActive(const Location& location) {
360 if (!kTrackAllTaskObjects) 389 if (!kTrackAllTaskObjects)
361 return NULL; // Not compiled in. 390 return NULL; // Not compiled in.
362 391
363 if (!IsActive()) 392 if (!tracking_status())
364 return NULL; 393 return NULL;
365 ThreadData* current_thread_data = Get(); 394 ThreadData* current_thread_data = Get();
366 if (!current_thread_data) 395 if (!current_thread_data)
367 return NULL; 396 return NULL;
368 return current_thread_data->TallyABirth(location); 397 return current_thread_data->TallyABirth(location);
369 } 398 }
370 399
371 // static 400 // static
372 void ThreadData::TallyADeathIfActive(const Births* birth, 401 void ThreadData::TallyRunOnNamedThreadIfTracking(
373 const base::TimeTicks& time_posted, 402 const base::TrackingInfo& completed_task,
374 const base::TimeTicks& delayed_start_time, 403 const TrackedTime& start_of_run,
375 const base::TimeTicks& start_of_run, 404 const TrackedTime& end_of_run) {
376 const base::TimeTicks& end_of_run) {
377 if (!kTrackAllTaskObjects) 405 if (!kTrackAllTaskObjects)
378 return; // Not compiled in. 406 return; // Not compiled in.
379 407
380 if (!IsActive() || !birth) 408 // Even if we have been DEACTIVATED, we will process any pending births so
409 // that our data structures (which counted the outstanding births) remain
410 // consistent.
411 const Births* birth = completed_task.birth_tally;
412 if (!birth)
381 return; 413 return;
382
383 ThreadData* current_thread_data = Get(); 414 ThreadData* current_thread_data = Get();
384 if (!current_thread_data) 415 if (!current_thread_data)
385 return; 416 return;
386 417
387 // To avoid conflating our stats with the delay duration in a PostDelayedTask, 418 // To avoid conflating our stats with the delay duration in a PostDelayedTask,
388 // we identify such tasks, and replace their post_time with the time they 419 // we identify such tasks, and replace their post_time with the time they
389 // were sechudled (requested?) to emerge from the delayed task queue. This 420 // were scheduled (requested?) to emerge from the delayed task queue. This
390 // means that queueing delay for such tasks will show how long they went 421 // means that queueing delay for such tasks will show how long they went
391 // unserviced, after they *could* be serviced. This is the same stat as we 422 // unserviced, after they *could* be serviced. This is the same stat as we
392 // have for non-delayed tasks, and we consistently call it queueing delay. 423 // have for non-delayed tasks, and we consistently call it queueing delay.
393 base::TimeTicks effective_post_time = 424 TrackedTime effective_post_time = completed_task.delayed_run_time.is_null()
394 (delayed_start_time.is_null()) ? time_posted : delayed_start_time; 425 ? tracked_objects::TrackedTime(completed_task.time_posted)
395 base::TimeDelta queue_duration = start_of_run - effective_post_time; 426 : tracked_objects::TrackedTime(completed_task.delayed_run_time);
396 base::TimeDelta run_duration = end_of_run - start_of_run; 427
428 // Watch out for a race where status_ is changing, and hence one or both
429 // of start_of_run or end_of_run is zero. IN that case, we didn't bother to
430 // get a time value since we "weren't tracking" and we were trying to be
431 // efficient by not calling for a genuine time value. For simplicity, we'll
432 // use a default zero duration when we can't calculate a true value.
433 Duration queue_duration;
434 Duration run_duration;
435 if (!start_of_run.is_null()) {
436 queue_duration = start_of_run - effective_post_time;
437 if (!end_of_run.is_null())
438 run_duration = end_of_run - start_of_run;
439 }
440 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
441 }
442
443 // static
444 void ThreadData::TallyRunOnWorkerThreadIfTracking(
445 const Births* birth,
446 const TrackedTime& time_posted,
447 const TrackedTime& start_of_run,
448 const TrackedTime& end_of_run) {
449 if (!kTrackAllTaskObjects)
450 return; // Not compiled in.
451
452 // Even if we have been DEACTIVATED, we will process any pending births so
453 // that our data structures (which counted the outstanding births) remain
454 // consistent.
455 if (!birth)
456 return;
457
458 // TODO(jar): Support the option to coalesce all worker-thread activity under
459 // one ThreadData instance that uses locks to protect *all* access. This will
460 // reduce memory (making it provably bounded), but run incrementally slower
461 // (since we'll use locks on TallyBirth and TallyDeath). The good news is
462 // that the locks on TallyDeath will be *after* the worker thread has run, and
463 // hence nothing will be waiting for the completion (... besides some other
464 // thread that might like to run). Also, the worker threads tasks are
465 // generally longer, and hence the cost of the lock may perchance be amortized
466 // over the long task's lifetime.
467 ThreadData* current_thread_data = Get();
468 if (!current_thread_data)
469 return;
470
471 Duration queue_duration;
472 Duration run_duration;
473 if (!start_of_run.is_null()) {
474 queue_duration = start_of_run - time_posted;
475 if (!end_of_run.is_null())
476 run_duration = end_of_run - start_of_run;
477 }
397 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); 478 current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
398 } 479 }
399 480
400 // static 481 // static
401 ThreadData* ThreadData::first() { 482 ThreadData* ThreadData::first() {
402 base::AutoLock lock(list_lock_); 483 base::AutoLock lock(*list_lock_);
403 return all_thread_data_list_head_; 484 return all_thread_data_list_head_;
404 } 485 }
405 486
406 // This may be called from another thread. 487 // This may be called from another thread.
407 void ThreadData::SnapshotBirthMap(BirthMap *output) const { 488 void ThreadData::SnapshotBirthMap(BirthMap *output) const {
408 base::AutoLock lock(lock_); 489 base::AutoLock lock(lock_);
409 for (BirthMap::const_iterator it = birth_map_.begin(); 490 for (BirthMap::const_iterator it = birth_map_.begin();
410 it != birth_map_.end(); ++it) 491 it != birth_map_.end(); ++it)
411 (*output)[it->first] = it->second; 492 (*output)[it->first] = it->second;
412 } 493 }
(...skipping 19 matching lines...) Expand all
432 void ThreadData::Reset() { 513 void ThreadData::Reset() {
433 base::AutoLock lock(lock_); 514 base::AutoLock lock(lock_);
434 for (DeathMap::iterator it = death_map_.begin(); 515 for (DeathMap::iterator it = death_map_.begin();
435 it != death_map_.end(); ++it) 516 it != death_map_.end(); ++it)
436 it->second.Clear(); 517 it->second.Clear();
437 for (BirthMap::iterator it = birth_map_.begin(); 518 for (BirthMap::iterator it = birth_map_.begin();
438 it != birth_map_.end(); ++it) 519 it != birth_map_.end(); ++it)
439 it->second->Clear(); 520 it->second->Clear();
440 } 521 }
441 522
442 // static 523 bool ThreadData::Initialize() {
443 bool ThreadData::StartTracking(bool status) {
444 if (!kTrackAllTaskObjects) 524 if (!kTrackAllTaskObjects)
445 return false; // Not compiled in. 525 return false; // Not compiled in.
446 526 if (status_ != UNINITIALIZED)
447 // Do a bit of class initialization. 527 return true;
448 if (!unregistered_thread_data_pool_) { 528 // Initialize all leaking constants that are difficult to toggle in and out
449 ThreadDataPool* initial_pool = new ThreadDataPool; 529 // of existance.
450 { 530 // First call must be made when single threaded at startup.
451 base::AutoLock lock(list_lock_); 531 // Perform the "real" TLS initialization now, and leave it intact through
452 if (!unregistered_thread_data_pool_) {
453 unregistered_thread_data_pool_ = initial_pool;
454 initial_pool = NULL;
455 }
456 }
457 delete initial_pool; // In case it was not used.
458 }
459
460 // Perform the "real" initialization now, and leave it intact through
461 // process termination. 532 // process termination.
462 if (!tls_index_.initialized()) 533 if (!tls_index_.initialized()) // Testing may have initialized this.
463 tls_index_.Initialize(&ThreadData::OnThreadTermination); 534 tls_index_.Initialize(&ThreadData::OnThreadTermination);
464 DCHECK(tls_index_.initialized()); 535 DCHECK(tls_index_.initialized());
536 ThreadDataPool* pool = new ThreadDataPool;
537 // TODO(jar): A linker initialized spin lock would be much safer than this
538 // allocation, which relies on being called while single threaded.
539 if (!list_lock_) // In case testing deleted this.
540 list_lock_ = new base::Lock;
541 status_ = kInitialStartupState;
465 542
466 if (!status) { 543 base::AutoLock lock(*list_lock_);
467 base::AutoLock lock(list_lock_); 544 DCHECK_EQ(unregistered_thread_data_pool_,
468 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); 545 reinterpret_cast<ThreadDataPool*>(NULL));
469 status_ = SHUTDOWN; 546 unregistered_thread_data_pool_ = pool;
470 return true; 547 ++incarnation_counter_;
471 }
472 base::AutoLock lock(list_lock_);
473 DCHECK_EQ(UNINITIALIZED, status_);
474 status_ = ACTIVE;
475 return true; 548 return true;
476 } 549 }
477 550
478 // static 551 // static
479 bool ThreadData::IsActive() { 552 bool ThreadData::InitializeAndSetTrackingStatus(bool status) {
553 if (!Initialize()) // No-op if already initialized.
554 return false; // Not compiled in.
555
556 status_ = status ? ACTIVE : DEACTIVATED;
557 return true;
558 }
559
560 // static
561 bool ThreadData::tracking_status() {
480 return status_ == ACTIVE; 562 return status_ == ACTIVE;
481 } 563 }
482 564
483 // static 565 // static
484 base::TimeTicks ThreadData::Now() { 566 TrackedTime ThreadData::Now() {
485 if (kTrackAllTaskObjects && status_ == ACTIVE) 567 if (kTrackAllTaskObjects && tracking_status())
486 return base::TimeTicks::Now(); 568 return TrackedTime::Now();
487 return base::TimeTicks(); // Super fast when disabled, or not compiled in. 569 return TrackedTime(); // Super fast when disabled, or not compiled.
488 } 570 }
489 571
490 // static 572 // static
491 void ThreadData::ShutdownSingleThreadedCleanup() { 573 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
492 // This is only called from test code, where we need to cleanup so that 574 // This is only called from test code, where we need to cleanup so that
493 // additional tests can be run. 575 // additional tests can be run.
494 // We must be single threaded... but be careful anyway. 576 // We must be single threaded... but be careful anyway.
495 if (!StartTracking(false)) 577 if (!InitializeAndSetTrackingStatus(false))
496 return; 578 return;
497 ThreadData* thread_data_list; 579 ThreadData* thread_data_list;
498 ThreadDataPool* final_pool; 580 ThreadDataPool* final_pool;
499 { 581 {
500 base::AutoLock lock(list_lock_); 582 base::AutoLock lock(*list_lock_);
501 thread_data_list = all_thread_data_list_head_; 583 thread_data_list = all_thread_data_list_head_;
502 all_thread_data_list_head_ = NULL; 584 all_thread_data_list_head_ = NULL;
503 final_pool = unregistered_thread_data_pool_; 585 final_pool = unregistered_thread_data_pool_;
504 unregistered_thread_data_pool_ = NULL; 586 unregistered_thread_data_pool_ = NULL;
587 ++incarnation_counter_;
505 } 588 }
506 589
590 // Put most global static back in pristine shape.
591 thread_number_counter_ = 0;
592 tls_index_.Set(NULL);
593 status_ = UNINITIALIZED;
594
595 // To avoid any chance of racing in unit tests, which is the only place we
596 // call this function, we may sometimes leak all the data structures we
597 // recovered, as they may still be in use on threads from prior tests!
598 if (leak)
599 return;
600
601 // When we want to cleanup (on a single thread), here is what we do.
602
507 if (final_pool) { 603 if (final_pool) {
508 // The thread_data_list contains *all* the instances, and we'll use it to 604 // The thread_data_list contains *all* the instances, and we'll use it to
509 // delete them. This pool has pointers to some instances, and we just 605 // delete them. This pool has pointers to some instances, and we just
510 // have to drop those pointers (and not do the deletes here). 606 // have to drop those pointers (and not do the deletes here).
511 while (!final_pool->empty()) 607 while (!final_pool->empty())
512 final_pool->pop(); 608 final_pool->pop();
513 delete final_pool; 609 delete final_pool;
514 } 610 }
515 611
516 // Do actual recursive delete in all ThreadData instances. 612 // Do actual recursive delete in all ThreadData instances.
517 while (thread_data_list) { 613 while (thread_data_list) {
518 ThreadData* next_thread_data = thread_data_list; 614 ThreadData* next_thread_data = thread_data_list;
519 thread_data_list = thread_data_list->next(); 615 thread_data_list = thread_data_list->next();
520 616
521 for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); 617 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
522 next_thread_data->birth_map_.end() != it; ++it) 618 next_thread_data->birth_map_.end() != it; ++it)
523 delete it->second; // Delete the Birth Records. 619 delete it->second; // Delete the Birth Records.
524 next_thread_data->birth_map_.clear(); 620 next_thread_data->birth_map_.clear();
525 next_thread_data->death_map_.clear(); 621 next_thread_data->death_map_.clear();
526 delete next_thread_data; // Includes all Death Records. 622 delete next_thread_data; // Includes all Death Records.
527 } 623 }
528 // Put most global static back in pristine shape.
529 thread_number_counter_ = 0;
530 tls_index_.Set(NULL);
531 status_ = UNINITIALIZED;
532 } 624 }
533 625
534 //------------------------------------------------------------------------------ 626 //------------------------------------------------------------------------------
535 // Individual 3-tuple of birth (place and thread) along with death thread, and 627 // Individual 3-tuple of birth (place and thread) along with death thread, and
536 // the accumulated stats for instances (DeathData). 628 // the accumulated stats for instances (DeathData).
537 629
538 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, 630 Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
539 const ThreadData& death_thread, 631 const ThreadData& death_thread,
540 const DeathData& death_data) 632 const DeathData& death_data)
541 : birth_(&birth_on_thread), 633 : birth_(&birth_on_thread),
(...skipping 25 matching lines...) Expand all
567 base::DictionaryValue* dictionary = new base::DictionaryValue; 659 base::DictionaryValue* dictionary = new base::DictionaryValue;
568 dictionary->Set("death_data", death_data_.ToValue()); 660 dictionary->Set("death_data", death_data_.ToValue());
569 dictionary->Set("birth_thread", 661 dictionary->Set("birth_thread",
570 base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); 662 base::Value::CreateStringValue(birth_->birth_thread()->thread_name()));
571 dictionary->Set("death_thread", 663 dictionary->Set("death_thread",
572 base::Value::CreateStringValue(DeathThreadName())); 664 base::Value::CreateStringValue(DeathThreadName()));
573 dictionary->Set("location", birth_->location().ToValue()); 665 dictionary->Set("location", birth_->location().ToValue());
574 return dictionary; 666 return dictionary;
575 } 667 }
576 668
577 void Snapshot::Add(const Snapshot& other) {
578 death_data_.AddDeathData(other.death_data_);
579 }
580
581 //------------------------------------------------------------------------------ 669 //------------------------------------------------------------------------------
582 // DataCollector 670 // DataCollector
583 671
584 DataCollector::DataCollector() { 672 DataCollector::DataCollector() {
585 if (!ThreadData::IsActive()) 673 if (!kTrackAllTaskObjects)
586 return; 674 return; // Not compiled in.
587 675
588 // Get an unchanging copy of a ThreadData list. 676 // Get an unchanging copy of a ThreadData list.
589 ThreadData* my_list = ThreadData::first(); 677 ThreadData* my_list = ThreadData::first();
590 678
591 // Gather data serially. 679 // Gather data serially.
592 // This hackish approach *can* get some slighly corrupt tallies, as we are 680 // This hackish approach *can* get some slighly corrupt tallies, as we are
593 // grabbing values without the protection of a lock, but it has the advantage 681 // grabbing values without the protection of a lock, but it has the advantage
594 // of working even with threads that don't have message loops. If a user 682 // of working even with threads that don't have message loops. If a user
595 // sees any strangeness, they can always just run their stats gathering a 683 // sees any strangeness, they can always just run their stats gathering a
596 // second time. 684 // second time.
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
733 void Comparator::Clear() { 821 void Comparator::Clear() {
734 if (tiebreaker_) { 822 if (tiebreaker_) {
735 tiebreaker_->Clear(); 823 tiebreaker_->Clear();
736 delete tiebreaker_; 824 delete tiebreaker_;
737 tiebreaker_ = NULL; 825 tiebreaker_ = NULL;
738 } 826 }
739 use_tiebreaker_for_sort_only_ = false; 827 use_tiebreaker_for_sort_only_ = false;
740 selector_ = NIL; 828 selector_ = NIL;
741 } 829 }
742 830
831 // static
832 Comparator::Selector Comparator::FindSelector(const std::string& keyword) {
833 // Sorting and aggretation keywords, which specify how to sort the data, or
834 // can specify a required match from the specified field in the record.
835 if (0 == keyword.compare("count"))
836 return COUNT;
837 if (0 == keyword.compare("totalduration"))
838 return TOTAL_RUN_DURATION;
839 if (0 == keyword.compare("duration"))
840 return AVERAGE_RUN_DURATION;
841 if (0 == keyword.compare("totalqueueduration"))
842 return TOTAL_QUEUE_DURATION;
843 if (0 == keyword.compare("averagequeueduration"))
844 return AVERAGE_QUEUE_DURATION;
845 if (0 == keyword.compare("birth"))
846 return BIRTH_THREAD;
847 if (0 == keyword.compare("death"))
848 return DEATH_THREAD;
849 if (0 == keyword.compare("file"))
850 return BIRTH_FILE;
851 if (0 == keyword.compare("function"))
852 return BIRTH_FUNCTION;
853 if (0 == keyword.compare("line"))
854 return BIRTH_LINE;
855 if (0 == keyword.compare("reset"))
856 return RESET_ALL_DATA;
857 return UNKNOWN_KEYWORD;
858 }
859
743 bool Comparator::operator()(const Snapshot& left, 860 bool Comparator::operator()(const Snapshot& left,
744 const Snapshot& right) const { 861 const Snapshot& right) const {
745 switch (selector_) { 862 switch (selector_) {
746 case BIRTH_THREAD: 863 case BIRTH_THREAD:
747 if (left.birth_thread() != right.birth_thread() && 864 if (left.birth_thread() != right.birth_thread() &&
748 left.birth_thread()->thread_name() != 865 left.birth_thread()->thread_name() !=
749 right.birth_thread()->thread_name()) 866 right.birth_thread()->thread_name())
750 return left.birth_thread()->thread_name() < 867 return left.birth_thread()->thread_name() <
751 right.birth_thread()->thread_name(); 868 right.birth_thread()->thread_name();
752 break; 869 break;
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
949 if (!tiebreaker_) { 1066 if (!tiebreaker_) {
950 use_tiebreaker_for_sort_only_ = true; 1067 use_tiebreaker_for_sort_only_ = true;
951 tiebreaker_ = new Comparator; 1068 tiebreaker_ = new Comparator;
952 tiebreaker_->SetTiebreaker(selector, ""); 1069 tiebreaker_->SetTiebreaker(selector, "");
953 } else { 1070 } else {
954 tiebreaker_->SetSubgroupTiebreaker(selector); 1071 tiebreaker_->SetSubgroupTiebreaker(selector);
955 } 1072 }
956 } 1073 }
957 1074
958 void Comparator::ParseKeyphrase(const std::string& key_phrase) { 1075 void Comparator::ParseKeyphrase(const std::string& key_phrase) {
959 typedef std::map<const std::string, Selector> KeyMap;
960 static KeyMap key_map;
961 static bool initialized = false;
962 if (!initialized) {
963 initialized = true;
964 // Sorting and aggretation keywords, which specify how to sort the data, or
965 // can specify a required match from the specified field in the record.
966 key_map["count"] = COUNT;
967 key_map["totalduration"] = TOTAL_RUN_DURATION;
968 key_map["duration"] = AVERAGE_RUN_DURATION;
969 key_map["totalqueueduration"] = TOTAL_QUEUE_DURATION;
970 key_map["averagequeueduration"] = AVERAGE_QUEUE_DURATION;
971 key_map["birth"] = BIRTH_THREAD;
972 key_map["death"] = DEATH_THREAD;
973 key_map["file"] = BIRTH_FILE;
974 key_map["function"] = BIRTH_FUNCTION;
975 key_map["line"] = BIRTH_LINE;
976
977 // Immediate commands that do not involve setting sort order.
978 key_map["reset"] = RESET_ALL_DATA;
979 }
980
981 std::string required; 1076 std::string required;
982 // Watch for: "sort_key=value" as we parse. 1077 // Watch for: "sort_key=value" as we parse.
983 size_t equal_offset = key_phrase.find('=', 0); 1078 size_t equal_offset = key_phrase.find('=', 0);
984 if (key_phrase.npos != equal_offset) { 1079 if (key_phrase.npos != equal_offset) {
985 // There is a value that must be matched for the data to display. 1080 // There is a value that must be matched for the data to display.
986 required = key_phrase.substr(equal_offset + 1, key_phrase.npos); 1081 required = key_phrase.substr(equal_offset + 1, key_phrase.npos);
987 } 1082 }
988 std::string keyword(key_phrase.substr(0, equal_offset)); 1083 std::string keyword(key_phrase.substr(0, equal_offset));
989 keyword = StringToLowerASCII(keyword); 1084 keyword = StringToLowerASCII(keyword);
990 KeyMap::iterator it = key_map.find(keyword); 1085 Selector selector = FindSelector(keyword);
991 if (key_map.end() == it) 1086 if (selector == UNKNOWN_KEYWORD)
992 return; // Unknown keyword. 1087 return;
993 if (it->second == RESET_ALL_DATA) 1088 if (selector == RESET_ALL_DATA) {
994 ThreadData::ResetAllThreadData(); 1089 ThreadData::ResetAllThreadData();
995 else 1090 return;
996 SetTiebreaker(key_map[keyword], required); 1091 }
1092 SetTiebreaker(selector, required);
997 } 1093 }
998 1094
999 bool Comparator::ParseQuery(const std::string& query) { 1095 bool Comparator::ParseQuery(const std::string& query) {
1000 // Parse each keyphrase between consecutive slashes. 1096 // Parse each keyphrase between consecutive slashes.
1001 for (size_t i = 0; i < query.size();) { 1097 for (size_t i = 0; i < query.size();) {
1002 size_t slash_offset = query.find('/', i); 1098 size_t slash_offset = query.find('/', i);
1003 ParseKeyphrase(query.substr(i, slash_offset - i)); 1099 ParseKeyphrase(query.substr(i, slash_offset - i));
1004 if (query.npos == slash_offset) 1100 if (query.npos == slash_offset)
1005 break; 1101 break;
1006 i = slash_offset + 1; 1102 i = slash_offset + 1;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1068 (combined_selectors_ & BIRTH_THREAD) ? "*" : 1164 (combined_selectors_ & BIRTH_THREAD) ? "*" :
1069 sample.birth().birth_thread()->thread_name().c_str(), 1165 sample.birth().birth_thread()->thread_name().c_str(),
1070 (combined_selectors_ & DEATH_THREAD) ? "*" : 1166 (combined_selectors_ & DEATH_THREAD) ? "*" :
1071 sample.DeathThreadName().c_str()); 1167 sample.DeathThreadName().c_str());
1072 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), 1168 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
1073 !(combined_selectors_ & BIRTH_FUNCTION), 1169 !(combined_selectors_ & BIRTH_FUNCTION),
1074 output); 1170 output);
1075 } 1171 }
1076 1172
1077 } // namespace tracked_objects 1173 } // namespace tracked_objects
OLDNEW
« no previous file with comments | « base/tracked_objects.h ('k') | base/tracked_objects_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698