OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/debug/trace_event_impl.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/base_switches.h" | |
10 #include "base/bind.h" | |
11 #include "base/command_line.h" | |
12 #include "base/debug/leak_annotations.h" | |
13 #include "base/debug/trace_event.h" | |
14 #include "base/debug/trace_event_synthetic_delay.h" | |
15 #include "base/float_util.h" | |
16 #include "base/format_macros.h" | |
17 #include "base/json/string_escape.h" | |
18 #include "base/lazy_instance.h" | |
19 #include "base/memory/singleton.h" | |
20 #include "base/message_loop/message_loop.h" | |
21 #include "base/process/process_metrics.h" | |
22 #include "base/stl_util.h" | |
23 #include "base/strings/string_number_conversions.h" | |
24 #include "base/strings/string_split.h" | |
25 #include "base/strings/string_tokenizer.h" | |
26 #include "base/strings/string_util.h" | |
27 #include "base/strings/stringprintf.h" | |
28 #include "base/strings/utf_string_conversions.h" | |
29 #include "base/synchronization/cancellation_flag.h" | |
30 #include "base/synchronization/waitable_event.h" | |
31 #include "base/sys_info.h" | |
32 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
33 #include "base/threading/platform_thread.h" | |
34 #include "base/threading/thread_id_name_manager.h" | |
35 #include "base/time/time.h" | |
36 | |
37 #if defined(OS_WIN) | |
38 #include "base/debug/trace_event_win.h" | |
39 #endif | |
40 | |
41 class DeleteTraceLogForTesting { | |
42 public: | |
43 static void Delete() { | |
44 Singleton<base::debug::TraceLog, | |
45 LeakySingletonTraits<base::debug::TraceLog> >::OnExit(0); | |
46 } | |
47 }; | |
48 | |
49 // The thread buckets for the sampling profiler. | |
50 BASE_EXPORT TRACE_EVENT_API_ATOMIC_WORD g_trace_state[3]; | |
51 | |
52 namespace base { | |
53 namespace debug { | |
54 | |
55 namespace { | |
56 | |
57 // The overhead of TraceEvent above this threshold will be reported in the | |
58 // trace. | |
59 const int kOverheadReportThresholdInMicroseconds = 50; | |
60 | |
61 // String options that can be used to initialize TraceOptions. | |
62 const char kRecordUntilFull[] = "record-until-full"; | |
63 const char kRecordContinuously[] = "record-continuously"; | |
64 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible"; | |
65 const char kTraceToConsole[] = "trace-to-console"; | |
66 const char kEnableSampling[] = "enable-sampling"; | |
67 const char kEnableSystrace[] = "enable-systrace"; | |
68 | |
69 // Controls the number of trace events we will buffer in-memory | |
70 // before throwing them away. | |
71 const size_t kTraceBufferChunkSize = TraceBufferChunk::kTraceBufferChunkSize; | |
72 const size_t kTraceEventVectorBigBufferChunks = | |
73 512000000 / kTraceBufferChunkSize; | |
74 const size_t kTraceEventVectorBufferChunks = 256000 / kTraceBufferChunkSize; | |
75 const size_t kTraceEventRingBufferChunks = kTraceEventVectorBufferChunks / 4; | |
76 const size_t kTraceEventBatchChunks = 1000 / kTraceBufferChunkSize; | |
77 // Can store results for 30 seconds with 1 ms sampling interval. | |
78 const size_t kMonitorTraceEventBufferChunks = 30000 / kTraceBufferChunkSize; | |
79 // ECHO_TO_CONSOLE needs a small buffer to hold the unfinished COMPLETE events. | |
80 const size_t kEchoToConsoleTraceEventBufferChunks = 256; | |
81 | |
82 const int kThreadFlushTimeoutMs = 3000; | |
83 | |
84 #if !defined(OS_NACL) | |
85 // These categories will cause deadlock when ECHO_TO_CONSOLE. crbug.com/325575. | |
86 const char kEchoToConsoleCategoryFilter[] = "-ipc,-task"; | |
87 #endif | |
88 | |
89 const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY("; | |
90 | |
91 #define MAX_CATEGORY_GROUPS 100 | |
92 | |
93 // Parallel arrays g_category_groups and g_category_group_enabled are separate | |
94 // so that a pointer to a member of g_category_group_enabled can be easily | |
95 // converted to an index into g_category_groups. This allows macros to deal | |
96 // only with char enabled pointers from g_category_group_enabled, and we can | |
97 // convert internally to determine the category name from the char enabled | |
98 // pointer. | |
99 const char* g_category_groups[MAX_CATEGORY_GROUPS] = { | |
100 "toplevel", | |
101 "tracing already shutdown", | |
102 "tracing categories exhausted; must increase MAX_CATEGORY_GROUPS", | |
103 "__metadata", | |
104 // For reporting trace_event overhead. For thread local event buffers only. | |
105 "trace_event_overhead"}; | |
106 | |
107 // The enabled flag is char instead of bool so that the API can be used from C. | |
108 unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = { 0 }; | |
109 // Indexes here have to match the g_category_groups array indexes above. | |
110 const int g_category_already_shutdown = 1; | |
111 const int g_category_categories_exhausted = 2; | |
112 const int g_category_metadata = 3; | |
113 const int g_category_trace_event_overhead = 4; | |
114 const int g_num_builtin_categories = 5; | |
115 // Skip default categories. | |
116 base::subtle::AtomicWord g_category_index = g_num_builtin_categories; | |
117 | |
118 // The name of the current thread. This is used to decide if the current | |
119 // thread name has changed. We combine all the seen thread names into the | |
120 // output name for the thread. | |
121 LazyInstance<ThreadLocalPointer<const char> >::Leaky | |
122 g_current_thread_name = LAZY_INSTANCE_INITIALIZER; | |
123 | |
124 TimeTicks ThreadNow() { | |
125 return TimeTicks::IsThreadNowSupported() ? | |
126 TimeTicks::ThreadNow() : TimeTicks(); | |
127 } | |
128 | |
129 class TraceBufferRingBuffer : public TraceBuffer { | |
130 public: | |
131 TraceBufferRingBuffer(size_t max_chunks) | |
132 : max_chunks_(max_chunks), | |
133 recyclable_chunks_queue_(new size_t[queue_capacity()]), | |
134 queue_head_(0), | |
135 queue_tail_(max_chunks), | |
136 current_iteration_index_(0), | |
137 current_chunk_seq_(1) { | |
138 chunks_.reserve(max_chunks); | |
139 for (size_t i = 0; i < max_chunks; ++i) | |
140 recyclable_chunks_queue_[i] = i; | |
141 } | |
142 | |
143 scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) override { | |
144 // Because the number of threads is much less than the number of chunks, | |
145 // the queue should never be empty. | |
146 DCHECK(!QueueIsEmpty()); | |
147 | |
148 *index = recyclable_chunks_queue_[queue_head_]; | |
149 queue_head_ = NextQueueIndex(queue_head_); | |
150 current_iteration_index_ = queue_head_; | |
151 | |
152 if (*index >= chunks_.size()) | |
153 chunks_.resize(*index + 1); | |
154 | |
155 TraceBufferChunk* chunk = chunks_[*index]; | |
156 chunks_[*index] = NULL; // Put NULL in the slot of a in-flight chunk. | |
157 if (chunk) | |
158 chunk->Reset(current_chunk_seq_++); | |
159 else | |
160 chunk = new TraceBufferChunk(current_chunk_seq_++); | |
161 | |
162 return scoped_ptr<TraceBufferChunk>(chunk); | |
163 } | |
164 | |
165 void ReturnChunk(size_t index, scoped_ptr<TraceBufferChunk> chunk) override { | |
166 // When this method is called, the queue should not be full because it | |
167 // can contain all chunks including the one to be returned. | |
168 DCHECK(!QueueIsFull()); | |
169 DCHECK(chunk); | |
170 DCHECK_LT(index, chunks_.size()); | |
171 DCHECK(!chunks_[index]); | |
172 chunks_[index] = chunk.release(); | |
173 recyclable_chunks_queue_[queue_tail_] = index; | |
174 queue_tail_ = NextQueueIndex(queue_tail_); | |
175 } | |
176 | |
177 bool IsFull() const override { return false; } | |
178 | |
179 size_t Size() const override { | |
180 // This is approximate because not all of the chunks are full. | |
181 return chunks_.size() * kTraceBufferChunkSize; | |
182 } | |
183 | |
184 size_t Capacity() const override { | |
185 return max_chunks_ * kTraceBufferChunkSize; | |
186 } | |
187 | |
188 TraceEvent* GetEventByHandle(TraceEventHandle handle) override { | |
189 if (handle.chunk_index >= chunks_.size()) | |
190 return NULL; | |
191 TraceBufferChunk* chunk = chunks_[handle.chunk_index]; | |
192 if (!chunk || chunk->seq() != handle.chunk_seq) | |
193 return NULL; | |
194 return chunk->GetEventAt(handle.event_index); | |
195 } | |
196 | |
197 const TraceBufferChunk* NextChunk() override { | |
198 if (chunks_.empty()) | |
199 return NULL; | |
200 | |
201 while (current_iteration_index_ != queue_tail_) { | |
202 size_t chunk_index = recyclable_chunks_queue_[current_iteration_index_]; | |
203 current_iteration_index_ = NextQueueIndex(current_iteration_index_); | |
204 if (chunk_index >= chunks_.size()) // Skip uninitialized chunks. | |
205 continue; | |
206 DCHECK(chunks_[chunk_index]); | |
207 return chunks_[chunk_index]; | |
208 } | |
209 return NULL; | |
210 } | |
211 | |
212 scoped_ptr<TraceBuffer> CloneForIteration() const override { | |
213 scoped_ptr<ClonedTraceBuffer> cloned_buffer(new ClonedTraceBuffer()); | |
214 for (size_t queue_index = queue_head_; queue_index != queue_tail_; | |
215 queue_index = NextQueueIndex(queue_index)) { | |
216 size_t chunk_index = recyclable_chunks_queue_[queue_index]; | |
217 if (chunk_index >= chunks_.size()) // Skip uninitialized chunks. | |
218 continue; | |
219 TraceBufferChunk* chunk = chunks_[chunk_index]; | |
220 cloned_buffer->chunks_.push_back(chunk ? chunk->Clone().release() : NULL); | |
221 } | |
222 return cloned_buffer.Pass(); | |
223 } | |
224 | |
225 private: | |
226 class ClonedTraceBuffer : public TraceBuffer { | |
227 public: | |
228 ClonedTraceBuffer() : current_iteration_index_(0) {} | |
229 | |
230 // The only implemented method. | |
231 const TraceBufferChunk* NextChunk() override { | |
232 return current_iteration_index_ < chunks_.size() ? | |
233 chunks_[current_iteration_index_++] : NULL; | |
234 } | |
235 | |
236 scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) override { | |
237 NOTIMPLEMENTED(); | |
238 return scoped_ptr<TraceBufferChunk>(); | |
239 } | |
240 void ReturnChunk(size_t index, scoped_ptr<TraceBufferChunk>) override { | |
241 NOTIMPLEMENTED(); | |
242 } | |
243 bool IsFull() const override { return false; } | |
244 size_t Size() const override { return 0; } | |
245 size_t Capacity() const override { return 0; } | |
246 TraceEvent* GetEventByHandle(TraceEventHandle handle) override { | |
247 return NULL; | |
248 } | |
249 scoped_ptr<TraceBuffer> CloneForIteration() const override { | |
250 NOTIMPLEMENTED(); | |
251 return scoped_ptr<TraceBuffer>(); | |
252 } | |
253 | |
254 size_t current_iteration_index_; | |
255 ScopedVector<TraceBufferChunk> chunks_; | |
256 }; | |
257 | |
258 bool QueueIsEmpty() const { | |
259 return queue_head_ == queue_tail_; | |
260 } | |
261 | |
262 size_t QueueSize() const { | |
263 return queue_tail_ > queue_head_ ? queue_tail_ - queue_head_ : | |
264 queue_tail_ + queue_capacity() - queue_head_; | |
265 } | |
266 | |
267 bool QueueIsFull() const { | |
268 return QueueSize() == queue_capacity() - 1; | |
269 } | |
270 | |
271 size_t queue_capacity() const { | |
272 // One extra space to help distinguish full state and empty state. | |
273 return max_chunks_ + 1; | |
274 } | |
275 | |
276 size_t NextQueueIndex(size_t index) const { | |
277 index++; | |
278 if (index >= queue_capacity()) | |
279 index = 0; | |
280 return index; | |
281 } | |
282 | |
283 size_t max_chunks_; | |
284 ScopedVector<TraceBufferChunk> chunks_; | |
285 | |
286 scoped_ptr<size_t[]> recyclable_chunks_queue_; | |
287 size_t queue_head_; | |
288 size_t queue_tail_; | |
289 | |
290 size_t current_iteration_index_; | |
291 uint32 current_chunk_seq_; | |
292 | |
293 DISALLOW_COPY_AND_ASSIGN(TraceBufferRingBuffer); | |
294 }; | |
295 | |
296 class TraceBufferVector : public TraceBuffer { | |
297 public: | |
298 TraceBufferVector(size_t max_chunks) | |
299 : in_flight_chunk_count_(0), | |
300 current_iteration_index_(0), | |
301 max_chunks_(max_chunks) { | |
302 chunks_.reserve(max_chunks_); | |
303 } | |
304 | |
305 scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) override { | |
306 // This function may be called when adding normal events or indirectly from | |
307 // AddMetadataEventsWhileLocked(). We can not DECHECK(!IsFull()) because we | |
308 // have to add the metadata events and flush thread-local buffers even if | |
309 // the buffer is full. | |
310 *index = chunks_.size(); | |
311 chunks_.push_back(NULL); // Put NULL in the slot of a in-flight chunk. | |
312 ++in_flight_chunk_count_; | |
313 // + 1 because zero chunk_seq is not allowed. | |
314 return scoped_ptr<TraceBufferChunk>( | |
315 new TraceBufferChunk(static_cast<uint32>(*index) + 1)); | |
316 } | |
317 | |
318 void ReturnChunk(size_t index, scoped_ptr<TraceBufferChunk> chunk) override { | |
319 DCHECK_GT(in_flight_chunk_count_, 0u); | |
320 DCHECK_LT(index, chunks_.size()); | |
321 DCHECK(!chunks_[index]); | |
322 --in_flight_chunk_count_; | |
323 chunks_[index] = chunk.release(); | |
324 } | |
325 | |
326 bool IsFull() const override { return chunks_.size() >= max_chunks_; } | |
327 | |
328 size_t Size() const override { | |
329 // This is approximate because not all of the chunks are full. | |
330 return chunks_.size() * kTraceBufferChunkSize; | |
331 } | |
332 | |
333 size_t Capacity() const override { | |
334 return max_chunks_ * kTraceBufferChunkSize; | |
335 } | |
336 | |
337 TraceEvent* GetEventByHandle(TraceEventHandle handle) override { | |
338 if (handle.chunk_index >= chunks_.size()) | |
339 return NULL; | |
340 TraceBufferChunk* chunk = chunks_[handle.chunk_index]; | |
341 if (!chunk || chunk->seq() != handle.chunk_seq) | |
342 return NULL; | |
343 return chunk->GetEventAt(handle.event_index); | |
344 } | |
345 | |
346 const TraceBufferChunk* NextChunk() override { | |
347 while (current_iteration_index_ < chunks_.size()) { | |
348 // Skip in-flight chunks. | |
349 const TraceBufferChunk* chunk = chunks_[current_iteration_index_++]; | |
350 if (chunk) | |
351 return chunk; | |
352 } | |
353 return NULL; | |
354 } | |
355 | |
356 scoped_ptr<TraceBuffer> CloneForIteration() const override { | |
357 NOTIMPLEMENTED(); | |
358 return scoped_ptr<TraceBuffer>(); | |
359 } | |
360 | |
361 private: | |
362 size_t in_flight_chunk_count_; | |
363 size_t current_iteration_index_; | |
364 size_t max_chunks_; | |
365 ScopedVector<TraceBufferChunk> chunks_; | |
366 | |
367 DISALLOW_COPY_AND_ASSIGN(TraceBufferVector); | |
368 }; | |
369 | |
370 template <typename T> | |
371 void InitializeMetadataEvent(TraceEvent* trace_event, | |
372 int thread_id, | |
373 const char* metadata_name, const char* arg_name, | |
374 const T& value) { | |
375 if (!trace_event) | |
376 return; | |
377 | |
378 int num_args = 1; | |
379 unsigned char arg_type; | |
380 unsigned long long arg_value; | |
381 ::trace_event_internal::SetTraceValue(value, &arg_type, &arg_value); | |
382 trace_event->Initialize(thread_id, | |
383 TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA, | |
384 &g_category_group_enabled[g_category_metadata], | |
385 metadata_name, ::trace_event_internal::kNoEventId, | |
386 num_args, &arg_name, &arg_type, &arg_value, NULL, | |
387 TRACE_EVENT_FLAG_NONE); | |
388 } | |
389 | |
390 class AutoThreadLocalBoolean { | |
391 public: | |
392 explicit AutoThreadLocalBoolean(ThreadLocalBoolean* thread_local_boolean) | |
393 : thread_local_boolean_(thread_local_boolean) { | |
394 DCHECK(!thread_local_boolean_->Get()); | |
395 thread_local_boolean_->Set(true); | |
396 } | |
397 ~AutoThreadLocalBoolean() { | |
398 thread_local_boolean_->Set(false); | |
399 } | |
400 | |
401 private: | |
402 ThreadLocalBoolean* thread_local_boolean_; | |
403 DISALLOW_COPY_AND_ASSIGN(AutoThreadLocalBoolean); | |
404 }; | |
405 | |
406 } // namespace | |
407 | |
408 void TraceBufferChunk::Reset(uint32 new_seq) { | |
409 for (size_t i = 0; i < next_free_; ++i) | |
410 chunk_[i].Reset(); | |
411 next_free_ = 0; | |
412 seq_ = new_seq; | |
413 } | |
414 | |
415 TraceEvent* TraceBufferChunk::AddTraceEvent(size_t* event_index) { | |
416 DCHECK(!IsFull()); | |
417 *event_index = next_free_++; | |
418 return &chunk_[*event_index]; | |
419 } | |
420 | |
421 scoped_ptr<TraceBufferChunk> TraceBufferChunk::Clone() const { | |
422 scoped_ptr<TraceBufferChunk> cloned_chunk(new TraceBufferChunk(seq_)); | |
423 cloned_chunk->next_free_ = next_free_; | |
424 for (size_t i = 0; i < next_free_; ++i) | |
425 cloned_chunk->chunk_[i].CopyFrom(chunk_[i]); | |
426 return cloned_chunk.Pass(); | |
427 } | |
428 | |
429 // A helper class that allows the lock to be acquired in the middle of the scope | |
430 // and unlocks at the end of scope if locked. | |
431 class TraceLog::OptionalAutoLock { | |
432 public: | |
433 explicit OptionalAutoLock(Lock& lock) | |
434 : lock_(lock), | |
435 locked_(false) { | |
436 } | |
437 | |
438 ~OptionalAutoLock() { | |
439 if (locked_) | |
440 lock_.Release(); | |
441 } | |
442 | |
443 void EnsureAcquired() { | |
444 if (!locked_) { | |
445 lock_.Acquire(); | |
446 locked_ = true; | |
447 } | |
448 } | |
449 | |
450 private: | |
451 Lock& lock_; | |
452 bool locked_; | |
453 DISALLOW_COPY_AND_ASSIGN(OptionalAutoLock); | |
454 }; | |
455 | |
456 // Use this function instead of TraceEventHandle constructor to keep the | |
457 // overhead of ScopedTracer (trace_event.h) constructor minimum. | |
458 void MakeHandle(uint32 chunk_seq, size_t chunk_index, size_t event_index, | |
459 TraceEventHandle* handle) { | |
460 DCHECK(chunk_seq); | |
461 DCHECK(chunk_index < (1u << 16)); | |
462 DCHECK(event_index < (1u << 16)); | |
463 handle->chunk_seq = chunk_seq; | |
464 handle->chunk_index = static_cast<uint16>(chunk_index); | |
465 handle->event_index = static_cast<uint16>(event_index); | |
466 } | |
467 | |
468 //////////////////////////////////////////////////////////////////////////////// | |
469 // | |
470 // TraceEvent | |
471 // | |
472 //////////////////////////////////////////////////////////////////////////////// | |
473 | |
474 namespace { | |
475 | |
476 size_t GetAllocLength(const char* str) { return str ? strlen(str) + 1 : 0; } | |
477 | |
478 // Copies |*member| into |*buffer|, sets |*member| to point to this new | |
479 // location, and then advances |*buffer| by the amount written. | |
480 void CopyTraceEventParameter(char** buffer, | |
481 const char** member, | |
482 const char* end) { | |
483 if (*member) { | |
484 size_t written = strlcpy(*buffer, *member, end - *buffer) + 1; | |
485 DCHECK_LE(static_cast<int>(written), end - *buffer); | |
486 *member = *buffer; | |
487 *buffer += written; | |
488 } | |
489 } | |
490 | |
491 } // namespace | |
492 | |
493 TraceEvent::TraceEvent() | |
494 : duration_(TimeDelta::FromInternalValue(-1)), | |
495 id_(0u), | |
496 category_group_enabled_(NULL), | |
497 name_(NULL), | |
498 thread_id_(0), | |
499 phase_(TRACE_EVENT_PHASE_BEGIN), | |
500 flags_(0) { | |
501 for (int i = 0; i < kTraceMaxNumArgs; ++i) | |
502 arg_names_[i] = NULL; | |
503 memset(arg_values_, 0, sizeof(arg_values_)); | |
504 } | |
505 | |
506 TraceEvent::~TraceEvent() { | |
507 } | |
508 | |
509 void TraceEvent::CopyFrom(const TraceEvent& other) { | |
510 timestamp_ = other.timestamp_; | |
511 thread_timestamp_ = other.thread_timestamp_; | |
512 duration_ = other.duration_; | |
513 id_ = other.id_; | |
514 category_group_enabled_ = other.category_group_enabled_; | |
515 name_ = other.name_; | |
516 thread_id_ = other.thread_id_; | |
517 phase_ = other.phase_; | |
518 flags_ = other.flags_; | |
519 parameter_copy_storage_ = other.parameter_copy_storage_; | |
520 | |
521 for (int i = 0; i < kTraceMaxNumArgs; ++i) { | |
522 arg_names_[i] = other.arg_names_[i]; | |
523 arg_types_[i] = other.arg_types_[i]; | |
524 arg_values_[i] = other.arg_values_[i]; | |
525 convertable_values_[i] = other.convertable_values_[i]; | |
526 } | |
527 } | |
528 | |
529 void TraceEvent::Initialize( | |
530 int thread_id, | |
531 TimeTicks timestamp, | |
532 TimeTicks thread_timestamp, | |
533 char phase, | |
534 const unsigned char* category_group_enabled, | |
535 const char* name, | |
536 unsigned long long id, | |
537 int num_args, | |
538 const char** arg_names, | |
539 const unsigned char* arg_types, | |
540 const unsigned long long* arg_values, | |
541 const scoped_refptr<ConvertableToTraceFormat>* convertable_values, | |
542 unsigned char flags) { | |
543 timestamp_ = timestamp; | |
544 thread_timestamp_ = thread_timestamp; | |
545 duration_ = TimeDelta::FromInternalValue(-1); | |
546 id_ = id; | |
547 category_group_enabled_ = category_group_enabled; | |
548 name_ = name; | |
549 thread_id_ = thread_id; | |
550 phase_ = phase; | |
551 flags_ = flags; | |
552 | |
553 // Clamp num_args since it may have been set by a third_party library. | |
554 num_args = (num_args > kTraceMaxNumArgs) ? kTraceMaxNumArgs : num_args; | |
555 int i = 0; | |
556 for (; i < num_args; ++i) { | |
557 arg_names_[i] = arg_names[i]; | |
558 arg_types_[i] = arg_types[i]; | |
559 | |
560 if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) | |
561 convertable_values_[i] = convertable_values[i]; | |
562 else | |
563 arg_values_[i].as_uint = arg_values[i]; | |
564 } | |
565 for (; i < kTraceMaxNumArgs; ++i) { | |
566 arg_names_[i] = NULL; | |
567 arg_values_[i].as_uint = 0u; | |
568 convertable_values_[i] = NULL; | |
569 arg_types_[i] = TRACE_VALUE_TYPE_UINT; | |
570 } | |
571 | |
572 bool copy = !!(flags & TRACE_EVENT_FLAG_COPY); | |
573 size_t alloc_size = 0; | |
574 if (copy) { | |
575 alloc_size += GetAllocLength(name); | |
576 for (i = 0; i < num_args; ++i) { | |
577 alloc_size += GetAllocLength(arg_names_[i]); | |
578 if (arg_types_[i] == TRACE_VALUE_TYPE_STRING) | |
579 arg_types_[i] = TRACE_VALUE_TYPE_COPY_STRING; | |
580 } | |
581 } | |
582 | |
583 bool arg_is_copy[kTraceMaxNumArgs]; | |
584 for (i = 0; i < num_args; ++i) { | |
585 // No copying of convertable types, we retain ownership. | |
586 if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) | |
587 continue; | |
588 | |
589 // We only take a copy of arg_vals if they are of type COPY_STRING. | |
590 arg_is_copy[i] = (arg_types_[i] == TRACE_VALUE_TYPE_COPY_STRING); | |
591 if (arg_is_copy[i]) | |
592 alloc_size += GetAllocLength(arg_values_[i].as_string); | |
593 } | |
594 | |
595 if (alloc_size) { | |
596 parameter_copy_storage_ = new RefCountedString; | |
597 parameter_copy_storage_->data().resize(alloc_size); | |
598 char* ptr = string_as_array(¶meter_copy_storage_->data()); | |
599 const char* end = ptr + alloc_size; | |
600 if (copy) { | |
601 CopyTraceEventParameter(&ptr, &name_, end); | |
602 for (i = 0; i < num_args; ++i) { | |
603 CopyTraceEventParameter(&ptr, &arg_names_[i], end); | |
604 } | |
605 } | |
606 for (i = 0; i < num_args; ++i) { | |
607 if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) | |
608 continue; | |
609 if (arg_is_copy[i]) | |
610 CopyTraceEventParameter(&ptr, &arg_values_[i].as_string, end); | |
611 } | |
612 DCHECK_EQ(end, ptr) << "Overrun by " << ptr - end; | |
613 } | |
614 } | |
615 | |
616 void TraceEvent::Reset() { | |
617 // Only reset fields that won't be initialized in Initialize(), or that may | |
618 // hold references to other objects. | |
619 duration_ = TimeDelta::FromInternalValue(-1); | |
620 parameter_copy_storage_ = NULL; | |
621 for (int i = 0; i < kTraceMaxNumArgs; ++i) | |
622 convertable_values_[i] = NULL; | |
623 } | |
624 | |
625 void TraceEvent::UpdateDuration(const TimeTicks& now, | |
626 const TimeTicks& thread_now) { | |
627 DCHECK(duration_.ToInternalValue() == -1); | |
628 duration_ = now - timestamp_; | |
629 thread_duration_ = thread_now - thread_timestamp_; | |
630 } | |
631 | |
632 // static | |
633 void TraceEvent::AppendValueAsJSON(unsigned char type, | |
634 TraceEvent::TraceValue value, | |
635 std::string* out) { | |
636 switch (type) { | |
637 case TRACE_VALUE_TYPE_BOOL: | |
638 *out += value.as_bool ? "true" : "false"; | |
639 break; | |
640 case TRACE_VALUE_TYPE_UINT: | |
641 StringAppendF(out, "%" PRIu64, static_cast<uint64>(value.as_uint)); | |
642 break; | |
643 case TRACE_VALUE_TYPE_INT: | |
644 StringAppendF(out, "%" PRId64, static_cast<int64>(value.as_int)); | |
645 break; | |
646 case TRACE_VALUE_TYPE_DOUBLE: { | |
647 // FIXME: base/json/json_writer.cc is using the same code, | |
648 // should be made into a common method. | |
649 std::string real; | |
650 double val = value.as_double; | |
651 if (IsFinite(val)) { | |
652 real = DoubleToString(val); | |
653 // Ensure that the number has a .0 if there's no decimal or 'e'. This | |
654 // makes sure that when we read the JSON back, it's interpreted as a | |
655 // real rather than an int. | |
656 if (real.find('.') == std::string::npos && | |
657 real.find('e') == std::string::npos && | |
658 real.find('E') == std::string::npos) { | |
659 real.append(".0"); | |
660 } | |
661 // The JSON spec requires that non-integer values in the range (-1,1) | |
662 // have a zero before the decimal point - ".52" is not valid, "0.52" is. | |
663 if (real[0] == '.') { | |
664 real.insert(0, "0"); | |
665 } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { | |
666 // "-.1" bad "-0.1" good | |
667 real.insert(1, "0"); | |
668 } | |
669 } else if (IsNaN(val)){ | |
670 // The JSON spec doesn't allow NaN and Infinity (since these are | |
671 // objects in EcmaScript). Use strings instead. | |
672 real = "\"NaN\""; | |
673 } else if (val < 0) { | |
674 real = "\"-Infinity\""; | |
675 } else { | |
676 real = "\"Infinity\""; | |
677 } | |
678 StringAppendF(out, "%s", real.c_str()); | |
679 break; | |
680 } | |
681 case TRACE_VALUE_TYPE_POINTER: | |
682 // JSON only supports double and int numbers. | |
683 // So as not to lose bits from a 64-bit pointer, output as a hex string. | |
684 StringAppendF(out, "\"0x%" PRIx64 "\"", static_cast<uint64>( | |
685 reinterpret_cast<intptr_t>( | |
686 value.as_pointer))); | |
687 break; | |
688 case TRACE_VALUE_TYPE_STRING: | |
689 case TRACE_VALUE_TYPE_COPY_STRING: | |
690 EscapeJSONString(value.as_string ? value.as_string : "NULL", true, out); | |
691 break; | |
692 default: | |
693 NOTREACHED() << "Don't know how to print this value"; | |
694 break; | |
695 } | |
696 } | |
697 | |
698 void TraceEvent::AppendAsJSON(std::string* out) const { | |
699 int64 time_int64 = timestamp_.ToInternalValue(); | |
700 int process_id = TraceLog::GetInstance()->process_id(); | |
701 // Category group checked at category creation time. | |
702 DCHECK(!strchr(name_, '"')); | |
703 StringAppendF(out, | |
704 "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 "," | |
705 "\"ph\":\"%c\",\"cat\":\"%s\",\"name\":\"%s\",\"args\":{", | |
706 process_id, | |
707 thread_id_, | |
708 time_int64, | |
709 phase_, | |
710 TraceLog::GetCategoryGroupName(category_group_enabled_), | |
711 name_); | |
712 | |
713 // Output argument names and values, stop at first NULL argument name. | |
714 for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) { | |
715 if (i > 0) | |
716 *out += ","; | |
717 *out += "\""; | |
718 *out += arg_names_[i]; | |
719 *out += "\":"; | |
720 | |
721 if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) | |
722 convertable_values_[i]->AppendAsTraceFormat(out); | |
723 else | |
724 AppendValueAsJSON(arg_types_[i], arg_values_[i], out); | |
725 } | |
726 *out += "}"; | |
727 | |
728 if (phase_ == TRACE_EVENT_PHASE_COMPLETE) { | |
729 int64 duration = duration_.ToInternalValue(); | |
730 if (duration != -1) | |
731 StringAppendF(out, ",\"dur\":%" PRId64, duration); | |
732 if (!thread_timestamp_.is_null()) { | |
733 int64 thread_duration = thread_duration_.ToInternalValue(); | |
734 if (thread_duration != -1) | |
735 StringAppendF(out, ",\"tdur\":%" PRId64, thread_duration); | |
736 } | |
737 } | |
738 | |
739 // Output tts if thread_timestamp is valid. | |
740 if (!thread_timestamp_.is_null()) { | |
741 int64 thread_time_int64 = thread_timestamp_.ToInternalValue(); | |
742 StringAppendF(out, ",\"tts\":%" PRId64, thread_time_int64); | |
743 } | |
744 | |
745 // If id_ is set, print it out as a hex string so we don't loose any | |
746 // bits (it might be a 64-bit pointer). | |
747 if (flags_ & TRACE_EVENT_FLAG_HAS_ID) | |
748 StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"", static_cast<uint64>(id_)); | |
749 | |
750 // Instant events also output their scope. | |
751 if (phase_ == TRACE_EVENT_PHASE_INSTANT) { | |
752 char scope = '?'; | |
753 switch (flags_ & TRACE_EVENT_FLAG_SCOPE_MASK) { | |
754 case TRACE_EVENT_SCOPE_GLOBAL: | |
755 scope = TRACE_EVENT_SCOPE_NAME_GLOBAL; | |
756 break; | |
757 | |
758 case TRACE_EVENT_SCOPE_PROCESS: | |
759 scope = TRACE_EVENT_SCOPE_NAME_PROCESS; | |
760 break; | |
761 | |
762 case TRACE_EVENT_SCOPE_THREAD: | |
763 scope = TRACE_EVENT_SCOPE_NAME_THREAD; | |
764 break; | |
765 } | |
766 StringAppendF(out, ",\"s\":\"%c\"", scope); | |
767 } | |
768 | |
769 *out += "}"; | |
770 } | |
771 | |
772 void TraceEvent::AppendPrettyPrinted(std::ostringstream* out) const { | |
773 *out << name_ << "["; | |
774 *out << TraceLog::GetCategoryGroupName(category_group_enabled_); | |
775 *out << "]"; | |
776 if (arg_names_[0]) { | |
777 *out << ", {"; | |
778 for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) { | |
779 if (i > 0) | |
780 *out << ", "; | |
781 *out << arg_names_[i] << ":"; | |
782 std::string value_as_text; | |
783 | |
784 if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) | |
785 convertable_values_[i]->AppendAsTraceFormat(&value_as_text); | |
786 else | |
787 AppendValueAsJSON(arg_types_[i], arg_values_[i], &value_as_text); | |
788 | |
789 *out << value_as_text; | |
790 } | |
791 *out << "}"; | |
792 } | |
793 } | |
794 | |
795 //////////////////////////////////////////////////////////////////////////////// | |
796 // | |
797 // TraceResultBuffer | |
798 // | |
799 //////////////////////////////////////////////////////////////////////////////// | |
800 | |
801 TraceResultBuffer::OutputCallback | |
802 TraceResultBuffer::SimpleOutput::GetCallback() { | |
803 return Bind(&SimpleOutput::Append, Unretained(this)); | |
804 } | |
805 | |
806 void TraceResultBuffer::SimpleOutput::Append( | |
807 const std::string& json_trace_output) { | |
808 json_output += json_trace_output; | |
809 } | |
810 | |
811 TraceResultBuffer::TraceResultBuffer() : append_comma_(false) { | |
812 } | |
813 | |
814 TraceResultBuffer::~TraceResultBuffer() { | |
815 } | |
816 | |
817 void TraceResultBuffer::SetOutputCallback( | |
818 const OutputCallback& json_chunk_callback) { | |
819 output_callback_ = json_chunk_callback; | |
820 } | |
821 | |
822 void TraceResultBuffer::Start() { | |
823 append_comma_ = false; | |
824 output_callback_.Run("["); | |
825 } | |
826 | |
827 void TraceResultBuffer::AddFragment(const std::string& trace_fragment) { | |
828 if (append_comma_) | |
829 output_callback_.Run(","); | |
830 append_comma_ = true; | |
831 output_callback_.Run(trace_fragment); | |
832 } | |
833 | |
834 void TraceResultBuffer::Finish() { | |
835 output_callback_.Run("]"); | |
836 } | |
837 | |
838 //////////////////////////////////////////////////////////////////////////////// | |
839 // | |
840 // TraceSamplingThread | |
841 // | |
842 //////////////////////////////////////////////////////////////////////////////// | |
843 class TraceBucketData; | |
844 typedef base::Callback<void(TraceBucketData*)> TraceSampleCallback; | |
845 | |
846 class TraceBucketData { | |
847 public: | |
848 TraceBucketData(base::subtle::AtomicWord* bucket, | |
849 const char* name, | |
850 TraceSampleCallback callback); | |
851 ~TraceBucketData(); | |
852 | |
853 TRACE_EVENT_API_ATOMIC_WORD* bucket; | |
854 const char* bucket_name; | |
855 TraceSampleCallback callback; | |
856 }; | |
857 | |
858 // This object must be created on the IO thread. | |
859 class TraceSamplingThread : public PlatformThread::Delegate { | |
860 public: | |
861 TraceSamplingThread(); | |
862 ~TraceSamplingThread() override; | |
863 | |
864 // Implementation of PlatformThread::Delegate: | |
865 void ThreadMain() override; | |
866 | |
867 static void DefaultSamplingCallback(TraceBucketData* bucekt_data); | |
868 | |
869 void Stop(); | |
870 void WaitSamplingEventForTesting(); | |
871 | |
872 private: | |
873 friend class TraceLog; | |
874 | |
875 void GetSamples(); | |
876 // Not thread-safe. Once the ThreadMain has been called, this can no longer | |
877 // be called. | |
878 void RegisterSampleBucket(TRACE_EVENT_API_ATOMIC_WORD* bucket, | |
879 const char* const name, | |
880 TraceSampleCallback callback); | |
881 // Splits a combined "category\0name" into the two component parts. | |
882 static void ExtractCategoryAndName(const char* combined, | |
883 const char** category, | |
884 const char** name); | |
885 std::vector<TraceBucketData> sample_buckets_; | |
886 bool thread_running_; | |
887 CancellationFlag cancellation_flag_; | |
888 WaitableEvent waitable_event_for_testing_; | |
889 }; | |
890 | |
891 | |
892 TraceSamplingThread::TraceSamplingThread() | |
893 : thread_running_(false), | |
894 waitable_event_for_testing_(false, false) { | |
895 } | |
896 | |
897 TraceSamplingThread::~TraceSamplingThread() { | |
898 } | |
899 | |
900 void TraceSamplingThread::ThreadMain() { | |
901 PlatformThread::SetName("Sampling Thread"); | |
902 thread_running_ = true; | |
903 const int kSamplingFrequencyMicroseconds = 1000; | |
904 while (!cancellation_flag_.IsSet()) { | |
905 PlatformThread::Sleep( | |
906 TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); | |
907 GetSamples(); | |
908 waitable_event_for_testing_.Signal(); | |
909 } | |
910 } | |
911 | |
912 // static | |
913 void TraceSamplingThread::DefaultSamplingCallback( | |
914 TraceBucketData* bucket_data) { | |
915 TRACE_EVENT_API_ATOMIC_WORD category_and_name = | |
916 TRACE_EVENT_API_ATOMIC_LOAD(*bucket_data->bucket); | |
917 if (!category_and_name) | |
918 return; | |
919 const char* const combined = | |
920 reinterpret_cast<const char* const>(category_and_name); | |
921 const char* category_group; | |
922 const char* name; | |
923 ExtractCategoryAndName(combined, &category_group, &name); | |
924 TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_SAMPLE, | |
925 TraceLog::GetCategoryGroupEnabled(category_group), | |
926 name, 0, 0, NULL, NULL, NULL, NULL, 0); | |
927 } | |
928 | |
929 void TraceSamplingThread::GetSamples() { | |
930 for (size_t i = 0; i < sample_buckets_.size(); ++i) { | |
931 TraceBucketData* bucket_data = &sample_buckets_[i]; | |
932 bucket_data->callback.Run(bucket_data); | |
933 } | |
934 } | |
935 | |
936 void TraceSamplingThread::RegisterSampleBucket( | |
937 TRACE_EVENT_API_ATOMIC_WORD* bucket, | |
938 const char* const name, | |
939 TraceSampleCallback callback) { | |
940 // Access to sample_buckets_ doesn't cause races with the sampling thread | |
941 // that uses the sample_buckets_, because it is guaranteed that | |
942 // RegisterSampleBucket is called before the sampling thread is created. | |
943 DCHECK(!thread_running_); | |
944 sample_buckets_.push_back(TraceBucketData(bucket, name, callback)); | |
945 } | |
946 | |
947 // static | |
948 void TraceSamplingThread::ExtractCategoryAndName(const char* combined, | |
949 const char** category, | |
950 const char** name) { | |
951 *category = combined; | |
952 *name = &combined[strlen(combined) + 1]; | |
953 } | |
954 | |
955 void TraceSamplingThread::Stop() { | |
956 cancellation_flag_.Set(); | |
957 } | |
958 | |
959 void TraceSamplingThread::WaitSamplingEventForTesting() { | |
960 waitable_event_for_testing_.Wait(); | |
961 } | |
962 | |
963 TraceBucketData::TraceBucketData(base::subtle::AtomicWord* bucket, | |
964 const char* name, | |
965 TraceSampleCallback callback) | |
966 : bucket(bucket), | |
967 bucket_name(name), | |
968 callback(callback) { | |
969 } | |
970 | |
971 TraceBucketData::~TraceBucketData() { | |
972 } | |
973 | |
974 //////////////////////////////////////////////////////////////////////////////// | |
975 // | |
976 // TraceOptions | |
977 // | |
978 //////////////////////////////////////////////////////////////////////////////// | |
979 | |
980 bool TraceOptions::SetFromString(const std::string& options_string) { | |
981 record_mode = RECORD_UNTIL_FULL; | |
982 enable_sampling = false; | |
983 enable_systrace = false; | |
984 | |
985 std::vector<std::string> split; | |
986 std::vector<std::string>::iterator iter; | |
987 base::SplitString(options_string, ',', &split); | |
988 for (iter = split.begin(); iter != split.end(); ++iter) { | |
989 if (*iter == kRecordUntilFull) { | |
990 record_mode = RECORD_UNTIL_FULL; | |
991 } else if (*iter == kRecordContinuously) { | |
992 record_mode = RECORD_CONTINUOUSLY; | |
993 } else if (*iter == kTraceToConsole) { | |
994 record_mode = ECHO_TO_CONSOLE; | |
995 } else if (*iter == kRecordAsMuchAsPossible) { | |
996 record_mode = RECORD_AS_MUCH_AS_POSSIBLE; | |
997 } else if (*iter == kEnableSampling) { | |
998 enable_sampling = true; | |
999 } else if (*iter == kEnableSystrace) { | |
1000 enable_systrace = true; | |
1001 } else { | |
1002 return false; | |
1003 } | |
1004 } | |
1005 return true; | |
1006 } | |
1007 | |
1008 std::string TraceOptions::ToString() const { | |
1009 std::string ret; | |
1010 switch (record_mode) { | |
1011 case RECORD_UNTIL_FULL: | |
1012 ret = kRecordUntilFull; | |
1013 break; | |
1014 case RECORD_CONTINUOUSLY: | |
1015 ret = kRecordContinuously; | |
1016 break; | |
1017 case ECHO_TO_CONSOLE: | |
1018 ret = kTraceToConsole; | |
1019 break; | |
1020 case RECORD_AS_MUCH_AS_POSSIBLE: | |
1021 ret = kRecordAsMuchAsPossible; | |
1022 break; | |
1023 default: | |
1024 NOTREACHED(); | |
1025 } | |
1026 if (enable_sampling) | |
1027 ret = ret + "," + kEnableSampling; | |
1028 if (enable_systrace) | |
1029 ret = ret + "," + kEnableSystrace; | |
1030 return ret; | |
1031 } | |
1032 | |
1033 //////////////////////////////////////////////////////////////////////////////// | |
1034 // | |
1035 // TraceLog | |
1036 // | |
1037 //////////////////////////////////////////////////////////////////////////////// | |
1038 | |
1039 class TraceLog::ThreadLocalEventBuffer | |
1040 : public MessageLoop::DestructionObserver { | |
1041 public: | |
1042 ThreadLocalEventBuffer(TraceLog* trace_log); | |
1043 ~ThreadLocalEventBuffer() override; | |
1044 | |
1045 TraceEvent* AddTraceEvent(TraceEventHandle* handle); | |
1046 | |
1047 void ReportOverhead(const TimeTicks& event_timestamp, | |
1048 const TimeTicks& event_thread_timestamp); | |
1049 | |
1050 TraceEvent* GetEventByHandle(TraceEventHandle handle) { | |
1051 if (!chunk_ || handle.chunk_seq != chunk_->seq() || | |
1052 handle.chunk_index != chunk_index_) | |
1053 return NULL; | |
1054 | |
1055 return chunk_->GetEventAt(handle.event_index); | |
1056 } | |
1057 | |
1058 int generation() const { return generation_; } | |
1059 | |
1060 private: | |
1061 // MessageLoop::DestructionObserver | |
1062 void WillDestroyCurrentMessageLoop() override; | |
1063 | |
1064 void FlushWhileLocked(); | |
1065 | |
1066 void CheckThisIsCurrentBuffer() const { | |
1067 DCHECK(trace_log_->thread_local_event_buffer_.Get() == this); | |
1068 } | |
1069 | |
1070 // Since TraceLog is a leaky singleton, trace_log_ will always be valid | |
1071 // as long as the thread exists. | |
1072 TraceLog* trace_log_; | |
1073 scoped_ptr<TraceBufferChunk> chunk_; | |
1074 size_t chunk_index_; | |
1075 int event_count_; | |
1076 TimeDelta overhead_; | |
1077 int generation_; | |
1078 | |
1079 DISALLOW_COPY_AND_ASSIGN(ThreadLocalEventBuffer); | |
1080 }; | |
1081 | |
1082 TraceLog::ThreadLocalEventBuffer::ThreadLocalEventBuffer(TraceLog* trace_log) | |
1083 : trace_log_(trace_log), | |
1084 chunk_index_(0), | |
1085 event_count_(0), | |
1086 generation_(trace_log->generation()) { | |
1087 // ThreadLocalEventBuffer is created only if the thread has a message loop, so | |
1088 // the following message_loop won't be NULL. | |
1089 MessageLoop* message_loop = MessageLoop::current(); | |
1090 message_loop->AddDestructionObserver(this); | |
1091 | |
1092 AutoLock lock(trace_log->lock_); | |
1093 trace_log->thread_message_loops_.insert(message_loop); | |
1094 } | |
1095 | |
1096 TraceLog::ThreadLocalEventBuffer::~ThreadLocalEventBuffer() { | |
1097 CheckThisIsCurrentBuffer(); | |
1098 MessageLoop::current()->RemoveDestructionObserver(this); | |
1099 | |
1100 // Zero event_count_ happens in either of the following cases: | |
1101 // - no event generated for the thread; | |
1102 // - the thread has no message loop; | |
1103 // - trace_event_overhead is disabled. | |
1104 if (event_count_) { | |
1105 InitializeMetadataEvent(AddTraceEvent(NULL), | |
1106 static_cast<int>(base::PlatformThread::CurrentId()), | |
1107 "overhead", "average_overhead", | |
1108 overhead_.InMillisecondsF() / event_count_); | |
1109 } | |
1110 | |
1111 { | |
1112 AutoLock lock(trace_log_->lock_); | |
1113 FlushWhileLocked(); | |
1114 trace_log_->thread_message_loops_.erase(MessageLoop::current()); | |
1115 } | |
1116 trace_log_->thread_local_event_buffer_.Set(NULL); | |
1117 } | |
1118 | |
1119 TraceEvent* TraceLog::ThreadLocalEventBuffer::AddTraceEvent( | |
1120 TraceEventHandle* handle) { | |
1121 CheckThisIsCurrentBuffer(); | |
1122 | |
1123 if (chunk_ && chunk_->IsFull()) { | |
1124 AutoLock lock(trace_log_->lock_); | |
1125 FlushWhileLocked(); | |
1126 chunk_.reset(); | |
1127 } | |
1128 if (!chunk_) { | |
1129 AutoLock lock(trace_log_->lock_); | |
1130 chunk_ = trace_log_->logged_events_->GetChunk(&chunk_index_); | |
1131 trace_log_->CheckIfBufferIsFullWhileLocked(); | |
1132 } | |
1133 if (!chunk_) | |
1134 return NULL; | |
1135 | |
1136 size_t event_index; | |
1137 TraceEvent* trace_event = chunk_->AddTraceEvent(&event_index); | |
1138 if (trace_event && handle) | |
1139 MakeHandle(chunk_->seq(), chunk_index_, event_index, handle); | |
1140 | |
1141 return trace_event; | |
1142 } | |
1143 | |
1144 void TraceLog::ThreadLocalEventBuffer::ReportOverhead( | |
1145 const TimeTicks& event_timestamp, | |
1146 const TimeTicks& event_thread_timestamp) { | |
1147 if (!g_category_group_enabled[g_category_trace_event_overhead]) | |
1148 return; | |
1149 | |
1150 CheckThisIsCurrentBuffer(); | |
1151 | |
1152 event_count_++; | |
1153 TimeTicks thread_now = ThreadNow(); | |
1154 TimeTicks now = trace_log_->OffsetNow(); | |
1155 TimeDelta overhead = now - event_timestamp; | |
1156 if (overhead.InMicroseconds() >= kOverheadReportThresholdInMicroseconds) { | |
1157 TraceEvent* trace_event = AddTraceEvent(NULL); | |
1158 if (trace_event) { | |
1159 trace_event->Initialize( | |
1160 static_cast<int>(PlatformThread::CurrentId()), | |
1161 event_timestamp, event_thread_timestamp, | |
1162 TRACE_EVENT_PHASE_COMPLETE, | |
1163 &g_category_group_enabled[g_category_trace_event_overhead], | |
1164 "overhead", 0, 0, NULL, NULL, NULL, NULL, 0); | |
1165 trace_event->UpdateDuration(now, thread_now); | |
1166 } | |
1167 } | |
1168 overhead_ += overhead; | |
1169 } | |
1170 | |
1171 void TraceLog::ThreadLocalEventBuffer::WillDestroyCurrentMessageLoop() { | |
1172 delete this; | |
1173 } | |
1174 | |
1175 void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() { | |
1176 if (!chunk_) | |
1177 return; | |
1178 | |
1179 trace_log_->lock_.AssertAcquired(); | |
1180 if (trace_log_->CheckGeneration(generation_)) { | |
1181 // Return the chunk to the buffer only if the generation matches. | |
1182 trace_log_->logged_events_->ReturnChunk(chunk_index_, chunk_.Pass()); | |
1183 } | |
1184 // Otherwise this method may be called from the destructor, or TraceLog will | |
1185 // find the generation mismatch and delete this buffer soon. | |
1186 } | |
1187 | |
1188 TraceLogStatus::TraceLogStatus() : event_capacity(0), event_count(0) { | |
1189 } | |
1190 | |
1191 TraceLogStatus::~TraceLogStatus() { | |
1192 } | |
1193 | |
1194 // static | |
1195 TraceLog* TraceLog::GetInstance() { | |
1196 return Singleton<TraceLog, LeakySingletonTraits<TraceLog> >::get(); | |
1197 } | |
1198 | |
1199 TraceLog::TraceLog() | |
1200 : mode_(DISABLED), | |
1201 num_traces_recorded_(0), | |
1202 event_callback_(0), | |
1203 dispatching_to_observer_list_(false), | |
1204 process_sort_index_(0), | |
1205 process_id_hash_(0), | |
1206 process_id_(0), | |
1207 watch_category_(0), | |
1208 trace_options_(kInternalRecordUntilFull), | |
1209 sampling_thread_handle_(0), | |
1210 category_filter_(CategoryFilter::kDefaultCategoryFilterString), | |
1211 event_callback_category_filter_( | |
1212 CategoryFilter::kDefaultCategoryFilterString), | |
1213 thread_shared_chunk_index_(0), | |
1214 generation_(0) { | |
1215 // Trace is enabled or disabled on one thread while other threads are | |
1216 // accessing the enabled flag. We don't care whether edge-case events are | |
1217 // traced or not, so we allow races on the enabled flag to keep the trace | |
1218 // macros fast. | |
1219 // TODO(jbates): ANNOTATE_BENIGN_RACE_SIZED crashes windows TSAN bots: | |
1220 // ANNOTATE_BENIGN_RACE_SIZED(g_category_group_enabled, | |
1221 // sizeof(g_category_group_enabled), | |
1222 // "trace_event category enabled"); | |
1223 for (int i = 0; i < MAX_CATEGORY_GROUPS; ++i) { | |
1224 ANNOTATE_BENIGN_RACE(&g_category_group_enabled[i], | |
1225 "trace_event category enabled"); | |
1226 } | |
1227 #if defined(OS_NACL) // NaCl shouldn't expose the process id. | |
1228 SetProcessID(0); | |
1229 #else | |
1230 SetProcessID(static_cast<int>(GetCurrentProcId())); | |
1231 | |
1232 // NaCl also shouldn't access the command line. | |
1233 if (CommandLine::InitializedForCurrentProcess() && | |
1234 CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToConsole)) { | |
1235 std::string filter = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
1236 switches::kTraceToConsole); | |
1237 if (filter.empty()) { | |
1238 filter = kEchoToConsoleCategoryFilter; | |
1239 } else { | |
1240 filter.append(","); | |
1241 filter.append(kEchoToConsoleCategoryFilter); | |
1242 } | |
1243 | |
1244 LOG(ERROR) << "Start " << switches::kTraceToConsole | |
1245 << " with CategoryFilter '" << filter << "'."; | |
1246 SetEnabled(CategoryFilter(filter), | |
1247 RECORDING_MODE, | |
1248 TraceOptions(ECHO_TO_CONSOLE)); | |
1249 } | |
1250 #endif | |
1251 | |
1252 logged_events_.reset(CreateTraceBuffer()); | |
1253 } | |
1254 | |
1255 TraceLog::~TraceLog() { | |
1256 } | |
1257 | |
1258 const unsigned char* TraceLog::GetCategoryGroupEnabled( | |
1259 const char* category_group) { | |
1260 TraceLog* tracelog = GetInstance(); | |
1261 if (!tracelog) { | |
1262 DCHECK(!g_category_group_enabled[g_category_already_shutdown]); | |
1263 return &g_category_group_enabled[g_category_already_shutdown]; | |
1264 } | |
1265 return tracelog->GetCategoryGroupEnabledInternal(category_group); | |
1266 } | |
1267 | |
1268 const char* TraceLog::GetCategoryGroupName( | |
1269 const unsigned char* category_group_enabled) { | |
1270 // Calculate the index of the category group by finding | |
1271 // category_group_enabled in g_category_group_enabled array. | |
1272 uintptr_t category_begin = | |
1273 reinterpret_cast<uintptr_t>(g_category_group_enabled); | |
1274 uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled); | |
1275 DCHECK(category_ptr >= category_begin && | |
1276 category_ptr < reinterpret_cast<uintptr_t>( | |
1277 g_category_group_enabled + MAX_CATEGORY_GROUPS)) << | |
1278 "out of bounds category pointer"; | |
1279 uintptr_t category_index = | |
1280 (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]); | |
1281 return g_category_groups[category_index]; | |
1282 } | |
1283 | |
1284 void TraceLog::UpdateCategoryGroupEnabledFlag(size_t category_index) { | |
1285 unsigned char enabled_flag = 0; | |
1286 const char* category_group = g_category_groups[category_index]; | |
1287 if (mode_ == RECORDING_MODE && | |
1288 category_filter_.IsCategoryGroupEnabled(category_group)) | |
1289 enabled_flag |= ENABLED_FOR_RECORDING; | |
1290 else if (mode_ == MONITORING_MODE && | |
1291 category_filter_.IsCategoryGroupEnabled(category_group)) | |
1292 enabled_flag |= ENABLED_FOR_MONITORING; | |
1293 if (event_callback_ && | |
1294 event_callback_category_filter_.IsCategoryGroupEnabled(category_group)) | |
1295 enabled_flag |= ENABLED_FOR_EVENT_CALLBACK; | |
1296 g_category_group_enabled[category_index] = enabled_flag; | |
1297 } | |
1298 | |
1299 void TraceLog::UpdateCategoryGroupEnabledFlags() { | |
1300 size_t category_index = base::subtle::NoBarrier_Load(&g_category_index); | |
1301 for (size_t i = 0; i < category_index; i++) | |
1302 UpdateCategoryGroupEnabledFlag(i); | |
1303 } | |
1304 | |
1305 void TraceLog::UpdateSyntheticDelaysFromCategoryFilter() { | |
1306 ResetTraceEventSyntheticDelays(); | |
1307 const CategoryFilter::StringList& delays = | |
1308 category_filter_.GetSyntheticDelayValues(); | |
1309 CategoryFilter::StringList::const_iterator ci; | |
1310 for (ci = delays.begin(); ci != delays.end(); ++ci) { | |
1311 StringTokenizer tokens(*ci, ";"); | |
1312 if (!tokens.GetNext()) | |
1313 continue; | |
1314 TraceEventSyntheticDelay* delay = | |
1315 TraceEventSyntheticDelay::Lookup(tokens.token()); | |
1316 while (tokens.GetNext()) { | |
1317 std::string token = tokens.token(); | |
1318 char* duration_end; | |
1319 double target_duration = strtod(token.c_str(), &duration_end); | |
1320 if (duration_end != token.c_str()) { | |
1321 delay->SetTargetDuration(TimeDelta::FromMicroseconds( | |
1322 static_cast<int64>(target_duration * 1e6))); | |
1323 } else if (token == "static") { | |
1324 delay->SetMode(TraceEventSyntheticDelay::STATIC); | |
1325 } else if (token == "oneshot") { | |
1326 delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT); | |
1327 } else if (token == "alternating") { | |
1328 delay->SetMode(TraceEventSyntheticDelay::ALTERNATING); | |
1329 } | |
1330 } | |
1331 } | |
1332 } | |
1333 | |
1334 const unsigned char* TraceLog::GetCategoryGroupEnabledInternal( | |
1335 const char* category_group) { | |
1336 DCHECK(!strchr(category_group, '"')) << | |
1337 "Category groups may not contain double quote"; | |
1338 // The g_category_groups is append only, avoid using a lock for the fast path. | |
1339 size_t current_category_index = base::subtle::Acquire_Load(&g_category_index); | |
1340 | |
1341 // Search for pre-existing category group. | |
1342 for (size_t i = 0; i < current_category_index; ++i) { | |
1343 if (strcmp(g_category_groups[i], category_group) == 0) { | |
1344 return &g_category_group_enabled[i]; | |
1345 } | |
1346 } | |
1347 | |
1348 unsigned char* category_group_enabled = NULL; | |
1349 // This is the slow path: the lock is not held in the case above, so more | |
1350 // than one thread could have reached here trying to add the same category. | |
1351 // Only hold to lock when actually appending a new category, and | |
1352 // check the categories groups again. | |
1353 AutoLock lock(lock_); | |
1354 size_t category_index = base::subtle::Acquire_Load(&g_category_index); | |
1355 for (size_t i = 0; i < category_index; ++i) { | |
1356 if (strcmp(g_category_groups[i], category_group) == 0) { | |
1357 return &g_category_group_enabled[i]; | |
1358 } | |
1359 } | |
1360 | |
1361 // Create a new category group. | |
1362 DCHECK(category_index < MAX_CATEGORY_GROUPS) << | |
1363 "must increase MAX_CATEGORY_GROUPS"; | |
1364 if (category_index < MAX_CATEGORY_GROUPS) { | |
1365 // Don't hold on to the category_group pointer, so that we can create | |
1366 // category groups with strings not known at compile time (this is | |
1367 // required by SetWatchEvent). | |
1368 const char* new_group = strdup(category_group); | |
1369 ANNOTATE_LEAKING_OBJECT_PTR(new_group); | |
1370 g_category_groups[category_index] = new_group; | |
1371 DCHECK(!g_category_group_enabled[category_index]); | |
1372 // Note that if both included and excluded patterns in the | |
1373 // CategoryFilter are empty, we exclude nothing, | |
1374 // thereby enabling this category group. | |
1375 UpdateCategoryGroupEnabledFlag(category_index); | |
1376 category_group_enabled = &g_category_group_enabled[category_index]; | |
1377 // Update the max index now. | |
1378 base::subtle::Release_Store(&g_category_index, category_index + 1); | |
1379 } else { | |
1380 category_group_enabled = | |
1381 &g_category_group_enabled[g_category_categories_exhausted]; | |
1382 } | |
1383 return category_group_enabled; | |
1384 } | |
1385 | |
1386 void TraceLog::GetKnownCategoryGroups( | |
1387 std::vector<std::string>* category_groups) { | |
1388 AutoLock lock(lock_); | |
1389 category_groups->push_back( | |
1390 g_category_groups[g_category_trace_event_overhead]); | |
1391 size_t category_index = base::subtle::NoBarrier_Load(&g_category_index); | |
1392 for (size_t i = g_num_builtin_categories; i < category_index; i++) | |
1393 category_groups->push_back(g_category_groups[i]); | |
1394 } | |
1395 | |
1396 void TraceLog::SetEnabled(const CategoryFilter& category_filter, | |
1397 Mode mode, | |
1398 const TraceOptions& options) { | |
1399 std::vector<EnabledStateObserver*> observer_list; | |
1400 { | |
1401 AutoLock lock(lock_); | |
1402 | |
1403 // Can't enable tracing when Flush() is in progress. | |
1404 DCHECK(!flush_message_loop_proxy_.get()); | |
1405 | |
1406 InternalTraceOptions new_options = | |
1407 GetInternalOptionsFromTraceOptions(options); | |
1408 | |
1409 InternalTraceOptions old_options = trace_options(); | |
1410 | |
1411 if (IsEnabled()) { | |
1412 if (new_options != old_options) { | |
1413 DLOG(ERROR) << "Attempting to re-enable tracing with a different " | |
1414 << "set of options."; | |
1415 } | |
1416 | |
1417 if (mode != mode_) { | |
1418 DLOG(ERROR) << "Attempting to re-enable tracing with a different mode."; | |
1419 } | |
1420 | |
1421 category_filter_.Merge(category_filter); | |
1422 UpdateCategoryGroupEnabledFlags(); | |
1423 return; | |
1424 } | |
1425 | |
1426 if (dispatching_to_observer_list_) { | |
1427 DLOG(ERROR) << | |
1428 "Cannot manipulate TraceLog::Enabled state from an observer."; | |
1429 return; | |
1430 } | |
1431 | |
1432 mode_ = mode; | |
1433 | |
1434 if (new_options != old_options) { | |
1435 subtle::NoBarrier_Store(&trace_options_, new_options); | |
1436 UseNextTraceBuffer(); | |
1437 } | |
1438 | |
1439 num_traces_recorded_++; | |
1440 | |
1441 category_filter_ = CategoryFilter(category_filter); | |
1442 UpdateCategoryGroupEnabledFlags(); | |
1443 UpdateSyntheticDelaysFromCategoryFilter(); | |
1444 | |
1445 if (new_options & kInternalEnableSampling) { | |
1446 sampling_thread_.reset(new TraceSamplingThread); | |
1447 sampling_thread_->RegisterSampleBucket( | |
1448 &g_trace_state[0], | |
1449 "bucket0", | |
1450 Bind(&TraceSamplingThread::DefaultSamplingCallback)); | |
1451 sampling_thread_->RegisterSampleBucket( | |
1452 &g_trace_state[1], | |
1453 "bucket1", | |
1454 Bind(&TraceSamplingThread::DefaultSamplingCallback)); | |
1455 sampling_thread_->RegisterSampleBucket( | |
1456 &g_trace_state[2], | |
1457 "bucket2", | |
1458 Bind(&TraceSamplingThread::DefaultSamplingCallback)); | |
1459 if (!PlatformThread::Create( | |
1460 0, sampling_thread_.get(), &sampling_thread_handle_)) { | |
1461 DCHECK(false) << "failed to create thread"; | |
1462 } | |
1463 } | |
1464 | |
1465 dispatching_to_observer_list_ = true; | |
1466 observer_list = enabled_state_observer_list_; | |
1467 } | |
1468 // Notify observers outside the lock in case they trigger trace events. | |
1469 for (size_t i = 0; i < observer_list.size(); ++i) | |
1470 observer_list[i]->OnTraceLogEnabled(); | |
1471 | |
1472 { | |
1473 AutoLock lock(lock_); | |
1474 dispatching_to_observer_list_ = false; | |
1475 } | |
1476 } | |
1477 | |
1478 TraceLog::InternalTraceOptions TraceLog::GetInternalOptionsFromTraceOptions( | |
1479 const TraceOptions& options) { | |
1480 InternalTraceOptions ret = | |
1481 options.enable_sampling ? kInternalEnableSampling : kInternalNone; | |
1482 switch (options.record_mode) { | |
1483 case RECORD_UNTIL_FULL: | |
1484 return ret | kInternalRecordUntilFull; | |
1485 case RECORD_CONTINUOUSLY: | |
1486 return ret | kInternalRecordContinuously; | |
1487 case ECHO_TO_CONSOLE: | |
1488 return ret | kInternalEchoToConsole; | |
1489 case RECORD_AS_MUCH_AS_POSSIBLE: | |
1490 return ret | kInternalRecordAsMuchAsPossible; | |
1491 } | |
1492 NOTREACHED(); | |
1493 return kInternalNone; | |
1494 } | |
1495 | |
1496 CategoryFilter TraceLog::GetCurrentCategoryFilter() { | |
1497 AutoLock lock(lock_); | |
1498 return category_filter_; | |
1499 } | |
1500 | |
1501 TraceOptions TraceLog::GetCurrentTraceOptions() const { | |
1502 TraceOptions ret; | |
1503 InternalTraceOptions option = trace_options(); | |
1504 ret.enable_sampling = (option & kInternalEnableSampling) != 0; | |
1505 if (option & kInternalRecordUntilFull) | |
1506 ret.record_mode = RECORD_UNTIL_FULL; | |
1507 else if (option & kInternalRecordContinuously) | |
1508 ret.record_mode = RECORD_CONTINUOUSLY; | |
1509 else if (option & kInternalEchoToConsole) | |
1510 ret.record_mode = ECHO_TO_CONSOLE; | |
1511 else if (option & kInternalRecordAsMuchAsPossible) | |
1512 ret.record_mode = RECORD_AS_MUCH_AS_POSSIBLE; | |
1513 else | |
1514 NOTREACHED(); | |
1515 return ret; | |
1516 } | |
1517 | |
1518 void TraceLog::SetDisabled() { | |
1519 AutoLock lock(lock_); | |
1520 SetDisabledWhileLocked(); | |
1521 } | |
1522 | |
1523 void TraceLog::SetDisabledWhileLocked() { | |
1524 lock_.AssertAcquired(); | |
1525 | |
1526 if (!IsEnabled()) | |
1527 return; | |
1528 | |
1529 if (dispatching_to_observer_list_) { | |
1530 DLOG(ERROR) | |
1531 << "Cannot manipulate TraceLog::Enabled state from an observer."; | |
1532 return; | |
1533 } | |
1534 | |
1535 mode_ = DISABLED; | |
1536 | |
1537 if (sampling_thread_.get()) { | |
1538 // Stop the sampling thread. | |
1539 sampling_thread_->Stop(); | |
1540 lock_.Release(); | |
1541 PlatformThread::Join(sampling_thread_handle_); | |
1542 lock_.Acquire(); | |
1543 sampling_thread_handle_ = PlatformThreadHandle(); | |
1544 sampling_thread_.reset(); | |
1545 } | |
1546 | |
1547 category_filter_.Clear(); | |
1548 subtle::NoBarrier_Store(&watch_category_, 0); | |
1549 watch_event_name_ = ""; | |
1550 UpdateCategoryGroupEnabledFlags(); | |
1551 AddMetadataEventsWhileLocked(); | |
1552 | |
1553 dispatching_to_observer_list_ = true; | |
1554 std::vector<EnabledStateObserver*> observer_list = | |
1555 enabled_state_observer_list_; | |
1556 | |
1557 { | |
1558 // Dispatch to observers outside the lock in case the observer triggers a | |
1559 // trace event. | |
1560 AutoUnlock unlock(lock_); | |
1561 for (size_t i = 0; i < observer_list.size(); ++i) | |
1562 observer_list[i]->OnTraceLogDisabled(); | |
1563 } | |
1564 dispatching_to_observer_list_ = false; | |
1565 } | |
1566 | |
1567 int TraceLog::GetNumTracesRecorded() { | |
1568 AutoLock lock(lock_); | |
1569 if (!IsEnabled()) | |
1570 return -1; | |
1571 return num_traces_recorded_; | |
1572 } | |
1573 | |
1574 void TraceLog::AddEnabledStateObserver(EnabledStateObserver* listener) { | |
1575 enabled_state_observer_list_.push_back(listener); | |
1576 } | |
1577 | |
1578 void TraceLog::RemoveEnabledStateObserver(EnabledStateObserver* listener) { | |
1579 std::vector<EnabledStateObserver*>::iterator it = | |
1580 std::find(enabled_state_observer_list_.begin(), | |
1581 enabled_state_observer_list_.end(), | |
1582 listener); | |
1583 if (it != enabled_state_observer_list_.end()) | |
1584 enabled_state_observer_list_.erase(it); | |
1585 } | |
1586 | |
1587 bool TraceLog::HasEnabledStateObserver(EnabledStateObserver* listener) const { | |
1588 std::vector<EnabledStateObserver*>::const_iterator it = | |
1589 std::find(enabled_state_observer_list_.begin(), | |
1590 enabled_state_observer_list_.end(), | |
1591 listener); | |
1592 return it != enabled_state_observer_list_.end(); | |
1593 } | |
1594 | |
1595 TraceLogStatus TraceLog::GetStatus() const { | |
1596 AutoLock lock(lock_); | |
1597 TraceLogStatus result; | |
1598 result.event_capacity = logged_events_->Capacity(); | |
1599 result.event_count = logged_events_->Size(); | |
1600 return result; | |
1601 } | |
1602 | |
1603 bool TraceLog::BufferIsFull() const { | |
1604 AutoLock lock(lock_); | |
1605 return logged_events_->IsFull(); | |
1606 } | |
1607 | |
1608 TraceBuffer* TraceLog::CreateTraceBuffer() { | |
1609 InternalTraceOptions options = trace_options(); | |
1610 if (options & kInternalRecordContinuously) | |
1611 return new TraceBufferRingBuffer(kTraceEventRingBufferChunks); | |
1612 else if ((options & kInternalEnableSampling) && mode_ == MONITORING_MODE) | |
1613 return new TraceBufferRingBuffer(kMonitorTraceEventBufferChunks); | |
1614 else if (options & kInternalEchoToConsole) | |
1615 return new TraceBufferRingBuffer(kEchoToConsoleTraceEventBufferChunks); | |
1616 else if (options & kInternalRecordAsMuchAsPossible) | |
1617 return CreateTraceBufferVectorOfSize(kTraceEventVectorBigBufferChunks); | |
1618 return CreateTraceBufferVectorOfSize(kTraceEventVectorBufferChunks); | |
1619 } | |
1620 | |
1621 TraceBuffer* TraceLog::CreateTraceBufferVectorOfSize(size_t max_chunks) { | |
1622 return new TraceBufferVector(max_chunks); | |
1623 } | |
1624 | |
1625 TraceEvent* TraceLog::AddEventToThreadSharedChunkWhileLocked( | |
1626 TraceEventHandle* handle, bool check_buffer_is_full) { | |
1627 lock_.AssertAcquired(); | |
1628 | |
1629 if (thread_shared_chunk_ && thread_shared_chunk_->IsFull()) { | |
1630 logged_events_->ReturnChunk(thread_shared_chunk_index_, | |
1631 thread_shared_chunk_.Pass()); | |
1632 } | |
1633 | |
1634 if (!thread_shared_chunk_) { | |
1635 thread_shared_chunk_ = logged_events_->GetChunk( | |
1636 &thread_shared_chunk_index_); | |
1637 if (check_buffer_is_full) | |
1638 CheckIfBufferIsFullWhileLocked(); | |
1639 } | |
1640 if (!thread_shared_chunk_) | |
1641 return NULL; | |
1642 | |
1643 size_t event_index; | |
1644 TraceEvent* trace_event = thread_shared_chunk_->AddTraceEvent(&event_index); | |
1645 if (trace_event && handle) { | |
1646 MakeHandle(thread_shared_chunk_->seq(), thread_shared_chunk_index_, | |
1647 event_index, handle); | |
1648 } | |
1649 return trace_event; | |
1650 } | |
1651 | |
1652 void TraceLog::CheckIfBufferIsFullWhileLocked() { | |
1653 lock_.AssertAcquired(); | |
1654 if (logged_events_->IsFull()) { | |
1655 if (buffer_limit_reached_timestamp_.is_null()) { | |
1656 buffer_limit_reached_timestamp_ = OffsetNow(); | |
1657 } | |
1658 SetDisabledWhileLocked(); | |
1659 } | |
1660 } | |
1661 | |
1662 void TraceLog::SetEventCallbackEnabled(const CategoryFilter& category_filter, | |
1663 EventCallback cb) { | |
1664 AutoLock lock(lock_); | |
1665 subtle::NoBarrier_Store(&event_callback_, | |
1666 reinterpret_cast<subtle::AtomicWord>(cb)); | |
1667 event_callback_category_filter_ = category_filter; | |
1668 UpdateCategoryGroupEnabledFlags(); | |
1669 }; | |
1670 | |
1671 void TraceLog::SetEventCallbackDisabled() { | |
1672 AutoLock lock(lock_); | |
1673 subtle::NoBarrier_Store(&event_callback_, 0); | |
1674 UpdateCategoryGroupEnabledFlags(); | |
1675 } | |
1676 | |
1677 // Flush() works as the following: | |
1678 // 1. Flush() is called in threadA whose message loop is saved in | |
1679 // flush_message_loop_proxy_; | |
1680 // 2. If thread_message_loops_ is not empty, threadA posts task to each message | |
1681 // loop to flush the thread local buffers; otherwise finish the flush; | |
1682 // 3. FlushCurrentThread() deletes the thread local event buffer: | |
1683 // - The last batch of events of the thread are flushed into the main buffer; | |
1684 // - The message loop will be removed from thread_message_loops_; | |
1685 // If this is the last message loop, finish the flush; | |
1686 // 4. If any thread hasn't finish its flush in time, finish the flush. | |
1687 void TraceLog::Flush(const TraceLog::OutputCallback& cb) { | |
1688 if (IsEnabled()) { | |
1689 // Can't flush when tracing is enabled because otherwise PostTask would | |
1690 // - generate more trace events; | |
1691 // - deschedule the calling thread on some platforms causing inaccurate | |
1692 // timing of the trace events. | |
1693 scoped_refptr<RefCountedString> empty_result = new RefCountedString; | |
1694 if (!cb.is_null()) | |
1695 cb.Run(empty_result, false); | |
1696 LOG(WARNING) << "Ignored TraceLog::Flush called when tracing is enabled"; | |
1697 return; | |
1698 } | |
1699 | |
1700 int generation = this->generation(); | |
1701 // Copy of thread_message_loops_ to be used without locking. | |
1702 std::vector<scoped_refptr<SingleThreadTaskRunner> > | |
1703 thread_message_loop_task_runners; | |
1704 { | |
1705 AutoLock lock(lock_); | |
1706 DCHECK(!flush_message_loop_proxy_.get()); | |
1707 flush_message_loop_proxy_ = MessageLoopProxy::current(); | |
1708 DCHECK(!thread_message_loops_.size() || flush_message_loop_proxy_.get()); | |
1709 flush_output_callback_ = cb; | |
1710 | |
1711 if (thread_shared_chunk_) { | |
1712 logged_events_->ReturnChunk(thread_shared_chunk_index_, | |
1713 thread_shared_chunk_.Pass()); | |
1714 } | |
1715 | |
1716 if (thread_message_loops_.size()) { | |
1717 for (hash_set<MessageLoop*>::const_iterator it = | |
1718 thread_message_loops_.begin(); | |
1719 it != thread_message_loops_.end(); ++it) { | |
1720 thread_message_loop_task_runners.push_back((*it)->task_runner()); | |
1721 } | |
1722 } | |
1723 } | |
1724 | |
1725 if (thread_message_loop_task_runners.size()) { | |
1726 for (size_t i = 0; i < thread_message_loop_task_runners.size(); ++i) { | |
1727 thread_message_loop_task_runners[i]->PostTask( | |
1728 FROM_HERE, | |
1729 Bind(&TraceLog::FlushCurrentThread, Unretained(this), generation)); | |
1730 } | |
1731 flush_message_loop_proxy_->PostDelayedTask( | |
1732 FROM_HERE, | |
1733 Bind(&TraceLog::OnFlushTimeout, Unretained(this), generation), | |
1734 TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs)); | |
1735 return; | |
1736 } | |
1737 | |
1738 FinishFlush(generation); | |
1739 } | |
1740 | |
1741 void TraceLog::ConvertTraceEventsToTraceFormat( | |
1742 scoped_ptr<TraceBuffer> logged_events, | |
1743 const TraceLog::OutputCallback& flush_output_callback) { | |
1744 | |
1745 if (flush_output_callback.is_null()) | |
1746 return; | |
1747 | |
1748 // The callback need to be called at least once even if there is no events | |
1749 // to let the caller know the completion of flush. | |
1750 bool has_more_events = true; | |
1751 do { | |
1752 scoped_refptr<RefCountedString> json_events_str_ptr = | |
1753 new RefCountedString(); | |
1754 | |
1755 for (size_t i = 0; i < kTraceEventBatchChunks; ++i) { | |
1756 const TraceBufferChunk* chunk = logged_events->NextChunk(); | |
1757 if (!chunk) { | |
1758 has_more_events = false; | |
1759 break; | |
1760 } | |
1761 for (size_t j = 0; j < chunk->size(); ++j) { | |
1762 if (i > 0 || j > 0) | |
1763 json_events_str_ptr->data().append(",\n"); | |
1764 chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data())); | |
1765 } | |
1766 } | |
1767 | |
1768 flush_output_callback.Run(json_events_str_ptr, has_more_events); | |
1769 } while (has_more_events); | |
1770 } | |
1771 | |
1772 void TraceLog::FinishFlush(int generation) { | |
1773 scoped_ptr<TraceBuffer> previous_logged_events; | |
1774 OutputCallback flush_output_callback; | |
1775 | |
1776 if (!CheckGeneration(generation)) | |
1777 return; | |
1778 | |
1779 { | |
1780 AutoLock lock(lock_); | |
1781 | |
1782 previous_logged_events.swap(logged_events_); | |
1783 UseNextTraceBuffer(); | |
1784 thread_message_loops_.clear(); | |
1785 | |
1786 flush_message_loop_proxy_ = NULL; | |
1787 flush_output_callback = flush_output_callback_; | |
1788 flush_output_callback_.Reset(); | |
1789 } | |
1790 | |
1791 ConvertTraceEventsToTraceFormat(previous_logged_events.Pass(), | |
1792 flush_output_callback); | |
1793 } | |
1794 | |
1795 // Run in each thread holding a local event buffer. | |
1796 void TraceLog::FlushCurrentThread(int generation) { | |
1797 { | |
1798 AutoLock lock(lock_); | |
1799 if (!CheckGeneration(generation) || !flush_message_loop_proxy_.get()) { | |
1800 // This is late. The corresponding flush has finished. | |
1801 return; | |
1802 } | |
1803 } | |
1804 | |
1805 // This will flush the thread local buffer. | |
1806 delete thread_local_event_buffer_.Get(); | |
1807 | |
1808 AutoLock lock(lock_); | |
1809 if (!CheckGeneration(generation) || !flush_message_loop_proxy_.get() || | |
1810 thread_message_loops_.size()) | |
1811 return; | |
1812 | |
1813 flush_message_loop_proxy_->PostTask( | |
1814 FROM_HERE, | |
1815 Bind(&TraceLog::FinishFlush, Unretained(this), generation)); | |
1816 } | |
1817 | |
1818 void TraceLog::OnFlushTimeout(int generation) { | |
1819 { | |
1820 AutoLock lock(lock_); | |
1821 if (!CheckGeneration(generation) || !flush_message_loop_proxy_.get()) { | |
1822 // Flush has finished before timeout. | |
1823 return; | |
1824 } | |
1825 | |
1826 LOG(WARNING) << | |
1827 "The following threads haven't finished flush in time. " | |
1828 "If this happens stably for some thread, please call " | |
1829 "TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop() from " | |
1830 "the thread to avoid its trace events from being lost."; | |
1831 for (hash_set<MessageLoop*>::const_iterator it = | |
1832 thread_message_loops_.begin(); | |
1833 it != thread_message_loops_.end(); ++it) { | |
1834 LOG(WARNING) << "Thread: " << (*it)->thread_name(); | |
1835 } | |
1836 } | |
1837 FinishFlush(generation); | |
1838 } | |
1839 | |
1840 void TraceLog::FlushButLeaveBufferIntact( | |
1841 const TraceLog::OutputCallback& flush_output_callback) { | |
1842 scoped_ptr<TraceBuffer> previous_logged_events; | |
1843 { | |
1844 AutoLock lock(lock_); | |
1845 AddMetadataEventsWhileLocked(); | |
1846 if (thread_shared_chunk_) { | |
1847 // Return the chunk to the main buffer to flush the sampling data. | |
1848 logged_events_->ReturnChunk(thread_shared_chunk_index_, | |
1849 thread_shared_chunk_.Pass()); | |
1850 } | |
1851 previous_logged_events = logged_events_->CloneForIteration().Pass(); | |
1852 } // release lock | |
1853 | |
1854 ConvertTraceEventsToTraceFormat(previous_logged_events.Pass(), | |
1855 flush_output_callback); | |
1856 } | |
1857 | |
1858 void TraceLog::UseNextTraceBuffer() { | |
1859 logged_events_.reset(CreateTraceBuffer()); | |
1860 subtle::NoBarrier_AtomicIncrement(&generation_, 1); | |
1861 thread_shared_chunk_.reset(); | |
1862 thread_shared_chunk_index_ = 0; | |
1863 } | |
1864 | |
1865 TraceEventHandle TraceLog::AddTraceEvent( | |
1866 char phase, | |
1867 const unsigned char* category_group_enabled, | |
1868 const char* name, | |
1869 unsigned long long id, | |
1870 int num_args, | |
1871 const char** arg_names, | |
1872 const unsigned char* arg_types, | |
1873 const unsigned long long* arg_values, | |
1874 const scoped_refptr<ConvertableToTraceFormat>* convertable_values, | |
1875 unsigned char flags) { | |
1876 int thread_id = static_cast<int>(base::PlatformThread::CurrentId()); | |
1877 base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime(); | |
1878 return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, | |
1879 name, id, thread_id, now, | |
1880 num_args, arg_names, | |
1881 arg_types, arg_values, | |
1882 convertable_values, flags); | |
1883 } | |
1884 | |
1885 TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( | |
1886 char phase, | |
1887 const unsigned char* category_group_enabled, | |
1888 const char* name, | |
1889 unsigned long long id, | |
1890 int thread_id, | |
1891 const TimeTicks& timestamp, | |
1892 int num_args, | |
1893 const char** arg_names, | |
1894 const unsigned char* arg_types, | |
1895 const unsigned long long* arg_values, | |
1896 const scoped_refptr<ConvertableToTraceFormat>* convertable_values, | |
1897 unsigned char flags) { | |
1898 TraceEventHandle handle = { 0, 0, 0 }; | |
1899 if (!*category_group_enabled) | |
1900 return handle; | |
1901 | |
1902 // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when | |
1903 // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) -> | |
1904 // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ... | |
1905 if (thread_is_in_trace_event_.Get()) | |
1906 return handle; | |
1907 | |
1908 AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_); | |
1909 | |
1910 DCHECK(name); | |
1911 | |
1912 if (flags & TRACE_EVENT_FLAG_MANGLE_ID) | |
1913 id ^= process_id_hash_; | |
1914 | |
1915 TimeTicks now = OffsetTimestamp(timestamp); | |
1916 TimeTicks thread_now = ThreadNow(); | |
1917 | |
1918 ThreadLocalEventBuffer* thread_local_event_buffer = NULL; | |
1919 // A ThreadLocalEventBuffer needs the message loop | |
1920 // - to know when the thread exits; | |
1921 // - to handle the final flush. | |
1922 // For a thread without a message loop or the message loop may be blocked, the | |
1923 // trace events will be added into the main buffer directly. | |
1924 if (!thread_blocks_message_loop_.Get() && MessageLoop::current()) { | |
1925 thread_local_event_buffer = thread_local_event_buffer_.Get(); | |
1926 if (thread_local_event_buffer && | |
1927 !CheckGeneration(thread_local_event_buffer->generation())) { | |
1928 delete thread_local_event_buffer; | |
1929 thread_local_event_buffer = NULL; | |
1930 } | |
1931 if (!thread_local_event_buffer) { | |
1932 thread_local_event_buffer = new ThreadLocalEventBuffer(this); | |
1933 thread_local_event_buffer_.Set(thread_local_event_buffer); | |
1934 } | |
1935 } | |
1936 | |
1937 // Check and update the current thread name only if the event is for the | |
1938 // current thread to avoid locks in most cases. | |
1939 if (thread_id == static_cast<int>(PlatformThread::CurrentId())) { | |
1940 const char* new_name = ThreadIdNameManager::GetInstance()-> | |
1941 GetName(thread_id); | |
1942 // Check if the thread name has been set or changed since the previous | |
1943 // call (if any), but don't bother if the new name is empty. Note this will | |
1944 // not detect a thread name change within the same char* buffer address: we | |
1945 // favor common case performance over corner case correctness. | |
1946 if (new_name != g_current_thread_name.Get().Get() && | |
1947 new_name && *new_name) { | |
1948 g_current_thread_name.Get().Set(new_name); | |
1949 | |
1950 AutoLock thread_info_lock(thread_info_lock_); | |
1951 | |
1952 hash_map<int, std::string>::iterator existing_name = | |
1953 thread_names_.find(thread_id); | |
1954 if (existing_name == thread_names_.end()) { | |
1955 // This is a new thread id, and a new name. | |
1956 thread_names_[thread_id] = new_name; | |
1957 } else { | |
1958 // This is a thread id that we've seen before, but potentially with a | |
1959 // new name. | |
1960 std::vector<StringPiece> existing_names; | |
1961 Tokenize(existing_name->second, ",", &existing_names); | |
1962 bool found = std::find(existing_names.begin(), | |
1963 existing_names.end(), | |
1964 new_name) != existing_names.end(); | |
1965 if (!found) { | |
1966 if (existing_names.size()) | |
1967 existing_name->second.push_back(','); | |
1968 existing_name->second.append(new_name); | |
1969 } | |
1970 } | |
1971 } | |
1972 } | |
1973 | |
1974 std::string console_message; | |
1975 if (*category_group_enabled & | |
1976 (ENABLED_FOR_RECORDING | ENABLED_FOR_MONITORING)) { | |
1977 OptionalAutoLock lock(lock_); | |
1978 | |
1979 TraceEvent* trace_event = NULL; | |
1980 if (thread_local_event_buffer) { | |
1981 trace_event = thread_local_event_buffer->AddTraceEvent(&handle); | |
1982 } else { | |
1983 lock.EnsureAcquired(); | |
1984 trace_event = AddEventToThreadSharedChunkWhileLocked(&handle, true); | |
1985 } | |
1986 | |
1987 if (trace_event) { | |
1988 trace_event->Initialize(thread_id, now, thread_now, phase, | |
1989 category_group_enabled, name, id, | |
1990 num_args, arg_names, arg_types, arg_values, | |
1991 convertable_values, flags); | |
1992 | |
1993 #if defined(OS_ANDROID) | |
1994 trace_event->SendToATrace(); | |
1995 #endif | |
1996 } | |
1997 | |
1998 if (trace_options() & kInternalEchoToConsole) { | |
1999 console_message = EventToConsoleMessage( | |
2000 phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase, | |
2001 timestamp, trace_event); | |
2002 } | |
2003 } | |
2004 | |
2005 if (console_message.size()) | |
2006 LOG(ERROR) << console_message; | |
2007 | |
2008 if (reinterpret_cast<const unsigned char*>(subtle::NoBarrier_Load( | |
2009 &watch_category_)) == category_group_enabled) { | |
2010 bool event_name_matches; | |
2011 WatchEventCallback watch_event_callback_copy; | |
2012 { | |
2013 AutoLock lock(lock_); | |
2014 event_name_matches = watch_event_name_ == name; | |
2015 watch_event_callback_copy = watch_event_callback_; | |
2016 } | |
2017 if (event_name_matches) { | |
2018 if (!watch_event_callback_copy.is_null()) | |
2019 watch_event_callback_copy.Run(); | |
2020 } | |
2021 } | |
2022 | |
2023 if (*category_group_enabled & ENABLED_FOR_EVENT_CALLBACK) { | |
2024 EventCallback event_callback = reinterpret_cast<EventCallback>( | |
2025 subtle::NoBarrier_Load(&event_callback_)); | |
2026 if (event_callback) { | |
2027 event_callback(now, | |
2028 phase == TRACE_EVENT_PHASE_COMPLETE ? | |
2029 TRACE_EVENT_PHASE_BEGIN : phase, | |
2030 category_group_enabled, name, id, | |
2031 num_args, arg_names, arg_types, arg_values, | |
2032 flags); | |
2033 } | |
2034 } | |
2035 | |
2036 if (thread_local_event_buffer) | |
2037 thread_local_event_buffer->ReportOverhead(now, thread_now); | |
2038 | |
2039 return handle; | |
2040 } | |
2041 | |
2042 // May be called when a COMPELETE event ends and the unfinished event has been | |
2043 // recycled (phase == TRACE_EVENT_PHASE_END and trace_event == NULL). | |
2044 std::string TraceLog::EventToConsoleMessage(unsigned char phase, | |
2045 const TimeTicks& timestamp, | |
2046 TraceEvent* trace_event) { | |
2047 AutoLock thread_info_lock(thread_info_lock_); | |
2048 | |
2049 // The caller should translate TRACE_EVENT_PHASE_COMPLETE to | |
2050 // TRACE_EVENT_PHASE_BEGIN or TRACE_EVENT_END. | |
2051 DCHECK(phase != TRACE_EVENT_PHASE_COMPLETE); | |
2052 | |
2053 TimeDelta duration; | |
2054 int thread_id = trace_event ? | |
2055 trace_event->thread_id() : PlatformThread::CurrentId(); | |
2056 if (phase == TRACE_EVENT_PHASE_END) { | |
2057 duration = timestamp - thread_event_start_times_[thread_id].top(); | |
2058 thread_event_start_times_[thread_id].pop(); | |
2059 } | |
2060 | |
2061 std::string thread_name = thread_names_[thread_id]; | |
2062 if (thread_colors_.find(thread_name) == thread_colors_.end()) | |
2063 thread_colors_[thread_name] = (thread_colors_.size() % 6) + 1; | |
2064 | |
2065 std::ostringstream log; | |
2066 log << base::StringPrintf("%s: \x1b[0;3%dm", | |
2067 thread_name.c_str(), | |
2068 thread_colors_[thread_name]); | |
2069 | |
2070 size_t depth = 0; | |
2071 if (thread_event_start_times_.find(thread_id) != | |
2072 thread_event_start_times_.end()) | |
2073 depth = thread_event_start_times_[thread_id].size(); | |
2074 | |
2075 for (size_t i = 0; i < depth; ++i) | |
2076 log << "| "; | |
2077 | |
2078 if (trace_event) | |
2079 trace_event->AppendPrettyPrinted(&log); | |
2080 if (phase == TRACE_EVENT_PHASE_END) | |
2081 log << base::StringPrintf(" (%.3f ms)", duration.InMillisecondsF()); | |
2082 | |
2083 log << "\x1b[0;m"; | |
2084 | |
2085 if (phase == TRACE_EVENT_PHASE_BEGIN) | |
2086 thread_event_start_times_[thread_id].push(timestamp); | |
2087 | |
2088 return log.str(); | |
2089 } | |
2090 | |
2091 void TraceLog::AddTraceEventEtw(char phase, | |
2092 const char* name, | |
2093 const void* id, | |
2094 const char* extra) { | |
2095 #if defined(OS_WIN) | |
2096 TraceEventETWProvider::Trace(name, phase, id, extra); | |
2097 #endif | |
2098 INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name, | |
2099 TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra); | |
2100 } | |
2101 | |
2102 void TraceLog::AddTraceEventEtw(char phase, | |
2103 const char* name, | |
2104 const void* id, | |
2105 const std::string& extra) { | |
2106 #if defined(OS_WIN) | |
2107 TraceEventETWProvider::Trace(name, phase, id, extra); | |
2108 #endif | |
2109 INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name, | |
2110 TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra); | |
2111 } | |
2112 | |
2113 void TraceLog::UpdateTraceEventDuration( | |
2114 const unsigned char* category_group_enabled, | |
2115 const char* name, | |
2116 TraceEventHandle handle) { | |
2117 // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when | |
2118 // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) -> | |
2119 // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ... | |
2120 if (thread_is_in_trace_event_.Get()) | |
2121 return; | |
2122 | |
2123 AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_); | |
2124 | |
2125 TimeTicks thread_now = ThreadNow(); | |
2126 TimeTicks now = OffsetNow(); | |
2127 | |
2128 std::string console_message; | |
2129 if (*category_group_enabled & ENABLED_FOR_RECORDING) { | |
2130 OptionalAutoLock lock(lock_); | |
2131 | |
2132 TraceEvent* trace_event = GetEventByHandleInternal(handle, &lock); | |
2133 if (trace_event) { | |
2134 DCHECK(trace_event->phase() == TRACE_EVENT_PHASE_COMPLETE); | |
2135 trace_event->UpdateDuration(now, thread_now); | |
2136 #if defined(OS_ANDROID) | |
2137 trace_event->SendToATrace(); | |
2138 #endif | |
2139 } | |
2140 | |
2141 if (trace_options() & kInternalEchoToConsole) { | |
2142 console_message = EventToConsoleMessage(TRACE_EVENT_PHASE_END, | |
2143 now, trace_event); | |
2144 } | |
2145 } | |
2146 | |
2147 if (console_message.size()) | |
2148 LOG(ERROR) << console_message; | |
2149 | |
2150 if (*category_group_enabled & ENABLED_FOR_EVENT_CALLBACK) { | |
2151 EventCallback event_callback = reinterpret_cast<EventCallback>( | |
2152 subtle::NoBarrier_Load(&event_callback_)); | |
2153 if (event_callback) { | |
2154 event_callback(now, TRACE_EVENT_PHASE_END, category_group_enabled, name, | |
2155 trace_event_internal::kNoEventId, 0, NULL, NULL, NULL, | |
2156 TRACE_EVENT_FLAG_NONE); | |
2157 } | |
2158 } | |
2159 } | |
2160 | |
2161 void TraceLog::SetWatchEvent(const std::string& category_name, | |
2162 const std::string& event_name, | |
2163 const WatchEventCallback& callback) { | |
2164 const unsigned char* category = GetCategoryGroupEnabled( | |
2165 category_name.c_str()); | |
2166 AutoLock lock(lock_); | |
2167 subtle::NoBarrier_Store(&watch_category_, | |
2168 reinterpret_cast<subtle::AtomicWord>(category)); | |
2169 watch_event_name_ = event_name; | |
2170 watch_event_callback_ = callback; | |
2171 } | |
2172 | |
2173 void TraceLog::CancelWatchEvent() { | |
2174 AutoLock lock(lock_); | |
2175 subtle::NoBarrier_Store(&watch_category_, 0); | |
2176 watch_event_name_ = ""; | |
2177 watch_event_callback_.Reset(); | |
2178 } | |
2179 | |
2180 void TraceLog::AddMetadataEventsWhileLocked() { | |
2181 lock_.AssertAcquired(); | |
2182 | |
2183 #if !defined(OS_NACL) // NaCl shouldn't expose the process id. | |
2184 InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), | |
2185 0, | |
2186 "num_cpus", "number", | |
2187 base::SysInfo::NumberOfProcessors()); | |
2188 #endif | |
2189 | |
2190 | |
2191 int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId()); | |
2192 if (process_sort_index_ != 0) { | |
2193 InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), | |
2194 current_thread_id, | |
2195 "process_sort_index", "sort_index", | |
2196 process_sort_index_); | |
2197 } | |
2198 | |
2199 if (process_name_.size()) { | |
2200 InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), | |
2201 current_thread_id, | |
2202 "process_name", "name", | |
2203 process_name_); | |
2204 } | |
2205 | |
2206 if (process_labels_.size() > 0) { | |
2207 std::vector<std::string> labels; | |
2208 for(base::hash_map<int, std::string>::iterator it = process_labels_.begin(); | |
2209 it != process_labels_.end(); | |
2210 it++) { | |
2211 labels.push_back(it->second); | |
2212 } | |
2213 InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), | |
2214 current_thread_id, | |
2215 "process_labels", "labels", | |
2216 JoinString(labels, ',')); | |
2217 } | |
2218 | |
2219 // Thread sort indices. | |
2220 for(hash_map<int, int>::iterator it = thread_sort_indices_.begin(); | |
2221 it != thread_sort_indices_.end(); | |
2222 it++) { | |
2223 if (it->second == 0) | |
2224 continue; | |
2225 InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), | |
2226 it->first, | |
2227 "thread_sort_index", "sort_index", | |
2228 it->second); | |
2229 } | |
2230 | |
2231 // Thread names. | |
2232 AutoLock thread_info_lock(thread_info_lock_); | |
2233 for(hash_map<int, std::string>::iterator it = thread_names_.begin(); | |
2234 it != thread_names_.end(); | |
2235 it++) { | |
2236 if (it->second.empty()) | |
2237 continue; | |
2238 InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), | |
2239 it->first, | |
2240 "thread_name", "name", | |
2241 it->second); | |
2242 } | |
2243 | |
2244 // If buffer is full, add a metadata record to report this. | |
2245 if (!buffer_limit_reached_timestamp_.is_null()) { | |
2246 InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), | |
2247 current_thread_id, | |
2248 "trace_buffer_overflowed", | |
2249 "overflowed_at_ts", | |
2250 buffer_limit_reached_timestamp_); | |
2251 } | |
2252 } | |
2253 | |
2254 void TraceLog::WaitSamplingEventForTesting() { | |
2255 if (!sampling_thread_) | |
2256 return; | |
2257 sampling_thread_->WaitSamplingEventForTesting(); | |
2258 } | |
2259 | |
2260 void TraceLog::DeleteForTesting() { | |
2261 DeleteTraceLogForTesting::Delete(); | |
2262 } | |
2263 | |
2264 TraceEvent* TraceLog::GetEventByHandle(TraceEventHandle handle) { | |
2265 return GetEventByHandleInternal(handle, NULL); | |
2266 } | |
2267 | |
2268 TraceEvent* TraceLog::GetEventByHandleInternal(TraceEventHandle handle, | |
2269 OptionalAutoLock* lock) { | |
2270 if (!handle.chunk_seq) | |
2271 return NULL; | |
2272 | |
2273 if (thread_local_event_buffer_.Get()) { | |
2274 TraceEvent* trace_event = | |
2275 thread_local_event_buffer_.Get()->GetEventByHandle(handle); | |
2276 if (trace_event) | |
2277 return trace_event; | |
2278 } | |
2279 | |
2280 // The event has been out-of-control of the thread local buffer. | |
2281 // Try to get the event from the main buffer with a lock. | |
2282 if (lock) | |
2283 lock->EnsureAcquired(); | |
2284 | |
2285 if (thread_shared_chunk_ && | |
2286 handle.chunk_index == thread_shared_chunk_index_) { | |
2287 return handle.chunk_seq == thread_shared_chunk_->seq() ? | |
2288 thread_shared_chunk_->GetEventAt(handle.event_index) : NULL; | |
2289 } | |
2290 | |
2291 return logged_events_->GetEventByHandle(handle); | |
2292 } | |
2293 | |
2294 void TraceLog::SetProcessID(int process_id) { | |
2295 process_id_ = process_id; | |
2296 // Create a FNV hash from the process ID for XORing. | |
2297 // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details. | |
2298 unsigned long long offset_basis = 14695981039346656037ull; | |
2299 unsigned long long fnv_prime = 1099511628211ull; | |
2300 unsigned long long pid = static_cast<unsigned long long>(process_id_); | |
2301 process_id_hash_ = (offset_basis ^ pid) * fnv_prime; | |
2302 } | |
2303 | |
2304 void TraceLog::SetProcessSortIndex(int sort_index) { | |
2305 AutoLock lock(lock_); | |
2306 process_sort_index_ = sort_index; | |
2307 } | |
2308 | |
2309 void TraceLog::SetProcessName(const std::string& process_name) { | |
2310 AutoLock lock(lock_); | |
2311 process_name_ = process_name; | |
2312 } | |
2313 | |
2314 void TraceLog::UpdateProcessLabel( | |
2315 int label_id, const std::string& current_label) { | |
2316 if(!current_label.length()) | |
2317 return RemoveProcessLabel(label_id); | |
2318 | |
2319 AutoLock lock(lock_); | |
2320 process_labels_[label_id] = current_label; | |
2321 } | |
2322 | |
2323 void TraceLog::RemoveProcessLabel(int label_id) { | |
2324 AutoLock lock(lock_); | |
2325 base::hash_map<int, std::string>::iterator it = process_labels_.find( | |
2326 label_id); | |
2327 if (it == process_labels_.end()) | |
2328 return; | |
2329 | |
2330 process_labels_.erase(it); | |
2331 } | |
2332 | |
2333 void TraceLog::SetThreadSortIndex(PlatformThreadId thread_id, int sort_index) { | |
2334 AutoLock lock(lock_); | |
2335 thread_sort_indices_[static_cast<int>(thread_id)] = sort_index; | |
2336 } | |
2337 | |
2338 void TraceLog::SetTimeOffset(TimeDelta offset) { | |
2339 time_offset_ = offset; | |
2340 } | |
2341 | |
2342 size_t TraceLog::GetObserverCountForTest() const { | |
2343 return enabled_state_observer_list_.size(); | |
2344 } | |
2345 | |
2346 void TraceLog::SetCurrentThreadBlocksMessageLoop() { | |
2347 thread_blocks_message_loop_.Set(true); | |
2348 if (thread_local_event_buffer_.Get()) { | |
2349 // This will flush the thread local buffer. | |
2350 delete thread_local_event_buffer_.Get(); | |
2351 } | |
2352 } | |
2353 | |
2354 bool CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace( | |
2355 const std::string& str) { | |
2356 return str.empty() || | |
2357 str.at(0) == ' ' || | |
2358 str.at(str.length() - 1) == ' '; | |
2359 } | |
2360 | |
2361 CategoryFilter::CategoryFilter(const std::string& filter_string) { | |
2362 if (!filter_string.empty()) | |
2363 Initialize(filter_string); | |
2364 else | |
2365 Initialize(CategoryFilter::kDefaultCategoryFilterString); | |
2366 } | |
2367 | |
2368 CategoryFilter::CategoryFilter() { | |
2369 Initialize(CategoryFilter::kDefaultCategoryFilterString); | |
2370 } | |
2371 | |
2372 CategoryFilter::CategoryFilter(const CategoryFilter& cf) | |
2373 : included_(cf.included_), | |
2374 disabled_(cf.disabled_), | |
2375 excluded_(cf.excluded_), | |
2376 delays_(cf.delays_) { | |
2377 } | |
2378 | |
2379 CategoryFilter::~CategoryFilter() { | |
2380 } | |
2381 | |
2382 CategoryFilter& CategoryFilter::operator=(const CategoryFilter& rhs) { | |
2383 if (this == &rhs) | |
2384 return *this; | |
2385 | |
2386 included_ = rhs.included_; | |
2387 disabled_ = rhs.disabled_; | |
2388 excluded_ = rhs.excluded_; | |
2389 delays_ = rhs.delays_; | |
2390 return *this; | |
2391 } | |
2392 | |
2393 void CategoryFilter::Initialize(const std::string& filter_string) { | |
2394 // Tokenize list of categories, delimited by ','. | |
2395 StringTokenizer tokens(filter_string, ","); | |
2396 // Add each token to the appropriate list (included_,excluded_). | |
2397 while (tokens.GetNext()) { | |
2398 std::string category = tokens.token(); | |
2399 // Ignore empty categories. | |
2400 if (category.empty()) | |
2401 continue; | |
2402 // Synthetic delays are of the form 'DELAY(delay;option;option;...)'. | |
2403 if (category.find(kSyntheticDelayCategoryFilterPrefix) == 0 && | |
2404 category.at(category.size() - 1) == ')') { | |
2405 category = category.substr( | |
2406 strlen(kSyntheticDelayCategoryFilterPrefix), | |
2407 category.size() - strlen(kSyntheticDelayCategoryFilterPrefix) - 1); | |
2408 size_t name_length = category.find(';'); | |
2409 if (name_length != std::string::npos && name_length > 0 && | |
2410 name_length != category.size() - 1) { | |
2411 delays_.push_back(category); | |
2412 } | |
2413 } else if (category.at(0) == '-') { | |
2414 // Excluded categories start with '-'. | |
2415 // Remove '-' from category string. | |
2416 category = category.substr(1); | |
2417 excluded_.push_back(category); | |
2418 } else if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")), | |
2419 TRACE_DISABLED_BY_DEFAULT("")) == 0) { | |
2420 disabled_.push_back(category); | |
2421 } else { | |
2422 included_.push_back(category); | |
2423 } | |
2424 } | |
2425 } | |
2426 | |
2427 void CategoryFilter::WriteString(const StringList& values, | |
2428 std::string* out, | |
2429 bool included) const { | |
2430 bool prepend_comma = !out->empty(); | |
2431 int token_cnt = 0; | |
2432 for (StringList::const_iterator ci = values.begin(); | |
2433 ci != values.end(); ++ci) { | |
2434 if (token_cnt > 0 || prepend_comma) | |
2435 StringAppendF(out, ","); | |
2436 StringAppendF(out, "%s%s", (included ? "" : "-"), ci->c_str()); | |
2437 ++token_cnt; | |
2438 } | |
2439 } | |
2440 | |
2441 void CategoryFilter::WriteString(const StringList& delays, | |
2442 std::string* out) const { | |
2443 bool prepend_comma = !out->empty(); | |
2444 int token_cnt = 0; | |
2445 for (StringList::const_iterator ci = delays.begin(); | |
2446 ci != delays.end(); ++ci) { | |
2447 if (token_cnt > 0 || prepend_comma) | |
2448 StringAppendF(out, ","); | |
2449 StringAppendF(out, "%s%s)", kSyntheticDelayCategoryFilterPrefix, | |
2450 ci->c_str()); | |
2451 ++token_cnt; | |
2452 } | |
2453 } | |
2454 | |
2455 std::string CategoryFilter::ToString() const { | |
2456 std::string filter_string; | |
2457 WriteString(included_, &filter_string, true); | |
2458 WriteString(disabled_, &filter_string, true); | |
2459 WriteString(excluded_, &filter_string, false); | |
2460 WriteString(delays_, &filter_string); | |
2461 return filter_string; | |
2462 } | |
2463 | |
2464 bool CategoryFilter::IsCategoryGroupEnabled( | |
2465 const char* category_group_name) const { | |
2466 // TraceLog should call this method only as part of enabling/disabling | |
2467 // categories. | |
2468 | |
2469 bool had_enabled_by_default = false; | |
2470 DCHECK(category_group_name); | |
2471 CStringTokenizer category_group_tokens( | |
2472 category_group_name, category_group_name + strlen(category_group_name), | |
2473 ","); | |
2474 while (category_group_tokens.GetNext()) { | |
2475 std::string category_group_token = category_group_tokens.token(); | |
2476 // Don't allow empty tokens, nor tokens with leading or trailing space. | |
2477 DCHECK(!CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace( | |
2478 category_group_token)) | |
2479 << "Disallowed category string"; | |
2480 if (IsCategoryEnabled(category_group_token.c_str())) { | |
2481 return true; | |
2482 } | |
2483 if (!MatchPattern(category_group_token.c_str(), | |
2484 TRACE_DISABLED_BY_DEFAULT("*"))) | |
2485 had_enabled_by_default = true; | |
2486 } | |
2487 // Do a second pass to check for explicitly disabled categories | |
2488 // (those explicitly enabled have priority due to first pass). | |
2489 category_group_tokens.Reset(); | |
2490 while (category_group_tokens.GetNext()) { | |
2491 std::string category_group_token = category_group_tokens.token(); | |
2492 for (StringList::const_iterator ci = excluded_.begin(); | |
2493 ci != excluded_.end(); ++ci) { | |
2494 if (MatchPattern(category_group_token.c_str(), ci->c_str())) | |
2495 return false; | |
2496 } | |
2497 } | |
2498 // If the category group is not excluded, and there are no included patterns | |
2499 // we consider this category group enabled, as long as it had categories | |
2500 // other than disabled-by-default. | |
2501 return included_.empty() && had_enabled_by_default; | |
2502 } | |
2503 | |
2504 bool CategoryFilter::IsCategoryEnabled(const char* category_name) const { | |
2505 StringList::const_iterator ci; | |
2506 | |
2507 // Check the disabled- filters and the disabled-* wildcard first so that a | |
2508 // "*" filter does not include the disabled. | |
2509 for (ci = disabled_.begin(); ci != disabled_.end(); ++ci) { | |
2510 if (MatchPattern(category_name, ci->c_str())) | |
2511 return true; | |
2512 } | |
2513 | |
2514 if (MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*"))) | |
2515 return false; | |
2516 | |
2517 for (ci = included_.begin(); ci != included_.end(); ++ci) { | |
2518 if (MatchPattern(category_name, ci->c_str())) | |
2519 return true; | |
2520 } | |
2521 | |
2522 return false; | |
2523 } | |
2524 | |
2525 bool CategoryFilter::HasIncludedPatterns() const { | |
2526 return !included_.empty(); | |
2527 } | |
2528 | |
2529 void CategoryFilter::Merge(const CategoryFilter& nested_filter) { | |
2530 // Keep included patterns only if both filters have an included entry. | |
2531 // Otherwise, one of the filter was specifying "*" and we want to honour the | |
2532 // broadest filter. | |
2533 if (HasIncludedPatterns() && nested_filter.HasIncludedPatterns()) { | |
2534 included_.insert(included_.end(), | |
2535 nested_filter.included_.begin(), | |
2536 nested_filter.included_.end()); | |
2537 } else { | |
2538 included_.clear(); | |
2539 } | |
2540 | |
2541 disabled_.insert(disabled_.end(), | |
2542 nested_filter.disabled_.begin(), | |
2543 nested_filter.disabled_.end()); | |
2544 excluded_.insert(excluded_.end(), | |
2545 nested_filter.excluded_.begin(), | |
2546 nested_filter.excluded_.end()); | |
2547 delays_.insert(delays_.end(), | |
2548 nested_filter.delays_.begin(), | |
2549 nested_filter.delays_.end()); | |
2550 } | |
2551 | |
2552 void CategoryFilter::Clear() { | |
2553 included_.clear(); | |
2554 disabled_.clear(); | |
2555 excluded_.clear(); | |
2556 } | |
2557 | |
2558 const CategoryFilter::StringList& | |
2559 CategoryFilter::GetSyntheticDelayValues() const { | |
2560 return delays_; | |
2561 } | |
2562 | |
2563 } // namespace debug | |
2564 } // namespace base | |
2565 | |
2566 namespace trace_event_internal { | |
2567 | |
2568 ScopedTraceBinaryEfficient::ScopedTraceBinaryEfficient( | |
2569 const char* category_group, const char* name) { | |
2570 // The single atom works because for now the category_group can only be "gpu". | |
2571 DCHECK(strcmp(category_group, "gpu") == 0); | |
2572 static TRACE_EVENT_API_ATOMIC_WORD atomic = 0; | |
2573 INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( | |
2574 category_group, atomic, category_group_enabled_); | |
2575 name_ = name; | |
2576 if (*category_group_enabled_) { | |
2577 event_handle_ = | |
2578 TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP( | |
2579 TRACE_EVENT_PHASE_COMPLETE, category_group_enabled_, name, | |
2580 trace_event_internal::kNoEventId, | |
2581 static_cast<int>(base::PlatformThread::CurrentId()), | |
2582 base::TimeTicks::NowFromSystemTraceTime(), | |
2583 0, NULL, NULL, NULL, NULL, TRACE_EVENT_FLAG_NONE); | |
2584 } | |
2585 } | |
2586 | |
2587 ScopedTraceBinaryEfficient::~ScopedTraceBinaryEfficient() { | |
2588 if (*category_group_enabled_) { | |
2589 TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_group_enabled_, | |
2590 name_, event_handle_); | |
2591 } | |
2592 } | |
2593 | |
2594 } // namespace trace_event_internal | |
OLD | NEW |