Index: trunk/src/base/debug/trace_event_impl.cc |
=================================================================== |
--- trunk/src/base/debug/trace_event_impl.cc (revision 237451) |
+++ trunk/src/base/debug/trace_event_impl.cc (working copy) |
@@ -99,6 +99,11 @@ |
LazyInstance<ThreadLocalPointer<const char> >::Leaky |
g_current_thread_name = LAZY_INSTANCE_INITIALIZER; |
+const char kRecordUntilFull[] = "record-until-full"; |
+const char kRecordContinuously[] = "record-continuously"; |
+const char kEnableSampling[] = "enable-sampling"; |
+const char kMonitorSampling[] = "monitor-sampling"; |
+ |
TimeTicks ThreadNow() { |
return TimeTicks::IsThreadNowSupported() ? |
TimeTicks::ThreadNow() : TimeTicks(); |
@@ -939,10 +944,12 @@ |
ThreadLocalEventBuffer(TraceLog* trace_log); |
virtual ~ThreadLocalEventBuffer(); |
- TraceEvent* AddTraceEvent(TraceEventHandle* handle); |
+ TraceEvent* AddTraceEvent(NotificationHelper* notifier, |
+ TraceEventHandle* handle); |
void ReportOverhead(const TimeTicks& event_timestamp, |
- const TimeTicks& event_thread_timestamp); |
+ const TimeTicks& event_thread_timestamp, |
+ NotificationHelper* notifier); |
TraceEvent* GetEventByHandle(TraceEventHandle handle) { |
if (!chunk_ || handle.chunk_seq != chunk_->seq() || |
@@ -999,10 +1006,12 @@ |
// - the thread has no message loop; |
// - trace_event_overhead is disabled. |
if (event_count_) { |
- InitializeMetadataEvent(AddTraceEvent(NULL), |
+ NotificationHelper notifier(trace_log_); |
+ InitializeMetadataEvent(AddTraceEvent(¬ifier, NULL), |
static_cast<int>(base::PlatformThread::CurrentId()), |
"overhead", "average_overhead", |
overhead_.InMillisecondsF() / event_count_); |
+ notifier.SendNotificationIfAny(); |
} |
{ |
@@ -1014,6 +1023,7 @@ |
} |
TraceEvent* TraceLog::ThreadLocalEventBuffer::AddTraceEvent( |
+ NotificationHelper* notifier, |
TraceEventHandle* handle) { |
CheckThisIsCurrentBuffer(); |
@@ -1025,7 +1035,7 @@ |
if (!chunk_) { |
AutoLock lock(trace_log_->lock_); |
chunk_ = trace_log_->logged_events_->GetChunk(&chunk_index_); |
- trace_log_->CheckIfBufferIsFullWhileLocked(); |
+ trace_log_->CheckIfBufferIsFullWhileLocked(notifier); |
} |
if (!chunk_) |
return NULL; |
@@ -1040,7 +1050,8 @@ |
void TraceLog::ThreadLocalEventBuffer::ReportOverhead( |
const TimeTicks& event_timestamp, |
- const TimeTicks& event_thread_timestamp) { |
+ const TimeTicks& event_thread_timestamp, |
+ NotificationHelper* notifier) { |
if (!g_category_group_enabled[g_category_trace_event_overhead]) |
return; |
@@ -1050,7 +1061,7 @@ |
TimeTicks now = trace_log_->OffsetNow(); |
TimeDelta overhead = now - event_timestamp; |
if (overhead.InMicroseconds() >= kOverheadReportThresholdInMicroseconds) { |
- TraceEvent* trace_event = AddTraceEvent(NULL); |
+ TraceEvent* trace_event = AddTraceEvent(notifier, NULL); |
if (trace_event) { |
trace_event->Initialize( |
static_cast<int>(PlatformThread::CurrentId()), |
@@ -1081,14 +1092,66 @@ |
// find the generation mismatch and delete this buffer soon. |
} |
+TraceLog::NotificationHelper::NotificationHelper(TraceLog* trace_log) |
+ : trace_log_(trace_log), |
+ notification_(0) { |
+} |
+ |
+TraceLog::NotificationHelper::~NotificationHelper() { |
+} |
+ |
+void TraceLog::NotificationHelper::AddNotificationWhileLocked( |
+ int notification) { |
+ trace_log_->lock_.AssertAcquired(); |
+ if (trace_log_->notification_callback_.is_null()) |
+ return; |
+ if (notification_ == 0) |
+ callback_copy_ = trace_log_->notification_callback_; |
+ notification_ |= notification; |
+} |
+ |
+void TraceLog::NotificationHelper::SendNotificationIfAny() { |
+ if (notification_) |
+ callback_copy_.Run(notification_); |
+} |
+ |
// static |
TraceLog* TraceLog::GetInstance() { |
return Singleton<TraceLog, LeakySingletonTraits<TraceLog> >::get(); |
} |
+// static |
+// Note, if you add more options here you also need to update: |
+// content/browser/devtools/devtools_tracing_handler:TraceOptionsFromString |
+TraceLog::Options TraceLog::TraceOptionsFromString(const std::string& options) { |
+ std::vector<std::string> split; |
+ base::SplitString(options, ',', &split); |
+ int ret = 0; |
+ for (std::vector<std::string>::iterator iter = split.begin(); |
+ iter != split.end(); |
+ ++iter) { |
+ if (*iter == kRecordUntilFull) { |
+ ret |= RECORD_UNTIL_FULL; |
+ } else if (*iter == kRecordContinuously) { |
+ ret |= RECORD_CONTINUOUSLY; |
+ } else if (*iter == kEnableSampling) { |
+ ret |= ENABLE_SAMPLING; |
+ } else if (*iter == kMonitorSampling) { |
+ ret |= MONITOR_SAMPLING; |
+ } else { |
+ NOTREACHED(); // Unknown option provided. |
+ } |
+ } |
+ if (!(ret & RECORD_UNTIL_FULL) && !(ret & RECORD_CONTINUOUSLY)) |
+ ret |= RECORD_UNTIL_FULL; // Default when no options are specified. |
+ |
+ return static_cast<Options>(ret); |
+} |
+ |
TraceLog::TraceLog() |
: enabled_(false), |
num_traces_recorded_(0), |
+ buffer_is_full_(0), |
event_callback_(0), |
dispatching_to_observer_list_(false), |
process_sort_index_(0), |
@@ -1265,6 +1328,7 @@ |
subtle::NoBarrier_Store(&trace_options_, options); |
logged_events_.reset(CreateTraceBuffer()); |
NextGeneration(); |
+ subtle::NoBarrier_Store(&buffer_is_full_, 0); |
} |
num_traces_recorded_++; |
@@ -1311,52 +1375,49 @@ |
} |
void TraceLog::SetDisabled() { |
- AutoLock lock(lock_); |
- SetDisabledWhileLocked(); |
-} |
+ std::vector<EnabledStateObserver*> observer_list; |
+ { |
+ AutoLock lock(lock_); |
+ if (!enabled_) |
+ return; |
-void TraceLog::SetDisabledWhileLocked() { |
- lock_.AssertAcquired(); |
+ if (dispatching_to_observer_list_) { |
+ DLOG(ERROR) |
+ << "Cannot manipulate TraceLog::Enabled state from an observer."; |
+ return; |
+ } |
- if (!enabled_) |
- return; |
+ enabled_ = false; |
- if (dispatching_to_observer_list_) { |
- DLOG(ERROR) |
- << "Cannot manipulate TraceLog::Enabled state from an observer."; |
- return; |
- } |
+ if (sampling_thread_.get()) { |
+ // Stop the sampling thread. |
+ sampling_thread_->Stop(); |
+ lock_.Release(); |
+ PlatformThread::Join(sampling_thread_handle_); |
+ lock_.Acquire(); |
+ sampling_thread_handle_ = PlatformThreadHandle(); |
+ sampling_thread_.reset(); |
+ } |
- enabled_ = false; |
+ category_filter_.Clear(); |
+ subtle::NoBarrier_Store(&watch_category_, 0); |
+ watch_event_name_ = ""; |
+ UpdateCategoryGroupEnabledFlags(); |
+ AddMetadataEventsWhileLocked(); |
- if (sampling_thread_.get()) { |
- // Stop the sampling thread. |
- sampling_thread_->Stop(); |
- lock_.Release(); |
- PlatformThread::Join(sampling_thread_handle_); |
- lock_.Acquire(); |
- sampling_thread_handle_ = PlatformThreadHandle(); |
- sampling_thread_.reset(); |
+ dispatching_to_observer_list_ = true; |
+ observer_list = enabled_state_observer_list_; |
} |
- category_filter_.Clear(); |
- subtle::NoBarrier_Store(&watch_category_, 0); |
- watch_event_name_ = ""; |
- UpdateCategoryGroupEnabledFlags(); |
- AddMetadataEventsWhileLocked(); |
+ // Dispatch to observers outside the lock in case the observer triggers a |
+ // trace event. |
+ for (size_t i = 0; i < observer_list.size(); ++i) |
+ observer_list[i]->OnTraceLogDisabled(); |
- dispatching_to_observer_list_ = true; |
- std::vector<EnabledStateObserver*> observer_list = |
- enabled_state_observer_list_; |
- |
{ |
- // Dispatch to observers outside the lock in case the observer triggers a |
- // trace event. |
- AutoUnlock unlock(lock_); |
- for (size_t i = 0; i < observer_list.size(); ++i) |
- observer_list[i]->OnTraceLogDisabled(); |
+ AutoLock lock(lock_); |
+ dispatching_to_observer_list_ = false; |
} |
- dispatching_to_observer_list_ = false; |
} |
int TraceLog::GetNumTracesRecorded() { |
@@ -1388,14 +1449,14 @@ |
} |
float TraceLog::GetBufferPercentFull() const { |
- AutoLock lock(lock_); |
return static_cast<float>(static_cast<double>(logged_events_->Size()) / |
logged_events_->Capacity()); |
} |
-bool TraceLog::BufferIsFull() const { |
+void TraceLog::SetNotificationCallback( |
+ const TraceLog::NotificationCallback& cb) { |
AutoLock lock(lock_); |
- return logged_events_->IsFull(); |
+ notification_callback_ = cb; |
} |
TraceBuffer* TraceLog::CreateTraceBuffer() { |
@@ -1410,7 +1471,7 @@ |
} |
TraceEvent* TraceLog::AddEventToThreadSharedChunkWhileLocked( |
- TraceEventHandle* handle, bool check_buffer_is_full) { |
+ NotificationHelper* notifier, TraceEventHandle* handle) { |
lock_.AssertAcquired(); |
if (thread_shared_chunk_ && thread_shared_chunk_->IsFull()) { |
@@ -1421,8 +1482,8 @@ |
if (!thread_shared_chunk_) { |
thread_shared_chunk_ = logged_events_->GetChunk( |
&thread_shared_chunk_index_); |
- if (check_buffer_is_full) |
- CheckIfBufferIsFullWhileLocked(); |
+ if (notifier) |
+ CheckIfBufferIsFullWhileLocked(notifier); |
} |
if (!thread_shared_chunk_) |
return NULL; |
@@ -1436,10 +1497,13 @@ |
return trace_event; |
} |
-void TraceLog::CheckIfBufferIsFullWhileLocked() { |
+void TraceLog::CheckIfBufferIsFullWhileLocked(NotificationHelper* notifier) { |
lock_.AssertAcquired(); |
- if (logged_events_->IsFull()) |
- SetDisabledWhileLocked(); |
+ if (!subtle::NoBarrier_Load(&buffer_is_full_) && logged_events_->IsFull()) { |
+ subtle::NoBarrier_Store(&buffer_is_full_, |
+ static_cast<subtle::AtomicWord>(1)); |
+ notifier->AddNotificationWhileLocked(TRACE_BUFFER_FULL); |
+ } |
} |
void TraceLog::SetEventCallbackEnabled(const CategoryFilter& category_filter, |
@@ -1556,6 +1620,7 @@ |
previous_logged_events.swap(logged_events_); |
logged_events_.reset(CreateTraceBuffer()); |
NextGeneration(); |
+ subtle::NoBarrier_Store(&buffer_is_full_, 0); |
thread_message_loops_.clear(); |
flush_message_loop_proxy_ = NULL; |
@@ -1675,6 +1740,8 @@ |
TimeTicks now = OffsetTimestamp(timestamp); |
TimeTicks thread_now = ThreadNow(); |
+ NotificationHelper notifier(this); |
+ |
ThreadLocalEventBuffer* thread_local_event_buffer = NULL; |
// A ThreadLocalEventBuffer needs the message loop |
// - to know when the thread exits; |
@@ -1731,13 +1798,15 @@ |
} |
TraceEvent* trace_event = NULL; |
- if ((*category_group_enabled & ENABLED_FOR_RECORDING)) { |
+ if ((*category_group_enabled & ENABLED_FOR_RECORDING) && |
+ !subtle::NoBarrier_Load(&buffer_is_full_)) { |
if (thread_local_event_buffer) { |
lock.EnsureReleased(); |
- trace_event = thread_local_event_buffer->AddTraceEvent(&handle); |
+ trace_event = thread_local_event_buffer->AddTraceEvent(¬ifier, |
+ &handle); |
} else { |
lock.EnsureAcquired(); |
- trace_event = AddEventToThreadSharedChunkWhileLocked(&handle, true); |
+ trace_event = AddEventToThreadSharedChunkWhileLocked(¬ifier, &handle); |
} |
if (trace_event) { |
@@ -1750,24 +1819,20 @@ |
trace_event->SendToATrace(); |
#endif |
} |
+ } |
- if (trace_options() & ECHO_TO_CONSOLE) { |
- lock.EnsureAcquired(); |
- OutputEventToConsoleWhileLocked( |
- phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase, |
- timestamp, trace_event); |
- } |
+ if (trace_options() & ECHO_TO_CONSOLE) { |
+ lock.EnsureAcquired(); |
+ OutputEventToConsoleWhileLocked( |
+ phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase, |
+ timestamp, trace_event); |
} |
if (reinterpret_cast<const unsigned char*>(subtle::NoBarrier_Load( |
&watch_category_)) == category_group_enabled) { |
lock.EnsureAcquired(); |
- if (watch_event_name_ == name) { |
- WatchEventCallback watch_event_callback_copy = watch_event_callback_; |
- lock.EnsureReleased(); |
- if (!watch_event_callback_copy.is_null()) |
- watch_event_callback_copy.Run(); |
- } |
+ if (watch_event_name_ == name) |
+ notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION); |
} |
lock.EnsureReleased(); |
@@ -1785,8 +1850,10 @@ |
} |
if (thread_local_event_buffer) |
- thread_local_event_buffer->ReportOverhead(now, thread_now); |
+ thread_local_event_buffer->ReportOverhead(now, thread_now, ¬ifier); |
+ notifier.SendNotificationIfAny(); |
+ |
return handle; |
} |
@@ -1895,22 +1962,19 @@ |
} |
void TraceLog::SetWatchEvent(const std::string& category_name, |
- const std::string& event_name, |
- const WatchEventCallback& callback) { |
+ const std::string& event_name) { |
const unsigned char* category = GetCategoryGroupEnabled( |
category_name.c_str()); |
AutoLock lock(lock_); |
subtle::NoBarrier_Store(&watch_category_, |
reinterpret_cast<subtle::AtomicWord>(category)); |
watch_event_name_ = event_name; |
- watch_event_callback_ = callback; |
} |
void TraceLog::CancelWatchEvent() { |
AutoLock lock(lock_); |
subtle::NoBarrier_Store(&watch_category_, 0); |
watch_event_name_ = ""; |
- watch_event_callback_.Reset(); |
} |
void TraceLog::AddMetadataEventsWhileLocked() { |
@@ -1918,14 +1982,14 @@ |
int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId()); |
if (process_sort_index_ != 0) { |
- InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), |
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, NULL), |
current_thread_id, |
"process_sort_index", "sort_index", |
process_sort_index_); |
} |
if (process_name_.size()) { |
- InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), |
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, NULL), |
current_thread_id, |
"process_name", "name", |
process_name_); |
@@ -1938,7 +2002,7 @@ |
it++) { |
labels.push_back(it->second); |
} |
- InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), |
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, NULL), |
current_thread_id, |
"process_labels", "labels", |
JoinString(labels, ',')); |
@@ -1950,7 +2014,7 @@ |
it++) { |
if (it->second == 0) |
continue; |
- InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), |
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, NULL), |
it->first, |
"thread_sort_index", "sort_index", |
it->second); |
@@ -1962,7 +2026,7 @@ |
it++) { |
if (it->second.empty()) |
continue; |
- InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), |
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, NULL), |
it->first, |
"thread_name", "name", |
it->second); |