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 namespace { |
| 20 // Flag to compile out almost all of the task tracking code. |
| 21 static const bool kTrackAllTaskObjects = true; |
19 | 22 |
20 #if defined(TRACK_ALL_TASK_OBJECTS) | 23 // When ThreadData is first initialized, should we start in an ACTIVE state to |
21 static const bool kTrackAllTaskObjects = true; | 24 // record all of the startup-time tasks, or should we start up DEACTIVATED, so |
22 #else | 25 // that we only record after parsing the command line flag --enable-tracking. |
23 static const bool kTrackAllTaskObjects = false; | 26 // Note that the flag may force either state, so this really controls only the |
24 #endif | 27 // period of time up until that flag is parsed. If there is no flag seen, then |
25 | 28 // this state may prevail for much or all of the process lifetime. |
26 // Can we count on thread termination to call for thread cleanup? If not, then | 29 static const ThreadData::Status kInitialStartupState = ThreadData::ACTIVE; |
27 // we can't risk putting references to ThreadData in TLS, as it will leak on | 30 } // anonymous namespace. |
28 // worker thread termination. | |
29 static const bool kWorkerThreadCleanupSupported = true; | |
30 | |
31 // A TLS slot which points to the ThreadData instance for the current thread. We | |
32 // do a fake initialization here (zeroing out data), and then the real in-place | |
33 // construction happens when we call tls_index_.Initialize(). | |
34 // static | |
35 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); | |
36 | |
37 // A global state variable to prevent repeated initialization during tests. | |
38 // static | |
39 AutoTracking::State AutoTracking::state_ = AutoTracking::kNeverBeenRun; | |
40 | |
41 // A locked protected counter to assign sequence number to threads. | |
42 // static | |
43 int ThreadData::thread_number_counter_ = 0; | |
44 | 31 |
45 //------------------------------------------------------------------------------ | 32 //------------------------------------------------------------------------------ |
46 // Death data tallies durations when a death takes place. | 33 // Death data tallies durations when a death takes place. |
47 | 34 |
48 void DeathData::RecordDeath(const TimeDelta& queue_duration, | 35 void DeathData::RecordDeath(const Duration& queue_duration, |
49 const TimeDelta& run_duration) { | 36 const Duration& run_duration) { |
50 ++count_; | 37 ++count_; |
51 queue_duration_ += queue_duration; | 38 queue_duration_ += queue_duration; |
52 run_duration_ += run_duration; | 39 run_duration_ += run_duration; |
53 } | 40 } |
54 | 41 |
55 int DeathData::AverageMsRunDuration() const { | 42 int DeathData::AverageMsRunDuration() const { |
56 if (run_duration_ == base::TimeDelta()) | 43 if (run_duration_ == Duration() || !count_) |
57 return 0; | 44 return 0; |
58 return static_cast<int>(run_duration_.InMilliseconds() / count_); | 45 // Add half of denominator to achieve rounding. |
| 46 return static_cast<int>(run_duration_.InMilliseconds() + count_ / 2) / |
| 47 count_; |
59 } | 48 } |
60 | 49 |
61 int DeathData::AverageMsQueueDuration() const { | 50 int DeathData::AverageMsQueueDuration() const { |
62 if (queue_duration_ == base::TimeDelta()) | 51 if (queue_duration_ == Duration() || !count_) |
63 return 0; | 52 return 0; |
64 return static_cast<int>(queue_duration_.InMilliseconds() / count_); | 53 // Add half of denominator to achieve rounding. |
| 54 return (static_cast<int>(queue_duration_.InMilliseconds() + count_ / 2) / |
| 55 count_); |
65 } | 56 } |
66 | 57 |
67 void DeathData::AddDeathData(const DeathData& other) { | 58 void DeathData::AddDeathData(const DeathData& other) { |
68 count_ += other.count_; | 59 count_ += other.count_; |
69 queue_duration_ += other.queue_duration_; | 60 queue_duration_ += other.queue_duration_; |
70 run_duration_ += other.run_duration_; | 61 run_duration_ += other.run_duration_; |
71 } | 62 } |
72 | 63 |
73 void DeathData::WriteHTML(std::string* output) const { | 64 void DeathData::WriteHTML(std::string* output) const { |
74 if (!count_) | 65 if (!count_) |
75 return; | 66 return; |
76 base::StringAppendF(output, "%s:%d, ", | 67 base::StringAppendF(output, "%s:%d, ", |
77 (count_ == 1) ? "Life" : "Lives", count_); | 68 (count_ == 1) ? "Life" : "Lives", count_); |
78 base::StringAppendF(output, "Run:%"PRId64"ms(%dms/life) ", | 69 // Be careful to leave static_casts intact, as the type returned by |
79 run_duration_.InMilliseconds(), | 70 // InMilliseconds() may not always be an int, even if it can generally fit |
| 71 // into an int. |
| 72 base::StringAppendF(output, "Run:%dms(%dms/life) ", |
| 73 static_cast<int>(run_duration_.InMilliseconds()), |
80 AverageMsRunDuration()); | 74 AverageMsRunDuration()); |
81 base::StringAppendF(output, "Queue:%"PRId64"ms(%dms/life) ", | 75 base::StringAppendF(output, "Queue:%dms(%dms/life) ", |
82 queue_duration_.InMilliseconds(), | 76 static_cast<int>(queue_duration_.InMilliseconds()), |
83 AverageMsQueueDuration()); | 77 AverageMsQueueDuration()); |
84 } | 78 } |
85 | 79 |
86 base::DictionaryValue* DeathData::ToValue() const { | 80 base::DictionaryValue* DeathData::ToValue() const { |
87 base::DictionaryValue* dictionary = new base::DictionaryValue; | 81 base::DictionaryValue* dictionary = new base::DictionaryValue; |
88 dictionary->Set("count", base::Value::CreateIntegerValue(count_)); | 82 dictionary->Set("count", base::Value::CreateIntegerValue(count_)); |
89 dictionary->Set("run_ms", | 83 dictionary->Set("run_ms", |
90 base::Value::CreateIntegerValue(run_duration_.InMilliseconds())); | 84 base::Value::CreateIntegerValue(run_duration_.InMilliseconds())); |
91 dictionary->Set("queue_ms", | 85 dictionary->Set("queue_ms", |
92 base::Value::CreateIntegerValue(queue_duration_.InMilliseconds())); | 86 base::Value::CreateIntegerValue(queue_duration_.InMilliseconds())); |
93 return dictionary; | 87 return dictionary; |
94 } | 88 } |
95 | 89 |
96 void DeathData::Clear() { | 90 void DeathData::Clear() { |
97 count_ = 0; | 91 count_ = 0; |
98 queue_duration_ = TimeDelta(); | 92 queue_duration_ = Duration(); |
99 run_duration_ = TimeDelta(); | 93 run_duration_ = Duration(); |
100 } | 94 } |
101 | 95 |
102 //------------------------------------------------------------------------------ | 96 //------------------------------------------------------------------------------ |
103 BirthOnThread::BirthOnThread(const Location& location, | 97 BirthOnThread::BirthOnThread(const Location& location, |
104 const ThreadData& current) | 98 const ThreadData& current) |
105 : location_(location), | 99 : location_(location), |
106 birth_thread_(¤t) {} | 100 birth_thread_(¤t) {} |
107 | 101 |
108 //------------------------------------------------------------------------------ | 102 //------------------------------------------------------------------------------ |
109 Births::Births(const Location& location, const ThreadData& current) | 103 Births::Births(const Location& location, const ThreadData& current) |
110 : BirthOnThread(location, current), | 104 : BirthOnThread(location, current), |
111 birth_count_(1) { } | 105 birth_count_(1) { } |
112 | 106 |
113 //------------------------------------------------------------------------------ | 107 //------------------------------------------------------------------------------ |
114 // ThreadData maintains the central data for all births and deaths. | 108 // ThreadData maintains the central data for all births and deaths. |
115 | 109 |
| 110 // TODO(jar): We should pull all these static vars together, into a struct, and |
| 111 // optimize layout so that we benefit from locality of reference during accesses |
| 112 // to them. |
| 113 |
| 114 // A TLS slot which points to the ThreadData instance for the current thread. We |
| 115 // do a fake initialization here (zeroing out data), and then the real in-place |
| 116 // construction happens when we call tls_index_.Initialize(). |
| 117 // static |
| 118 base::ThreadLocalStorage::Slot ThreadData::tls_index_(base::LINKER_INITIALIZED); |
| 119 |
| 120 // A lock-protected counter to assign sequence number to threads. |
| 121 // static |
| 122 int ThreadData::thread_number_counter_ = 0; |
| 123 |
116 // static | 124 // static |
117 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; | 125 ThreadData* ThreadData::all_thread_data_list_head_ = NULL; |
118 | 126 |
119 // static | 127 // static |
120 ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL; | 128 ThreadData::ThreadDataPool* ThreadData::unregistered_thread_data_pool_ = NULL; |
121 | 129 |
122 // static | 130 // static |
123 base::Lock ThreadData::list_lock_; | 131 base::Lock* ThreadData::list_lock_; |
124 | 132 |
125 // static | 133 // static |
126 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; | 134 ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; |
127 | 135 |
128 ThreadData::ThreadData(const std::string& suggested_name) | 136 ThreadData::ThreadData(const std::string& suggested_name) |
129 : next_(NULL), | 137 : next_(NULL), |
130 is_a_worker_thread_(false) { | 138 is_a_worker_thread_(false) { |
131 DCHECK_GE(suggested_name.size(), 0u); | 139 DCHECK_GE(suggested_name.size(), 0u); |
132 thread_name_ = suggested_name; | 140 thread_name_ = suggested_name; |
133 PushToHeadOfList(); | 141 PushToHeadOfList(); |
134 } | 142 } |
135 | 143 |
136 ThreadData::ThreadData() : next_(NULL), is_a_worker_thread_(true) { | 144 ThreadData::ThreadData() : next_(NULL), is_a_worker_thread_(true) { |
137 int thread_number; | 145 int thread_number; |
138 { | 146 { |
139 base::AutoLock lock(list_lock_); | 147 base::AutoLock lock(*list_lock_); |
140 thread_number = ++thread_number_counter_; | 148 thread_number = ++thread_number_counter_; |
141 } | 149 } |
142 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); | 150 base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number); |
143 PushToHeadOfList(); | 151 PushToHeadOfList(); |
144 } | 152 } |
145 | 153 |
146 ThreadData::~ThreadData() {} | 154 ThreadData::~ThreadData() {} |
147 | 155 |
148 void ThreadData::PushToHeadOfList() { | 156 void ThreadData::PushToHeadOfList() { |
149 DCHECK(!next_); | 157 DCHECK(!next_); |
150 base::AutoLock lock(list_lock_); | 158 base::AutoLock lock(*list_lock_); |
151 next_ = all_thread_data_list_head_; | 159 next_ = all_thread_data_list_head_; |
152 all_thread_data_list_head_ = this; | 160 all_thread_data_list_head_ = this; |
153 } | 161 } |
154 | 162 |
155 // static | 163 // static |
156 void ThreadData::InitializeThreadContext(const std::string& suggested_name) { | 164 void ThreadData::InitializeThreadContext(const std::string& suggested_name) { |
157 if (!tls_index_.initialized()) | 165 if (!Initialize()) // Always initialize if needed. |
158 return; // For unittests only. | 166 return; |
159 DCHECK_EQ(tls_index_.Get(), reinterpret_cast<void*>(NULL)); | 167 ThreadData* current_thread_data = |
160 ThreadData* current_thread_data = new ThreadData(suggested_name); | 168 reinterpret_cast<ThreadData*>(tls_index_.Get()); |
| 169 if (current_thread_data) |
| 170 return; // Browser tests instigate this. |
| 171 current_thread_data = new ThreadData(suggested_name); |
161 tls_index_.Set(current_thread_data); | 172 tls_index_.Set(current_thread_data); |
162 } | 173 } |
163 | 174 |
164 // static | 175 // static |
165 ThreadData* ThreadData::Get() { | 176 ThreadData* ThreadData::Get() { |
166 if (!tls_index_.initialized()) | 177 if (!tls_index_.initialized()) |
167 return NULL; // For unittests only. | 178 return NULL; // For unittests only. |
168 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); | 179 ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get()); |
169 if (registered) | 180 if (registered) |
170 return registered; | 181 return registered; |
171 | 182 |
172 // We must be a worker thread, since we didn't pre-register. | 183 // We must be a worker thread, since we didn't pre-register. |
173 ThreadData* worker_thread_data = NULL; | 184 ThreadData* worker_thread_data = NULL; |
174 { | 185 { |
175 base::AutoLock lock(list_lock_); | 186 base::AutoLock lock(*list_lock_); |
176 if (!unregistered_thread_data_pool_->empty()) { | 187 if (!unregistered_thread_data_pool_->empty()) { |
177 worker_thread_data = | 188 worker_thread_data = |
178 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); | 189 const_cast<ThreadData*>(unregistered_thread_data_pool_->top()); |
179 unregistered_thread_data_pool_->pop(); | 190 unregistered_thread_data_pool_->pop(); |
180 } | 191 } |
181 } | 192 } |
182 | 193 |
183 // If we can't find a previously used instance, then we have to create one. | 194 // If we can't find a previously used instance, then we have to create one. |
184 if (!worker_thread_data) | 195 if (!worker_thread_data) |
185 worker_thread_data = new ThreadData(); | 196 worker_thread_data = new ThreadData(); |
(...skipping 10 matching lines...) Expand all Loading... |
196 if (!thread_data) | 207 if (!thread_data) |
197 return; | 208 return; |
198 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); | 209 reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); |
199 DCHECK_EQ(tls_index_.Get(), reinterpret_cast<ThreadData*>(NULL)); | 210 DCHECK_EQ(tls_index_.Get(), reinterpret_cast<ThreadData*>(NULL)); |
200 } | 211 } |
201 | 212 |
202 void ThreadData::OnThreadTerminationCleanup() const { | 213 void ThreadData::OnThreadTerminationCleanup() const { |
203 tls_index_.Set(NULL); | 214 tls_index_.Set(NULL); |
204 if (!is_a_worker_thread_) | 215 if (!is_a_worker_thread_) |
205 return; | 216 return; |
206 base::AutoLock lock(list_lock_); | 217 base::AutoLock lock(*list_lock_); |
207 unregistered_thread_data_pool_->push(this); | 218 unregistered_thread_data_pool_->push(this); |
208 } | 219 } |
209 | 220 |
210 // static | 221 // static |
211 void ThreadData::WriteHTML(const std::string& query, std::string* output) { | 222 void ThreadData::WriteHTML(const std::string& query, std::string* output) { |
212 if (!ThreadData::IsActive()) | 223 if (!ThreadData::tracking_status()) |
213 return; // Not yet initialized. | 224 return; // Not yet initialized. |
214 | 225 |
215 DataCollector collected_data; // Gather data. | 226 DataCollector collected_data; // Gather data. |
216 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 227 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
217 | 228 |
218 // Data Gathering is complete. Now to sort/process/render. | 229 // Data Gathering is complete. Now to sort/process/render. |
219 DataCollector::Collection* collection = collected_data.collection(); | 230 DataCollector::Collection* collection = collected_data.collection(); |
220 | 231 |
221 // Create filtering and sort comparison object. | 232 // Create filtering and sort comparison object. |
222 Comparator comparator; | 233 Comparator comparator; |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 // Print aggregate stats for the group. | 320 // Print aggregate stats for the group. |
310 output->append("<br>"); | 321 output->append("<br>"); |
311 subtotals.WriteHTML(output); | 322 subtotals.WriteHTML(output); |
312 output->append("<br><hr><br>"); | 323 output->append("<br><hr><br>"); |
313 subtotals.Clear(); | 324 subtotals.Clear(); |
314 } | 325 } |
315 } | 326 } |
316 } | 327 } |
317 | 328 |
318 // static | 329 // static |
319 base::Value* ThreadData::ToValue(int process_type) { | 330 base::DictionaryValue* ThreadData::ToValue() { |
320 DataCollector collected_data; // Gather data. | 331 DataCollector collected_data; // Gather data. |
321 collected_data.AddListOfLivingObjects(); // Add births that are still alive. | 332 collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
322 base::ListValue* list = collected_data.ToValue(); | 333 base::ListValue* list = collected_data.ToValue(); |
323 base::DictionaryValue* dictionary = new base::DictionaryValue(); | 334 base::DictionaryValue* dictionary = new base::DictionaryValue(); |
324 dictionary->Set("list", list); | 335 dictionary->Set("list", list); |
325 dictionary->SetInteger("process", process_type); | |
326 return dictionary; | 336 return dictionary; |
327 } | 337 } |
328 | 338 |
329 Births* ThreadData::TallyABirth(const Location& location) { | 339 Births* ThreadData::TallyABirth(const Location& location) { |
330 BirthMap::iterator it = birth_map_.find(location); | 340 BirthMap::iterator it = birth_map_.find(location); |
331 if (it != birth_map_.end()) { | 341 if (it != birth_map_.end()) { |
332 it->second->RecordBirth(); | 342 it->second->RecordBirth(); |
333 return it->second; | 343 return it->second; |
334 } | 344 } |
335 | 345 |
336 Births* tracker = new Births(location, *this); | 346 Births* tracker = new Births(location, *this); |
337 // Lock since the map may get relocated now, and other threads sometimes | 347 // Lock since the map may get relocated now, and other threads sometimes |
338 // snapshot it (but they lock before copying it). | 348 // snapshot it (but they lock before copying it). |
339 base::AutoLock lock(lock_); | 349 base::AutoLock lock(lock_); |
340 birth_map_[location] = tracker; | 350 birth_map_[location] = tracker; |
341 return tracker; | 351 return tracker; |
342 } | 352 } |
343 | 353 |
344 void ThreadData::TallyADeath(const Births& birth, | 354 void ThreadData::TallyADeath(const Births& birth, |
345 const TimeDelta& queue_duration, | 355 const Duration& queue_duration, |
346 const TimeDelta& run_duration) { | 356 const Duration& run_duration) { |
347 DeathMap::iterator it = death_map_.find(&birth); | 357 DeathMap::iterator it = death_map_.find(&birth); |
348 DeathData* death_data; | 358 DeathData* death_data; |
349 if (it != death_map_.end()) { | 359 if (it != death_map_.end()) { |
350 death_data = &it->second; | 360 death_data = &it->second; |
351 } else { | 361 } else { |
352 base::AutoLock lock(lock_); // Lock since the map may get relocated now. | 362 base::AutoLock lock(lock_); // Lock since the map may get relocated now. |
353 death_data = &death_map_[&birth]; | 363 death_data = &death_map_[&birth]; |
354 } // Release lock ASAP. | 364 } // Release lock ASAP. |
355 death_data->RecordDeath(queue_duration, run_duration); | 365 death_data->RecordDeath(queue_duration, run_duration); |
356 } | 366 } |
357 | 367 |
358 // static | 368 // static |
359 Births* ThreadData::TallyABirthIfActive(const Location& location) { | 369 Births* ThreadData::TallyABirthIfActive(const Location& location) { |
360 if (!kTrackAllTaskObjects) | 370 if (!kTrackAllTaskObjects) |
361 return NULL; // Not compiled in. | 371 return NULL; // Not compiled in. |
362 | 372 |
363 if (!IsActive()) | 373 if (!tracking_status()) |
364 return NULL; | 374 return NULL; |
365 ThreadData* current_thread_data = Get(); | 375 ThreadData* current_thread_data = Get(); |
366 if (!current_thread_data) | 376 if (!current_thread_data) |
367 return NULL; | 377 return NULL; |
368 return current_thread_data->TallyABirth(location); | 378 return current_thread_data->TallyABirth(location); |
369 } | 379 } |
370 | 380 |
371 // static | 381 // static |
372 void ThreadData::TallyADeathIfActive(const Births* birth, | 382 void ThreadData::TallyRunOnNamedThreadIfTracking( |
373 const base::TimeTicks& time_posted, | 383 const base::TrackingInfo& completed_task, |
374 const base::TimeTicks& delayed_start_time, | 384 const TrackedTime& start_of_run, |
375 const base::TimeTicks& start_of_run, | 385 const TrackedTime& end_of_run) { |
376 const base::TimeTicks& end_of_run) { | |
377 if (!kTrackAllTaskObjects) | 386 if (!kTrackAllTaskObjects) |
378 return; // Not compiled in. | 387 return; // Not compiled in. |
379 | 388 |
380 if (!IsActive() || !birth) | 389 // Even if we have been DEACTIVATED, we will process any pending births so |
| 390 // that our data structures (which counted the outstanding births) remain |
| 391 // consistent. |
| 392 const Births* birth = completed_task.birth_tally; |
| 393 if (!birth) |
381 return; | 394 return; |
382 | |
383 ThreadData* current_thread_data = Get(); | 395 ThreadData* current_thread_data = Get(); |
384 if (!current_thread_data) | 396 if (!current_thread_data) |
385 return; | 397 return; |
386 | 398 |
387 // To avoid conflating our stats with the delay duration in a PostDelayedTask, | 399 // To avoid conflating our stats with the delay duration in a PostDelayedTask, |
388 // we identify such tasks, and replace their post_time with the time they | 400 // we identify such tasks, and replace their post_time with the time they |
389 // were sechudled (requested?) to emerge from the delayed task queue. This | 401 // were scheduled (requested?) to emerge from the delayed task queue. This |
390 // means that queueing delay for such tasks will show how long they went | 402 // means that queueing delay for such tasks will show how long they went |
391 // unserviced, after they *could* be serviced. This is the same stat as we | 403 // unserviced, after they *could* be serviced. This is the same stat as we |
392 // have for non-delayed tasks, and we consistently call it queueing delay. | 404 // have for non-delayed tasks, and we consistently call it queueing delay. |
393 base::TimeTicks effective_post_time = | 405 TrackedTime effective_post_time = completed_task.delayed_run_time.is_null() |
394 (delayed_start_time.is_null()) ? time_posted : delayed_start_time; | 406 ? tracked_objects::TrackedTime(completed_task.time_posted) |
395 base::TimeDelta queue_duration = start_of_run - effective_post_time; | 407 : tracked_objects::TrackedTime(completed_task.delayed_run_time); |
396 base::TimeDelta run_duration = end_of_run - start_of_run; | 408 |
| 409 Duration queue_duration = start_of_run - effective_post_time; |
| 410 Duration run_duration = end_of_run - start_of_run; |
| 411 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); |
| 412 } |
| 413 |
| 414 // static |
| 415 void ThreadData::TallyRunOnWorkerThreadIfTracking( |
| 416 const Births* birth, |
| 417 const TrackedTime& time_posted, |
| 418 const TrackedTime& start_of_run, |
| 419 const TrackedTime& end_of_run) { |
| 420 if (!kTrackAllTaskObjects) |
| 421 return; // Not compiled in. |
| 422 |
| 423 // Even if we have been DEACTIVATED, we will process any pending births so |
| 424 // that our data structures (which counted the outstanding births) remain |
| 425 // consistent. |
| 426 if (!birth) |
| 427 return; |
| 428 |
| 429 // TODO(jar): Support the option to coalesce all worker-thread activity under |
| 430 // one ThreadData instance that uses locks to protect *all* access. This will |
| 431 // reduce memory (making it provably bounded), but run incrementally slower |
| 432 // (since we'll use locks on TallyBirth and TallyDeath). The good news is |
| 433 // that the locks on TallyDeath will be *after* the worker thread has run, and |
| 434 // hence nothing will be waiting for the completion (... besides some other |
| 435 // thread that might like to run). Also, the worker threads tasks are |
| 436 // generally longer, and hence the cost of the lock may perchance be amortized |
| 437 // over the long task's lifetime. |
| 438 ThreadData* current_thread_data = Get(); |
| 439 if (!current_thread_data) |
| 440 return; |
| 441 |
| 442 Duration queue_duration = start_of_run - time_posted; |
| 443 Duration run_duration = end_of_run - start_of_run; |
397 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); | 444 current_thread_data->TallyADeath(*birth, queue_duration, run_duration); |
398 } | 445 } |
399 | 446 |
400 // static | 447 // static |
401 ThreadData* ThreadData::first() { | 448 ThreadData* ThreadData::first() { |
402 base::AutoLock lock(list_lock_); | 449 base::AutoLock lock(*list_lock_); |
403 return all_thread_data_list_head_; | 450 return all_thread_data_list_head_; |
404 } | 451 } |
405 | 452 |
406 // This may be called from another thread. | 453 // This may be called from another thread. |
407 void ThreadData::SnapshotBirthMap(BirthMap *output) const { | 454 void ThreadData::SnapshotBirthMap(BirthMap *output) const { |
408 base::AutoLock lock(lock_); | 455 base::AutoLock lock(lock_); |
409 for (BirthMap::const_iterator it = birth_map_.begin(); | 456 for (BirthMap::const_iterator it = birth_map_.begin(); |
410 it != birth_map_.end(); ++it) | 457 it != birth_map_.end(); ++it) |
411 (*output)[it->first] = it->second; | 458 (*output)[it->first] = it->second; |
412 } | 459 } |
(...skipping 19 matching lines...) Expand all Loading... |
432 void ThreadData::Reset() { | 479 void ThreadData::Reset() { |
433 base::AutoLock lock(lock_); | 480 base::AutoLock lock(lock_); |
434 for (DeathMap::iterator it = death_map_.begin(); | 481 for (DeathMap::iterator it = death_map_.begin(); |
435 it != death_map_.end(); ++it) | 482 it != death_map_.end(); ++it) |
436 it->second.Clear(); | 483 it->second.Clear(); |
437 for (BirthMap::iterator it = birth_map_.begin(); | 484 for (BirthMap::iterator it = birth_map_.begin(); |
438 it != birth_map_.end(); ++it) | 485 it != birth_map_.end(); ++it) |
439 it->second->Clear(); | 486 it->second->Clear(); |
440 } | 487 } |
441 | 488 |
442 // static | 489 bool ThreadData::Initialize() { |
443 bool ThreadData::StartTracking(bool status) { | |
444 if (!kTrackAllTaskObjects) | 490 if (!kTrackAllTaskObjects) |
445 return false; // Not compiled in. | 491 return false; // Not compiled in. |
446 | 492 if (status_ != UNINITIALIZED) |
447 // Do a bit of class initialization. | 493 return true; |
448 if (!unregistered_thread_data_pool_) { | 494 // Initialize all leaking constants that are difficult to toggle in and out |
449 ThreadDataPool* initial_pool = new ThreadDataPool; | 495 // of existance. |
450 { | 496 // First call must be made when single threaded at startup. |
451 base::AutoLock lock(list_lock_); | 497 // Perform the "real" TLS initialization now, and leave it intact through |
452 if (!unregistered_thread_data_pool_) { | |
453 unregistered_thread_data_pool_ = initial_pool; | |
454 initial_pool = NULL; | |
455 } | |
456 } | |
457 delete initial_pool; // In case it was not used. | |
458 } | |
459 | |
460 // Perform the "real" initialization now, and leave it intact through | |
461 // process termination. | 498 // process termination. |
462 if (!tls_index_.initialized()) | 499 if (!tls_index_.initialized()) // Testing may have initialized this. |
463 tls_index_.Initialize(&ThreadData::OnThreadTermination); | 500 tls_index_.Initialize(&ThreadData::OnThreadTermination); |
464 DCHECK(tls_index_.initialized()); | 501 DCHECK(tls_index_.initialized()); |
465 | 502 unregistered_thread_data_pool_ = new ThreadDataPool; |
466 if (!status) { | 503 // TODO(jar): A linker initialized spin lock would be much safer than this |
467 base::AutoLock lock(list_lock_); | 504 // allocation, which relies on being called while single threaded. |
468 DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); | 505 if (!list_lock_) // In case testing deleted this. |
469 status_ = SHUTDOWN; | 506 list_lock_ = new base::Lock; |
470 return true; | 507 status_ = kInitialStartupState; |
471 } | |
472 base::AutoLock lock(list_lock_); | |
473 DCHECK_EQ(UNINITIALIZED, status_); | |
474 status_ = ACTIVE; | |
475 return true; | 508 return true; |
476 } | 509 } |
477 | 510 |
478 // static | 511 // static |
479 bool ThreadData::IsActive() { | 512 bool ThreadData::InitializeAndSetTrackingStatus(bool status) { |
| 513 if (!Initialize()) // No-op if already initialized. |
| 514 return false; // Not compiled in. |
| 515 |
| 516 status_ = status ? ACTIVE : DEACTIVATED; |
| 517 return true; |
| 518 } |
| 519 |
| 520 // static |
| 521 bool ThreadData::tracking_status() { |
480 return status_ == ACTIVE; | 522 return status_ == ACTIVE; |
481 } | 523 } |
482 | 524 |
483 // static | 525 // static |
484 base::TimeTicks ThreadData::Now() { | 526 TrackedTime ThreadData::Now() { |
485 if (kTrackAllTaskObjects && status_ == ACTIVE) | 527 if (!kTrackAllTaskObjects || status_ != ACTIVE) |
486 return base::TimeTicks::Now(); | 528 return TrackedTime(); // Super fast when disabled, or not compiled. |
487 return base::TimeTicks(); // Super fast when disabled, or not compiled in. | 529 return TrackedTime::Now(); |
488 } | 530 } |
489 | 531 |
490 // static | 532 // static |
491 void ThreadData::ShutdownSingleThreadedCleanup() { | 533 void ThreadData::ShutdownSingleThreadedCleanup() { |
492 // This is only called from test code, where we need to cleanup so that | 534 // This is only called from test code, where we need to cleanup so that |
493 // additional tests can be run. | 535 // additional tests can be run. |
494 // We must be single threaded... but be careful anyway. | 536 // We must be single threaded... but be careful anyway. |
495 if (!StartTracking(false)) | 537 if (!InitializeAndSetTrackingStatus(false)) |
496 return; | 538 return; |
497 ThreadData* thread_data_list; | 539 ThreadData* thread_data_list; |
498 ThreadDataPool* final_pool; | 540 ThreadDataPool* final_pool; |
499 { | 541 { |
500 base::AutoLock lock(list_lock_); | 542 base::AutoLock lock(*list_lock_); |
501 thread_data_list = all_thread_data_list_head_; | 543 thread_data_list = all_thread_data_list_head_; |
502 all_thread_data_list_head_ = NULL; | 544 all_thread_data_list_head_ = NULL; |
503 final_pool = unregistered_thread_data_pool_; | 545 final_pool = unregistered_thread_data_pool_; |
504 unregistered_thread_data_pool_ = NULL; | 546 unregistered_thread_data_pool_ = NULL; |
505 } | 547 } |
506 | 548 |
507 if (final_pool) { | 549 if (final_pool) { |
508 // The thread_data_list contains *all* the instances, and we'll use it to | 550 // The thread_data_list contains *all* the instances, and we'll use it to |
509 // delete them. This pool has pointers to some instances, and we just | 551 // delete them. This pool has pointers to some instances, and we just |
510 // have to drop those pointers (and not do the deletes here). | 552 // have to drop those pointers (and not do the deletes here). |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
567 base::DictionaryValue* dictionary = new base::DictionaryValue; | 609 base::DictionaryValue* dictionary = new base::DictionaryValue; |
568 dictionary->Set("death_data", death_data_.ToValue()); | 610 dictionary->Set("death_data", death_data_.ToValue()); |
569 dictionary->Set("birth_thread", | 611 dictionary->Set("birth_thread", |
570 base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); | 612 base::Value::CreateStringValue(birth_->birth_thread()->thread_name())); |
571 dictionary->Set("death_thread", | 613 dictionary->Set("death_thread", |
572 base::Value::CreateStringValue(DeathThreadName())); | 614 base::Value::CreateStringValue(DeathThreadName())); |
573 dictionary->Set("location", birth_->location().ToValue()); | 615 dictionary->Set("location", birth_->location().ToValue()); |
574 return dictionary; | 616 return dictionary; |
575 } | 617 } |
576 | 618 |
577 void Snapshot::Add(const Snapshot& other) { | |
578 death_data_.AddDeathData(other.death_data_); | |
579 } | |
580 | |
581 //------------------------------------------------------------------------------ | 619 //------------------------------------------------------------------------------ |
582 // DataCollector | 620 // DataCollector |
583 | 621 |
584 DataCollector::DataCollector() { | 622 DataCollector::DataCollector() { |
585 if (!ThreadData::IsActive()) | 623 if (!kTrackAllTaskObjects) |
| 624 return; // Not compiled in. |
| 625 if (!ThreadData::tracking_status()) |
586 return; | 626 return; |
587 | 627 |
588 // Get an unchanging copy of a ThreadData list. | 628 // Get an unchanging copy of a ThreadData list. |
589 ThreadData* my_list = ThreadData::first(); | 629 ThreadData* my_list = ThreadData::first(); |
590 | 630 |
591 // Gather data serially. | 631 // Gather data serially. |
592 // This hackish approach *can* get some slighly corrupt tallies, as we are | 632 // This hackish approach *can* get some slighly corrupt tallies, as we are |
593 // grabbing values without the protection of a lock, but it has the advantage | 633 // grabbing values without the protection of a lock, but it has the advantage |
594 // of working even with threads that don't have message loops. If a user | 634 // of working even with threads that don't have message loops. If a user |
595 // sees any strangeness, they can always just run their stats gathering a | 635 // sees any strangeness, they can always just run their stats gathering a |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
733 void Comparator::Clear() { | 773 void Comparator::Clear() { |
734 if (tiebreaker_) { | 774 if (tiebreaker_) { |
735 tiebreaker_->Clear(); | 775 tiebreaker_->Clear(); |
736 delete tiebreaker_; | 776 delete tiebreaker_; |
737 tiebreaker_ = NULL; | 777 tiebreaker_ = NULL; |
738 } | 778 } |
739 use_tiebreaker_for_sort_only_ = false; | 779 use_tiebreaker_for_sort_only_ = false; |
740 selector_ = NIL; | 780 selector_ = NIL; |
741 } | 781 } |
742 | 782 |
| 783 // static |
| 784 Comparator::Selector Comparator::FindSelector(const std::string& keyword) { |
| 785 // Sorting and aggretation keywords, which specify how to sort the data, or |
| 786 // can specify a required match from the specified field in the record. |
| 787 if (0 == keyword.compare("count")) |
| 788 return COUNT; |
| 789 if (0 == keyword.compare("totalduration")) |
| 790 return TOTAL_RUN_DURATION; |
| 791 if (0 == keyword.compare("duration")) |
| 792 return AVERAGE_RUN_DURATION; |
| 793 if (0 == keyword.compare("totalqueueduration")) |
| 794 return TOTAL_QUEUE_DURATION; |
| 795 if (0 == keyword.compare("averagequeueduration")) |
| 796 return AVERAGE_QUEUE_DURATION; |
| 797 if (0 == keyword.compare("birth")) |
| 798 return BIRTH_THREAD; |
| 799 if (0 == keyword.compare("death")) |
| 800 return DEATH_THREAD; |
| 801 if (0 == keyword.compare("file")) |
| 802 return BIRTH_FILE; |
| 803 if (0 == keyword.compare("function")) |
| 804 return BIRTH_FUNCTION; |
| 805 if (0 == keyword.compare("line")) |
| 806 return BIRTH_LINE; |
| 807 if (0 == keyword.compare("reset")) |
| 808 return RESET_ALL_DATA; |
| 809 return UNKNOWN_KEYWORD; |
| 810 } |
| 811 |
743 bool Comparator::operator()(const Snapshot& left, | 812 bool Comparator::operator()(const Snapshot& left, |
744 const Snapshot& right) const { | 813 const Snapshot& right) const { |
745 switch (selector_) { | 814 switch (selector_) { |
746 case BIRTH_THREAD: | 815 case BIRTH_THREAD: |
747 if (left.birth_thread() != right.birth_thread() && | 816 if (left.birth_thread() != right.birth_thread() && |
748 left.birth_thread()->thread_name() != | 817 left.birth_thread()->thread_name() != |
749 right.birth_thread()->thread_name()) | 818 right.birth_thread()->thread_name()) |
750 return left.birth_thread()->thread_name() < | 819 return left.birth_thread()->thread_name() < |
751 right.birth_thread()->thread_name(); | 820 right.birth_thread()->thread_name(); |
752 break; | 821 break; |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
949 if (!tiebreaker_) { | 1018 if (!tiebreaker_) { |
950 use_tiebreaker_for_sort_only_ = true; | 1019 use_tiebreaker_for_sort_only_ = true; |
951 tiebreaker_ = new Comparator; | 1020 tiebreaker_ = new Comparator; |
952 tiebreaker_->SetTiebreaker(selector, ""); | 1021 tiebreaker_->SetTiebreaker(selector, ""); |
953 } else { | 1022 } else { |
954 tiebreaker_->SetSubgroupTiebreaker(selector); | 1023 tiebreaker_->SetSubgroupTiebreaker(selector); |
955 } | 1024 } |
956 } | 1025 } |
957 | 1026 |
958 void Comparator::ParseKeyphrase(const std::string& key_phrase) { | 1027 void Comparator::ParseKeyphrase(const std::string& key_phrase) { |
959 typedef std::map<const std::string, Selector> KeyMap; | |
960 static KeyMap key_map; | |
961 static bool initialized = false; | |
962 if (!initialized) { | |
963 initialized = true; | |
964 // Sorting and aggretation keywords, which specify how to sort the data, or | |
965 // can specify a required match from the specified field in the record. | |
966 key_map["count"] = COUNT; | |
967 key_map["totalduration"] = TOTAL_RUN_DURATION; | |
968 key_map["duration"] = AVERAGE_RUN_DURATION; | |
969 key_map["totalqueueduration"] = TOTAL_QUEUE_DURATION; | |
970 key_map["averagequeueduration"] = AVERAGE_QUEUE_DURATION; | |
971 key_map["birth"] = BIRTH_THREAD; | |
972 key_map["death"] = DEATH_THREAD; | |
973 key_map["file"] = BIRTH_FILE; | |
974 key_map["function"] = BIRTH_FUNCTION; | |
975 key_map["line"] = BIRTH_LINE; | |
976 | |
977 // Immediate commands that do not involve setting sort order. | |
978 key_map["reset"] = RESET_ALL_DATA; | |
979 } | |
980 | |
981 std::string required; | 1028 std::string required; |
982 // Watch for: "sort_key=value" as we parse. | 1029 // Watch for: "sort_key=value" as we parse. |
983 size_t equal_offset = key_phrase.find('=', 0); | 1030 size_t equal_offset = key_phrase.find('=', 0); |
984 if (key_phrase.npos != equal_offset) { | 1031 if (key_phrase.npos != equal_offset) { |
985 // There is a value that must be matched for the data to display. | 1032 // There is a value that must be matched for the data to display. |
986 required = key_phrase.substr(equal_offset + 1, key_phrase.npos); | 1033 required = key_phrase.substr(equal_offset + 1, key_phrase.npos); |
987 } | 1034 } |
988 std::string keyword(key_phrase.substr(0, equal_offset)); | 1035 std::string keyword(key_phrase.substr(0, equal_offset)); |
989 keyword = StringToLowerASCII(keyword); | 1036 keyword = StringToLowerASCII(keyword); |
990 KeyMap::iterator it = key_map.find(keyword); | 1037 Selector selector = FindSelector(keyword); |
991 if (key_map.end() == it) | 1038 if (selector == UNKNOWN_KEYWORD) |
992 return; // Unknown keyword. | 1039 return; |
993 if (it->second == RESET_ALL_DATA) | 1040 if (selector == RESET_ALL_DATA) { |
994 ThreadData::ResetAllThreadData(); | 1041 ThreadData::ResetAllThreadData(); |
995 else | 1042 return; |
996 SetTiebreaker(key_map[keyword], required); | 1043 } |
| 1044 SetTiebreaker(selector, required); |
997 } | 1045 } |
998 | 1046 |
999 bool Comparator::ParseQuery(const std::string& query) { | 1047 bool Comparator::ParseQuery(const std::string& query) { |
1000 // Parse each keyphrase between consecutive slashes. | 1048 // Parse each keyphrase between consecutive slashes. |
1001 for (size_t i = 0; i < query.size();) { | 1049 for (size_t i = 0; i < query.size();) { |
1002 size_t slash_offset = query.find('/', i); | 1050 size_t slash_offset = query.find('/', i); |
1003 ParseKeyphrase(query.substr(i, slash_offset - i)); | 1051 ParseKeyphrase(query.substr(i, slash_offset - i)); |
1004 if (query.npos == slash_offset) | 1052 if (query.npos == slash_offset) |
1005 break; | 1053 break; |
1006 i = slash_offset + 1; | 1054 i = slash_offset + 1; |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1068 (combined_selectors_ & BIRTH_THREAD) ? "*" : | 1116 (combined_selectors_ & BIRTH_THREAD) ? "*" : |
1069 sample.birth().birth_thread()->thread_name().c_str(), | 1117 sample.birth().birth_thread()->thread_name().c_str(), |
1070 (combined_selectors_ & DEATH_THREAD) ? "*" : | 1118 (combined_selectors_ & DEATH_THREAD) ? "*" : |
1071 sample.DeathThreadName().c_str()); | 1119 sample.DeathThreadName().c_str()); |
1072 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), | 1120 sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), |
1073 !(combined_selectors_ & BIRTH_FUNCTION), | 1121 !(combined_selectors_ & BIRTH_FUNCTION), |
1074 output); | 1122 output); |
1075 } | 1123 } |
1076 | 1124 |
1077 } // namespace tracked_objects | 1125 } // namespace tracked_objects |
OLD | NEW |