ipc/chromium/src/base/tracked_objects.cc

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:f50a989eb785
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/tracked_objects.h"
6
7 #include <math.h>
8
9 #include "base/string_util.h"
10
11 using base::TimeDelta;
12
13 namespace tracked_objects {
14
15 // A TLS slot to the TrackRegistry for the current thread.
16 // static
17 TLSSlot ThreadData::tls_index_(base::LINKER_INITIALIZED);
18
19 //------------------------------------------------------------------------------
20 // Death data tallies durations when a death takes place.
21
22 void DeathData::RecordDeath(const TimeDelta& duration) {
23 ++count_;
24 life_duration_ += duration;
25 int64_t milliseconds = duration.InMilliseconds();
26 square_duration_ += milliseconds * milliseconds;
27 }
28
29 int DeathData::AverageMsDuration() const {
30 return static_cast<int>(life_duration_.InMilliseconds() / count_);
31 }
32
33 double DeathData::StandardDeviation() const {
34 double average = AverageMsDuration();
35 double variance = static_cast<float>(square_duration_)/count_
36 - average * average;
37 return sqrt(variance);
38 }
39
40
41 void DeathData::AddDeathData(const DeathData& other) {
42 count_ += other.count_;
43 life_duration_ += other.life_duration_;
44 square_duration_ += other.square_duration_;
45 }
46
47 void DeathData::Write(std::string* output) const {
48 if (!count_)
49 return;
50 if (1 == count_)
51 StringAppendF(output, "(1)Life in %dms ", AverageMsDuration());
52 else
53 StringAppendF(output, "(%d)Lives %dms/life ", count_, AverageMsDuration());
54 }
55
56 void DeathData::Clear() {
57 count_ = 0;
58 life_duration_ = TimeDelta();
59 square_duration_ = 0;
60 }
61
62 //------------------------------------------------------------------------------
63
64 BirthOnThread::BirthOnThread(const Location& location)
65 : location_(location),
66 birth_thread_(ThreadData::current()) { }
67
68 //------------------------------------------------------------------------------
69 Births::Births(const Location& location)
70 : BirthOnThread(location),
71 birth_count_(0) { }
72
73 //------------------------------------------------------------------------------
74 // ThreadData maintains the central data for all births and death.
75
76 // static
77 ThreadData* ThreadData::first_ = NULL;
78 // static
79 Lock ThreadData::list_lock_;
80
81 // static
82 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
83
84 ThreadData::ThreadData() : next_(NULL), message_loop_(MessageLoop::current()) {}
85
86 // static
87 ThreadData* ThreadData::current() {
88 if (!tls_index_.initialized())
89 return NULL;
90
91 ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get());
92 if (!registry) {
93 // We have to create a new registry for ThreadData.
94 bool too_late_to_create = false;
95 {
96 registry = new ThreadData;
97 AutoLock lock(list_lock_);
98 // Use lock to insure we have most recent status.
99 if (!IsActive()) {
100 too_late_to_create = true;
101 } else {
102 // Use lock to insert into list.
103 registry->next_ = first_;
104 first_ = registry;
105 }
106 } // Release lock.
107 if (too_late_to_create) {
108 delete registry;
109 registry = NULL;
110 } else {
111 tls_index_.Set(registry);
112 }
113 }
114 return registry;
115 }
116
117 Births* ThreadData::FindLifetime(const Location& location) {
118 if (!message_loop_) // In case message loop wasn't yet around...
119 message_loop_ = MessageLoop::current(); // Find it now.
120
121 BirthMap::iterator it = birth_map_.find(location);
122 if (it != birth_map_.end())
123 return it->second;
124 Births* tracker = new Births(location);
125
126 // Lock since the map may get relocated now, and other threads sometimes
127 // snapshot it (but they lock before copying it).
128 AutoLock lock(lock_);
129 birth_map_[location] = tracker;
130 return tracker;
131 }
132
133 void ThreadData::TallyADeath(const Births& lifetimes,
134 const TimeDelta& duration) {
135 if (!message_loop_) // In case message loop wasn't yet around...
136 message_loop_ = MessageLoop::current(); // Find it now.
137
138 DeathMap::iterator it = death_map_.find(&lifetimes);
139 if (it != death_map_.end()) {
140 it->second.RecordDeath(duration);
141 return;
142 }
143
144 AutoLock lock(lock_); // Lock since the map may get relocated now.
145 death_map_[&lifetimes].RecordDeath(duration);
146 }
147
148 // static
149 ThreadData* ThreadData::first() {
150 AutoLock lock(list_lock_);
151 return first_;
152 }
153
154 const std::string ThreadData::ThreadName() const {
155 if (message_loop_)
156 return message_loop_->thread_name();
157 return "ThreadWithoutMessageLoop";
158 }
159
160 // This may be called from another thread.
161 void ThreadData::SnapshotBirthMap(BirthMap *output) const {
162 AutoLock lock(*const_cast<Lock*>(&lock_));
163 for (BirthMap::const_iterator it = birth_map_.begin();
164 it != birth_map_.end(); ++it)
165 (*output)[it->first] = it->second;
166 }
167
168 // This may be called from another thread.
169 void ThreadData::SnapshotDeathMap(DeathMap *output) const {
170 AutoLock lock(*const_cast<Lock*>(&lock_));
171 for (DeathMap::const_iterator it = death_map_.begin();
172 it != death_map_.end(); ++it)
173 (*output)[it->first] = it->second;
174 }
175
176 #ifdef OS_WIN
177 void ThreadData::RunOnAllThreads(void (*function)()) {
178 ThreadData* list = first(); // Get existing list.
179
180 std::vector<MessageLoop*> message_loops;
181 for (ThreadData* it = list; it; it = it->next()) {
182 if (current() != it && it->message_loop())
183 message_loops.push_back(it->message_loop());
184 }
185
186 ThreadSafeDownCounter* counter =
187 new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us!
188
189 HANDLE completion_handle = CreateEvent(NULL, false, false, NULL);
190 // Tell all other threads to run.
191 for (size_t i = 0; i < message_loops.size(); ++i)
192 message_loops[i]->PostTask(FROM_HERE,
193 new RunTheStatic(function, completion_handle, counter));
194
195 // Also run Task on our thread.
196 RunTheStatic local_task(function, completion_handle, counter);
197 local_task.Run();
198
199 WaitForSingleObject(completion_handle, INFINITE);
200 int ret_val = CloseHandle(completion_handle);
201 DCHECK(ret_val);
202 }
203 #endif
204
205 // static
206 bool ThreadData::StartTracking(bool status) {
207 #ifndef TRACK_ALL_TASK_OBJECTS
208 return false; // Not compiled in.
209 #else
210 if (!status) {
211 AutoLock lock(list_lock_);
212 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN);
213 status_ = SHUTDOWN;
214 return true;
215 }
216 AutoLock lock(list_lock_);
217 DCHECK(status_ == UNINITIALIZED);
218 CHECK(tls_index_.Initialize(NULL));
219 status_ = ACTIVE;
220 return true;
221 #endif
222 }
223
224 // static
225 bool ThreadData::IsActive() {
226 return status_ == ACTIVE;
227 }
228
229 #ifdef OS_WIN
230 // static
231 void ThreadData::ShutdownMultiThreadTracking() {
232 // Using lock, guarantee that no new ThreadData instances will be created.
233 if (!StartTracking(false))
234 return;
235
236 RunOnAllThreads(ShutdownDisablingFurtherTracking);
237
238 // Now the *only* threads that might change the database are the threads with
239 // no messages loops. They might still be adding data to their birth records,
240 // but since no objects are deleted on those threads, there will be no further
241 // access to to cross-thread data.
242 // We could do a cleanup on all threads except for the ones without
243 // MessageLoops, but we won't bother doing cleanup (destruction of data) yet.
244 return;
245 }
246 #endif
247
248 // static
249 void ThreadData::ShutdownSingleThreadedCleanup() {
250 // We must be single threaded... but be careful anyway.
251 if (!StartTracking(false))
252 return;
253 ThreadData* thread_data_list;
254 {
255 AutoLock lock(list_lock_);
256 thread_data_list = first_;
257 first_ = NULL;
258 }
259
260 while (thread_data_list) {
261 ThreadData* next_thread_data = thread_data_list;
262 thread_data_list = thread_data_list->next();
263
264 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
265 next_thread_data->birth_map_.end() != it; ++it)
266 delete it->second; // Delete the Birth Records.
267 next_thread_data->birth_map_.clear();
268 next_thread_data->death_map_.clear();
269 delete next_thread_data; // Includes all Death Records.
270 }
271
272 CHECK(tls_index_.initialized());
273 tls_index_.Free();
274 DCHECK(!tls_index_.initialized());
275 status_ = UNINITIALIZED;
276 }
277
278 // static
279 void ThreadData::ShutdownDisablingFurtherTracking() {
280 // Redundantly set status SHUTDOWN on this thread.
281 if (!StartTracking(false))
282 return;
283 }
284
285
286 //------------------------------------------------------------------------------
287
288 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
289 : remaining_count_(count) {
290 DCHECK(remaining_count_ > 0);
291 }
292
293 bool ThreadData::ThreadSafeDownCounter::LastCaller() {
294 {
295 AutoLock lock(lock_);
296 if (--remaining_count_)
297 return false;
298 } // Release lock, so we can delete everything in this instance.
299 delete this;
300 return true;
301 }
302
303 //------------------------------------------------------------------------------
304 #ifdef OS_WIN
305 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function,
306 HANDLE completion_handle,
307 ThreadSafeDownCounter* counter)
308 : function_(function),
309 completion_handle_(completion_handle),
310 counter_(counter) {
311 }
312
313 void ThreadData::RunTheStatic::Run() {
314 function_();
315 if (counter_->LastCaller())
316 SetEvent(completion_handle_);
317 }
318 #endif
319
320 //------------------------------------------------------------------------------
321 // Individual 3-tuple of birth (place and thread) along with death thread, and
322 // the accumulated stats for instances (DeathData).
323
324 Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
325 const ThreadData& death_thread,
326 const DeathData& death_data)
327 : birth_(&birth_on_thread),
328 death_thread_(&death_thread),
329 death_data_(death_data) {
330 }
331
332 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
333 : birth_(&birth_on_thread),
334 death_thread_(NULL),
335 death_data_(DeathData(count)) {
336 }
337
338 const std::string Snapshot::DeathThreadName() const {
339 if (death_thread_)
340 return death_thread_->ThreadName();
341 return "Still_Alive";
342 }
343
344 void Snapshot::Write(std::string* output) const {
345 death_data_.Write(output);
346 StringAppendF(output, "%s->%s ",
347 birth_->birth_thread()->ThreadName().c_str(),
348 death_thread_->ThreadName().c_str());
349 birth_->location().Write(true, true, output);
350 }
351
352 void Snapshot::Add(const Snapshot& other) {
353 death_data_.AddDeathData(other.death_data_);
354 }
355
356 //------------------------------------------------------------------------------
357 // DataCollector
358
359 DataCollector::DataCollector() {
360 DCHECK(ThreadData::IsActive());
361
362 ThreadData* my_list = ThreadData::current()->first();
363
364 count_of_contributing_threads_ = 0;
365 for (ThreadData* thread_data = my_list;
366 thread_data;
367 thread_data = thread_data->next()) {
368 ++count_of_contributing_threads_;
369 }
370
371 // Gather data serially. A different constructor could be used to do in
372 // parallel, and then invoke an OnCompletion task.
373 for (ThreadData* thread_data = my_list;
374 thread_data;
375 thread_data = thread_data->next()) {
376 Append(*thread_data);
377 }
378 }
379
380 void DataCollector::Append(const ThreadData& thread_data) {
381 // Get copy of data (which is done under ThreadData's lock).
382 ThreadData::BirthMap birth_map;
383 thread_data.SnapshotBirthMap(&birth_map);
384 ThreadData::DeathMap death_map;
385 thread_data.SnapshotDeathMap(&death_map);
386
387 // Use our lock to protect our accumulation activity.
388 AutoLock lock(accumulation_lock_);
389
390 DCHECK(count_of_contributing_threads_);
391
392 for (ThreadData::DeathMap::const_iterator it = death_map.begin();
393 it != death_map.end(); ++it) {
394 collection_.push_back(Snapshot(*it->first, thread_data, it->second));
395 global_birth_count_[it->first] -= it->first->birth_count();
396 }
397
398 for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
399 it != birth_map.end(); ++it) {
400 global_birth_count_[it->second] += it->second->birth_count();
401 }
402
403 --count_of_contributing_threads_;
404 }
405
406 DataCollector::Collection* DataCollector::collection() {
407 DCHECK(!count_of_contributing_threads_);
408 return &collection_;
409 }
410
411 void DataCollector::AddListOfLivingObjects() {
412 DCHECK(!count_of_contributing_threads_);
413 for (BirthCount::iterator it = global_birth_count_.begin();
414 it != global_birth_count_.end(); ++it) {
415 if (it->second > 0)
416 collection_.push_back(Snapshot(*it->first, it->second));
417 }
418 }
419
420 //------------------------------------------------------------------------------
421 // Aggregation
422
423 void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
424 AddBirth(snapshot.birth());
425 death_threads_[snapshot.death_thread()]++;
426 AddDeathData(snapshot.death_data());
427 }
428
429 void Aggregation::AddBirths(const Births& births) {
430 AddBirth(births);
431 birth_count_ += births.birth_count();
432 }
433 void Aggregation::AddBirth(const BirthOnThread& birth) {
434 AddBirthPlace(birth.location());
435 birth_threads_[birth.birth_thread()]++;
436 }
437
438 void Aggregation::AddBirthPlace(const Location& location) {
439 locations_[location]++;
440 birth_files_[location.file_name()]++;
441 }
442
443 void Aggregation::Write(std::string* output) const {
444 if (locations_.size() == 1) {
445 locations_.begin()->first.Write(true, true, output);
446 } else {
447 StringAppendF(output, "%d Locations. ", locations_.size());
448 if (birth_files_.size() > 1)
449 StringAppendF(output, "%d Files. ", birth_files_.size());
450 else
451 StringAppendF(output, "All born in %s. ",
452 birth_files_.begin()->first.c_str());
453 }
454
455 if (birth_threads_.size() > 1)
456 StringAppendF(output, "%d BirthingThreads. ", birth_threads_.size());
457 else
458 StringAppendF(output, "All born on %s. ",
459 birth_threads_.begin()->first->ThreadName().c_str());
460
461 if (death_threads_.size() > 1) {
462 StringAppendF(output, "%d DeathThreads. ", death_threads_.size());
463 } else {
464 if (death_threads_.begin()->first)
465 StringAppendF(output, "All deleted on %s. ",
466 death_threads_.begin()->first->ThreadName().c_str());
467 else
468 output->append("All these objects are still alive.");
469 }
470
471 if (birth_count_ > 1)
472 StringAppendF(output, "Births=%d ", birth_count_);
473
474 DeathData::Write(output);
475 }
476
477 void Aggregation::Clear() {
478 birth_count_ = 0;
479 birth_files_.clear();
480 locations_.clear();
481 birth_threads_.clear();
482 DeathData::Clear();
483 death_threads_.clear();
484 }
485
486 //------------------------------------------------------------------------------
487 // Comparison object for sorting.
488
489 Comparator::Comparator()
490 : selector_(NIL),
491 tiebreaker_(NULL),
492 combined_selectors_(0),
493 use_tiebreaker_for_sort_only_(false) {}
494
495 void Comparator::Clear() {
496 if (tiebreaker_) {
497 tiebreaker_->Clear();
498 delete tiebreaker_;
499 tiebreaker_ = NULL;
500 }
501 use_tiebreaker_for_sort_only_ = false;
502 selector_ = NIL;
503 }
504
505 void Comparator::Sort(DataCollector::Collection* collection) const {
506 std::sort(collection->begin(), collection->end(), *this);
507 }
508
509
510 bool Comparator::operator()(const Snapshot& left,
511 const Snapshot& right) const {
512 switch (selector_) {
513 case BIRTH_THREAD:
514 if (left.birth_thread() != right.birth_thread() &&
515 left.birth_thread()->ThreadName() !=
516 right.birth_thread()->ThreadName())
517 return left.birth_thread()->ThreadName() <
518 right.birth_thread()->ThreadName();
519 break;
520
521 case DEATH_THREAD:
522 if (left.death_thread() != right.death_thread() &&
523 left.DeathThreadName() !=
524 right.DeathThreadName()) {
525 if (!left.death_thread())
526 return true;
527 if (!right.death_thread())
528 return false;
529 return left.DeathThreadName() <
530 right.DeathThreadName();
531 }
532 break;
533
534 case BIRTH_FILE:
535 if (left.location().file_name() != right.location().file_name()) {
536 int comp = strcmp(left.location().file_name(),
537 right.location().file_name());
538 if (comp)
539 return 0 > comp;
540 }
541 break;
542
543 case BIRTH_FUNCTION:
544 if (left.location().function_name() != right.location().function_name()) {
545 int comp = strcmp(left.location().function_name(),
546 right.location().function_name());
547 if (comp)
548 return 0 > comp;
549 }
550 break;
551
552 case BIRTH_LINE:
553 if (left.location().line_number() != right.location().line_number())
554 return left.location().line_number() <
555 right.location().line_number();
556 break;
557
558 case COUNT:
559 if (left.count() != right.count())
560 return left.count() > right.count(); // Sort large at front of vector.
561 break;
562
563 case AVERAGE_DURATION:
564 if (left.AverageMsDuration() != right.AverageMsDuration())
565 return left.AverageMsDuration() > right.AverageMsDuration();
566 break;
567
568 default:
569 break;
570 }
571 if (tiebreaker_)
572 return tiebreaker_->operator()(left, right);
573 return false;
574 }
575
576 bool Comparator::Equivalent(const Snapshot& left,
577 const Snapshot& right) const {
578 switch (selector_) {
579 case BIRTH_THREAD:
580 if (left.birth_thread() != right.birth_thread() &&
581 left.birth_thread()->ThreadName() !=
582 right.birth_thread()->ThreadName())
583 return false;
584 break;
585
586 case DEATH_THREAD:
587 if (left.death_thread() != right.death_thread() &&
588 left.DeathThreadName() != right.DeathThreadName())
589 return false;
590 break;
591
592 case BIRTH_FILE:
593 if (left.location().file_name() != right.location().file_name()) {
594 int comp = strcmp(left.location().file_name(),
595 right.location().file_name());
596 if (comp)
597 return false;
598 }
599 break;
600
601 case BIRTH_FUNCTION:
602 if (left.location().function_name() != right.location().function_name()) {
603 int comp = strcmp(left.location().function_name(),
604 right.location().function_name());
605 if (comp)
606 return false;
607 }
608 break;
609
610 case COUNT:
611 if (left.count() != right.count())
612 return false;
613 break;
614
615 case AVERAGE_DURATION:
616 if (left.life_duration() != right.life_duration())
617 return false;
618 break;
619
620 default:
621 break;
622 }
623 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
624 return tiebreaker_->Equivalent(left, right);
625 return true;
626 }
627
628 bool Comparator::Acceptable(const Snapshot& sample) const {
629 if (required_.size()) {
630 switch (selector_) {
631 case BIRTH_THREAD:
632 if (sample.birth_thread()->ThreadName().find(required_) ==
633 std::string::npos)
634 return false;
635 break;
636
637 case DEATH_THREAD:
638 if (sample.DeathThreadName().find(required_) == std::string::npos)
639 return false;
640 break;
641
642 case BIRTH_FILE:
643 if (!strstr(sample.location().file_name(), required_.c_str()))
644 return false;
645 break;
646
647 case BIRTH_FUNCTION:
648 if (!strstr(sample.location().function_name(), required_.c_str()))
649 return false;
650 break;
651
652 default:
653 break;
654 }
655 }
656 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
657 return tiebreaker_->Acceptable(sample);
658 return true;
659 }
660
661 void Comparator::SetTiebreaker(Selector selector, const std::string required) {
662 if (selector == selector_ || NIL == selector)
663 return;
664 combined_selectors_ |= selector;
665 if (NIL == selector_) {
666 selector_ = selector;
667 if (required.size())
668 required_ = required;
669 return;
670 }
671 if (tiebreaker_) {
672 if (use_tiebreaker_for_sort_only_) {
673 Comparator* temp = new Comparator;
674 temp->tiebreaker_ = tiebreaker_;
675 tiebreaker_ = temp;
676 }
677 } else {
678 tiebreaker_ = new Comparator;
679 DCHECK(!use_tiebreaker_for_sort_only_);
680 }
681 tiebreaker_->SetTiebreaker(selector, required);
682 }
683
684 bool Comparator::IsGroupedBy(Selector selector) const {
685 return 0 != (selector & combined_selectors_);
686 }
687
688 void Comparator::SetSubgroupTiebreaker(Selector selector) {
689 if (selector == selector_ || NIL == selector)
690 return;
691 if (!tiebreaker_) {
692 use_tiebreaker_for_sort_only_ = true;
693 tiebreaker_ = new Comparator;
694 tiebreaker_->SetTiebreaker(selector, "");
695 } else {
696 tiebreaker_->SetSubgroupTiebreaker(selector);
697 }
698 }
699
700 bool Comparator::WriteSortGrouping(const Snapshot& sample,
701 std::string* output) const {
702 bool wrote_data = false;
703 switch (selector_) {
704 case BIRTH_THREAD:
705 StringAppendF(output, "All new on %s ",
706 sample.birth_thread()->ThreadName().c_str());
707 wrote_data = true;
708 break;
709
710 case DEATH_THREAD:
711 if (sample.death_thread())
712 StringAppendF(output, "All deleted on %s ",
713 sample.DeathThreadName().c_str());
714 else
715 output->append("All still alive ");
716 wrote_data = true;
717 break;
718
719 case BIRTH_FILE:
720 StringAppendF(output, "All born in %s ",
721 sample.location().file_name());
722 break;
723
724 case BIRTH_FUNCTION:
725 output->append("All born in ");
726 sample.location().WriteFunctionName(output);
727 output->push_back(' ');
728 break;
729
730 default:
731 break;
732 }
733 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
734 wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
735 }
736 return wrote_data;
737 }
738
739 void Comparator::WriteSnapshot(const Snapshot& sample,
740 std::string* output) const {
741 sample.death_data().Write(output);
742 if (!(combined_selectors_ & BIRTH_THREAD) ||
743 !(combined_selectors_ & DEATH_THREAD))
744 StringAppendF(output, "%s->%s ",
745 (combined_selectors_ & BIRTH_THREAD) ? "*" :
746 sample.birth().birth_thread()->ThreadName().c_str(),
747 (combined_selectors_ & DEATH_THREAD) ? "*" :
748 sample.DeathThreadName().c_str());
749 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
750 !(combined_selectors_ & BIRTH_FUNCTION),
751 output);
752 }
753
754 } // namespace tracked_objects

mercurial