OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/tracked_objects.h" | 5 #include "base/tracked_objects.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 | 8 |
9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
12 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
13 #include "base/threading/thread_restrictions.h" | 13 #include "base/threading/thread_restrictions.h" |
14 | 14 |
15 using base::TimeDelta; | 15 using base::TimeDelta; |
16 | 16 |
17 namespace tracked_objects { | 17 namespace tracked_objects { |
18 | 18 |
19 // A TLS slot to the TrackRegistry for the current thread. | 19 // A TLS slot to the TrackRegistry for the current thread. |
20 // static | 20 // static |
21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); | 21 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); |
22 | 22 |
23 // A global state variable to prevent repeated initialization during tests. | 23 // A global state variable to prevent repeated initialization during tests. |
24 // static | 24 // static |
25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; | 25 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; |
26 | 26 |
27 // A locked protected counter to assign sequence number to threads. | |
28 // static | |
29 int ThreadData::thread_number_counter = 0; | |
30 | |
27 //------------------------------------------------------------------------------ | 31 //------------------------------------------------------------------------------ |
28 // Death data tallies durations when a death takes place. | 32 // Death data tallies durations when a death takes place. |
29 | 33 |
30 void DeathData::RecordDeath(const TimeDelta& duration) { | 34 void DeathData::RecordDeath(const TimeDelta& duration) { |
31 ++count_; | 35 ++count_; |
32 life_duration_ += duration; | 36 life_duration_ += duration; |
33 int64 milliseconds = duration.InMilliseconds(); | |
34 square_duration_ += milliseconds * milliseconds; | |
35 } | 37 } |
36 | 38 |
37 int DeathData::AverageMsDuration() const { | 39 int DeathData::AverageMsDuration() const { |
38 return static_cast<int>(life_duration_.InMilliseconds() / count_); | 40 return static_cast<int>(life_duration_.InMilliseconds() / count_); |
39 } | 41 } |
40 | 42 |
41 double DeathData::StandardDeviation() const { | |
42 double average = AverageMsDuration(); | |
43 double variance = static_cast<float>(square_duration_)/count_ | |
44 - average * average; | |
45 return sqrt(variance); | |
46 } | |
47 | |
48 | |
49 void DeathData::AddDeathData(const DeathData& other) { | 43 void DeathData::AddDeathData(const DeathData& other) { |
50 count_ += other.count_; | 44 count_ += other.count_; |
51 life_duration_ += other.life_duration_; | 45 life_duration_ += other.life_duration_; |
52 square_duration_ += other.square_duration_; | |
53 } | 46 } |
54 | 47 |
55 void DeathData::Write(std::string* output) const { | 48 void DeathData::Write(std::string* output) const { |
56 if (!count_) | 49 if (!count_) |
57 return; | 50 return; |
58 if (1 == count_) { | 51 base::StringAppendF(output, "Lives:%d, Total:%dms Avg:%dms/life ", |
59 base::StringAppendF(output, "(1)Life in %dms ", AverageMsDuration()); | 52 count_, |
60 } else { | 53 static_cast<int>(life_duration_.InMilliseconds()), |
61 base::StringAppendF(output, "(%d)Lives %dms/life ", | 54 AverageMsDuration()); |
62 count_, AverageMsDuration()); | |
63 } | |
64 } | 55 } |
65 | 56 |
66 void DeathData::Clear() { | 57 void DeathData::Clear() { |
67 count_ = 0; | 58 count_ = 0; |
68 life_duration_ = TimeDelta(); | 59 life_duration_ = TimeDelta(); |
69 square_duration_ = 0; | |
70 } | 60 } |
71 | 61 |
72 //------------------------------------------------------------------------------ | 62 //------------------------------------------------------------------------------ |
73 BirthOnThread::BirthOnThread(const Location& location) | 63 BirthOnThread::BirthOnThread(const Location& location) |
74 : location_(location), | 64 : location_(location), |
75 birth_thread_(ThreadData::current()) { } | 65 birth_thread_(ThreadData::FactoryGet(NULL)) { } |
76 | 66 |
77 //------------------------------------------------------------------------------ | 67 //------------------------------------------------------------------------------ |
78 Births::Births(const Location& location) | 68 Births::Births(const Location& location) |
79 : BirthOnThread(location), | 69 : BirthOnThread(location), |
80 birth_count_(1) { } | 70 birth_count_(1) { } |
81 | 71 |
82 //------------------------------------------------------------------------------ | 72 //------------------------------------------------------------------------------ |
83 // ThreadData maintains the central data for all births and death. | 73 // ThreadData maintains the central data for all births and death. |
84 | 74 |
85 // static | 75 // static |
86 ThreadData* ThreadData::first_ = NULL; | 76 ThreadData* ThreadData::first_ = NULL; |
87 // static | 77 // static |
88 base::Lock ThreadData::list_lock_; | 78 base::Lock ThreadData::list_lock_; |
89 | 79 |
90 // static | 80 // static |
91 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 81 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
92 | 82 |
93 ThreadData::ThreadData() : next_(NULL) { | 83 ThreadData::ThreadData(const char* suggested_name) |
94 // This shouldn't use the MessageLoop::current() LazyInstance since this might | 84 : next_(NULL), |
95 // be used on a non-joinable thread. | 85 thread_number_(0) { |
96 // http://crbug.com/62728 | 86 if (suggested_name) { |
97 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | 87 DCHECK_GE(strlen(suggested_name), 0u); |
98 message_loop_ = MessageLoop::current(); | 88 thread_name_ = suggested_name; |
89 return; | |
90 } | |
91 { | |
92 base::AutoLock lock(list_lock_); | |
93 thread_number_ = ++thread_number_counter; | |
94 } | |
95 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number_); | |
99 } | 96 } |
100 | 97 |
101 ThreadData::~ThreadData() {} | 98 ThreadData::~ThreadData() {} |
102 | 99 |
103 // static | 100 // static |
104 ThreadData* ThreadData::current() { | 101 ThreadData* ThreadData::FactoryGet(const char* suggested_name) { |
105 if (!tls_index_.initialized()) | 102 if (!tls_index_.initialized()) |
106 return NULL; | 103 return NULL; |
107 | 104 |
108 ThreadData* registry = static_cast<ThreadData*>(tls_index_.Get()); | 105 ThreadData* registered = static_cast<ThreadData*>(tls_index_.Get()); |
109 if (!registry) { | 106 if (!registered) { |
110 // We have to create a new registry for ThreadData. | 107 // We have to create a new registry entry for this ThreadData. |
111 bool too_late_to_create = false; | 108 bool too_late_to_create = false; |
109 // TODO(jar): Host all unamed (Worker) threads in *one* ThreadData instance, | |
110 // (with locking protection on that instance) or else recycle and re-use | |
111 // worker thread ThreadData when the worker thread terminates. | |
112 registered = new ThreadData(suggested_name); | |
112 { | 113 { |
113 registry = new ThreadData; | |
114 base::AutoLock lock(list_lock_); | 114 base::AutoLock lock(list_lock_); |
115 // Use lock to insure we have most recent status. | 115 // Use lock to insure we have most recent status. |
116 if (!IsActive()) { | 116 if (!IsActive()) { |
117 too_late_to_create = true; | 117 too_late_to_create = true; |
118 } else { | 118 } else { |
119 // Use lock to insert into list. | 119 // Use list_lock_ to insert as new head of list. |
120 registry->next_ = first_; | 120 registered->next_ = first_; |
121 first_ = registry; | 121 first_ = registered; |
122 } | 122 } |
123 } // Release lock. | 123 } // Release lock. |
124 if (too_late_to_create) { | 124 if (too_late_to_create) { |
125 delete registry; | 125 delete registered; |
126 registry = NULL; | 126 registered = NULL; |
127 } else { | 127 } else { |
128 tls_index_.Set(registry); | 128 tls_index_.Set(registered); |
129 } | 129 } |
130 } | 130 } |
131 return registry; | 131 DCHECK_GT(registered->thread_name_.size(), 0u); |
132 // If this DCHECK fails, then a task was *probabably* created on this thread | |
133 // before the thread object was initialized with a name etc. | |
134 DCHECK(!suggested_name || | |
135 registered->thread_name_.compare(suggested_name) == 0); | |
136 | |
137 return registered; | |
132 } | 138 } |
133 | 139 |
134 // static | 140 // static |
135 void ThreadData::WriteHTML(const std::string& query, std::string* output) { | 141 void ThreadData::WriteHTML(const std::string& query, std::string* output) { |
136 if (!ThreadData::IsActive()) | 142 if (!ThreadData::IsActive()) |
137 return; // Not yet initialized. | 143 return; // Not yet initialized. |
138 | 144 |
139 DCHECK(ThreadData::current()); | |
140 DataCollector collected_data; // Gather data. | 145 DataCollector collected_data; // Gather data. |
141 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 146 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
142 | 147 |
143 // Data Gathering is complete. Now to sort/process/render. | 148 // Data Gathering is complete. Now to sort/process/render. |
144 DataCollector::Collection* collection = collected_data.collection(); | 149 DataCollector::Collection* collection = collected_data.collection(); |
145 | 150 |
146 // Create filtering and sort comparison object. | 151 // Create filtering and sort comparison object. |
147 Comparator comparator; | 152 Comparator comparator; |
148 comparator.ParseQuery(query); | 153 comparator.ParseQuery(query); |
149 | 154 |
150 // Filter out acceptable (matching) instances. | 155 // Filter out acceptable (matching) instances. |
151 DataCollector::Collection match_array; | 156 DataCollector::Collection match_array; |
152 for (DataCollector::Collection::iterator it = collection->begin(); | 157 for (DataCollector::Collection::iterator it = collection->begin(); |
153 it != collection->end(); ++it) { | 158 it != collection->end(); ++it) { |
154 if (comparator.Acceptable(*it)) | 159 if (comparator.Acceptable(*it)) |
155 match_array.push_back(*it); | 160 match_array.push_back(*it); |
156 } | 161 } |
157 | 162 |
158 comparator.Sort(&match_array); | 163 comparator.Sort(&match_array); |
159 | 164 |
160 WriteHTMLTotalAndSubtotals(match_array, comparator, output); | 165 WriteHTMLTotalAndSubtotals(match_array, comparator, output); |
161 | 166 |
162 comparator.Clear(); // Delete tiebreaker_ instances. | 167 comparator.Clear(); // Delete tiebreaker_ instances. |
163 | 168 |
164 output->append("</pre>"); | 169 output->append("</pre>"); |
165 | 170 |
166 const char* help_string = "The following are the keywords that can be used to" | 171 const char* help_string = "The following are the keywords that can be used to" |
167 " sort and aggregate the data, or to select data.<br><ul>" | 172 " sort and aggregate the data, or to select data.<br><ul>" |
168 "<li><b>count</b> Number of instances seen." | 173 "<li><b>count</b> Number of instances seen." |
169 "<li><b>duration</b> Duration in ms from construction to descrution." | 174 "<li><b>duration</b> Average duration in ms of Run() time." |
175 "<li><b>totalduration</b> Summed durations in ms of Run() times." | |
170 "<li><b>birth</b> Thread on which the task was constructed." | 176 "<li><b>birth</b> Thread on which the task was constructed." |
171 "<li><b>death</b> Thread on which the task was run and deleted." | 177 "<li><b>death</b> Thread on which the task was run and deleted." |
172 "<li><b>file</b> File in which the task was contructed." | 178 "<li><b>file</b> File in which the task was contructed." |
173 "<li><b>function</b> Function in which the task was constructed." | 179 "<li><b>function</b> Function in which the task was constructed." |
174 "<li><b>line</b> Line number of the file in which the task was constructed." | 180 "<li><b>line</b> Line number of the file in which the task was constructed." |
175 "</ul><br>" | 181 "</ul><br>" |
176 "As examples:<ul>" | 182 "As examples:<ul>" |
177 "<li><b>about:tracking/file</b> would sort the above data by file, and" | 183 "<li><b>about:tracking/file</b> would sort the above data by file, and" |
178 " aggregate data on a per-file basis." | 184 " aggregate data on a per-file basis." |
179 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" | 185 "<li><b>about:tracking/file=Dns</b> would only list data for tasks" |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
227 output->append("<br>"); | 233 output->append("<br>"); |
228 subtotals.Write(output); | 234 subtotals.Write(output); |
229 output->append("<br><hr><br>"); | 235 output->append("<br><hr><br>"); |
230 subtotals.Clear(); | 236 subtotals.Clear(); |
231 } | 237 } |
232 } | 238 } |
233 } | 239 } |
234 } | 240 } |
235 | 241 |
236 Births* ThreadData::TallyABirth(const Location& location) { | 242 Births* ThreadData::TallyABirth(const Location& location) { |
237 { | |
238 // This shouldn't use the MessageLoop::current() LazyInstance since this | |
239 // might be used on a non-joinable thread. | |
240 // http://crbug.com/62728 | |
jar (doing other things)
2011/10/14 02:29:53
This was the primary motivation for this cleanup C
| |
241 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | |
242 if (!message_loop_) // In case message loop wasn't yet around... | |
243 message_loop_ = MessageLoop::current(); // Find it now. | |
244 } | |
245 | |
246 BirthMap::iterator it = birth_map_.find(location); | 243 BirthMap::iterator it = birth_map_.find(location); |
247 if (it != birth_map_.end()) { | 244 if (it != birth_map_.end()) { |
248 it->second->RecordBirth(); | 245 it->second->RecordBirth(); |
249 return it->second; | 246 return it->second; |
250 } | 247 } |
251 | 248 |
252 Births* tracker = new Births(location); | 249 Births* tracker = new Births(location); |
253 // Lock since the map may get relocated now, and other threads sometimes | 250 // Lock since the map may get relocated now, and other threads sometimes |
254 // snapshot it (but they lock before copying it). | 251 // snapshot it (but they lock before copying it). |
255 base::AutoLock lock(lock_); | 252 base::AutoLock lock(lock_); |
256 birth_map_[location] = tracker; | 253 birth_map_[location] = tracker; |
257 return tracker; | 254 return tracker; |
258 } | 255 } |
259 | 256 |
260 void ThreadData::TallyADeath(const Births& lifetimes, | 257 void ThreadData::TallyADeath(const Births& the_birth, |
261 const TimeDelta& duration) { | 258 const TimeDelta& duration) { |
262 { | 259 DeathMap::iterator it = death_map_.find(&the_birth); |
263 // http://crbug.com/62728 | |
jar (doing other things)
2011/10/14 02:29:53
Again... the motivation for the CL.
| |
264 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; | |
265 if (!message_loop_) // In case message loop wasn't yet around... | |
266 message_loop_ = MessageLoop::current(); // Find it now. | |
267 } | |
268 | |
269 DeathMap::iterator it = death_map_.find(&lifetimes); | |
270 if (it != death_map_.end()) { | 260 if (it != death_map_.end()) { |
271 it->second.RecordDeath(duration); | 261 it->second.RecordDeath(duration); |
272 return; | 262 return; |
273 } | 263 } |
274 | 264 |
275 base::AutoLock lock(lock_); // Lock since the map may get relocated now. | 265 base::AutoLock lock(lock_); // Lock since the map may get relocated now. |
276 death_map_[&lifetimes].RecordDeath(duration); | 266 death_map_[&the_birth].RecordDeath(duration); |
277 } | 267 } |
278 | 268 |
279 // static | 269 // static |
280 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 270 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
281 if (IsActive()) { | 271 #if !defined(TRACK_ALL_TASK_OBJECTS) |
282 ThreadData* current_thread_data = current(); | 272 return NULL; // Not compiled in. |
283 if (current_thread_data) { | 273 #endif |
284 return current_thread_data->TallyABirth(location); | 274 if (!IsActive()) |
285 } | 275 return NULL; |
286 } | 276 ThreadData* current_thread_data = FactoryGet(NULL); |
287 | 277 if (!current_thread_data) |
288 return NULL; | 278 return NULL; |
279 return current_thread_data->TallyABirth(location); | |
289 } | 280 } |
290 | 281 |
291 // static | 282 // static |
292 void ThreadData::TallyADeathIfActive(const Births* the_birth, | 283 void ThreadData::TallyADeathIfActive(const Births* the_birth, |
293 const base::TimeDelta& duration) { | 284 const base::TimeTicks& time_posted, |
294 if (IsActive() && the_birth) { | 285 const base::TimeTicks& start_of_run, |
295 current()->TallyADeath(*the_birth, duration); | 286 const MessageLoop* message_loop) { |
296 } | 287 #if !defined(TRACK_ALL_TASK_OBJECTS) |
288 return; // Not compiled in. | |
289 #endif | |
290 if (!IsActive() || !the_birth) | |
291 return; | |
292 ThreadData* current_thread_data = FactoryGet(NULL); | |
293 if (!current_thread_data) | |
294 return; | |
295 DCHECK(!message_loop || | |
296 message_loop->thread_name() == current_thread_data->thread_name_); | |
297 // We don't currently handle queueing time duration, since time_posted, so | |
298 // we discard time_posted. | |
299 current_thread_data->TallyADeath(*the_birth, | |
300 base::TimeTicks::Now() - start_of_run); | |
297 } | 301 } |
298 | 302 |
299 // static | 303 // static |
300 ThreadData* ThreadData::first() { | 304 ThreadData* ThreadData::first() { |
301 base::AutoLock lock(list_lock_); | 305 base::AutoLock lock(list_lock_); |
302 return first_; | 306 return first_; |
303 } | 307 } |
304 | 308 |
305 const std::string ThreadData::ThreadName() const { | |
306 if (message_loop_) | |
307 return message_loop_->thread_name(); | |
jar (doing other things)
2011/10/14 02:29:53
We now store the thread name, and don't need to ke
| |
308 return "ThreadWithoutMessageLoop"; | |
309 } | |
310 | |
311 // This may be called from another thread. | 309 // This may be called from another thread. |
312 void ThreadData::SnapshotBirthMap(BirthMap *output) const { | 310 void ThreadData::SnapshotBirthMap(BirthMap *output) const { |
313 base::AutoLock lock(lock_); | 311 base::AutoLock lock(lock_); |
314 for (BirthMap::const_iterator it = birth_map_.begin(); | 312 for (BirthMap::const_iterator it = birth_map_.begin(); |
315 it != birth_map_.end(); ++it) | 313 it != birth_map_.end(); ++it) |
316 (*output)[it->first] = it->second; | 314 (*output)[it->first] = it->second; |
317 } | 315 } |
318 | 316 |
319 // This may be called from another thread. | 317 // This may be called from another thread. |
320 void ThreadData::SnapshotDeathMap(DeathMap *output) const { | 318 void ThreadData::SnapshotDeathMap(DeathMap *output) const { |
321 base::AutoLock lock(lock_); | 319 base::AutoLock lock(lock_); |
322 for (DeathMap::const_iterator it = death_map_.begin(); | 320 for (DeathMap::const_iterator it = death_map_.begin(); |
323 it != death_map_.end(); ++it) | 321 it != death_map_.end(); ++it) |
324 (*output)[it->first] = it->second; | 322 (*output)[it->first] = it->second; |
325 } | 323 } |
326 | 324 |
327 // static | 325 // static |
328 void ThreadData::ResetAllThreadData() { | 326 void ThreadData::ResetAllThreadData() { |
329 ThreadData* my_list = ThreadData::current()->first(); | 327 ThreadData* my_list = ThreadData::FactoryGet(NULL)->first(); |
330 | 328 |
331 for (ThreadData* thread_data = my_list; | 329 for (ThreadData* thread_data = my_list; |
332 thread_data; | 330 thread_data; |
333 thread_data = thread_data->next()) | 331 thread_data = thread_data->next()) |
334 thread_data->Reset(); | 332 thread_data->Reset(); |
335 } | 333 } |
336 | 334 |
337 void ThreadData::Reset() { | 335 void ThreadData::Reset() { |
338 base::AutoLock lock(lock_); | 336 base::AutoLock lock(lock_); |
339 for (DeathMap::iterator it = death_map_.begin(); | 337 for (DeathMap::iterator it = death_map_.begin(); |
340 it != death_map_.end(); ++it) | 338 it != death_map_.end(); ++it) |
341 it->second.Clear(); | 339 it->second.Clear(); |
342 for (BirthMap::iterator it = birth_map_.begin(); | 340 for (BirthMap::iterator it = birth_map_.begin(); |
343 it != birth_map_.end(); ++it) | 341 it != birth_map_.end(); ++it) |
344 it->second->Clear(); | 342 it->second->Clear(); |
345 } | 343 } |
346 | 344 |
347 #ifdef OS_WIN | |
348 // A class used to count down which is accessed by several threads. This is | |
jar (doing other things)
2011/10/14 02:29:53
We no longer do any terminaition cleanup... so all
| |
349 // used to make sure RunOnAllThreads() actually runs a task on the expected | |
350 // count of threads. | |
351 class ThreadData::ThreadSafeDownCounter { | |
352 public: | |
353 // Constructor sets the count, once and for all. | |
354 explicit ThreadSafeDownCounter(size_t count); | |
355 | |
356 // Decrement the count, and return true if we hit zero. Also delete this | |
357 // instance automatically when we hit zero. | |
358 bool LastCaller(); | |
359 | |
360 private: | |
361 size_t remaining_count_; | |
362 base::Lock lock_; // protect access to remaining_count_. | |
363 }; | |
364 | |
365 ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) | |
366 : remaining_count_(count) { | |
367 DCHECK_GT(remaining_count_, 0u); | |
368 } | |
369 | |
370 bool ThreadData::ThreadSafeDownCounter::LastCaller() { | |
371 { | |
372 base::AutoLock lock(lock_); | |
373 if (--remaining_count_) | |
374 return false; | |
375 } // Release lock, so we can delete everything in this instance. | |
376 delete this; | |
377 return true; | |
378 } | |
379 | |
380 // A Task class that runs a static method supplied, and checks to see if this | |
381 // is the last tasks instance (on last thread) that will run the method. | |
382 // IF this is the last run, then the supplied event is signalled. | |
383 class ThreadData::RunTheStatic : public Task { | |
384 public: | |
385 typedef void (*FunctionPointer)(); | |
386 RunTheStatic(FunctionPointer function, | |
387 HANDLE completion_handle, | |
388 ThreadSafeDownCounter* counter); | |
389 // Run the supplied static method, and optionally set the event. | |
390 void Run(); | |
391 | |
392 private: | |
393 FunctionPointer function_; | |
394 HANDLE completion_handle_; | |
395 // Make sure enough tasks are called before completion is signaled. | |
396 ThreadSafeDownCounter* counter_; | |
397 | |
398 DISALLOW_COPY_AND_ASSIGN(RunTheStatic); | |
399 }; | |
400 | |
401 ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function, | |
402 HANDLE completion_handle, | |
403 ThreadSafeDownCounter* counter) | |
404 : function_(function), | |
405 completion_handle_(completion_handle), | |
406 counter_(counter) { | |
407 } | |
408 | |
409 void ThreadData::RunTheStatic::Run() { | |
410 function_(); | |
411 if (counter_->LastCaller()) | |
412 SetEvent(completion_handle_); | |
413 } | |
414 | |
415 // TODO(jar): This should use condition variables, and be cross platform. | |
416 void ThreadData::RunOnAllThreads(void (*function)()) { | |
417 ThreadData* list = first(); // Get existing list. | |
418 | |
419 std::vector<MessageLoop*> message_loops; | |
420 for (ThreadData* it = list; it; it = it->next()) { | |
421 if (current() != it && it->message_loop()) | |
422 message_loops.push_back(it->message_loop()); | |
jar (doing other things)
2011/10/14 02:29:53
Since we don't do this racy cleanup attempt, we do
| |
423 } | |
424 | |
425 ThreadSafeDownCounter* counter = | |
426 new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us! | |
427 | |
428 HANDLE completion_handle = CreateEvent(NULL, false, false, NULL); | |
429 // Tell all other threads to run. | |
430 for (size_t i = 0; i < message_loops.size(); ++i) | |
431 message_loops[i]->PostTask( | |
432 FROM_HERE, new RunTheStatic(function, completion_handle, counter)); | |
433 | |
434 // Also run Task on our thread. | |
435 RunTheStatic local_task(function, completion_handle, counter); | |
436 local_task.Run(); | |
437 | |
438 WaitForSingleObject(completion_handle, INFINITE); | |
439 int ret_val = CloseHandle(completion_handle); | |
440 DCHECK(ret_val); | |
441 } | |
442 #endif // OS_WIN | |
443 | |
444 // static | 345 // static |
445 bool ThreadData::StartTracking(bool status) { | 346 bool ThreadData::StartTracking(bool status) { |
446 #if !defined(TRACK_ALL_TASK_OBJECTS) | 347 #if !defined(TRACK_ALL_TASK_OBJECTS) |
447 return false; // Not compiled in. | 348 return false; // Not compiled in. |
448 #endif | 349 #endif |
449 | 350 |
450 if (!status) { | 351 if (!status) { |
451 base::AutoLock lock(list_lock_); | 352 base::AutoLock lock(list_lock_); |
452 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); | 353 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); |
453 status_ = SHUTDOWN; | 354 status_ = SHUTDOWN; |
454 return true; | 355 return true; |
455 } | 356 } |
456 base::AutoLock lock(list_lock_); | 357 base::AutoLock lock(list_lock_); |
457 DCHECK_EQ(UNINITIALIZED, status_); | 358 DCHECK_EQ(UNINITIALIZED, status_); |
458 CHECK(tls_index_.Initialize(NULL)); | 359 CHECK(tls_index_.Initialize(NULL)); |
459 status_ = ACTIVE; | 360 status_ = ACTIVE; |
460 return true; | 361 return true; |
461 } | 362 } |
462 | 363 |
463 // static | 364 // static |
464 bool ThreadData::IsActive() { | 365 bool ThreadData::IsActive() { |
465 return status_ == ACTIVE; | 366 return status_ == ACTIVE; |
466 } | 367 } |
467 | 368 |
468 #ifdef OS_WIN | |
469 // static | 369 // static |
470 void ThreadData::ShutdownMultiThreadTracking() { | 370 base::TimeTicks ThreadData::Now() { |
471 // Using lock, guarantee that no new ThreadData instances will be created. | 371 #if defined(TRACK_ALL_TASK_OBJECTS) |
472 if (!StartTracking(false)) | 372 if (status_ == ACTIVE) |
473 return; | 373 return base::TimeTicks::Now(); |
474 | 374 #endif |
475 RunOnAllThreads(ShutdownDisablingFurtherTracking); | 375 return base::TimeTicks(); // Super fast when disabled, or not compiled in. |
476 | |
477 // Now the *only* threads that might change the database are the threads with | |
478 // no messages loops. They might still be adding data to their birth records, | |
479 // but since no objects are deleted on those threads, there will be no further | |
480 // access to to cross-thread data. | |
481 // We could do a cleanup on all threads except for the ones without | |
482 // MessageLoops, but we won't bother doing cleanup (destruction of data) yet. | |
483 return; | |
484 } | 376 } |
485 #endif | |
486 | 377 |
487 // static | 378 // static |
488 void ThreadData::ShutdownSingleThreadedCleanup() { | 379 void ThreadData::ShutdownSingleThreadedCleanup() { |
380 NOTREACHED(); | |
jar (doing other things)
2011/10/14 15:48:42
This has to be removed.
This code is used is sing
| |
489 // We must be single threaded... but be careful anyway. | 381 // We must be single threaded... but be careful anyway. |
490 if (!StartTracking(false)) | 382 if (!StartTracking(false)) |
491 return; | 383 return; |
492 ThreadData* thread_data_list; | 384 ThreadData* thread_data_list; |
493 { | 385 { |
494 base::AutoLock lock(list_lock_); | 386 base::AutoLock lock(list_lock_); |
495 thread_data_list = first_; | 387 thread_data_list = first_; |
496 first_ = NULL; | 388 first_ = NULL; |
497 } | 389 } |
498 | 390 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
534 } | 426 } |
535 | 427 |
536 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) | 428 Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) |
537 : birth_(&birth_on_thread), | 429 : birth_(&birth_on_thread), |
538 death_thread_(NULL), | 430 death_thread_(NULL), |
539 death_data_(DeathData(count)) { | 431 death_data_(DeathData(count)) { |
540 } | 432 } |
541 | 433 |
542 const std::string Snapshot::DeathThreadName() const { | 434 const std::string Snapshot::DeathThreadName() const { |
543 if (death_thread_) | 435 if (death_thread_) |
544 return death_thread_->ThreadName(); | 436 return death_thread_->thread_name(); |
545 return "Still_Alive"; | 437 return "Still_Alive"; |
546 } | 438 } |
547 | 439 |
548 void Snapshot::Write(std::string* output) const { | 440 void Snapshot::Write(std::string* output) const { |
549 death_data_.Write(output); | 441 death_data_.Write(output); |
550 base::StringAppendF(output, "%s->%s ", | 442 base::StringAppendF(output, "%s->%s ", |
551 birth_->birth_thread()->ThreadName().c_str(), | 443 birth_->birth_thread()->thread_name().c_str(), |
552 death_thread_->ThreadName().c_str()); | 444 death_thread_->thread_name().c_str()); |
553 birth_->location().Write(true, true, output); | 445 birth_->location().Write(true, true, output); |
554 } | 446 } |
555 | 447 |
556 void Snapshot::Add(const Snapshot& other) { | 448 void Snapshot::Add(const Snapshot& other) { |
557 death_data_.AddDeathData(other.death_data_); | 449 death_data_.AddDeathData(other.death_data_); |
558 } | 450 } |
559 | 451 |
560 //------------------------------------------------------------------------------ | 452 //------------------------------------------------------------------------------ |
561 // DataCollector | 453 // DataCollector |
562 | 454 |
563 DataCollector::DataCollector() { | 455 DataCollector::DataCollector() { |
564 DCHECK(ThreadData::IsActive()); | 456 DCHECK(ThreadData::IsActive()); |
565 | 457 |
566 // Get an unchanging copy of a ThreadData list. | 458 // Get an unchanging copy of a ThreadData list. |
567 ThreadData* my_list = ThreadData::current()->first(); | 459 ThreadData* my_list = ThreadData::FactoryGet(NULL)->first(); |
568 | 460 |
569 count_of_contributing_threads_ = 0; | 461 count_of_contributing_threads_ = 0; |
570 for (ThreadData* thread_data = my_list; | 462 for (ThreadData* thread_data = my_list; |
571 thread_data; | 463 thread_data; |
572 thread_data = thread_data->next()) { | 464 thread_data = thread_data->next()) { |
573 ++count_of_contributing_threads_; | 465 ++count_of_contributing_threads_; |
574 } | 466 } |
575 | 467 |
576 // Gather data serially. A different constructor could be used to do in | 468 // Gather data serially. |
577 // parallel, and then invoke an OnCompletion task. | |
578 // This hackish approach *can* get some slighly corrupt tallies, as we are | 469 // This hackish approach *can* get some slighly corrupt tallies, as we are |
579 // grabbing values without the protection of a lock, but it has the advantage | 470 // grabbing values without the protection of a lock, but it has the advantage |
580 // of working even with threads that don't have message loops. If a user | 471 // of working even with threads that don't have message loops. If a user |
581 // sees any strangeness, they can always just run their stats gathering a | 472 // sees any strangeness, they can always just run their stats gathering a |
582 // second time. | 473 // second time. |
583 // TODO(jar): Provide version that gathers stats safely via PostTask in all | |
584 // cases where thread_data supplies a message_loop to post to. Be careful to | |
585 // handle message_loops that are destroyed!?! | |
586 for (ThreadData* thread_data = my_list; | 474 for (ThreadData* thread_data = my_list; |
587 thread_data; | 475 thread_data; |
588 thread_data = thread_data->next()) { | 476 thread_data = thread_data->next()) { |
589 Append(*thread_data); | 477 Append(*thread_data); |
590 } | 478 } |
591 } | 479 } |
592 | 480 |
593 DataCollector::~DataCollector() { | 481 DataCollector::~DataCollector() { |
594 } | 482 } |
595 | 483 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
674 base::StringAppendF(output, "All born in %s. ", | 562 base::StringAppendF(output, "All born in %s. ", |
675 birth_files_.begin()->first.c_str()); | 563 birth_files_.begin()->first.c_str()); |
676 } | 564 } |
677 } | 565 } |
678 | 566 |
679 if (birth_threads_.size() > 1) { | 567 if (birth_threads_.size() > 1) { |
680 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", | 568 base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", |
681 birth_threads_.size()); | 569 birth_threads_.size()); |
682 } else { | 570 } else { |
683 base::StringAppendF(output, "All born on %s. ", | 571 base::StringAppendF(output, "All born on %s. ", |
684 birth_threads_.begin()->first->ThreadName().c_str()); | 572 birth_threads_.begin()->first->thread_name().c_str()); |
685 } | 573 } |
686 | 574 |
687 if (death_threads_.size() > 1) { | 575 if (death_threads_.size() > 1) { |
688 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", | 576 base::StringAppendF(output, "%" PRIuS " DeathThreads. ", |
689 death_threads_.size()); | 577 death_threads_.size()); |
690 } else { | 578 } else { |
691 if (death_threads_.begin()->first) { | 579 if (death_threads_.begin()->first) { |
692 base::StringAppendF(output, "All deleted on %s. ", | 580 base::StringAppendF(output, "All deleted on %s. ", |
693 death_threads_.begin()->first->ThreadName().c_str()); | 581 death_threads_.begin()->first->thread_name().c_str()); |
694 } else { | 582 } else { |
695 output->append("All these objects are still alive."); | 583 output->append("All these objects are still alive."); |
696 } | 584 } |
697 } | 585 } |
698 | 586 |
699 if (birth_count_ > 1) | 587 if (birth_count_ > 1) |
700 base::StringAppendF(output, "Births=%d ", birth_count_); | 588 base::StringAppendF(output, "Births=%d ", birth_count_); |
701 | 589 |
702 DeathData::Write(output); | 590 DeathData::Write(output); |
703 } | 591 } |
(...skipping 24 matching lines...) Expand all Loading... | |
728 } | 616 } |
729 use_tiebreaker_for_sort_only_ = false; | 617 use_tiebreaker_for_sort_only_ = false; |
730 selector_ = NIL; | 618 selector_ = NIL; |
731 } | 619 } |
732 | 620 |
733 bool Comparator::operator()(const Snapshot& left, | 621 bool Comparator::operator()(const Snapshot& left, |
734 const Snapshot& right) const { | 622 const Snapshot& right) const { |
735 switch (selector_) { | 623 switch (selector_) { |
736 case BIRTH_THREAD: | 624 case BIRTH_THREAD: |
737 if (left.birth_thread() != right.birth_thread() && | 625 if (left.birth_thread() != right.birth_thread() && |
738 left.birth_thread()->ThreadName() != | 626 left.birth_thread()->thread_name() != |
739 right.birth_thread()->ThreadName()) | 627 right.birth_thread()->thread_name()) |
740 return left.birth_thread()->ThreadName() < | 628 return left.birth_thread()->thread_name() < |
741 right.birth_thread()->ThreadName(); | 629 right.birth_thread()->thread_name(); |
742 break; | 630 break; |
743 | 631 |
744 case DEATH_THREAD: | 632 case DEATH_THREAD: |
745 if (left.death_thread() != right.death_thread() && | 633 if (left.death_thread() != right.death_thread() && |
746 left.DeathThreadName() != | 634 left.DeathThreadName() != |
747 right.DeathThreadName()) { | 635 right.DeathThreadName()) { |
748 if (!left.death_thread()) | 636 if (!left.death_thread()) |
749 return true; | 637 return true; |
750 if (!right.death_thread()) | 638 if (!right.death_thread()) |
751 return false; | 639 return false; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
783 return left.count() > right.count(); // Sort large at front of vector. | 671 return left.count() > right.count(); // Sort large at front of vector. |
784 break; | 672 break; |
785 | 673 |
786 case AVERAGE_DURATION: | 674 case AVERAGE_DURATION: |
787 if (!left.count() || !right.count()) | 675 if (!left.count() || !right.count()) |
788 break; | 676 break; |
789 if (left.AverageMsDuration() != right.AverageMsDuration()) | 677 if (left.AverageMsDuration() != right.AverageMsDuration()) |
790 return left.AverageMsDuration() > right.AverageMsDuration(); | 678 return left.AverageMsDuration() > right.AverageMsDuration(); |
791 break; | 679 break; |
792 | 680 |
681 case TOTAL_DURATION: | |
682 if (!left.count() || !right.count()) | |
683 break; | |
684 if (left.life_duration() != right.life_duration()) | |
685 return left.life_duration() > right.life_duration(); | |
686 break; | |
687 | |
793 default: | 688 default: |
794 break; | 689 break; |
795 } | 690 } |
796 if (tiebreaker_) | 691 if (tiebreaker_) |
797 return tiebreaker_->operator()(left, right); | 692 return tiebreaker_->operator()(left, right); |
798 return false; | 693 return false; |
799 } | 694 } |
800 | 695 |
801 void Comparator::Sort(DataCollector::Collection* collection) const { | 696 void Comparator::Sort(DataCollector::Collection* collection) const { |
802 std::sort(collection->begin(), collection->end(), *this); | 697 std::sort(collection->begin(), collection->end(), *this); |
803 } | 698 } |
804 | 699 |
805 bool Comparator::Equivalent(const Snapshot& left, | 700 bool Comparator::Equivalent(const Snapshot& left, |
806 const Snapshot& right) const { | 701 const Snapshot& right) const { |
807 switch (selector_) { | 702 switch (selector_) { |
808 case BIRTH_THREAD: | 703 case BIRTH_THREAD: |
809 if (left.birth_thread() != right.birth_thread() && | 704 if (left.birth_thread() != right.birth_thread() && |
810 left.birth_thread()->ThreadName() != | 705 left.birth_thread()->thread_name() != |
811 right.birth_thread()->ThreadName()) | 706 right.birth_thread()->thread_name()) |
812 return false; | 707 return false; |
813 break; | 708 break; |
814 | 709 |
815 case DEATH_THREAD: | 710 case DEATH_THREAD: |
816 if (left.death_thread() != right.death_thread() && | 711 if (left.death_thread() != right.death_thread() && |
817 left.DeathThreadName() != right.DeathThreadName()) | 712 left.DeathThreadName() != right.DeathThreadName()) |
818 return false; | 713 return false; |
819 break; | 714 break; |
820 | 715 |
821 case BIRTH_FILE: | 716 case BIRTH_FILE: |
822 if (left.location().file_name() != right.location().file_name()) { | 717 if (left.location().file_name() != right.location().file_name()) { |
823 int comp = strcmp(left.location().file_name(), | 718 int comp = strcmp(left.location().file_name(), |
824 right.location().file_name()); | 719 right.location().file_name()); |
825 if (comp) | 720 if (comp) |
826 return false; | 721 return false; |
827 } | 722 } |
828 break; | 723 break; |
829 | 724 |
830 case BIRTH_FUNCTION: | 725 case BIRTH_FUNCTION: |
831 if (left.location().function_name() != right.location().function_name()) { | 726 if (left.location().function_name() != right.location().function_name()) { |
832 int comp = strcmp(left.location().function_name(), | 727 int comp = strcmp(left.location().function_name(), |
833 right.location().function_name()); | 728 right.location().function_name()); |
834 if (comp) | 729 if (comp) |
835 return false; | 730 return false; |
836 } | 731 } |
837 break; | 732 break; |
838 | 733 |
839 case COUNT: | 734 case COUNT: |
840 if (left.count() != right.count()) | |
jar (doing other things)
2011/10/14 02:29:53
I decided it was stupid to aggregate groups of res
| |
841 return false; | |
842 break; | |
843 | |
844 case AVERAGE_DURATION: | 735 case AVERAGE_DURATION: |
845 if (left.life_duration() != right.life_duration()) | 736 case TOTAL_DURATION: |
846 return false; | 737 // We don't produce separate aggretation when only counts or times differ. |
847 break; | 738 break; |
848 | 739 |
849 default: | 740 default: |
850 break; | 741 break; |
851 } | 742 } |
852 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) | 743 if (tiebreaker_ && !use_tiebreaker_for_sort_only_) |
853 return tiebreaker_->Equivalent(left, right); | 744 return tiebreaker_->Equivalent(left, right); |
854 return true; | 745 return true; |
855 } | 746 } |
856 | 747 |
857 bool Comparator::Acceptable(const Snapshot& sample) const { | 748 bool Comparator::Acceptable(const Snapshot& sample) const { |
858 if (required_.size()) { | 749 if (required_.size()) { |
859 switch (selector_) { | 750 switch (selector_) { |
860 case BIRTH_THREAD: | 751 case BIRTH_THREAD: |
861 if (sample.birth_thread()->ThreadName().find(required_) == | 752 if (sample.birth_thread()->thread_name().find(required_) == |
862 std::string::npos) | 753 std::string::npos) |
863 return false; | 754 return false; |
864 break; | 755 break; |
865 | 756 |
866 case DEATH_THREAD: | 757 case DEATH_THREAD: |
867 if (sample.DeathThreadName().find(required_) == std::string::npos) | 758 if (sample.DeathThreadName().find(required_) == std::string::npos) |
868 return false; | 759 return false; |
869 break; | 760 break; |
870 | 761 |
871 case BIRTH_FILE: | 762 case BIRTH_FILE: |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
927 } | 818 } |
928 | 819 |
929 void Comparator::ParseKeyphrase(const std::string& key_phrase) { | 820 void Comparator::ParseKeyphrase(const std::string& key_phrase) { |
930 typedef std::map<const std::string, Selector> KeyMap; | 821 typedef std::map<const std::string, Selector> KeyMap; |
931 static KeyMap key_map; | 822 static KeyMap key_map; |
932 static bool initialized = false; | 823 static bool initialized = false; |
933 if (!initialized) { | 824 if (!initialized) { |
934 initialized = true; | 825 initialized = true; |
935 // Sorting and aggretation keywords, which specify how to sort the data, or | 826 // Sorting and aggretation keywords, which specify how to sort the data, or |
936 // can specify a required match from the specified field in the record. | 827 // can specify a required match from the specified field in the record. |
937 key_map["count"] = COUNT; | 828 key_map["count"] = COUNT; |
938 key_map["duration"] = AVERAGE_DURATION; | 829 key_map["totalduration"] = TOTAL_DURATION; |
939 key_map["birth"] = BIRTH_THREAD; | 830 key_map["duration"] = AVERAGE_DURATION; |
940 key_map["death"] = DEATH_THREAD; | 831 key_map["birth"] = BIRTH_THREAD; |
941 key_map["file"] = BIRTH_FILE; | 832 key_map["death"] = DEATH_THREAD; |
942 key_map["function"] = BIRTH_FUNCTION; | 833 key_map["file"] = BIRTH_FILE; |
943 key_map["line"] = BIRTH_LINE; | 834 key_map["function"] = BIRTH_FUNCTION; |
835 key_map["line"] = BIRTH_LINE; | |
944 | 836 |
945 // Immediate commands that do not involve setting sort order. | 837 // Immediate commands that do not involve setting sort order. |
946 key_map["reset"] = RESET_ALL_DATA; | 838 key_map["reset"] = RESET_ALL_DATA; |
947 } | 839 } |
948 | 840 |
949 std::string required; | 841 std::string required; |
950 // Watch for: "sort_key=value" as we parse. | 842 // Watch for: "sort_key=value" as we parse. |
951 size_t equal_offset = key_phrase.find('=', 0); | 843 size_t equal_offset = key_phrase.find('=', 0); |
952 if (key_phrase.npos != equal_offset) { | 844 if (key_phrase.npos != equal_offset) { |
953 // There is a value that must be matched for the data to display. | 845 // There is a value that must be matched for the data to display. |
(...skipping 15 matching lines...) Expand all Loading... | |
969 for (size_t i = 0; i < query.size();) { | 861 for (size_t i = 0; i < query.size();) { |
970 size_t slash_offset = query.find('/', i); | 862 size_t slash_offset = query.find('/', i); |
971 ParseKeyphrase(query.substr(i, slash_offset - i)); | 863 ParseKeyphrase(query.substr(i, slash_offset - i)); |
972 if (query.npos == slash_offset) | 864 if (query.npos == slash_offset) |
973 break; | 865 break; |
974 i = slash_offset + 1; | 866 i = slash_offset + 1; |
975 } | 867 } |
976 | 868 |
977 // Select subgroup ordering (if we want to display the subgroup) | 869 // Select subgroup ordering (if we want to display the subgroup) |
978 SetSubgroupTiebreaker(COUNT); | 870 SetSubgroupTiebreaker(COUNT); |
871 SetSubgroupTiebreaker(TOTAL_DURATION); | |
979 SetSubgroupTiebreaker(AVERAGE_DURATION); | 872 SetSubgroupTiebreaker(AVERAGE_DURATION); |
980 SetSubgroupTiebreaker(BIRTH_THREAD); | 873 SetSubgroupTiebreaker(BIRTH_THREAD); |
981 SetSubgroupTiebreaker(DEATH_THREAD); | 874 SetSubgroupTiebreaker(DEATH_THREAD); |
982 SetSubgroupTiebreaker(BIRTH_FUNCTION); | 875 SetSubgroupTiebreaker(BIRTH_FUNCTION); |
983 SetSubgroupTiebreaker(BIRTH_FILE); | 876 SetSubgroupTiebreaker(BIRTH_FILE); |
984 SetSubgroupTiebreaker(BIRTH_LINE); | 877 SetSubgroupTiebreaker(BIRTH_LINE); |
985 | 878 |
986 return true; | 879 return true; |
987 } | 880 } |
988 | 881 |
989 bool Comparator::WriteSortGrouping(const Snapshot& sample, | 882 bool Comparator::WriteSortGrouping(const Snapshot& sample, |
990 std::string* output) const { | 883 std::string* output) const { |
991 bool wrote_data = false; | 884 bool wrote_data = false; |
992 switch (selector_) { | 885 switch (selector_) { |
993 case BIRTH_THREAD: | 886 case BIRTH_THREAD: |
994 base::StringAppendF(output, "All new on %s ", | 887 base::StringAppendF(output, "All new on %s ", |
995 sample.birth_thread()->ThreadName().c_str()); | 888 sample.birth_thread()->thread_name().c_str()); |
996 wrote_data = true; | 889 wrote_data = true; |
997 break; | 890 break; |
998 | 891 |
999 case DEATH_THREAD: | 892 case DEATH_THREAD: |
1000 if (sample.death_thread()) { | 893 if (sample.death_thread()) { |
1001 base::StringAppendF(output, "All deleted on %s ", | 894 base::StringAppendF(output, "All deleted on %s ", |
1002 sample.DeathThreadName().c_str()); | 895 sample.DeathThreadName().c_str()); |
1003 } else { | 896 } else { |
1004 output->append("All still alive "); | 897 output->append("All still alive "); |
1005 } | 898 } |
(...skipping 20 matching lines...) Expand all Loading... | |
1026 return wrote_data; | 919 return wrote_data; |
1027 } | 920 } |
1028 | 921 |
1029 void Comparator::WriteSnapshot(const Snapshot& sample, | 922 void Comparator::WriteSnapshot(const Snapshot& sample, |
1030 std::string* output) const { | 923 std::string* output) const { |
1031 sample.death_data().Write(output); | 924 sample.death_data().Write(output); |
1032 if (!(combined_selectors_ & BIRTH_THREAD) || | 925 if (!(combined_selectors_ & BIRTH_THREAD) || |
1033 !(combined_selectors_ & DEATH_THREAD)) | 926 !(combined_selectors_ & DEATH_THREAD)) |
1034 base::StringAppendF(output, "%s->%s ", | 927 base::StringAppendF(output, "%s->%s ", |
1035 (combined_selectors_ & BIRTH_THREAD) ? "*" : | 928 (combined_selectors_ & BIRTH_THREAD) ? "*" : |
1036 sample.birth().birth_thread()->ThreadName().c_str(), | 929 sample.birth().birth_thread()->thread_name().c_str(), |
1037 (combined_selectors_ & DEATH_THREAD) ? "*" : | 930 (combined_selectors_ & DEATH_THREAD) ? "*" : |
1038 sample.DeathThreadName().c_str()); | 931 sample.DeathThreadName().c_str()); |
1039 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), | 932 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), |
1040 !(combined_selectors_ & BIRTH_FUNCTION), | 933 !(combined_selectors_ & BIRTH_FUNCTION), |
1041 output); | 934 output); |
1042 } | 935 } |
1043 | 936 |
1044 } // namespace tracked_objects | 937 } // namespace tracked_objects |
OLD | NEW |