0

Move code to read macOS resource coalition stats to //components/power_metrics.

This allows reuse by the code that records histograms in Chrome and
the "power_sampler" tool in //tools/mac/power.

Bug: 1254332
Change-Id: I6c0d9fcdee9d556b061868a90344843b4e80d587
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3265524
Commit-Queue: François Doray <fdoray@chromium.org>
Reviewed-by: Jochen Eisinger <jochen@chromium.org>
Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/main@{#945055}
This commit is contained in:
Francois Doray
2021-11-24 19:13:10 +00:00
committed by Chromium LUCI CQ
parent c5705a1ef9
commit dbfac69059
10 changed files with 159 additions and 92 deletions

@ -5609,7 +5609,6 @@ static_library("browser") {
"obsolete_system/obsolete_system_mac.cc",
"password_manager/password_manager_util_mac.h",
"password_manager/password_manager_util_mac.mm",
"performance_monitor/resource_coalition_internal_types_mac.h",
"performance_monitor/resource_coalition_mac.h",
"performance_monitor/resource_coalition_mac.mm",
"platform_util_mac.mm",
@ -5637,6 +5636,7 @@ static_library("browser") {
"//chrome/services/mac_notifications/public/mojom",
"//components/crash/core/app",
"//components/metal_util",
"//components/power_metrics",
"//components/remote_cocoa/browser:browser",
"//sandbox/mac:seatbelt",
"//sandbox/policy",

@ -14,16 +14,46 @@
#include "base/process/process_handle.h"
#include "base/strings/sys_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/performance_monitor/resource_coalition_internal_types_mac.h"
extern "C" int coalition_info_resource_usage(
uint64_t cid,
struct coalition_resource_usage* cru,
size_t sz);
#include "components/power_metrics/resource_coalition_mac.h"
namespace performance_monitor {
namespace {
static_assert(
THREAD_QOS_DEFAULT ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kDefault),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_MAINTENANCE ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kMaintenance),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_BACKGROUND ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kBackground),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_UTILITY ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kUtility),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_LEGACY ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kLegacy),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_USER_INITIATED ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kUserInitiated),
"QoSLevels indexes should match the OS defined ones.");
static_assert(THREAD_QOS_USER_INTERACTIVE ==
static_cast<int>(performance_monitor::ResourceCoalition::
QoSLevels::kUserInteractive),
"QoSLevels indexes should match the OS defined ones.");
const char kCoalitionAvailabilityHistogram[] =
"PerformanceMonitor.ResourceCoalition.Availability";
@ -40,19 +70,6 @@ enum class CoalitionAvailability {
kMaxValue = kNotAloneInCoalition
};
// Returns the coalition ID that a given process belongs to, or nullopt if this
// information isn't available.
absl::optional<uint64_t> GetProcessCoalitionId(const base::ProcessId pid) {
proc_pidcoalitioninfo coalition_info = {};
int res = proc_pidinfo(pid, PROC_PIDCOALITIONINFO, 0, &coalition_info,
sizeof(coalition_info));
if (res != sizeof(coalition_info))
return absl::nullopt;
return coalition_info.coalition_id[COALITION_TYPE_RESOURCE];
}
// Returns the coalition ID that the current process belongs to. If this isn't
// available or deemed not usable (e.g. if the process is not alone in its
// coalition) this will return nullopt and |availability_details| will receive
@ -61,7 +78,7 @@ absl::optional<uint64_t> GetProcessCoalitionId(const base::ProcessId pid) {
absl::optional<uint64_t> GetCurrentCoalitionId(
CoalitionAvailability* availability_details) {
DCHECK(availability_details);
auto cid = GetProcessCoalitionId(base::GetCurrentProcId());
auto cid = power_metrics::GetProcessCoalitionId(base::GetCurrentProcId());
if (!cid.has_value()) {
*availability_details = CoalitionAvailability::kCoalitionIDNotAvailable;
@ -69,15 +86,13 @@ absl::optional<uint64_t> GetCurrentCoalitionId(
}
// Check if resource usage metrics can be retrieved for this coalition ID.
coalition_resource_usage cru = {};
uint64_t res = coalition_info_resource_usage(cid.value(), &cru, sizeof(cru));
if (res != 0) {
if (!power_metrics::GetCoalitionResourceUsage(cid.value())) {
*availability_details =
CoalitionAvailability::kCoalitionResourceUsageNotAvailable;
return absl::nullopt;
}
auto parent_cid = GetProcessCoalitionId(
auto parent_cid = power_metrics::GetProcessCoalitionId(
base::GetParentProcessId(base::GetCurrentProcessHandle()));
if (!parent_cid.has_value()) {
@ -97,20 +112,6 @@ absl::optional<uint64_t> GetCurrentCoalitionId(
return cid;
}
// Returns the resource usage coalition data for the given coalition ID.
// This assumes that resource coalition data are always available for a given
// coalition ID (i.e. the coalition has a lifetime that exceeds the usage of the
// ID).
std::unique_ptr<coalition_resource_usage> GetResourceUsageData(
int64_t coalition_id) {
auto cru = std::make_unique<coalition_resource_usage>();
uint64_t res = coalition_info_resource_usage(
coalition_id, cru.get(), sizeof(coalition_resource_usage));
DCHECK_EQ(0U, res);
return cru;
}
NSDictionary* MaybeGetDictionaryFromPath(const base::FilePath& path) {
// The folder where the energy coefficient plist files are stored.
NSString* plist_path_string = base::SysUTF8ToNSString(path.value().c_str());
@ -147,11 +148,13 @@ ResourceCoalition::~ResourceCoalition() = default;
absl::optional<ResourceCoalition::DataRate> ResourceCoalition::GetDataRate() {
DCHECK(IsAvailable());
DCHECK_EQ(GetProcessCoalitionId(base::GetCurrentProcId()).value(),
coalition_id_.value());
DCHECK_EQ(
power_metrics::GetProcessCoalitionId(base::GetCurrentProcId()).value(),
coalition_id_.value());
DCHECK(last_data_sample_);
return GetDataRateImpl(GetResourceUsageData(coalition_id_.value()),
base::TimeTicks::Now());
return GetDataRateImpl(
power_metrics::GetCoalitionResourceUsage(coalition_id_.value()),
base::TimeTicks::Now());
}
absl::optional<ResourceCoalition::DataRate>
@ -166,7 +169,8 @@ ResourceCoalition::GetDataRateFromFakeDataForTesting(
}
void ResourceCoalition::SetCoalitionIDToCurrentProcessIdForTesting() {
SetCoalitionId(GetProcessCoalitionId(base::GetCurrentProcId()));
SetCoalitionId(
power_metrics::GetProcessCoalitionId(base::GetCurrentProcId()));
}
// static
@ -340,7 +344,8 @@ void ResourceCoalition::EnsureEnergyImpactCoefficientsIfAvailable() {
void ResourceCoalition::SetCoalitionId(absl::optional<uint64_t> coalition_id) {
coalition_id_ = coalition_id;
if (coalition_id_.has_value()) {
last_data_sample_ = GetResourceUsageData(coalition_id_.value());
last_data_sample_ =
power_metrics::GetCoalitionResourceUsage(coalition_id_.value());
last_data_sample_timestamp_ = base::TimeTicks::Now();
}
}

@ -18,8 +18,8 @@
#include "base/strings/string_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "chrome/browser/performance_monitor/resource_coalition_internal_types_mac.h"
#include "chrome/common/chrome_paths.h"
#include "components/power_metrics/resource_coalition_internal_types_mac.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace performance_monitor {

@ -5582,6 +5582,7 @@ test("unit_tests") {
"//chrome/services/mac_notifications/public/mojom",
"//chrome/utility/safe_browsing/mac",
"//chrome/utility/safe_browsing/mac:dmg_common",
"//components/power_metrics",
"//ui/events/devices:test_support",
]
}

@ -0,0 +1,15 @@
# Copyright 2021 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.
if (is_mac) {
static_library("power_metrics") {
sources = [
"resource_coalition_internal_types_mac.h",
"resource_coalition_mac.h",
"resource_coalition_mac.mm",
]
deps = [ "//base" ]
}
}

@ -0,0 +1,12 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Internals>Power"
}
team_email: "chrome-catan-battery@google.com"

@ -0,0 +1,2 @@
etiennep@chromium.org
fdoray@chromium.org

@ -2,17 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file defines some data types used to retrieve the coalition data from
// the OS. It's only meant to be included in resource_coalition_mac.h and its
// test files.
// This file defines data types used to retrieve coalition metrics from the OS.
#ifndef CHROME_BROWSER_PERFORMANCE_MONITOR_RESOURCE_COALITION_INTERNAL_TYPES_MAC_H_
#define CHROME_BROWSER_PERFORMANCE_MONITOR_RESOURCE_COALITION_INTERNAL_TYPES_MAC_H_
#ifndef COMPONENTS_POWER_METRICS_RESOURCE_COALITION_INTERNAL_TYPES_MAC_H_
#define COMPONENTS_POWER_METRICS_RESOURCE_COALITION_INTERNAL_TYPES_MAC_H_
#include "chrome/browser/performance_monitor/resource_coalition_mac.h"
#include <stdint.h>
// Comes from osfmk/mach/coalition.h
#define COALITION_TYPE_RESOURCE (0)
#define COALITION_TYPE_JETSAM (1)
#define COALITION_TYPE_MAX (1)
@ -36,45 +33,7 @@
static_assert(COALITION_NUM_THREAD_QOS_TYPES == THREAD_QOS_LAST,
"Unexpected number of QoS levels.");
static_assert(
THREAD_QOS_DEFAULT ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kDefault),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_MAINTENANCE ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kMaintenance),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_BACKGROUND ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kBackground),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_UTILITY ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kUtility),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_LEGACY ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kLegacy),
"QoSLevels indexes should match the OS defined ones.");
static_assert(
THREAD_QOS_USER_INITIATED ==
static_cast<int>(
performance_monitor::ResourceCoalition::QoSLevels::kUserInitiated),
"QoSLevels indexes should match the OS defined ones.");
static_assert(THREAD_QOS_USER_INTERACTIVE ==
static_cast<int>(performance_monitor::ResourceCoalition::
QoSLevels::kUserInteractive),
"QoSLevels indexes should match the OS defined ones.");
// Comes from bsd/sys/coalition.h
//
// TODO(crbug.com/1229686): Report some data derived from the tasks_started and
// tasks_exited counters.
struct coalition_resource_usage {
uint64_t tasks_started;
uint64_t tasks_exited;
@ -117,4 +76,4 @@ struct proc_pidcoalitioninfo {
// Comes from bsd/sys/proc_info.h
#define PROC_PIDCOALITIONINFO 20
#endif // CHROME_BROWSER_PERFORMANCE_MONITOR_RESOURCE_COALITION_INTERNAL_TYPES_MAC_H_
#endif // COMPONENTS_POWER_METRICS_RESOURCE_COALITION_INTERNAL_TYPES_MAC_H_

@ -0,0 +1,34 @@
// Copyright 2021 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.
// Resource Coalition is an (undocumented) mechanism available in macOS that
// allows retrieving accrued resource usage metrics for a group of processes,
// including processes that have died. Typically, a coalition includes a root
// process and its descendants. The source can help understand the mechanism:
// https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/coalition.c
#ifndef COMPONENTS_POWER_METRICS_RESOURCE_COALITION_MAC_H_
#define COMPONENTS_POWER_METRICS_RESOURCE_COALITION_MAC_H_
#include <stdint.h>
#include <memory>
#include "base/process/process_handle.h"
#include "components/power_metrics/resource_coalition_internal_types_mac.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace power_metrics {
// Returns the coalition id for the process identified by |pid| or nullopt if
// not available.
absl::optional<uint64_t> GetProcessCoalitionId(base::ProcessId pid);
// Returns resource usage data for the coalition identified by |coalition_id|,
// or nullptr if not available.
std::unique_ptr<coalition_resource_usage> GetCoalitionResourceUsage(
int64_t coalition_id);
} // namespace power_metrics
#endif // COMPONENTS_PPOWER_METRICS_RESOURCE_COALITION_MAC_H_

@ -0,0 +1,39 @@
// Copyright 2021 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 "components/power_metrics/resource_coalition_mac.h"
#include <libproc.h>
#include "base/check_op.h"
extern "C" int coalition_info_resource_usage(
uint64_t cid,
struct coalition_resource_usage* cru,
size_t sz);
namespace power_metrics {
absl::optional<uint64_t> GetProcessCoalitionId(base::ProcessId pid) {
proc_pidcoalitioninfo coalition_info = {};
int res = proc_pidinfo(pid, PROC_PIDCOALITIONINFO, 0, &coalition_info,
sizeof(coalition_info));
if (res != sizeof(coalition_info))
return absl::nullopt;
return coalition_info.coalition_id[COALITION_TYPE_RESOURCE];
}
std::unique_ptr<coalition_resource_usage> GetCoalitionResourceUsage(
int64_t coalition_id) {
auto cru = std::make_unique<coalition_resource_usage>();
uint64_t res = coalition_info_resource_usage(
coalition_id, cru.get(), sizeof(coalition_resource_usage));
if (res == 0U)
return cru;
return nullptr;
}
} // power_metrics