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

Side by Side Diff: base/tracked_objects.cc

Issue 8606001: Check that thread contexts are cleaned up during profiling (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') | chrome/browser/browser_process_impl.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/stringprintf.h" 11 #include "base/stringprintf.h"
12 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
13 #include "build/build_config.h" 13 #include "build/build_config.h"
14 #include "base/port.h" 14 #include "base/port.h"
15 15
16 #include "base/win/dllmain.cc"
rvargas (doing something else) 2011/11/24 00:09:21 definitely not cool.
17
16 using base::TimeDelta; 18 using base::TimeDelta;
17 19
18 namespace tracked_objects { 20 namespace tracked_objects {
19 21
20 namespace { 22 namespace {
21 // Flag to compile out almost all of the task tracking code. 23 // Flag to compile out almost all of the task tracking code.
22 static const bool kTrackAllTaskObjects = true; 24 static const bool kTrackAllTaskObjects = true;
23 25
24 // When ThreadData is first initialized, should we start in an ACTIVE state to 26 // When ThreadData is first initialized, should we start in an ACTIVE state to
25 // record all of the startup-time tasks, or should we start up DEACTIVATED, so 27 // record all of the startup-time tasks, or should we start up DEACTIVATED, so
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 // TODO(jar): We should pull all these static vars together, into a struct, and 119 // TODO(jar): We should pull all these static vars together, into a struct, and
118 // optimize layout so that we benefit from locality of reference during accesses 120 // optimize layout so that we benefit from locality of reference during accesses
119 // to them. 121 // to them.
120 122
121 // A TLS slot which points to the ThreadData instance for the current thread. We 123 // A TLS slot which points to the ThreadData instance for the current thread. We
122 // do a fake initialization here (zeroing out data), and then the real in-place 124 // do a fake initialization here (zeroing out data), and then the real in-place
123 // construction happens when we call tls_index_.Initialize(). 125 // construction happens when we call tls_index_.Initialize().
124 // static 126 // static
125 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); 127 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED);
126 128
127 // A lock-protected counter to assign sequence number to threads.
128 // static 129 // static
129 int ThreadData::thread_number_counter_ = 0; 130 int ThreadData::worker_thread_data_creation_count_ = 0;
131
132 // static
133 int ThreadData::cleanup_count_ = 0;
130 134
131 // static 135 // static
132 int ThreadData::incarnation_counter_ = 0; 136 int ThreadData::incarnation_counter_ = 0;
133 137
134 // static 138 // static
135 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; 139 ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
136 140
137 // static 141 // static
138 ThreadData* ThreadData::first_retired_worker_ = NULL; 142 ThreadData* ThreadData::first_retired_worker_ = NULL;
139 143
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 // static 194 // static
191 ThreadData* ThreadData::Get() { 195 ThreadData* ThreadData::Get() {
192 if (!tls_index_.initialized()) 196 if (!tls_index_.initialized())
193 return NULL; // For unittests only. 197 return NULL; // For unittests only.
194 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); 198 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get());
195 if (registered) 199 if (registered)
196 return registered; 200 return registered;
197 201
198 // We must be a worker thread, since we didn't pre-register. 202 // We must be a worker thread, since we didn't pre-register.
199 ThreadData* worker_thread_data = NULL; 203 ThreadData* worker_thread_data = NULL;
200 int thread_number = 0; 204 int worker_thread_number = 0;
201 { 205 {
202 base::AutoLock lock(*list_lock_.Pointer()); 206 base::AutoLock lock(*list_lock_.Pointer());
203 if (first_retired_worker_) { 207 if (first_retired_worker_) {
204 worker_thread_data = first_retired_worker_; 208 worker_thread_data = first_retired_worker_;
205 first_retired_worker_ = first_retired_worker_->next_retired_worker_; 209 first_retired_worker_ = first_retired_worker_->next_retired_worker_;
206 worker_thread_data->next_retired_worker_ = NULL; 210 worker_thread_data->next_retired_worker_ = NULL;
207 } else { 211 } else {
208 thread_number = ++thread_number_counter_; 212 worker_thread_number = ++worker_thread_data_creation_count_;
209 } 213 }
210 } 214 }
211 215
212 // If we can't find a previously used instance, then we have to create one. 216 // If we can't find a previously used instance, then we have to create one.
213 if (!worker_thread_data) 217 if (!worker_thread_data) {
214 worker_thread_data = new ThreadData(thread_number); 218 DCHECK_GT(worker_thread_number, 0);
219 worker_thread_data = new ThreadData(worker_thread_number);
220 }
215 DCHECK_GT(worker_thread_data->worker_thread_number_, 0); 221 DCHECK_GT(worker_thread_data->worker_thread_number_, 0);
216 222
217 tls_index_.Set(worker_thread_data); 223 tls_index_.Set(worker_thread_data);
218 return worker_thread_data; 224 return worker_thread_data;
219 } 225 }
220 226
221 // static 227 // static
222 void ThreadData::OnThreadTermination(void* thread_data) { 228 void ThreadData::OnThreadTermination(void* thread_data) {
229 // We must NOT do any allocations during this callback. There is a chance
230 // that the allocator is no longer active on this thread.
223 if (!kTrackAllTaskObjects) 231 if (!kTrackAllTaskObjects)
224 return; // Not compiled in. 232 return; // Not compiled in.
225 if (!thread_data) 233 if (!thread_data)
226 return; 234 return;
227 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); 235 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup();
228 } 236 }
229 237
230 void ThreadData::OnThreadTerminationCleanup() { 238 void ThreadData::OnThreadTerminationCleanup() {
231 if (!worker_thread_number_) 239 // The list_lock_ was created when we registered the callback, so it won't be
232 return; 240 // allocated here despite the lazy reference.
233 base::AutoLock lock(*list_lock_.Pointer()); 241 base::AutoLock lock(*list_lock_.Pointer());
234 if (incarnation_counter_ != incarnation_count_for_pool_) 242 if (incarnation_counter_ != incarnation_count_for_pool_)
235 return; // ThreadData was constructed in an earlier unit test. 243 return; // ThreadData was constructed in an earlier unit test.
244 ++cleanup_count_;
245 // Only worker threads need to be retired and reused.
246 if (!worker_thread_number_) {
247 return;
248 }
236 // We must NOT do any allocations during this callback. 249 // We must NOT do any allocations during this callback.
237 // Using the simple linked lists avoids all allocations. 250 // Using the simple linked lists avoids all allocations.
238 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL)); 251 DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL));
239 this->next_retired_worker_ = first_retired_worker_; 252 this->next_retired_worker_ = first_retired_worker_;
240 first_retired_worker_ = this; 253 first_retired_worker_ = this;
241 } 254 }
242 255
243 // static 256 // static
244 base::DictionaryValue* ThreadData::ToValue() { 257 base::DictionaryValue* ThreadData::ToValue() {
245 DataCollector collected_data; // Gather data. 258 DataCollector collected_data; // Gather data.
246 collected_data.AddListOfLivingObjects(); // Add births that are still alive. 259 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
247 base::ListValue* list = collected_data.ToValue(); 260 base::ListValue* list = collected_data.ToValue();
248 base::DictionaryValue* dictionary = new base::DictionaryValue(); 261 base::DictionaryValue* dictionary = new base::DictionaryValue();
249 dictionary->Set("list", list); 262 dictionary->Set("list", list);
250 return dictionary; 263 return dictionary;
251 } 264 }
252 265
253 Births* ThreadData::TallyABirth(const Location& location) { 266 Births* ThreadData::TallyABirth(const Location& location) {
254 BirthMap::iterator it = birth_map_.find(location); 267 BirthMap::iterator it = birth_map_.find(location);
255 if (it != birth_map_.end()) { 268 if (it != birth_map_.end()) {
256 it->second->RecordBirth(); 269 it->second->RecordBirth();
257 return it->second; 270 return it->second;
258 } 271 }
259 272
260 Births* tracker = new Births(location, *this); 273 Births* tracker = new Births(location, *this);
261 // Lock since the map may get relocated now, and other threads sometimes 274 // Lock since the map may get relocated now, and other threads sometimes
262 // snapshot it (but they lock before copying it). 275 // snapshot it (but they lock before copying it).
263 base::AutoLock lock(lock_); 276 base::AutoLock lock(map_lock_);
264 birth_map_[location] = tracker; 277 birth_map_[location] = tracker;
265 return tracker; 278 return tracker;
266 } 279 }
267 280
268 void ThreadData::TallyADeath(const Births& birth, 281 void ThreadData::TallyADeath(const Births& birth,
269 DurationInt queue_duration, 282 DurationInt queue_duration,
270 DurationInt run_duration) { 283 DurationInt run_duration) {
271 DeathMap::iterator it = death_map_.find(&birth); 284 DeathMap::iterator it = death_map_.find(&birth);
272 DeathData* death_data; 285 DeathData* death_data;
273 if (it != death_map_.end()) { 286 if (it != death_map_.end()) {
274 death_data = &it->second; 287 death_data = &it->second;
275 } else { 288 } else {
276 base::AutoLock lock(lock_); // Lock since the map may get relocated now. 289 base::AutoLock lock(map_lock_); // Lock as the map may get relocated now.
277 death_data = &death_map_[&birth]; 290 death_data = &death_map_[&birth];
278 } // Release lock ASAP. 291 } // Release lock ASAP.
279 death_data->RecordDeath(queue_duration, run_duration); 292 death_data->RecordDeath(queue_duration, run_duration);
280 } 293 }
281 294
282 // static 295 // static
283 Births* ThreadData::TallyABirthIfActive(const Location& location) { 296 Births* ThreadData::TallyABirthIfActive(const Location& location) {
284 if (!kTrackAllTaskObjects) 297 if (!kTrackAllTaskObjects)
285 return NULL; // Not compiled in. 298 return NULL; // Not compiled in.
286 299
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 } 412 }
400 413
401 // static 414 // static
402 ThreadData* ThreadData::first() { 415 ThreadData* ThreadData::first() {
403 base::AutoLock lock(*list_lock_.Pointer()); 416 base::AutoLock lock(*list_lock_.Pointer());
404 return all_thread_data_list_head_; 417 return all_thread_data_list_head_;
405 } 418 }
406 419
407 // This may be called from another thread. 420 // This may be called from another thread.
408 void ThreadData::SnapshotBirthMap(BirthMap *output) const { 421 void ThreadData::SnapshotBirthMap(BirthMap *output) const {
409 base::AutoLock lock(lock_); 422 base::AutoLock lock(map_lock_);
410 for (BirthMap::const_iterator it = birth_map_.begin(); 423 for (BirthMap::const_iterator it = birth_map_.begin();
411 it != birth_map_.end(); ++it) 424 it != birth_map_.end(); ++it)
412 (*output)[it->first] = it->second; 425 (*output)[it->first] = it->second;
413 } 426 }
414 427
415 // This may be called from another thread. 428 // This may be called from another thread.
416 void ThreadData::SnapshotDeathMap(DeathMap *output) const { 429 void ThreadData::SnapshotDeathMap(DeathMap *output) const {
417 base::AutoLock lock(lock_); 430 base::AutoLock lock(map_lock_);
418 for (DeathMap::const_iterator it = death_map_.begin(); 431 for (DeathMap::const_iterator it = death_map_.begin();
419 it != death_map_.end(); ++it) 432 it != death_map_.end(); ++it)
420 (*output)[it->first] = it->second; 433 (*output)[it->first] = it->second;
421 } 434 }
422 435
423 // static 436 // static
424 void ThreadData::ResetAllThreadData() { 437 void ThreadData::ResetAllThreadData() {
425 ThreadData* my_list = first(); 438 ThreadData* my_list = first();
426 439
427 for (ThreadData* thread_data = my_list; 440 for (ThreadData* thread_data = my_list;
428 thread_data; 441 thread_data;
429 thread_data = thread_data->next()) 442 thread_data = thread_data->next())
430 thread_data->Reset(); 443 thread_data->Reset();
431 } 444 }
432 445
433 void ThreadData::Reset() { 446 void ThreadData::Reset() {
434 base::AutoLock lock(lock_); 447 base::AutoLock lock(map_lock_);
435 for (DeathMap::iterator it = death_map_.begin(); 448 for (DeathMap::iterator it = death_map_.begin();
436 it != death_map_.end(); ++it) 449 it != death_map_.end(); ++it)
437 it->second.Clear(); 450 it->second.Clear();
438 for (BirthMap::iterator it = birth_map_.begin(); 451 for (BirthMap::iterator it = birth_map_.begin();
439 it != birth_map_.end(); ++it) 452 it != birth_map_.end(); ++it)
440 it->second->Clear(); 453 it->second->Clear();
441 } 454 }
442 455
443 bool ThreadData::Initialize() { 456 bool ThreadData::Initialize() {
444 if (!kTrackAllTaskObjects) 457 if (!kTrackAllTaskObjects)
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 } 513 }
501 514
502 // static 515 // static
503 TrackedTime ThreadData::Now() { 516 TrackedTime ThreadData::Now() {
504 if (kTrackAllTaskObjects && tracking_status()) 517 if (kTrackAllTaskObjects && tracking_status())
505 return TrackedTime::Now(); 518 return TrackedTime::Now();
506 return TrackedTime(); // Super fast when disabled, or not compiled. 519 return TrackedTime(); // Super fast when disabled, or not compiled.
507 } 520 }
508 521
509 // static 522 // static
523 void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) {
524 base::AutoLock lock(*list_lock_.Pointer());
525 if (worker_thread_data_creation_count_ == 0)
526 return; // We haven't really run much, and couldn't have leaked.
527 // Verify that we've at least shutdown/cleanup the major namesd threads. The
528 // caller should tell us how many thread shutdowns should have taken place by
529 // now.
530 return; // TODO(jar): until this is working on XP, don't run the real test.
531 CHECK_GT(cleanup_count_, major_threads_shutdown_count);
532 }
533
534 // static
510 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { 535 void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
511 // This is only called from test code, where we need to cleanup so that 536 // This is only called from test code, where we need to cleanup so that
512 // additional tests can be run. 537 // additional tests can be run.
513 // We must be single threaded... but be careful anyway. 538 // We must be single threaded... but be careful anyway.
514 if (!InitializeAndSetTrackingStatus(false)) 539 if (!InitializeAndSetTrackingStatus(false))
515 return; 540 return;
516 ThreadData* thread_data_list; 541 ThreadData* thread_data_list;
517 { 542 {
518 base::AutoLock lock(*list_lock_.Pointer()); 543 base::AutoLock lock(*list_lock_.Pointer());
519 thread_data_list = all_thread_data_list_head_; 544 thread_data_list = all_thread_data_list_head_;
520 all_thread_data_list_head_ = NULL; 545 all_thread_data_list_head_ = NULL;
521 ++incarnation_counter_; 546 ++incarnation_counter_;
522 // To be clean, break apart the retired worker list (though we leak them). 547 // To be clean, break apart the retired worker list (though we leak them).
523 while(first_retired_worker_) { 548 while(first_retired_worker_) {
524 ThreadData* worker = first_retired_worker_; 549 ThreadData* worker = first_retired_worker_;
525 CHECK_GT(worker->worker_thread_number_, 0); 550 CHECK_GT(worker->worker_thread_number_, 0);
526 first_retired_worker_ = worker->next_retired_worker_; 551 first_retired_worker_ = worker->next_retired_worker_;
527 worker->next_retired_worker_ = NULL; 552 worker->next_retired_worker_ = NULL;
528 } 553 }
529 } 554 }
530 555
531 // Put most global static back in pristine shape. 556 // Put most global static back in pristine shape.
532 thread_number_counter_ = 0; 557 worker_thread_data_creation_count_ = 0;
558 cleanup_count_ = 0;
533 tls_index_.Set(NULL); 559 tls_index_.Set(NULL);
534 status_ = UNINITIALIZED; 560 status_ = UNINITIALIZED;
535 561
536 // To avoid any chance of racing in unit tests, which is the only place we 562 // To avoid any chance of racing in unit tests, which is the only place we
537 // call this function, we may sometimes leak all the data structures we 563 // call this function, we may sometimes leak all the data structures we
538 // recovered, as they may still be in use on threads from prior tests! 564 // recovered, as they may still be in use on threads from prior tests!
539 if (leak) 565 if (leak)
540 return; 566 return;
541 567
542 // When we want to cleanup (on a single thread), here is what we do. 568 // When we want to cleanup (on a single thread), here is what we do.
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
649 675
650 base::ListValue* DataCollector::ToValue() const { 676 base::ListValue* DataCollector::ToValue() const {
651 base::ListValue* list = new base::ListValue; 677 base::ListValue* list = new base::ListValue;
652 for (size_t i = 0; i < collection_.size(); ++i) { 678 for (size_t i = 0; i < collection_.size(); ++i) {
653 list->Append(collection_[i].ToValue()); 679 list->Append(collection_[i].ToValue());
654 } 680 }
655 return list; 681 return list;
656 } 682 }
657 683
658 } // namespace tracked_objects 684 } // namespace tracked_objects
OLDNEW
« no previous file with comments | « base/tracked_objects.h ('k') | chrome/browser/browser_process_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698