blob: ab68dbcc422782fc5d9121ef8fdb05aa488abdac [file] [log] [blame]
// Copyright 2018 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 "components/tracing/common/tracing_sampler_profiler.h"
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/memory/ref_counted_memory.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread.h"
#include "base/trace_event/trace_buffer.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace tracing {
namespace {
using base::trace_event::TraceLog;
class TracingSampleProfilerTest : public testing::Test {
public:
TracingSampleProfilerTest() : testing::Test() {}
~TracingSampleProfilerTest() override {}
void SetUp() override {
TraceLog* tracelog = TraceLog::GetInstance();
ASSERT_TRUE(tracelog);
ASSERT_FALSE(tracelog->IsEnabled());
trace_buffer_.SetOutputCallback(json_output_.GetCallback());
}
void TearDown() override {
EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled());
// Be sure there is no pending/running tasks.
scoped_task_environment_.RunUntilIdle();
}
void BeginTrace() {
base::trace_event::TraceConfig config(
TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
base::trace_event::RECORD_UNTIL_FULL);
TraceLog::GetInstance()->SetEnabled(config, TraceLog::RECORDING_MODE);
EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
}
void WaitForEvents() {
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
}
// Returns whether of not the sampler profiling is able to unwind the stack
// on this platform.
bool IsStackUnwindingSupported() {
#if defined(OS_MACOSX) || defined(OS_WIN) && defined(_WIN64) || \
(defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD))
return true;
#else
return false;
#endif
}
static void TraceDataCallback(
const base::RepeatingCallback<void()>& callback,
std::string* output,
const scoped_refptr<base::RefCountedString>& json_events_str,
bool has_more_events) {
if (output->size() > 1 && !json_events_str->data().empty()) {
output->append(",");
}
output->append(json_events_str->data());
if (!has_more_events) {
callback.Run();
}
}
void EndTracing() {
std::string json_data = "[";
TraceLog::GetInstance()->SetDisabled();
base::RunLoop run_loop;
TraceLog::GetInstance()->Flush(base::BindRepeating(
&TracingSampleProfilerTest::TraceDataCallback, run_loop.QuitClosure(),
base::Unretained(&json_data)));
run_loop.Run();
json_data.append("]");
std::string error_msg;
std::unique_ptr<base::Value> trace_data =
base::JSONReader::ReadAndReturnErrorDeprecated(json_data, 0, nullptr,
&error_msg);
CHECK(trace_data) << "JSON parsing failed (" << error_msg << ")";
base::ListValue* list;
CHECK(trace_data->GetAsList(&list));
for (size_t i = 0; i < list->GetSize(); i++) {
base::Value* item = nullptr;
if (list->Get(i, &item)) {
base::DictionaryValue* dict;
CHECK(item->GetAsDictionary(&dict));
std::string name;
CHECK(dict->GetString("name", &name));
if (name == "StackCpuSampling") {
events_stack_received_count_++;
} else if (name == "ProcessPriority") {
events_priority_received_count_++;
}
}
}
}
void ValidateReceivedEvents() {
if (IsStackUnwindingSupported()) {
EXPECT_GT(events_stack_received_count_, 0U);
EXPECT_GT(events_priority_received_count_, 0U);
} else {
EXPECT_EQ(events_stack_received_count_, 0U);
EXPECT_EQ(events_priority_received_count_, 0U);
}
}
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
// We want our singleton torn down after each test.
base::ShadowingAtExitManager at_exit_manager_;
base::trace_event::TraceResultBuffer trace_buffer_;
base::trace_event::TraceResultBuffer::SimpleOutput json_output_;
// Number of stack sampling events received.
size_t events_stack_received_count_ = 0;
// Number of priority sampling events received.
size_t events_priority_received_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(TracingSampleProfilerTest);
};
// Stub module for testing.
class TestModule : public base::ModuleCache::Module {
public:
TestModule() = default;
TestModule(const TestModule&) = delete;
TestModule& operator=(const TestModule&) = delete;
uintptr_t GetBaseAddress() const override { return 0; }
std::string GetId() const override { return ""; }
base::FilePath GetDebugBasename() const override { return base::FilePath(); }
size_t GetSize() const override { return 0; }
bool IsNative() const override { return true; }
};
} // namespace
TEST_F(TracingSampleProfilerTest, OnSampleCompleted) {
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
ValidateReceivedEvents();
}
TEST_F(TracingSampleProfilerTest, JoinRunningTracing) {
BeginTrace();
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
ValidateReceivedEvents();
}
TEST_F(TracingSampleProfilerTest, SamplingChildThread) {
base::Thread sampled_thread("sampling_profiler_test");
sampled_thread.Start();
sampled_thread.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&TracingSamplerProfiler::CreateOnChildThread));
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
ValidateReceivedEvents();
}
TEST(TracingProfileBuilderTest, ValidModule) {
TestModule module;
TracingSamplerProfiler::TracingProfileBuilder profile_builder(
(base::PlatformThreadId()));
profile_builder.OnSampleCompleted({base::Frame(0x1010, &module)});
}
TEST(TracingProfileBuilderTest, InvalidModule) {
TracingSamplerProfiler::TracingProfileBuilder profile_builder(
(base::PlatformThreadId()));
profile_builder.OnSampleCompleted({base::Frame(0x1010, nullptr)});
}
} // namespace tracing