From be3afa15d22e101c5b4306d3618af5c14d0deead Mon Sep 17 00:00:00 2001 From: esmael Date: Tue, 13 Aug 2024 11:11:23 -0700 Subject: [PATCH] Add navigator.hardwareConcurrency support for linux. b/341774149 --- cobalt/base/process/process_metrics_helper.cc | 57 +++++++++++++++++++ cobalt/base/process/process_metrics_helper.h | 2 + .../process/process_metrics_helper_test.cc | 23 ++++++++ cobalt/black_box_tests/black_box_tests.py | 1 + .../testdata/navigator_test.html | 29 ++++++++++ .../black_box_tests/tests/navigator_test.py | 47 +++++++++++++++ cobalt/browser/idl_files.gni | 1 + cobalt/dom/navigator.cc | 6 ++ cobalt/dom/navigator.h | 2 + cobalt/dom/navigator.idl | 1 + cobalt/dom/navigator_concurrent_hardware.idl | 18 ++++++ cobalt/dom/navigator_test.cc | 11 ++++ 12 files changed, 198 insertions(+) create mode 100644 cobalt/black_box_tests/testdata/navigator_test.html create mode 100644 cobalt/black_box_tests/tests/navigator_test.py create mode 100644 cobalt/dom/navigator_concurrent_hardware.idl diff --git a/cobalt/base/process/process_metrics_helper.cc b/cobalt/base/process/process_metrics_helper.cc index 2effea1935ca..aa7ecec1c2f0 100644 --- a/cobalt/base/process/process_metrics_helper.cc +++ b/cobalt/base/process/process_metrics_helper.cc @@ -36,6 +36,7 @@ namespace base { namespace { static std::atomic clock_ticks_per_s{0}; +static std::atomic number_of_configured_processors{0}; ProcessMetricsHelper::ReadCallback GetReadCallback(const FilePath& path) { return BindOnce( @@ -178,4 +179,60 @@ TimeDelta ProcessMetricsHelper::GetCPUUsage(const FilePath& path, ticks_per_s); } +// static +int ProcessMetricsHelper::GetNumberOfConfiguredProcessors( + ReadCallback read_callback) { + absl::optional contents = std::move(read_callback).Run(); + if (!contents.has_value()) { + // Expected for non-Linux platforms. + LOG(WARNING) << "Not supported for platform."; + return -1; + } + + auto chunks = + SplitString(*contents, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + int count = 0; + int lowerProcessorNumber = -1; + int upperProcessorNumber = -1; + for (const auto& chunk : chunks) { + auto chunkParts = + SplitString(chunk, "-", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + if (chunkParts.size() == 0 || chunkParts.size() > 2) { + LOG(ERROR) << "Unexpected format of processor count chunks."; + return -2; + } + if (!StringToInt(chunkParts[0], &lowerProcessorNumber)) { + LOG(ERROR) << "Unexpected format of processor count chunks."; + return -3; + } + if (chunkParts.size() == 1) { + count++; + continue; + } + if (!StringToInt(chunkParts[1], &upperProcessorNumber)) { + LOG(ERROR) << "Unexpected format of processor count chunks."; + return -3; + } + if (upperProcessorNumber <= lowerProcessorNumber) { + LOG(ERROR) << "Unexpected format of processor count chunks."; + return -4; + } + count += 1 + upperProcessorNumber - lowerProcessorNumber; + } + + return count; +} + +// static +int ProcessMetricsHelper::GetNumberOfConfiguredProcessors() { + int stored_value = number_of_configured_processors.load(); + if (stored_value != 0) { + return stored_value; + } + int result = GetNumberOfConfiguredProcessors( + GetReadCallback(FilePath("/sys/devices/system/cpu/possible"))); + number_of_configured_processors.store(result); + return result; +} + } // namespace base diff --git a/cobalt/base/process/process_metrics_helper.h b/cobalt/base/process/process_metrics_helper.h index 4635506c9ea3..303c6011be41 100644 --- a/cobalt/base/process/process_metrics_helper.h +++ b/cobalt/base/process/process_metrics_helper.h @@ -38,6 +38,7 @@ class ProcessMetricsHelper { static void PopulateClockTicksPerS(); static TimeDelta GetCumulativeCPUUsage(); static Value GetCumulativeCPUUsagePerThread(); + static int GetNumberOfConfiguredProcessors(); private: friend class ProcessMetricsHelperTest; @@ -47,6 +48,7 @@ class ProcessMetricsHelper { static Fields GetProcStatFields(const FilePath&, std::initializer_list); static TimeDelta GetCPUUsage(ReadCallback, int); static TimeDelta GetCPUUsage(const FilePath&, int); + static int GetNumberOfConfiguredProcessors(ReadCallback); }; } // namespace base diff --git a/cobalt/base/process/process_metrics_helper_test.cc b/cobalt/base/process/process_metrics_helper_test.cc index 484571105662..630a2a30d1ce 100644 --- a/cobalt/base/process/process_metrics_helper_test.cc +++ b/cobalt/base/process/process_metrics_helper_test.cc @@ -114,6 +114,17 @@ class ProcessMetricsHelperTest : public testing::Test { return ProcessMetricsHelper::GetCPUUsage(std::move(stat_callback), ticks_per_s); } + + int GetNumberOfConfiguredProcessorsWithMockData(const std::string& contents) { + return ProcessMetricsHelper::GetNumberOfConfiguredProcessors(BindOnce( + [](const std::string& contents) -> absl::optional { + if (contents.size() == 0) { + return absl::nullopt; + } + return contents; + }, + contents)); + } }; ProcessMetricsHelper::ReadCallback GetNulloptCallback() { @@ -220,4 +231,16 @@ TEST_F(ProcessMetricsHelperTest, GetCumulativeCPUUsagePerThread) { thread3.Stop(); } +TEST_F(ProcessMetricsHelperTest, GetNumberOfConfiguredProcessors) { + EXPECT_EQ(-1, GetNumberOfConfiguredProcessorsWithMockData("")); + EXPECT_EQ(-2, GetNumberOfConfiguredProcessorsWithMockData("0-2-3")); + EXPECT_EQ(-3, GetNumberOfConfiguredProcessorsWithMockData("a")); + EXPECT_EQ(-4, GetNumberOfConfiguredProcessorsWithMockData("3-0")); + EXPECT_EQ(1, GetNumberOfConfiguredProcessorsWithMockData("0")); + EXPECT_EQ(96, GetNumberOfConfiguredProcessorsWithMockData("0-95")); + EXPECT_EQ(96, + GetNumberOfConfiguredProcessorsWithMockData("0-5,6,7-12,13,14-95")); + EXPECT_EQ(96, ProcessMetricsHelper::GetNumberOfConfiguredProcessors()); +} + } // namespace base diff --git a/cobalt/black_box_tests/black_box_tests.py b/cobalt/black_box_tests/black_box_tests.py index 8b0d39755513..e9f2f868a4a8 100755 --- a/cobalt/black_box_tests/black_box_tests.py +++ b/cobalt/black_box_tests/black_box_tests.py @@ -74,6 +74,7 @@ # 'h5vcc_watchdog_api_test', 'http_cache', 'javascript_profiler', + 'navigator_test', 'performance_resource_timing_test', 'persistent_cookie', 'pointer_event_on_fixed_element_test', diff --git a/cobalt/black_box_tests/testdata/navigator_test.html b/cobalt/black_box_tests/testdata/navigator_test.html new file mode 100644 index 000000000000..46126c6319f2 --- /dev/null +++ b/cobalt/black_box_tests/testdata/navigator_test.html @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/cobalt/black_box_tests/tests/navigator_test.py b/cobalt/black_box_tests/tests/navigator_test.py new file mode 100644 index 000000000000..9ec99a5665e8 --- /dev/null +++ b/cobalt/black_box_tests/tests/navigator_test.py @@ -0,0 +1,47 @@ +# Copyright 2024 The Cobalt Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Test navigator API.""" + +from cobalt.black_box_tests import black_box_tests +from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer +import logging + +PLATFORMS_SUPPORTED = [ + 'linux-x64x11', + 'linux-x64x11-egl', + 'linux-x64x11-gcc-6-3', + 'linux-x64x11-skia', + 'android-arm', + 'android-arm64', + 'android-arm64-vulkan', + 'android-x86', + 'raspi-2', + 'raspi-2-skia', + 'linux-x64x11-clang-crosstool', +] + + +class NavigatorTest(black_box_tests.BlackBoxTestCase): + + def test_navigator(self): + if self.launcher_params.platform not in PLATFORMS_SUPPORTED: + logging.warning('Blackbox tests disabled for platform:%s', + self.launcher_params.platform) + return + + with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server: + url = server.GetURL(file_name='testdata/navigator_test.html') + with self.CreateCobaltRunner(url=url) as runner: + runner.WaitForJSTestsSetup() + self.assertTrue(runner.JSTestsSucceeded()) diff --git a/cobalt/browser/idl_files.gni b/cobalt/browser/idl_files.gni index 0d0747ea5a6e..77116a71e9f2 100644 --- a/cobalt/browser/idl_files.gni +++ b/cobalt/browser/idl_files.gni @@ -399,6 +399,7 @@ dependency_idl_files = [ "//cobalt/dom/global_event_handlers.idl", "//cobalt/dom/html_element_cssom_view.idl", "//cobalt/dom/mouse_event_cssom_view.idl", + "//cobalt/dom/navigator_concurrent_hardware.idl", "//cobalt/dom/navigator_licenses.idl", "//cobalt/dom/navigator_plugins.idl", "//cobalt/dom/navigator_storage_utils.idl", diff --git a/cobalt/dom/navigator.cc b/cobalt/dom/navigator.cc index 7ad053c7c7e5..b809fc6f9155 100644 --- a/cobalt/dom/navigator.cc +++ b/cobalt/dom/navigator.cc @@ -19,6 +19,7 @@ #include "base/optional.h" #include "base/trace_event/trace_event.h" +#include "cobalt/base/process/process_metrics_helper.h" #include "cobalt/dom/captions/system_caption_settings.h" #include "cobalt/dom/dom_settings.h" #include "cobalt/dom/embedded_licenses.h" // Generated file. @@ -154,6 +155,11 @@ Navigator::Navigator(script::EnvironmentSettings* settings, new media_capture::MediaDevices(settings, script_value_factory())), system_caption_settings_(captions) {} +uint64_t Navigator::hardware_concurrency() const { + int count = base::ProcessMetricsHelper::GetNumberOfConfiguredProcessors(); + return count <= 0 ? 0 : static_cast(count); +} + const std::string Navigator::licenses() const { GeneratedResourceMap resource_map; DOMEmbeddedResources::GenerateMap(resource_map); diff --git a/cobalt/dom/navigator.h b/cobalt/dom/navigator.h index 73e928e99f15..30d393a46a54 100644 --- a/cobalt/dom/navigator.h +++ b/cobalt/dom/navigator.h @@ -47,6 +47,8 @@ class Navigator : public web::NavigatorBase { Navigator(const Navigator&) = delete; Navigator& operator=(const Navigator&) = delete; + uint64_t hardware_concurrency() const; + // Web API: NavigatorLicenses const std::string licenses() const; diff --git a/cobalt/dom/navigator.idl b/cobalt/dom/navigator.idl index 6a215209dd09..8bd07f464b67 100644 --- a/cobalt/dom/navigator.idl +++ b/cobalt/dom/navigator.idl @@ -16,6 +16,7 @@ interface Navigator {}; +Navigator implements NavigatorConcurrentHardware; Navigator implements NavigatorID; Navigator implements NavigatorLanguage; Navigator implements NavigatorPlugins; diff --git a/cobalt/dom/navigator_concurrent_hardware.idl b/cobalt/dom/navigator_concurrent_hardware.idl new file mode 100644 index 000000000000..39cde72516ef --- /dev/null +++ b/cobalt/dom/navigator_concurrent_hardware.idl @@ -0,0 +1,18 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +[NoInterfaceObject] +interface NavigatorConcurrentHardware { + readonly attribute unsigned long long hardwareConcurrency; +}; diff --git a/cobalt/dom/navigator_test.cc b/cobalt/dom/navigator_test.cc index 297288a72452..7042c7897a07 100644 --- a/cobalt/dom/navigator_test.cc +++ b/cobalt/dom/navigator_test.cc @@ -16,6 +16,7 @@ #include #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "cobalt/bindings/testing/utils.h" #include "cobalt/dom/testing/test_with_javascript.h" #include "cobalt/web/testing/gtest_workarounds.h" @@ -160,5 +161,15 @@ TEST_F(NavigatorTest, NavigatorOnline) { EXPECT_EQ("true", result); } +TEST_F(NavigatorTest, NavigatorConcurrentHardware) { + std::string result; + EXPECT_TRUE(EvaluateScript("typeof navigator.hardwareConcurrency", &result)); + EXPECT_EQ("number", result); + EXPECT_TRUE(EvaluateScript("navigator.hardwareConcurrency", &result)); + int count = -1; + EXPECT_TRUE(base::StringToInt(result, &count)); + EXPECT_GE(count, 0); +} + } // namespace dom } // namespace cobalt