diff options
Diffstat (limited to 'chromium/chrome/common/stack_sampling_configuration.cc')
-rw-r--r-- | chromium/chrome/common/stack_sampling_configuration.cc | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/chromium/chrome/common/stack_sampling_configuration.cc b/chromium/chrome/common/stack_sampling_configuration.cc new file mode 100644 index 00000000000..2d8c22e6b0d --- /dev/null +++ b/chromium/chrome/common/stack_sampling_configuration.cc @@ -0,0 +1,219 @@ +// 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 "chrome/common/stack_sampling_configuration.h" + +#include "base/command_line.h" +#include "base/lazy_instance.h" +#include "base/rand_util.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" +#include "chrome/common/channel_info.h" +#include "chrome/common/chrome_switches.h" +#include "components/version_info/version_info.h" +#include "content/public/common/content_switches.h" +#include "extensions/buildflags/buildflags.h" + +#if defined(OS_WIN) +#include "base/win/static_constants.h" +#endif + +#if defined(OS_MACOSX) +#include "base/mac/mac_util.h" +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/common/switches.h" +#endif + +namespace { + +base::LazyInstance<StackSamplingConfiguration>::Leaky g_configuration = + LAZY_INSTANCE_INITIALIZER; + +// The profiler is currently only implemented for Windows x64 and Mac x64. +bool IsProfilerSupported() { +#if (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) || defined(OS_MACOSX) +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + // Only run on canary and dev. + const version_info::Channel channel = chrome::GetChannel(); + return channel == version_info::Channel::CANARY || + channel == version_info::Channel::DEV; +#else + return true; +#endif +#else + return false; +#endif +} + +// Returns true if the current execution is taking place in the browser process. +bool IsBrowserProcess() { + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line->GetSwitchValueASCII(switches::kProcessType); + return process_type.empty(); +} + +// True if the command line corresponds to an extension renderer process. +bool IsExtensionRenderer(const base::CommandLine& command_line) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return command_line.HasSwitch(extensions::switches::kExtensionProcess); +#else + return false; +#endif +} + +bool ShouldEnableProfilerForNextRendererProcess() { + // Enable for every N-th renderer process, where N = 5. + return base::RandInt(0, 4) == 0; +} + +} // namespace + +StackSamplingConfiguration::StackSamplingConfiguration() + : configuration_(GenerateConfiguration()) { +} + +base::StackSamplingProfiler::SamplingParams +StackSamplingConfiguration::GetSamplingParamsForCurrentProcess() const { + base::StackSamplingProfiler::SamplingParams params; + params.initial_delay = base::TimeDelta::FromMilliseconds(0); + params.sampling_interval = base::TimeDelta::FromMilliseconds(0); + params.samples_per_profile = 0; + + if (IsProfilerEnabledForCurrentProcess()) { + const base::TimeDelta duration = base::TimeDelta::FromSeconds(30); + params.sampling_interval = base::TimeDelta::FromMilliseconds(100); + params.samples_per_profile = duration / params.sampling_interval; + } + + return params; +} + +bool StackSamplingConfiguration::IsProfilerEnabledForCurrentProcess() const { + if (IsBrowserProcess()) { + return configuration_ == PROFILE_ENABLED || + configuration_ == PROFILE_CONTROL; + } + + DCHECK_EQ(PROFILE_FROM_COMMAND_LINE, configuration_); + // This is a child process. The |kStartStackProfiler| switch passed by the + // browser process determines whether the profiler is enabled for the process. + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + return command_line->HasSwitch(switches::kStartStackProfiler); +} + +bool StackSamplingConfiguration::GetSyntheticFieldTrial( + std::string* trial_name, + std::string* group_name) const { + DCHECK(IsBrowserProcess()); + + if (!IsProfilerSupported()) + return false; + + *trial_name = "SyntheticStackProfilingConfiguration"; + *group_name = std::string(); + switch (configuration_) { + case PROFILE_DISABLED: + *group_name = "Disabled"; + break; + + case PROFILE_CONTROL: + *group_name = "Control"; + break; + + case PROFILE_ENABLED: + *group_name = "Enabled"; + break; + + case PROFILE_FROM_COMMAND_LINE: + NOTREACHED(); + break; + } + + return !group_name->empty(); +} + +void StackSamplingConfiguration::AppendCommandLineSwitchForChildProcess( + const std::string& process_type, + base::CommandLine* command_line) const { + DCHECK(IsBrowserProcess()); + + bool enable = + configuration_ == PROFILE_ENABLED || configuration_ == PROFILE_CONTROL; + if (!enable) + return; + if (process_type == switches::kGpuProcess || + (process_type == switches::kRendererProcess && + // Do not start the profiler for extension processes since profiling the + // compositor thread in them is not useful. + !IsExtensionRenderer(*command_line) && + ShouldEnableProfilerForNextRendererProcess())) { + command_line->AppendSwitch(switches::kStartStackProfiler); + } +} + +// static +StackSamplingConfiguration* StackSamplingConfiguration::Get() { + return g_configuration.Pointer(); +} + +// static +StackSamplingConfiguration::ProfileConfiguration +StackSamplingConfiguration::ChooseConfiguration( + const std::vector<Variation>& variations) { + int total_weight = 0; + for (const Variation& variation : variations) + total_weight += variation.weight; + DCHECK_EQ(100, total_weight); + + int chosen = base::RandInt(0, total_weight - 1); // Max is inclusive. + int cumulative_weight = 0; + for (const auto& variation : variations) { + if (chosen >= cumulative_weight && + chosen < cumulative_weight + variation.weight) { + return variation.config; + } + cumulative_weight += variation.weight; + } + NOTREACHED(); + return PROFILE_DISABLED; +} + +// static +StackSamplingConfiguration::ProfileConfiguration +StackSamplingConfiguration::GenerateConfiguration() { + if (!IsBrowserProcess()) + return PROFILE_FROM_COMMAND_LINE; + + if (!IsProfilerSupported()) + return PROFILE_DISABLED; + +#if defined(OS_WIN) + // Do not start the profiler when Application Verifier is in use; running them + // simultaneously can cause crashes and has no known use case. + if (GetModuleHandleA(base::win::kApplicationVerifierDllName)) + return PROFILE_DISABLED; +#endif + + switch (chrome::GetChannel()) { + // Enable the profiler unconditionally for development/waterfall builds. + case version_info::Channel::UNKNOWN: + return PROFILE_ENABLED; + +#if (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) || defined(OS_MACOSX) + case version_info::Channel::CANARY: + case version_info::Channel::DEV: + return ChooseConfiguration({{PROFILE_ENABLED, 80}, + {PROFILE_CONTROL, 10}, + {PROFILE_DISABLED, 10}}); +#endif + + default: + return PROFILE_DISABLED; + } +} |