blob: 9f96e8707a5e5e24fdc9db90298c3d1d56c89c0a [file] [log] [blame]
// Copyright 2019 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 <vector>
#include "base/bind.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/threading/platform_thread.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/version_info/channel.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace {
// Class that intercepts and stores profiles provided to the
// CallStackProfileMetricsProvider. Intercept() is invoked on the profiler
// thread while FetchProfiles() is invoked on the main thread.
class ProfileInterceptor {
public:
// Get the static object instance. This object must leak because there is no
// synchronization between it and the profiler thread which can invoke
// Intercept at any time.
static ProfileInterceptor& GetInstance() {
static base::NoDestructor<ProfileInterceptor> instance;
return *instance;
}
void Intercept(metrics::SampledProfile profile) {
base::AutoLock lock(lock_);
profiles_.push_back(std::move(profile));
}
std::vector<metrics::SampledProfile> FetchProfiles() {
base::AutoLock lock(lock_);
std::vector<metrics::SampledProfile> profiles;
profiles.swap(profiles_);
return profiles;
}
private:
base::Lock lock_;
std::vector<metrics::SampledProfile> profiles_ GUARDED_BY(lock_);
};
class StackSamplingBrowserTest : public InProcessBrowserTest {
public:
void SetUp() override {
// Arrange to intercept the CPU profiles at the time they're provided to the
// metrics component.
metrics::CallStackProfileMetricsProvider::
SetCpuInterceptorCallbackForTesting(base::BindRepeating(
&ProfileInterceptor::Intercept,
base::Unretained(&ProfileInterceptor::GetInstance())));
InProcessBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// Enable the special browser test mode.
command_line->AppendSwitchASCII(switches::kStartStackProfiler,
switches::kStartStackProfilerBrowserTest);
}
};
// Wait for a profile with the specified properties. Checks once per second
// until the profile is seen or we time out.
bool WaitForProfile(metrics::SampledProfile::TriggerEvent trigger_event,
metrics::Process process,
metrics::Thread thread) {
// Profiling is only enabled for trunk builds and canary and dev channels.
// Perform an early return and pass the test for the other channels.
switch (chrome::GetChannel()) {
case version_info::Channel::UNKNOWN:
case version_info::Channel::CANARY:
case version_info::Channel::DEV:
break;
default:
return true;
}
// The profiling duration is one second when enabling browser test mode via
// the kStartStackProfilerBrowserTest switch argument. We expect to see the
// profiles shortly thereafter, but wait up to 30 seconds to give ample time
// to avoid flaky failures.
int seconds_to_wait = 30;
do {
std::vector<metrics::SampledProfile> profiles =
ProfileInterceptor::GetInstance().FetchProfiles();
const bool was_received =
std::find_if(profiles.begin(), profiles.end(),
[&](const metrics::SampledProfile& profile) {
return profile.trigger_event() == trigger_event &&
profile.process() == process &&
profile.thread() == thread;
}) != profiles.end();
if (was_received)
return true;
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
// Manually spinning message loop is fine here because the main thread
// message loop will not be continuously busy at Chrome startup, and we will
// spin it enough over 30 seconds to ensure that any necessary processing is
// done.
base::RunLoop().RunUntilIdle();
} while (--seconds_to_wait > 0);
return false;
}
} // namespace
// Check that we receive startup profiles in the browser process for profiled
// processes/threads. We've seen multiple breakages previously where profiles
// were dropped as a result of bugs introduced by mojo refactorings.
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest, BrowserProcessMainThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::BROWSER_PROCESS, metrics::MAIN_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest, BrowserProcessIOThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::BROWSER_PROCESS, metrics::IO_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest, GpuProcessMainThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::GPU_PROCESS, metrics::MAIN_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest, GpuProcessIOThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::GPU_PROCESS, metrics::IO_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest, GpuProcessCompositorThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::GPU_PROCESS, metrics::COMPOSITOR_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest, RendererProcessMainThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::RENDERER_PROCESS, metrics::MAIN_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest, RendererProcessIOThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::RENDERER_PROCESS, metrics::IO_THREAD));
}
IN_PROC_BROWSER_TEST_F(StackSamplingBrowserTest,
RendererProcessCompositorThread) {
EXPECT_TRUE(WaitForProfile(metrics::SampledProfile::PROCESS_STARTUP,
metrics::RENDERER_PROCESS,
metrics::COMPOSITOR_THREAD));
}