blob: 7e4731042be95e5c3810c40aeb17bac4b068ecb7 [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// 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.
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>
8
initial.commit3f4a7322008-07-27 06:49:38 +09009#include "base/string_util.h"
10
11namespace tracked_objects {
12
evanm@google.comf26fd3a2008-08-21 07:54:52 +090013// A TLS slot to the TrackRegistry for the current thread.
initial.commit3f4a7322008-07-27 06:49:38 +090014// static
evanm@google.comf26fd3a2008-08-21 07:54:52 +090015TLSSlot ThreadData::tls_index_(base::LINKER_INITIALIZED);
initial.commit3f4a7322008-07-27 06:49:38 +090016
17//------------------------------------------------------------------------------
18// Death data tallies durations when a death takes place.
19
20void DeathData::RecordDeath(const TimeDelta& duration) {
21 ++count_;
22 life_duration_ += duration;
23 int64 milliseconds = duration.InMilliseconds();
24 square_duration_ += milliseconds * milliseconds;
25}
26
27int DeathData::AverageMsDuration() const {
28 return static_cast<int>(life_duration_.InMilliseconds() / count_);
29}
30
31double DeathData::StandardDeviation() const {
32 double average = AverageMsDuration();
33 double variance = static_cast<float>(square_duration_)/count_
34 - average * average;
35 return sqrt(variance);
36}
37
38
39void DeathData::AddDeathData(const DeathData& other) {
40 count_ += other.count_;
41 life_duration_ += other.life_duration_;
42 square_duration_ += other.square_duration_;
43}
44
45void DeathData::Write(std::string* output) const {
46 if (!count_)
47 return;
48 if (1 == count_)
49 StringAppendF(output, "(1)Life in %dms ", count_, AverageMsDuration());
50 else
51 StringAppendF(output, "(%d)Lives %dms/life ", count_, AverageMsDuration());
52}
53
54void DeathData::Clear() {
55 count_ = 0;
56 life_duration_ = TimeDelta();
57 square_duration_ = 0;
58}
59
60//------------------------------------------------------------------------------
61
62BirthOnThread::BirthOnThread(const Location& location)
63 : location_(location),
64 birth_thread_(ThreadData::current()) { }
65
66//------------------------------------------------------------------------------
67Births::Births(const Location& location)
68 : BirthOnThread(location),
69 birth_count_(0) { }
70
71//------------------------------------------------------------------------------
72// ThreadData maintains the central data for all births and death.
73
74// static
75ThreadData* ThreadData::first_ = NULL;
76// static
77Lock ThreadData::list_lock_;
78
79// static
80ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
81
evanm@google.comf26fd3a2008-08-21 07:54:52 +090082ThreadData::ThreadData() : message_loop_(MessageLoop::current()) {}
initial.commit3f4a7322008-07-27 06:49:38 +090083
84// static
85ThreadData* ThreadData::current() {
evanm@google.comf26fd3a2008-08-21 07:54:52 +090086 if (!tls_index_.initialized())
87 return NULL;
initial.commit3f4a7322008-07-27 06:49:38 +090088
evanm@google.comf26fd3a2008-08-21 07:54:52 +090089 ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get());
initial.commit3f4a7322008-07-27 06:49:38 +090090 if (!registry) {
91 // We have to create a new registry for ThreadData.
92 bool too_late_to_create = false;
93 {
94 registry = new ThreadData;
95 AutoLock lock(list_lock_);
96 // Use lock to insure we have most recent status.
97 if (!IsActive()) {
98 too_late_to_create = true;
99 } else {
100 // Use lock to insert into list.
101 registry->next_ = first_;
102 first_ = registry;
103 }
104 } // Release lock.
105 if (too_late_to_create) {
106 delete registry;
107 registry = NULL;
108 } else {
evanm@google.comf26fd3a2008-08-21 07:54:52 +0900109 tls_index_.Set(registry);
initial.commit3f4a7322008-07-27 06:49:38 +0900110 }
111 }
112 return registry;
113}
114
115// Do mininimal fixups for searching function names.
116static std::string UnescapeQuery(const std::string& query) {
117 std::string result;
118 for (size_t i = 0; i < query.size(); i++) {
119 char next = query[i];
120 if ('%' == next && i + 2 < query.size()) {
121 std::string hex = query.substr(i + 1, 2);
122 char replacement = '\0';
123 // Only bother with "<", ">", and " ".
124 if (LowerCaseEqualsASCII(hex, "3c"))
125 replacement ='<';
126 else if (LowerCaseEqualsASCII(hex, "3e"))
127 replacement = '>';
128 else if (hex == "20")
129 replacement = ' ';
130 if (replacement) {
131 next = replacement;
132 i += 2;
133 }
134 }
135 result.push_back(next);
136 }
137 return result;
138}
139
140// static
141void ThreadData::WriteHTML(const std::string& query, std::string* output) {
142 if (!ThreadData::IsActive())
143 return; // Not yet initialized.
144
145 DCHECK(ThreadData::current());
146
147 output->append("<html><head><title>About Objects");
148 std::string escaped_query = UnescapeQuery(query);
149 if (!escaped_query.empty())
150 output->append(" - " + escaped_query);
151 output->append("</title></head><body><pre>");
152
153 DataCollector collected_data; // Gather data.
154 collected_data.AddListOfLivingObjects(); // Add births that are still alive.
155
156 // Data Gathering is complete. Now to sort/process/render.
157 DataCollector::Collection* collection = collected_data.collection();
158
159 // Create filtering and sort comparison object.
160 Comparator comparator;
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900161 comparator.ParseQuery(escaped_query);
initial.commit3f4a7322008-07-27 06:49:38 +0900162
163 // Filter out acceptable (matching) instances.
164 DataCollector::Collection match_array;
165 for (DataCollector::Collection::iterator it = collection->begin();
166 it != collection->end(); ++it) {
167 if (comparator.Acceptable(*it))
168 match_array.push_back(*it);
169 }
170
171 comparator.Sort(&match_array);
172
173 WriteHTMLTotalAndSubtotals(match_array, comparator, output);
174
175 comparator.Clear(); // Delete tiebreaker_ instances.
176
177 output->append("</pre></body></html>");
178}
179
180// static
181void ThreadData::WriteHTMLTotalAndSubtotals(
182 const DataCollector::Collection& match_array,
183 const Comparator& comparator,
184 std::string* output) {
185 if (!match_array.size()) {
186 output->append("There were no tracked matches.");
187 } else {
188 // Aggregate during printing
189 Aggregation totals;
190 for (size_t i = 0; i < match_array.size(); ++i) {
191 totals.AddDeathSnapshot(match_array[i]);
192 }
193 output->append("Aggregate Stats: ");
194 totals.Write(output);
195 output->append("<hr><hr>");
196
197 Aggregation subtotals;
198 for (size_t i = 0; i < match_array.size(); ++i) {
199 if (0 == i || !comparator.Equivalent(match_array[i - 1],
200 match_array[i])) {
201 // Print group's defining characteristics.
202 comparator.WriteSortGrouping(match_array[i], output);
203 output->append("<br><br>");
204 }
205 comparator.WriteSnapshot(match_array[i], output);
206 output->append("<br>");
207 subtotals.AddDeathSnapshot(match_array[i]);
208 if (i + 1 >= match_array.size() ||
209 !comparator.Equivalent(match_array[i],
210 match_array[i + 1])) {
211 // Print aggregate stats for the group.
212 output->append("<br>");
213 subtotals.Write(output);
214 output->append("<br><hr><br>");
215 subtotals.Clear();
216 }
217 }
218 }
219}
220
221Births* ThreadData::FindLifetime(const Location& location) {
222 if (!message_loop_) // In case message loop wasn't yet around...
223 message_loop_ = MessageLoop::current(); // Find it now.
224
225 BirthMap::iterator it = birth_map_.find(location);
226 if (it != birth_map_.end())
227 return it->second;
228 Births* tracker = new Births(location);
229
230 // Lock since the map may get relocated now, and other threads sometimes
231 // snapshot it (but they lock before copying it).
232 AutoLock lock(lock_);
233 birth_map_[location] = tracker;
234 return tracker;
235}
236
237void ThreadData::TallyADeath(const Births& lifetimes,
238 const TimeDelta& duration) {
239 if (!message_loop_) // In case message loop wasn't yet around...
240 message_loop_ = MessageLoop::current(); // Find it now.
241
242 DeathMap::iterator it = death_map_.find(&lifetimes);
243 if (it != death_map_.end()) {
244 it->second.RecordDeath(duration);
245 return;
246 }
247
248 AutoLock lock(lock_); // Lock since the map may get relocated now.
249 death_map_[&lifetimes].RecordDeath(duration);
250}
251
252// static
253ThreadData* ThreadData::first() {
254 AutoLock lock(list_lock_);
255 return first_;
256}
257
258const std::string ThreadData::ThreadName() const {
259 if (message_loop_)
260 return message_loop_->thread_name();
261 return "ThreadWithoutMessageLoop";
262}
263
264// This may be called from another thread.
265void ThreadData::SnapshotBirthMap(BirthMap *output) const {
266 AutoLock lock(*const_cast<Lock*>(&lock_));
267 for (BirthMap::const_iterator it = birth_map_.begin();
268 it != birth_map_.end(); ++it)
269 (*output)[it->first] = it->second;
270}
271
272// This may be called from another thread.
273void ThreadData::SnapshotDeathMap(DeathMap *output) const {
274 AutoLock lock(*const_cast<Lock*>(&lock_));
275 for (DeathMap::const_iterator it = death_map_.begin();
276 it != death_map_.end(); ++it)
277 (*output)[it->first] = it->second;
278}
279
paulg@google.com2919c092008-08-09 05:03:42 +0900280#ifdef OS_WIN
initial.commit3f4a7322008-07-27 06:49:38 +0900281void ThreadData::RunOnAllThreads(void (*function)()) {
282 ThreadData* list = first(); // Get existing list.
283
284 std::vector<MessageLoop*> message_loops;
285 for (ThreadData* it = list; it; it = it->next()) {
286 if (current() != it && it->message_loop())
287 message_loops.push_back(it->message_loop());
288 }
289
290 ThreadSafeDownCounter* counter =
291 new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us!
292
293 HANDLE completion_handle = CreateEvent(NULL, false, false, NULL);
294 // Tell all other threads to run.
295 for (size_t i = 0; i < message_loops.size(); ++i)
296 message_loops[i]->PostTask(FROM_HERE,
297 new RunTheStatic(function, completion_handle, counter));
298
299 // Also run Task on our thread.
300 RunTheStatic local_task(function, completion_handle, counter);
301 local_task.Run();
302
303 WaitForSingleObject(completion_handle, INFINITE);
304 int ret_val = CloseHandle(completion_handle);
305 DCHECK(ret_val);
306}
paulg@google.com2919c092008-08-09 05:03:42 +0900307#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900308
309// static
310bool ThreadData::StartTracking(bool status) {
311#ifndef TRACK_ALL_TASK_OBJECTS
312 return false; // Not compiled in.
313#endif
314
315 if (!status) {
316 AutoLock lock(list_lock_);
317 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN);
318 status_ = SHUTDOWN;
319 return true;
320 }
initial.commit3f4a7322008-07-27 06:49:38 +0900321 AutoLock lock(list_lock_);
322 DCHECK(status_ == UNINITIALIZED);
evanm@google.comf26fd3a2008-08-21 07:54:52 +0900323 CHECK(tls_index_.Initialize(NULL));
initial.commit3f4a7322008-07-27 06:49:38 +0900324 status_ = ACTIVE;
325 return true;
326}
327
328// static
329bool ThreadData::IsActive() {
330 return status_ == ACTIVE;
331}
332
paulg@google.com2919c092008-08-09 05:03:42 +0900333#ifdef OS_WIN
initial.commit3f4a7322008-07-27 06:49:38 +0900334// static
335void ThreadData::ShutdownMultiThreadTracking() {
336 // Using lock, guarantee that no new ThreadData instances will be created.
337 if (!StartTracking(false))
338 return;
339
340 RunOnAllThreads(ShutdownDisablingFurtherTracking);
341
342 // Now the *only* threads that might change the database are the threads with
343 // no messages loops. They might still be adding data to their birth records,
344 // but since no objects are deleted on those threads, there will be no further
345 // access to to cross-thread data.
346 // We could do a cleanup on all threads except for the ones without
347 // MessageLoops, but we won't bother doing cleanup (destruction of data) yet.
348 return;
349}
paulg@google.com2919c092008-08-09 05:03:42 +0900350#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900351
352// static
353void ThreadData::ShutdownSingleThreadedCleanup() {
354 // We must be single threaded... but be careful anyway.
355 if (!StartTracking(false))
356 return;
357 ThreadData* thread_data_list;
358 {
359 AutoLock lock(list_lock_);
360 thread_data_list = first_;
361 first_ = NULL;
362 }
363
364 while (thread_data_list) {
365 ThreadData* next_thread_data = thread_data_list;
366 thread_data_list = thread_data_list->next();
367
368 for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
369 next_thread_data->birth_map_.end() != it; ++it)
370 delete it->second; // Delete the Birth Records.
371 next_thread_data->birth_map_.clear();
372 next_thread_data->death_map_.clear();
373 delete next_thread_data; // Includes all Death Records.
374 }
375
evanm@google.comf26fd3a2008-08-21 07:54:52 +0900376 CHECK(tls_index_.initialized());
377 tls_index_.Free();
378 DCHECK(!tls_index_.initialized());
initial.commit3f4a7322008-07-27 06:49:38 +0900379 status_ = UNINITIALIZED;
380}
381
382// static
383void ThreadData::ShutdownDisablingFurtherTracking() {
384 // Redundantly set status SHUTDOWN on this thread.
385 if (!StartTracking(false))
386 return;
387}
388
389
390//------------------------------------------------------------------------------
391
392ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
393 : remaining_count_(count) {
394 DCHECK(remaining_count_ > 0);
395}
396
397bool ThreadData::ThreadSafeDownCounter::LastCaller() {
398 {
399 AutoLock lock(lock_);
400 if (--remaining_count_)
401 return false;
402 } // Release lock, so we can delete everything in this instance.
403 delete this;
404 return true;
405}
406
407//------------------------------------------------------------------------------
paulg@google.com2919c092008-08-09 05:03:42 +0900408#ifdef OS_WIN
initial.commit3f4a7322008-07-27 06:49:38 +0900409ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function,
410 HANDLE completion_handle,
411 ThreadSafeDownCounter* counter)
412 : function_(function),
413 completion_handle_(completion_handle),
414 counter_(counter) {
415}
416
417void ThreadData::RunTheStatic::Run() {
paulg@google.com2919c092008-08-09 05:03:42 +0900418 function_();
419 if (counter_->LastCaller())
420 SetEvent(completion_handle_);
421}
422#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900423
424//------------------------------------------------------------------------------
425// Individual 3-tuple of birth (place and thread) along with death thread, and
426// the accumulated stats for instances (DeathData).
427
428Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
429 const ThreadData& death_thread,
430 const DeathData& death_data)
431 : birth_(&birth_on_thread),
432 death_thread_(&death_thread),
433 death_data_(death_data) {
434}
435
436Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
437 : birth_(&birth_on_thread),
438 death_thread_(NULL),
439 death_data_(DeathData(count)) {
440}
441
442const std::string Snapshot::DeathThreadName() const {
443 if (death_thread_)
444 return death_thread_->ThreadName();
445 return "Still_Alive";
446}
447
448void Snapshot::Write(std::string* output) const {
449 death_data_.Write(output);
450 StringAppendF(output, "%s->%s ",
451 birth_->birth_thread()->ThreadName().c_str(),
452 death_thread_->ThreadName().c_str());
453 birth_->location().Write(true, true, output);
454}
455
456void Snapshot::Add(const Snapshot& other) {
457 death_data_.AddDeathData(other.death_data_);
458}
459
460//------------------------------------------------------------------------------
461// DataCollector
462
463DataCollector::DataCollector() {
464 DCHECK(ThreadData::IsActive());
465
466 ThreadData* my_list = ThreadData::current()->first();
467
468 count_of_contributing_threads_ = 0;
469 for (ThreadData* thread_data = my_list;
470 thread_data;
471 thread_data = thread_data->next()) {
472 ++count_of_contributing_threads_;
473 }
474
475 // Gather data serially. A different constructor could be used to do in
476 // parallel, and then invoke an OnCompletion task.
477 for (ThreadData* thread_data = my_list;
478 thread_data;
479 thread_data = thread_data->next()) {
480 Append(*thread_data);
481 }
482}
483
484void DataCollector::Append(const ThreadData& thread_data) {
485 // Get copy of data (which is done under ThreadData's lock).
486 ThreadData::BirthMap birth_map;
487 thread_data.SnapshotBirthMap(&birth_map);
488 ThreadData::DeathMap death_map;
489 thread_data.SnapshotDeathMap(&death_map);
490
491 // Use our lock to protect our accumulation activity.
492 AutoLock lock(accumulation_lock_);
493
494 DCHECK(count_of_contributing_threads_);
495
496 for (ThreadData::DeathMap::const_iterator it = death_map.begin();
497 it != death_map.end(); ++it) {
498 collection_.push_back(Snapshot(*it->first, thread_data, it->second));
499 global_birth_count_[it->first] -= it->first->birth_count();
500 }
501
502 for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
503 it != birth_map.end(); ++it) {
504 global_birth_count_[it->second] += it->second->birth_count();
505 }
506
507 --count_of_contributing_threads_;
508}
509
510DataCollector::Collection* DataCollector::collection() {
511 DCHECK(!count_of_contributing_threads_);
512 return &collection_;
513}
514
515void DataCollector::AddListOfLivingObjects() {
516 DCHECK(!count_of_contributing_threads_);
517 for (BirthCount::iterator it = global_birth_count_.begin();
518 it != global_birth_count_.end(); ++it) {
519 if (it->second > 0)
520 collection_.push_back(Snapshot(*it->first, it->second));
521 }
522}
523
524//------------------------------------------------------------------------------
525// Aggregation
526
527void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
528 AddBirth(snapshot.birth());
529 death_threads_[snapshot.death_thread()]++;
530 AddDeathData(snapshot.death_data());
531}
532
533void Aggregation::AddBirths(const Births& births) {
534 AddBirth(births);
535 birth_count_ += births.birth_count();
536}
537void Aggregation::AddBirth(const BirthOnThread& birth) {
538 AddBirthPlace(birth.location());
539 birth_threads_[birth.birth_thread()]++;
540}
541
542void Aggregation::AddBirthPlace(const Location& location) {
543 locations_[location]++;
544 birth_files_[location.file_name()]++;
545}
546
547void Aggregation::Write(std::string* output) const {
548 if (locations_.size() == 1) {
549 locations_.begin()->first.Write(true, true, output);
550 } else {
551 StringAppendF(output, "%d Locations. ", locations_.size());
552 if (birth_files_.size() > 1)
553 StringAppendF(output, "%d Files. ", birth_files_.size());
554 else
555 StringAppendF(output, "All born in %s. ",
556 birth_files_.begin()->first.c_str());
557 }
558
559 if (birth_threads_.size() > 1)
560 StringAppendF(output, "%d BirthingThreads. ", birth_threads_.size());
561 else
562 StringAppendF(output, "All born on %s. ",
563 birth_threads_.begin()->first->ThreadName().c_str());
564
565 if (death_threads_.size() > 1) {
566 StringAppendF(output, "%d DeathThreads. ", death_threads_.size());
567 } else {
568 if (death_threads_.begin()->first)
569 StringAppendF(output, "All deleted on %s. ",
570 death_threads_.begin()->first->ThreadName().c_str());
571 else
572 output->append("All these objects are still alive.");
573 }
574
575 if (birth_count_ > 1)
576 StringAppendF(output, "Births=%d ", birth_count_);
577
578 DeathData::Write(output);
579}
580
581void Aggregation::Clear() {
582 birth_count_ = 0;
583 birth_files_.clear();
584 locations_.clear();
585 birth_threads_.clear();
586 DeathData::Clear();
587 death_threads_.clear();
588}
589
590//------------------------------------------------------------------------------
591// Comparison object for sorting.
592
593Comparator::Comparator()
594 : selector_(NIL),
595 tiebreaker_(NULL),
596 combined_selectors_(0),
597 use_tiebreaker_for_sort_only_(false) {}
598
599void Comparator::Clear() {
600 if (tiebreaker_) {
601 tiebreaker_->Clear();
602 delete tiebreaker_;
603 tiebreaker_ = NULL;
604 }
605 use_tiebreaker_for_sort_only_ = false;
606 selector_ = NIL;
607}
608
609void Comparator::Sort(DataCollector::Collection* collection) const {
610 std::sort(collection->begin(), collection->end(), *this);
611}
612
613
614bool Comparator::operator()(const Snapshot& left,
615 const Snapshot& right) const {
616 switch (selector_) {
617 case BIRTH_THREAD:
618 if (left.birth_thread() != right.birth_thread() &&
619 left.birth_thread()->ThreadName() !=
620 right.birth_thread()->ThreadName())
621 return left.birth_thread()->ThreadName() <
622 right.birth_thread()->ThreadName();
623 break;
624
625 case DEATH_THREAD:
626 if (left.death_thread() != right.death_thread() &&
627 left.DeathThreadName() !=
628 right.DeathThreadName()) {
629 if (!left.death_thread())
630 return true;
631 if (!right.death_thread())
632 return false;
633 return left.DeathThreadName() <
634 right.DeathThreadName();
635 }
636 break;
637
638 case BIRTH_FILE:
639 if (left.location().file_name() != right.location().file_name()) {
640 int comp = strcmp(left.location().file_name(),
641 right.location().file_name());
642 if (comp)
643 return 0 > comp;
644 }
645 break;
646
647 case BIRTH_FUNCTION:
648 if (left.location().function_name() != right.location().function_name()) {
649 int comp = strcmp(left.location().function_name(),
650 right.location().function_name());
651 if (comp)
652 return 0 > comp;
653 }
654 break;
655
656 case BIRTH_LINE:
657 if (left.location().line_number() != right.location().line_number())
658 return left.location().line_number() <
659 right.location().line_number();
660 break;
661
662 case COUNT:
663 if (left.count() != right.count())
664 return left.count() > right.count(); // Sort large at front of vector.
665 break;
666
667 case AVERAGE_DURATION:
668 if (left.AverageMsDuration() != right.AverageMsDuration())
669 return left.AverageMsDuration() > right.AverageMsDuration();
670 break;
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900671
672 default:
673 break;
initial.commit3f4a7322008-07-27 06:49:38 +0900674 }
675 if (tiebreaker_)
676 return tiebreaker_->operator()(left, right);
677 return false;
678}
679
680bool Comparator::Equivalent(const Snapshot& left,
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900681 const Snapshot& right) const {
initial.commit3f4a7322008-07-27 06:49:38 +0900682 switch (selector_) {
683 case BIRTH_THREAD:
684 if (left.birth_thread() != right.birth_thread() &&
685 left.birth_thread()->ThreadName() !=
686 right.birth_thread()->ThreadName())
687 return false;
688 break;
689
690 case DEATH_THREAD:
691 if (left.death_thread() != right.death_thread() &&
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900692 left.DeathThreadName() != right.DeathThreadName())
initial.commit3f4a7322008-07-27 06:49:38 +0900693 return false;
694 break;
695
696 case BIRTH_FILE:
697 if (left.location().file_name() != right.location().file_name()) {
698 int comp = strcmp(left.location().file_name(),
699 right.location().file_name());
700 if (comp)
701 return false;
702 }
703 break;
704
705 case BIRTH_FUNCTION:
706 if (left.location().function_name() != right.location().function_name()) {
707 int comp = strcmp(left.location().function_name(),
708 right.location().function_name());
709 if (comp)
710 return false;
711 }
712 break;
713
714 case COUNT:
715 if (left.count() != right.count())
716 return false;
717 break;
718
719 case AVERAGE_DURATION:
720 if (left.life_duration() != right.life_duration())
721 return false;
722 break;
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900723
724 default:
725 break;
initial.commit3f4a7322008-07-27 06:49:38 +0900726 }
727 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
728 return tiebreaker_->Equivalent(left, right);
729 return true;
730}
731
732bool Comparator::Acceptable(const Snapshot& sample) const {
733 if (required_.size()) {
734 switch (selector_) {
735 case BIRTH_THREAD:
paulg@google.com2919c092008-08-09 05:03:42 +0900736 if (sample.birth_thread()->ThreadName().find(required_) ==
737 std::string::npos)
initial.commit3f4a7322008-07-27 06:49:38 +0900738 return false;
739 break;
740
741 case DEATH_THREAD:
paulg@google.com2919c092008-08-09 05:03:42 +0900742 if (sample.DeathThreadName().find(required_) == std::string::npos)
initial.commit3f4a7322008-07-27 06:49:38 +0900743 return false;
744 break;
745
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900746 case BIRTH_FILE:
initial.commit3f4a7322008-07-27 06:49:38 +0900747 if (!strstr(sample.location().file_name(), required_.c_str()))
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900748 return false;
initial.commit3f4a7322008-07-27 06:49:38 +0900749 break;
750
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900751 case BIRTH_FUNCTION:
initial.commit3f4a7322008-07-27 06:49:38 +0900752 if (!strstr(sample.location().function_name(), required_.c_str()))
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900753 return false;
754 break;
755
756 default:
initial.commit3f4a7322008-07-27 06:49:38 +0900757 break;
758 }
759 }
760 if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
761 return tiebreaker_->Acceptable(sample);
762 return true;
763}
764
765void Comparator::SetTiebreaker(Selector selector, const std::string required) {
766 if (selector == selector_ || NIL == selector)
767 return;
768 combined_selectors_ |= selector;
769 if (NIL == selector_) {
770 selector_ = selector;
771 if (required.size())
772 required_ = required;
773 return;
774 }
775 if (tiebreaker_) {
776 if (use_tiebreaker_for_sort_only_) {
777 Comparator* temp = new Comparator;
778 temp->tiebreaker_ = tiebreaker_;
779 tiebreaker_ = temp;
780 }
781 } else {
782 tiebreaker_ = new Comparator;
783 DCHECK(!use_tiebreaker_for_sort_only_);
784 }
785 tiebreaker_->SetTiebreaker(selector, required);
786}
787
788bool Comparator::IsGroupedBy(Selector selector) const {
789 return 0 != (selector & combined_selectors_);
790}
791
792void Comparator::SetSubgroupTiebreaker(Selector selector) {
793 if (selector == selector_ || NIL == selector)
794 return;
795 if (!tiebreaker_) {
796 use_tiebreaker_for_sort_only_ = true;
797 tiebreaker_ = new Comparator;
798 tiebreaker_->SetTiebreaker(selector, "");
799 } else {
800 tiebreaker_->SetSubgroupTiebreaker(selector);
801 }
802}
803
804void Comparator::ParseKeyphrase(const std::string key_phrase) {
805 static std::map<const std::string, Selector> key_map;
806 static bool initialized = false;
807 if (!initialized) {
808 initialized = true;
809 key_map["count"] = COUNT;
810 key_map["duration"] = AVERAGE_DURATION;
811 key_map["birth"] = BIRTH_THREAD;
812 key_map["death"] = DEATH_THREAD;
813 key_map["file"] = BIRTH_FILE;
814 key_map["function"] = BIRTH_FUNCTION;
815 key_map["line"] = BIRTH_LINE;
816 }
817
818 std::string required;
819 size_t equal_offset = key_phrase.find('=', 0);
820 if (key_phrase.npos != equal_offset)
821 required = key_phrase.substr(equal_offset + 1, key_phrase.npos);
822 std::string keyword(key_phrase.substr(0, equal_offset));
823 keyword = StringToLowerASCII(keyword);
824 if (key_map.end() == key_map.find(keyword))
825 return;
826 SetTiebreaker(key_map[keyword], required);
827}
828
829bool Comparator::ParseQuery(const std::string query) {
830 for (size_t i = 0; i < query.size();) {
831 size_t slash_offset = query.find('/', i);
832 ParseKeyphrase(query.substr(i, slash_offset - i));
833 if (query.npos == slash_offset)
834 break;
835 i = slash_offset + 1;
836 }
837
838 // Select subgroup ordering (if we want to display the subgroup)
839 SetSubgroupTiebreaker(COUNT);
840 SetSubgroupTiebreaker(AVERAGE_DURATION);
841 SetSubgroupTiebreaker(BIRTH_THREAD);
842 SetSubgroupTiebreaker(DEATH_THREAD);
843 SetSubgroupTiebreaker(BIRTH_FUNCTION);
844 SetSubgroupTiebreaker(BIRTH_FILE);
845 SetSubgroupTiebreaker(BIRTH_LINE);
846
847 return true;
848}
849
850bool Comparator::WriteSortGrouping(const Snapshot& sample,
851 std::string* output) const {
852 bool wrote_data = false;
853 switch (selector_) {
initial.commit3f4a7322008-07-27 06:49:38 +0900854 case BIRTH_THREAD:
855 StringAppendF(output, "All new on %s ",
856 sample.birth_thread()->ThreadName().c_str());
857 wrote_data = true;
858 break;
859
860 case DEATH_THREAD:
861 if (sample.death_thread())
862 StringAppendF(output, "All deleted on %s ",
863 sample.DeathThreadName().c_str());
864 else
865 output->append("All still alive ");
866 wrote_data = true;
867 break;
868
869 case BIRTH_FILE:
870 StringAppendF(output, "All born in %s ",
871 sample.location().file_name());
872 break;
873
874 case BIRTH_FUNCTION:
875 output->append("All born in ");
876 sample.location().WriteFunctionName(output);
877 output->push_back(' ');
878 break;
mmentovai@google.coma44cb322008-08-20 12:25:47 +0900879
880 default:
881 break;
initial.commit3f4a7322008-07-27 06:49:38 +0900882 }
883 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
884 wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
885 }
886 return wrote_data;
887}
888
889void Comparator::WriteSnapshot(const Snapshot& sample,
890 std::string* output) const {
891 sample.death_data().Write(output);
892 if (!(combined_selectors_ & BIRTH_THREAD) ||
893 !(combined_selectors_ & DEATH_THREAD))
894 StringAppendF(output, "%s->%s ",
895 (combined_selectors_ & BIRTH_THREAD) ? "*" :
896 sample.birth().birth_thread()->ThreadName().c_str(),
897 (combined_selectors_ & DEATH_THREAD) ? "*" :
898 sample.DeathThreadName().c_str());
899 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
900 !(combined_selectors_ & BIRTH_FUNCTION),
901 output);
902}
903
904} // namespace tracked_objects
license.botf003cfe2008-08-24 09:55:55 +0900905