|
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 |