|  | // 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 |