| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| |
| #include "base/trace_event/trace_event.h" |
| #include "base/trace_event/trace_event_impl.h" |
| #include "base/trace_event/trace_log.h" |
| #include "base/trace_event/trace_sampling_thread.h" |
| |
| namespace base { |
| namespace trace_event { |
| |
| class TraceBucketData { |
| public: |
| TraceBucketData(base::subtle::AtomicWord* bucket, |
| const char* name, |
| TraceSampleCallback callback); |
| ~TraceBucketData(); |
| |
| TRACE_EVENT_API_ATOMIC_WORD* bucket; |
| const char* bucket_name; |
| TraceSampleCallback callback; |
| }; |
| |
| TraceSamplingThread::TraceSamplingThread() |
| : thread_running_(false), |
| waitable_event_for_testing_(WaitableEvent::ResetPolicy::AUTOMATIC, |
| WaitableEvent::InitialState::NOT_SIGNALED) {} |
| |
| TraceSamplingThread::~TraceSamplingThread() {} |
| |
| void TraceSamplingThread::ThreadMain() { |
| PlatformThread::SetName("Sampling Thread"); |
| thread_running_ = true; |
| const int kSamplingFrequencyMicroseconds = 1000; |
| while (!cancellation_flag_.IsSet()) { |
| PlatformThread::Sleep( |
| TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds)); |
| GetSamples(); |
| waitable_event_for_testing_.Signal(); |
| } |
| } |
| |
| // static |
| void TraceSamplingThread::DefaultSamplingCallback( |
| TraceBucketData* bucket_data) { |
| TRACE_EVENT_API_ATOMIC_WORD category_and_name = |
| TRACE_EVENT_API_ATOMIC_LOAD(*bucket_data->bucket); |
| if (!category_and_name) |
| return; |
| const char* const combined = |
| reinterpret_cast<const char* const>(category_and_name); |
| const char* category_group; |
| const char* name; |
| ExtractCategoryAndName(combined, &category_group, &name); |
| TRACE_EVENT_API_ADD_TRACE_EVENT( |
| TRACE_EVENT_PHASE_SAMPLE, |
| TraceLog::GetCategoryGroupEnabled(category_group), name, |
| trace_event_internal::kGlobalScope, trace_event_internal::kNoId, 0, |
| NULL, NULL, NULL, NULL, 0); |
| } |
| |
| void TraceSamplingThread::GetSamples() { |
| for (size_t i = 0; i < sample_buckets_.size(); ++i) { |
| TraceBucketData* bucket_data = &sample_buckets_[i]; |
| bucket_data->callback.Run(bucket_data); |
| } |
| } |
| |
| void TraceSamplingThread::RegisterSampleBucket( |
| TRACE_EVENT_API_ATOMIC_WORD* bucket, |
| const char* const name, |
| TraceSampleCallback callback) { |
| // Access to sample_buckets_ doesn't cause races with the sampling thread |
| // that uses the sample_buckets_, because it is guaranteed that |
| // RegisterSampleBucket is called before the sampling thread is created. |
| DCHECK(!thread_running_); |
| sample_buckets_.push_back(TraceBucketData(bucket, name, callback)); |
| } |
| |
| // static |
| void TraceSamplingThread::ExtractCategoryAndName(const char* combined, |
| const char** category, |
| const char** name) { |
| *category = combined; |
| *name = &combined[strlen(combined) + 1]; |
| } |
| |
| void TraceSamplingThread::Stop() { |
| cancellation_flag_.Set(); |
| } |
| |
| void TraceSamplingThread::WaitSamplingEventForTesting() { |
| waitable_event_for_testing_.Wait(); |
| } |
| |
| TraceBucketData::TraceBucketData(base::subtle::AtomicWord* bucket, |
| const char* name, |
| TraceSampleCallback callback) |
| : bucket(bucket), bucket_name(name), callback(callback) {} |
| |
| TraceBucketData::~TraceBucketData() {} |
| |
| } // namespace trace_event |
| } // namespace base |