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