0

Feature to return the number of "physical" cores when mitigation is

enabled on Mac.

If we are not able to establish if we are in mitigation mode fall back
to returning number of "logical" cores.

Bug: 997677
Change-Id: I8ccc4fefa8e494051dbc1f241548962cb02eefbb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3714401
Commit-Queue: Alex Attar <aattar@google.com>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Francois Pierre Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1048590}
This commit is contained in:
Alex Attar
2022-09-19 14:13:26 +00:00
committed by Chromium LUCI CQ
parent af23fdccda
commit 8990259674
6 changed files with 166 additions and 45 deletions

@ -17,8 +17,19 @@
#include "base/time/time.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_MAC)
#include "base/feature_list.h"
#endif
namespace base {
#if BUILDFLAG(IS_MAC)
// When enabled, NumberOfProcessors() returns the number of physical processors
// instead of the number of logical processors if CPU security mitigations are
// enabled for the current process.
extern const Feature kNumberOfCoresWithCpuSecurityMitigation;
#endif
namespace debug {
FORWARD_DECLARE_TEST(SystemMetricsTest, ParseMeminfo);
}
@ -28,7 +39,11 @@ struct SystemMemoryInfoKB;
class BASE_EXPORT SysInfo {
public:
// Return the number of logical processors/cores on the current machine.
// Returns the number of processors/cores available for the current
// application. This is typically the number of logical cores installed on the
// system, but could instead be the number of physical cores when
// SetIsCpuSecurityMitigationsEnabled() has been invoked to indicate that CPU
// security mitigations are enabled on Mac.
static int NumberOfProcessors();
// Return the number of bytes of physical memory on the current machine.
@ -211,6 +226,13 @@ class BASE_EXPORT SysInfo {
// On Desktop this returns true when memory <= 2GB.
static bool IsLowEndDevice();
#if BUILDFLAG(IS_MAC)
// Sets whether CPU security mitigations are enabled for the current process.
// This is used to control the behavior of NumberOfProcessors(), see comment
// on that method.
static void SetIsCpuSecurityMitigationsEnabled(bool is_enabled);
#endif
private:
FRIEND_TEST_ALL_PREFIXES(SysInfoTest, AmountOfAvailablePhysicalMemory);
FRIEND_TEST_ALL_PREFIXES(debug::SystemMetricsTest, ParseMeminfo);

@ -5,6 +5,9 @@
#ifndef BASE_SYSTEM_SYS_INFO_INTERNAL_H_
#define BASE_SYSTEM_SYS_INFO_INTERNAL_H_
#include "base/base_export.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
namespace internal {
@ -25,6 +28,11 @@ class LazySysInfoValue {
const T value_;
};
// Exposed for testing.
BASE_EXPORT absl::optional<int> NumberOfPhysicalProcessors();
BASE_EXPORT int NumberOfProcessors();
} // namespace internal
} // namespace base

@ -35,49 +35,16 @@
#include <sys/vfs.h>
#endif
#if BUILDFLAG(IS_MAC)
#include <sys/sysctl.h>
#include "base/feature_list.h"
#include "base/system/sys_info_internal.h"
#endif
namespace {
#if !BUILDFLAG(IS_OPENBSD)
int NumberOfProcessors() {
// sysconf returns the number of "logical" (not "physical") processors on both
// Mac and Linux. So we get the number of max available "logical" processors.
//
// Note that the number of "currently online" processors may be fewer than the
// returned value of NumberOfProcessors(). On some platforms, the kernel may
// make some processors offline intermittently, to save power when system
// loading is low.
//
// One common use case that needs to know the processor count is to create
// optimal number of threads for optimization. It should make plan according
// to the number of "max available" processors instead of "currently online"
// ones. The kernel should be smart enough to make all processors online when
// it has sufficient number of threads waiting to run.
long res = sysconf(_SC_NPROCESSORS_CONF);
if (res == -1) {
NOTREACHED();
return 1;
}
int num_cpus = static_cast<int>(res);
#if BUILDFLAG(IS_LINUX)
// Restrict the CPU count based on the process's CPU affinity mask, if
// available.
cpu_set_t* cpu_set = CPU_ALLOC(num_cpus);
size_t cpu_set_size = CPU_ALLOC_SIZE(num_cpus);
int ret = sched_getaffinity(0, cpu_set_size, cpu_set);
if (ret == 0) {
num_cpus = CPU_COUNT_S(cpu_set_size, cpu_set);
}
CPU_FREE(cpu_set);
#endif // BUILDFLAG(IS_LINUX)
return num_cpus;
}
base::LazyInstance<base::internal::LazySysInfoValue<int, NumberOfProcessors>>::
Leaky g_lazy_number_of_processors = LAZY_INSTANCE_INITIALIZER;
#endif // !BUILDFLAG(IS_OPENBSD)
#if BUILDFLAG(IS_MAC)
bool is_cpu_security_mitigation_enabled = false;
#endif
uint64_t AmountOfVirtualMemory() {
struct rlimit limit;
@ -144,9 +111,81 @@ bool GetDiskSpaceInfo(const base::FilePath& path,
namespace base {
namespace internal {
int NumberOfProcessors() {
#if BUILDFLAG(IS_MAC)
// When CPU security mitigation is enabled, return number of "physical"
// cores and not the number of "logical" cores. CPU security mitigations
// disables hyper-threading for the current application, which effectively
// limits the number of concurrently executing threads to the number of
// physical cores.
if (base::FeatureList::IsEnabled(
base::kNumberOfCoresWithCpuSecurityMitigation) &&
is_cpu_security_mitigation_enabled) {
absl::optional<int> number_of_physical_cores =
internal::NumberOfPhysicalProcessors();
if (number_of_physical_cores.has_value())
return number_of_physical_cores.value();
}
#endif // BUILDFLAG(IS_MAC)
// sysconf returns the number of "logical" (not "physical") processors on both
// Mac and Linux. So we get the number of max available "logical" processors.
//
// Note that the number of "currently online" processors may be fewer than the
// returned value of NumberOfProcessors(). On some platforms, the kernel may
// make some processors offline intermittently, to save power when system
// loading is low.
//
// One common use case that needs to know the processor count is to create
// optimal number of threads for optimization. It should make plan according
// to the number of "max available" processors instead of "currently online"
// ones. The kernel should be smart enough to make all processors online when
// it has sufficient number of threads waiting to run.
long res = sysconf(_SC_NPROCESSORS_CONF);
if (res == -1) {
NOTREACHED();
return 1;
}
int num_cpus = static_cast<int>(res);
#if BUILDFLAG(IS_LINUX)
// Restrict the CPU count based on the process's CPU affinity mask, if
// available.
cpu_set_t* cpu_set = CPU_ALLOC(num_cpus);
size_t cpu_set_size = CPU_ALLOC_SIZE(num_cpus);
int ret = sched_getaffinity(0, cpu_set_size, cpu_set);
if (ret == 0) {
num_cpus = CPU_COUNT_S(cpu_set_size, cpu_set);
}
CPU_FREE(cpu_set);
#endif // BUILDFLAG(IS_LINUX)
return num_cpus;
}
#if BUILDFLAG(IS_MAC)
absl::optional<int> NumberOfPhysicalProcessors() {
uint32_t sysctl_value = 0;
size_t length = sizeof(sysctl_value);
if (sysctlbyname("hw.physicalcpu_max", &sysctl_value, &length, nullptr, 0) ==
0) {
return static_cast<int>(sysctl_value);
}
return absl::nullopt;
}
#endif // BUILDFLAG(IS_LINUX)
} // namespace internal
#if !BUILDFLAG(IS_OPENBSD)
int SysInfo::NumberOfProcessors() {
return g_lazy_number_of_processors.Get().value();
static int number_of_processors = internal::NumberOfProcessors();
return number_of_processors;
}
#endif // !BUILDFLAG(IS_OPENBSD)
@ -250,4 +289,15 @@ size_t SysInfo::VMAllocationGranularity() {
return checked_cast<size_t>(getpagesize());
}
} // namespace base
#if BUILDFLAG(IS_MAC)
const BASE_EXPORT base::Feature kNumberOfCoresWithCpuSecurityMitigation = {
"NumberOfCoresWithCpuSecurityMitigation",
base::FEATURE_DISABLED_BY_DEFAULT};
void SysInfo::SetIsCpuSecurityMitigationsEnabled(bool is_enabled) {
is_cpu_security_mitigation_enabled = is_enabled;
}
#endif // BUILDFLAG(IS_MAC)
} // namespace base

@ -40,6 +40,11 @@
#include "base/win/wmi.h"
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_MAC)
#include "base/system/sys_info_internal.h"
#include "base/test/scoped_feature_list.h"
#endif // BUILDFLAG(IS_MAC)
namespace base {
#if BUILDFLAG(IS_ANDROID)
@ -56,6 +61,22 @@ TEST_F(SysInfoTest, NumProcs) {
EXPECT_GE(SysInfo::NumberOfProcessors(), 1);
}
#if BUILDFLAG(IS_MAC)
TEST_F(SysInfoTest, NumProcsWithSecurityMitigationEnabled) {
// Verify that the number of number of available processors available when CPU
// security mitigation is enabled is the number of available "physical"
// processors.
test::ScopedFeatureList feature_list_;
feature_list_.InitAndEnableFeature(kNumberOfCoresWithCpuSecurityMitigation);
SysInfo::SetIsCpuSecurityMitigationsEnabled(true);
EXPECT_EQ(internal::NumberOfProcessors(),
internal::NumberOfPhysicalProcessors());
// Reset to default value
SysInfo::SetIsCpuSecurityMitigationsEnabled(false);
}
#endif // BUILDFLAG(IS_MAC)
TEST_F(SysInfoTest, AmountOfMem) {
// We aren't actually testing that it's correct, just that it's sane.
EXPECT_GT(SysInfo::AmountOfPhysicalMemory(), 0u);

@ -57,6 +57,10 @@
#include <stdlib.h>
#endif
#if BUILDFLAG(IS_MAC)
#include "base/system/sys_info.h"
#endif
#if BUILDFLAG(IS_WIN)
sandbox::TargetServices* g_target_services = NULL;
#else
@ -69,6 +73,12 @@ namespace content {
int PpapiPluginMain(MainFunctionParams parameters) {
const base::CommandLine& command_line = *parameters.command_line;
#if BUILDFLAG(IS_MAC)
// Specified when launching the process in
// PpapiPluginSandboxedProcessLauncherDelegate::EnableCpuSecurityMitigations.
base::SysInfo::SetIsCpuSecurityMitigationsEnabled(true);
#endif
#if BUILDFLAG(IS_WIN)
// https://crbug.com/1139752 Premature unload of shell32 caused process to
// crash during process shutdown. Fixed in Windows 11.

@ -50,6 +50,10 @@
#include "v8/include/v8-wasm-trap-handler-posix.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "base/system/sys_info.h"
#endif
namespace {
void SetV8FlagIfFeature(const base::Feature& feature, const char* v8_flag) {
@ -95,6 +99,12 @@ RenderProcessImpl::RenderProcessImpl()
: RenderProcess(GetThreadPoolInitParams()) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
#if BUILDFLAG(IS_MAC)
// Specified when launching the process in
// RendererSandboxedProcessLauncherDelegate::EnableCpuSecurityMitigations
base::SysInfo::SetIsCpuSecurityMitigationsEnabled(true);
#endif
#if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
// Some official builds ship with DCHECKs compiled in. Failing DCHECKs then
// are either fatal or simply log the error, based on a feature flag.