summaryrefslogtreecommitdiffstats
path: root/base/debug
diff options
context:
space:
mode:
Diffstat (limited to 'base/debug')
-rw-r--r--base/debug/trace_event.h7
-rw-r--r--base/debug/trace_event_impl.cc189
-rw-r--r--base/debug/trace_event_impl.h17
-rw-r--r--base/debug/trace_event_internal.h12
-rw-r--r--base/debug/trace_event_unittest.cc24
5 files changed, 244 insertions, 5 deletions
diff --git a/base/debug/trace_event.h b/base/debug/trace_event.h
index 3459a94..0636180 100644
--- a/base/debug/trace_event.h
+++ b/base/debug/trace_event.h
@@ -69,6 +69,13 @@
// Defines visibility for classes in trace_event_internal.h
#define TRACE_EVENT_API_CLASS_EXPORT BASE_EXPORT
+// The thread buckets for the sampling profiler.
+TRACE_EVENT_API_CLASS_EXPORT extern TRACE_EVENT_API_ATOMIC_WORD g_trace_state0;
+TRACE_EVENT_API_CLASS_EXPORT extern TRACE_EVENT_API_ATOMIC_WORD g_trace_state1;
+TRACE_EVENT_API_CLASS_EXPORT extern TRACE_EVENT_API_ATOMIC_WORD g_trace_state2;
+#define TRACE_EVENT_API_THREAD_BUCKET(thread_bucket) \
+ g_trace_state##thread_bucket
+
////////////////////////////////////////////////////////////////////////////////
#include "base/debug/trace_event_internal.h"
diff --git a/base/debug/trace_event_impl.cc b/base/debug/trace_event_impl.cc
index 5eafd52..81687ab 100644
--- a/base/debug/trace_event_impl.cc
+++ b/base/debug/trace_event_impl.cc
@@ -18,6 +18,8 @@
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/strings/string_tokenizer.h"
+#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/waitable_event.h"
#include "base/sys_info.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/platform_thread.h"
@@ -38,6 +40,11 @@ class DeleteTraceLogForTesting {
}
};
+// The thread buckets for the sampling profiler.
+BASE_EXPORT TRACE_EVENT_API_ATOMIC_WORD g_trace_state0;
+BASE_EXPORT TRACE_EVENT_API_ATOMIC_WORD g_trace_state1;
+BASE_EXPORT TRACE_EVENT_API_ATOMIC_WORD g_trace_state2;
+
namespace base {
namespace debug {
@@ -320,6 +327,147 @@ void TraceResultBuffer::Finish() {
////////////////////////////////////////////////////////////////////////////////
//
+// TraceSamplingThread
+//
+////////////////////////////////////////////////////////////////////////////////
+class TraceBucketData;
+typedef base::Callback<void(TraceBucketData*)> TraceSampleCallback;
+
+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;
+};
+
+// This object must be created on the IO thread.
+class TraceSamplingThread : public PlatformThread::Delegate {
+ public:
+ TraceSamplingThread();
+ virtual ~TraceSamplingThread();
+
+ // Implementation of PlatformThread::Delegate:
+ virtual void ThreadMain() OVERRIDE;
+
+ static void DefaultSampleCallback(TraceBucketData* bucekt_data);
+
+ void Stop();
+ void InstallWaitableEventForSamplingTesting(WaitableEvent* waitable_event);
+
+ private:
+ friend class TraceLog;
+
+ void GetSamples();
+ // Not thread-safe. Once the ThreadMain has been called, this can no longer
+ // be called.
+ void RegisterSampleBucket(TRACE_EVENT_API_ATOMIC_WORD* bucket,
+ const char* const name,
+ TraceSampleCallback callback);
+ // Splits a combined "category\0name" into the two component parts.
+ static void ExtractCategoryAndName(const char* combined,
+ const char** category,
+ const char** name);
+ std::vector<TraceBucketData> sample_buckets_;
+ bool thread_running_;
+ scoped_ptr<CancellationFlag> cancellation_flag_;
+ scoped_ptr<WaitableEvent> waitable_event_for_testing_;
+};
+
+
+TraceSamplingThread::TraceSamplingThread()
+ : thread_running_(false) {
+ cancellation_flag_.reset(new CancellationFlag);
+}
+
+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();
+ if (waitable_event_for_testing_.get())
+ waitable_event_for_testing_->Signal();
+ }
+}
+
+// static
+void TraceSamplingThread::DefaultSampleCallback(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;
+ const char* name;
+ ExtractCategoryAndName(combined, &category, &name);
+ TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_SAMPLE,
+ TraceLog::GetCategoryEnabled(category),
+ name,
+ 0,
+ 0,
+ 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) {
+ 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::InstallWaitableEventForSamplingTesting(
+ WaitableEvent* waitable_event) {
+ waitable_event_for_testing_.reset(waitable_event);
+}
+
+
+TraceBucketData::TraceBucketData(base::subtle::AtomicWord* bucket,
+ const char* name,
+ TraceSampleCallback callback)
+ : bucket(bucket),
+ bucket_name(name),
+ callback(callback) {
+}
+
+TraceBucketData::~TraceBucketData() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
// TraceLog
//
////////////////////////////////////////////////////////////////////////////////
@@ -381,7 +529,8 @@ TraceLog::TraceLog()
: enable_count_(0),
dispatching_to_observer_list_(false),
watch_category_(NULL),
- trace_options_(RECORD_UNTIL_FULL) {
+ trace_options_(RECORD_UNTIL_FULL),
+ sampling_thread_handle_(0) {
// Trace is enabled or disabled on one thread while other threads are
// accessing the enabled flag. We don't care whether edge-case events are
// traced or not, so we allow races on the enabled flag to keep the trace
@@ -555,6 +704,26 @@ void TraceLog::SetEnabled(const std::vector<std::string>& included_categories,
EnableMatchingCategories(included_categories_, CATEGORY_ENABLED, 0);
else
EnableMatchingCategories(excluded_categories_, 0, CATEGORY_ENABLED);
+
+ if (options & ENABLE_SAMPLING) {
+ sampling_thread_.reset(new TraceSamplingThread);
+ sampling_thread_->RegisterSampleBucket(
+ &g_trace_state0,
+ "bucket0",
+ Bind(&TraceSamplingThread::DefaultSampleCallback));
+ sampling_thread_->RegisterSampleBucket(
+ &g_trace_state1,
+ "bucket1",
+ Bind(&TraceSamplingThread::DefaultSampleCallback));
+ sampling_thread_->RegisterSampleBucket(
+ &g_trace_state2,
+ "bucket2",
+ Bind(&TraceSamplingThread::DefaultSampleCallback));
+ if (!PlatformThread::Create(
+ 0, sampling_thread_.get(), &sampling_thread_handle_)) {
+ DCHECK(false) << "failed to create thread";
+ }
+ }
}
void TraceLog::SetEnabled(const std::string& categories, Options options) {
@@ -600,8 +769,19 @@ void TraceLog::SetDisabled() {
return;
}
+ if (sampling_thread_.get()) {
+ // Stop the sampling thread.
+ sampling_thread_->Stop();
+ lock_.Release();
+ PlatformThread::Join(sampling_thread_handle_);
+ lock_.Acquire();
+ sampling_thread_handle_ = 0;
+ sampling_thread_.reset();
+ }
+
dispatching_to_observer_list_ = true;
- FOR_EACH_OBSERVER(EnabledStateChangedObserver, enabled_state_observer_list_,
+ FOR_EACH_OBSERVER(EnabledStateChangedObserver,
+ enabled_state_observer_list_,
OnTraceLogWillDisable());
dispatching_to_observer_list_ = false;
@@ -834,6 +1014,11 @@ void TraceLog::AddThreadNameMetadataEvents() {
}
}
+void TraceLog::InstallWaitableEventForSamplingTesting(
+ WaitableEvent* waitable_event) {
+ sampling_thread_->InstallWaitableEventForSamplingTesting(waitable_event);
+}
+
void TraceLog::DeleteForTesting() {
DeleteTraceLogForTesting::Delete();
}
diff --git a/base/debug/trace_event_impl.h b/base/debug/trace_event_impl.h
index c73fbcb..193e6b4 100644
--- a/base/debug/trace_event_impl.h
+++ b/base/debug/trace_event_impl.h
@@ -6,8 +6,6 @@
#ifndef BASE_DEBUG_TRACE_EVENT_IMPL_H_
#define BASE_DEBUG_TRACE_EVENT_IMPL_H_
-#include "build/build_config.h"
-
#include <string>
#include <vector>
@@ -18,6 +16,7 @@
#include "base/string_util.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
#include "base/timer.h"
// Older style trace macros with explicit id and extra data
@@ -42,6 +41,8 @@ struct StaticMemorySingletonTraits;
namespace base {
+class WaitableEvent;
+
namespace debug {
const int kTraceMaxNumArgs = 2;
@@ -157,6 +158,8 @@ class BASE_EXPORT TraceResultBuffer {
bool append_comma_;
};
+class TraceSamplingThread;
+
class BASE_EXPORT TraceLog {
public:
// Notification is a mask of one or more of the following events.
@@ -171,7 +174,9 @@ class BASE_EXPORT TraceLog {
// Options determines how the trace buffer stores data.
enum Options {
- RECORD_UNTIL_FULL = 1 << 0
+ RECORD_UNTIL_FULL = 1 << 0,
+ // Enable the sampling profiler.
+ ENABLE_SAMPLING = 1 << 1,
};
static TraceLog* GetInstance();
@@ -312,6 +317,8 @@ class BASE_EXPORT TraceLog {
// Exposed for unittesting:
+ void InstallWaitableEventForSamplingTesting(WaitableEvent* waitable_event);
+
// Allows deleting our singleton instance.
static void DeleteForTesting();
@@ -413,6 +420,10 @@ class BASE_EXPORT TraceLog {
Options trace_options_;
+ // Sampling thread handles.
+ scoped_ptr<TraceSamplingThread> sampling_thread_;
+ PlatformThreadHandle sampling_thread_handle_;
+
DISALLOW_COPY_AND_ASSIGN(TraceLog);
};
diff --git a/base/debug/trace_event_internal.h b/base/debug/trace_event_internal.h
index b10210e..65fe34a 100644
--- a/base/debug/trace_event_internal.h
+++ b/base/debug/trace_event_internal.h
@@ -233,6 +233,17 @@
category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
arg2_name, arg2_val)
+// Sets the current sample state to the given category and name (both must be
+// constant strings). These states are intended for a sampling profiler.
+// Implementation note: we store category and name together because we don't
+// want the inconsistency/expense of storing two pointers.
+// |thread_bucket| is [0..2] and is used to statically isolate samples in one
+// thread from others.
+#define TRACE_EVENT_SAMPLE_STATE(thread_bucket, category, name) \
+ TRACE_EVENT_API_ATOMIC_STORE( \
+ TRACE_EVENT_API_THREAD_BUCKET(thread_bucket), \
+ reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>(category "\0" name));
+
// Records a single BEGIN event called "name" immediately, with 0, 1 or 2
// associated arguments. If the category is not enabled, then this
// does nothing.
@@ -682,6 +693,7 @@
#define TRACE_EVENT_PHASE_FLOW_END ('f')
#define TRACE_EVENT_PHASE_METADATA ('M')
#define TRACE_EVENT_PHASE_COUNTER ('C')
+#define TRACE_EVENT_PHASE_SAMPLE ('P')
// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
#define TRACE_EVENT_FLAG_NONE (static_cast<unsigned char>(0))
diff --git a/base/debug/trace_event_unittest.cc b/base/debug/trace_event_unittest.cc
index fd6bbd2..e8e6c8a 100644
--- a/base/debug/trace_event_unittest.cc
+++ b/base/debug/trace_event_unittest.cc
@@ -1494,5 +1494,29 @@ TEST_F(TraceEventTestFixture, TraceOptionsParsing) {
TraceLog::TraceOptionsFromString("record-until-full"));
}
+TEST_F(TraceEventTestFixture, TraceSampling) {
+ ManualTestSetUp();
+
+ event_watch_notification_ = 0;
+ TraceLog::GetInstance()->SetEnabled(
+ std::string("*"),
+ TraceLog::Options(TraceLog::RECORD_UNTIL_FULL |
+ TraceLog::ENABLE_SAMPLING));
+
+ WaitableEvent* sampled = new WaitableEvent(false, false);
+ TraceLog::GetInstance()->InstallWaitableEventForSamplingTesting(sampled);
+
+ TRACE_EVENT_SAMPLE_STATE(1, "cc", "Stuff");
+ sampled->Wait();
+ TRACE_EVENT_SAMPLE_STATE(1, "cc", "Things");
+ sampled->Wait();
+
+ EndTraceAndFlush();
+
+ // Make sure we hit at least once.
+ EXPECT_TRUE(FindNamePhase("Stuff", "P"));
+ EXPECT_TRUE(FindNamePhase("Things", "P"));
+}
+
} // namespace debug
} // namespace base