blob: 56012dfa29f99a44c21e363e6adce614d3f0143f [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 "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
#include <limits>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/run_loop.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 "services/tracing/perfetto/test_utils.h"
#include "services/tracing/public/cpp/buildflags.h"
#include "services/tracing/public/cpp/perfetto/producer_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
#include "base/test/trace_event_analyzer.h"
#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"
#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampling_thread_win.h"
#endif
#if defined(OS_MAC)
#include "base/mac/mac_util.h"
#endif
namespace tracing {
namespace {
using base::trace_event::TraceLog;
using ::testing::Invoke;
using ::testing::Return;
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
class MockLoaderLockSampler : public LoaderLockSampler {
public:
MockLoaderLockSampler() = default;
~MockLoaderLockSampler() override = default;
MOCK_METHOD(bool, IsLoaderLockHeld, (), (const, override));
};
class LoaderLockEventAnalyzer {
public:
LoaderLockEventAnalyzer() {
trace_analyzer::Start(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"));
}
size_t CountEvents() {
std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer =
trace_analyzer::Stop();
trace_analyzer::TraceEventVector events;
return analyzer->FindEvents(
trace_analyzer::Query::EventName() ==
trace_analyzer::Query::String(
LoaderLockSamplingThread::kLoaderLockHeldEventName),
&events);
}
};
#endif // BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
class TracingSampleProfilerTest : public TracingUnitTest {
public:
TracingSampleProfilerTest() = default;
TracingSampleProfilerTest(const TracingSampleProfilerTest&) = delete;
TracingSampleProfilerTest& operator=(const TracingSampleProfilerTest&) =
delete;
~TracingSampleProfilerTest() override = default;
void SetUp() override {
TracingUnitTest::SetUp();
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
// Override the default LoaderLockSampler because in production it is
// expected to be called from a single thread, and each test may re-create
// the sampling thread.
ON_CALL(mock_loader_lock_sampler_, IsLoaderLockHeld())
.WillByDefault(Return(false));
LoaderLockSamplingThread::SetLoaderLockSamplerForTesting(
&mock_loader_lock_sampler_);
#endif
events_stack_received_count_ = 0u;
auto perfetto_wrapper = std::make_unique<base::tracing::PerfettoTaskRunner>(
base::ThreadTaskRunnerHandle::Get());
producer_ =
std::make_unique<TestProducerClient>(std::move(perfetto_wrapper),
/*log_only_main_thread=*/false);
}
void TearDown() override {
producer_.reset();
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
LoaderLockSamplingThread::SetLoaderLockSamplerForTesting(nullptr);
#endif
TracingUnitTest::TearDown();
}
void BeginTrace() {
TracingSamplerProfiler::StartTracingForTesting(producer_.get());
}
void WaitForEvents() { base::PlatformThread::Sleep(base::Milliseconds(200)); }
void EndTracing() {
TracingSamplerProfiler::StopTracingForTesting();
base::RunLoop().RunUntilIdle();
auto& packets = producer_->finalized_packets();
for (auto& packet : packets) {
if (packet->has_streaming_profile_packet()) {
events_stack_received_count_++;
}
}
}
void ValidateReceivedEvents() {
if (TracingSamplerProfiler::IsStackUnwindingSupported()) {
EXPECT_GT(events_stack_received_count_, 0U);
} else {
EXPECT_EQ(events_stack_received_count_, 0U);
}
}
uint32_t FindProfilerSequenceId() {
uint32_t profile_sequence_id = std::numeric_limits<uint32_t>::max();
auto& packets = producer_->finalized_packets();
for (auto& packet : packets) {
if (packet->has_streaming_profile_packet()) {
profile_sequence_id = packet->trusted_packet_sequence_id();
break;
}
}
EXPECT_NE(profile_sequence_id, std::numeric_limits<uint32_t>::max());
return profile_sequence_id;
}
const TestProducerClient* producer() const { return producer_.get(); }
protected:
// We want our singleton torn down after each test.
base::ShadowingAtExitManager at_exit_manager_;
base::trace_event::TraceResultBuffer trace_buffer_;
std::unique_ptr<TestProducerClient> producer_;
// Number of stack sampling events received.
size_t events_stack_received_count_ = 0;
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
MockLoaderLockSampler mock_loader_lock_sampler_;
#endif
};
// Stub module for testing.
class TestModule : public base::ModuleCache::Module {
public:
TestModule() = default;
TestModule(const TestModule&) = delete;
TestModule& operator=(const TestModule&) = delete;
void set_id(const std::string& id) { id_ = id; }
uintptr_t GetBaseAddress() const override { return 0; }
std::string GetId() const override { return id_; }
base::FilePath GetDebugBasename() const override { return base::FilePath(); }
size_t GetSize() const override { return 0; }
bool IsNative() const override { return true; }
private:
std::string id_;
};
bool ShouldSkipTestForMacOS11() {
#if defined(OS_MAC)
// The sampling profiler does not work on macOS 11 and is disabled.
// See https://crbug.com/1101399 and https://crbug.com/1098119.
// DCHECK here so that when the sampling profiler is re-enabled on macOS 11,
// these tests are also re-enabled.
if (base::mac::IsAtLeastOS11()) {
DCHECK(!base::StackSamplingProfiler::IsSupportedForCurrentPlatform());
return true;
}
#endif
return false;
}
} // namespace
TEST_F(TracingSampleProfilerTest, OnSampleCompleted) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
ValidateReceivedEvents();
}
TEST_F(TracingSampleProfilerTest, JoinRunningTracing) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
BeginTrace();
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
ValidateReceivedEvents();
}
TEST_F(TracingSampleProfilerTest, TestStartupTracing) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
TracingSamplerProfiler::SetupStartupTracingForTesting();
base::RunLoop().RunUntilIdle();
WaitForEvents();
auto start_tracing_ts = TRACE_TIME_TICKS_NOW();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
if (TracingSamplerProfiler::IsStackUnwindingSupported()) {
uint32_t seq_id = FindProfilerSequenceId();
auto& packets = producer()->finalized_packets();
int64_t reference_ts = 0;
int64_t first_profile_ts = 0;
for (auto& packet : packets) {
if (packet->trusted_packet_sequence_id() == seq_id) {
if (packet->has_thread_descriptor()) {
reference_ts = packet->thread_descriptor().reference_timestamp_us();
} else if (packet->has_streaming_profile_packet()) {
first_profile_ts =
reference_ts +
packet->streaming_profile_packet().timestamp_delta_us(0);
break;
}
}
}
// Expect first sample before tracing started.
EXPECT_LT(first_profile_ts,
start_tracing_ts.since_origin().InMicroseconds());
}
}
TEST_F(TracingSampleProfilerTest, JoinStartupTracing) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
TracingSamplerProfiler::SetupStartupTracingForTesting();
base::RunLoop().RunUntilIdle();
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
WaitForEvents();
auto start_tracing_ts = TRACE_TIME_TICKS_NOW();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
if (TracingSamplerProfiler::IsStackUnwindingSupported()) {
uint32_t seq_id = FindProfilerSequenceId();
auto& packets = producer()->finalized_packets();
int64_t reference_ts = 0;
int64_t first_profile_ts = 0;
for (auto& packet : packets) {
if (packet->trusted_packet_sequence_id() == seq_id) {
if (packet->has_thread_descriptor()) {
reference_ts = packet->thread_descriptor().reference_timestamp_us();
} else if (packet->has_streaming_profile_packet()) {
first_profile_ts =
reference_ts +
packet->streaming_profile_packet().timestamp_delta_us(0);
break;
}
}
}
// Expect first sample before tracing started.
EXPECT_LT(first_profile_ts,
start_tracing_ts.since_origin().InMicroseconds());
}
}
TEST_F(TracingSampleProfilerTest, SamplingChildThread) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
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();
sampled_thread.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&TracingSamplerProfiler::DeleteOnChildThreadForTesting));
base::RunLoop().RunUntilIdle();
}
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnMainThread) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
LoaderLockEventAnalyzer event_analyzer;
bool lock_held = false;
size_t call_count = 0;
EXPECT_CALL(mock_loader_lock_sampler_, IsLoaderLockHeld())
.WillRepeatedly(Invoke([&lock_held, &call_count]() {
++call_count;
lock_held = !lock_held;
return lock_held;
}));
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
// Since the loader lock state changed each time it was sampled an event
// should be emitted each time.
ASSERT_GE(call_count, 1U);
EXPECT_EQ(event_analyzer.CountEvents(), call_count);
}
TEST_F(TracingSampleProfilerTest, SampleLoaderLockAlwaysHeld) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
LoaderLockEventAnalyzer event_analyzer;
EXPECT_CALL(mock_loader_lock_sampler_, IsLoaderLockHeld())
.WillRepeatedly(Return(true));
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
// An event should be emitted at the first sample when the loader lock was
// held, and then not again since the state never changed.
EXPECT_EQ(event_analyzer.CountEvents(), 1U);
}
TEST_F(TracingSampleProfilerTest, SampleLoaderLockNeverHeld) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
LoaderLockEventAnalyzer event_analyzer;
EXPECT_CALL(mock_loader_lock_sampler_, IsLoaderLockHeld())
.WillRepeatedly(Return(false));
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
// No events should be emitted since the lock is never held.
EXPECT_EQ(event_analyzer.CountEvents(), 0U);
}
TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnChildThread) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
LoaderLockEventAnalyzer event_analyzer;
// Loader lock should only be sampled on main thread.
EXPECT_CALL(mock_loader_lock_sampler_, IsLoaderLockHeld()).Times(0);
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();
sampled_thread.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&TracingSamplerProfiler::DeleteOnChildThreadForTesting));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(event_analyzer.CountEvents(), 0U);
}
TEST_F(TracingSampleProfilerTest, SampleLoaderLockWithoutMock) {
if (ShouldSkipTestForMacOS11())
GTEST_SKIP() << "Stack sampler is not supported on macOS 11";
// Use the real loader lock sampler. This tests that it is initialized
// correctly in TracingSamplerProfiler.
LoaderLockSamplingThread::SetLoaderLockSamplerForTesting(nullptr);
// This must be the only thread that uses the real loader lock sampler in the
// test process.
auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
// The loader lock may or may not be held during the test, so there's no
// output to test. The test passes if it reaches the end without crashing.
}
#endif // BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
class TracingProfileBuilderTest : public TracingUnitTest {
public:
void SetUp() override {
TracingUnitTest::SetUp();
auto perfetto_wrapper = std::make_unique<base::tracing::PerfettoTaskRunner>(
base::ThreadTaskRunnerHandle::Get());
producer_client_ = std::make_unique<TestProducerClient>(
std::move(perfetto_wrapper), /*log_only_main_thread=*/false);
}
void TearDown() override {
producer_client_.reset();
TracingUnitTest::TearDown();
}
TestProducerClient* producer() { return producer_client_.get(); }
private:
std::unique_ptr<TestProducerClient> producer_client_;
};
TEST_F(TracingProfileBuilderTest, ValidModule) {
TestModule module;
TracingSamplerProfiler::TracingProfileBuilder profile_builder(
base::PlatformThreadId(), std::make_unique<TestTraceWriter>(producer()),
false);
profile_builder.OnSampleCompleted({base::Frame(0x1010, &module)},
base::TimeTicks());
}
TEST_F(TracingProfileBuilderTest, InvalidModule) {
TracingSamplerProfiler::TracingProfileBuilder profile_builder(
base::PlatformThreadId(), std::make_unique<TestTraceWriter>(producer()),
false);
profile_builder.OnSampleCompleted({base::Frame(0x1010, nullptr)},
base::TimeTicks());
}
#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
TEST_F(TracingProfileBuilderTest, MangleELFModuleID) {
TestModule module;
// See explanation for the module_id mangling in
// TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit.
module.set_id("7F0715C286F8B16C10E4AD349CDA3B9B56C7A773");
TracingSamplerProfiler::TracingProfileBuilder profile_builder(
base::PlatformThreadId(), std::make_unique<TestTraceWriter>(producer()),
false);
profile_builder.OnSampleCompleted({base::Frame(0x1010, &module)},
base::TimeTicks());
producer()->FlushPacketIfPossible();
bool found_build_id = false;
for (unsigned i = 0; i < producer()->GetFinalizedPacketCount(); ++i) {
const perfetto::protos::TracePacket* packet =
producer()->GetFinalizedPacket(i);
if (!packet->has_interned_data() ||
packet->interned_data().build_ids_size() == 0) {
return;
}
found_build_id = true;
EXPECT_EQ(packet->interned_data().build_ids(0).str(),
"C215077FF8866CB110E4AD349CDA3B9B0");
}
EXPECT_TRUE(found_build_id);
}
#endif
} // namespace tracing