Commit 11748b2a authored by Robert Sesek's avatar Robert Sesek Committed by Chromium LUCI CQ

Add a new Android.ArmCpuPart UMA.

This will record the ARM CPU's implementer and part number values.

Bug: 1164549
Change-Id: I09ea0d852fda6b05c50a16b64bfcde6f4f5f62b6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2615326
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarJesse Doherty <jwd@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843243}
parent 6cbdc4ff
......@@ -35,9 +35,20 @@
#include <asm/hwcap.h>
#include <sys/auxv.h>
#include "base/files/file_util.h"
#include "base/numerics/checked_math.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
// Temporary definitions until a new hwcap.h is pulled in.
#define HWCAP2_MTE (1 << 18)
#define HWCAP2_BTI (1 << 17)
struct ProcCpuInfo {
std::string brand;
uint8_t implementer = 0;
uint32_t part_number = 0;
};
#endif
#if defined(ARCH_CPU_X86_FAMILY)
......@@ -138,33 +149,68 @@ uint64_t xgetbv(uint32_t xcr) {
#if defined(ARCH_CPU_ARM_FAMILY) && \
(defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS))
std::string* CpuInfoBrand() {
static std::string* brand = []() {
StringPairs::const_iterator FindFirstProcCpuKey(const StringPairs& pairs,
StringPiece key) {
return ranges::find_if(pairs, [key](const StringPairs::value_type& pair) {
return TrimWhitespaceASCII(pair.first, base::TRIM_ALL) == key;
});
}
// Parses information about the ARM processor. Note that depending on the CPU
// package, processor configuration, and/or kernel version, this may only
// report information about the processor on which this thread is running. This
// can happen on heterogeneous-processor SoCs like Snapdragon 808, which has 4
// Cortex-A53 and 2 Cortex-A57. Unfortunately there is not a universally
// reliable way to examine the CPU part information for all cores.
const ProcCpuInfo* ParseProcCpu() {
static const ProcCpuInfo* info = []() {
// This function finds the value from /proc/cpuinfo under the key "model
// name" or "Processor". "model name" is used in Linux 3.8 and later (3.7
// and later for arm64) and is shown once per CPU. "Processor" is used in
// earler versions and is shown only once at the top of /proc/cpuinfo
// regardless of the number CPUs.
const char kModelNamePrefix[] = "model name\t: ";
const char kProcessorPrefix[] = "Processor\t: ";
std::string contents;
ReadFileToString(FilePath("/proc/cpuinfo"), &contents);
DCHECK(!contents.empty());
std::istringstream iss(contents);
std::string line;
while (std::getline(iss, line)) {
if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0)
return new std::string(line.substr(strlen(kModelNamePrefix)));
if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)
return new std::string(line.substr(strlen(kProcessorPrefix)));
const char kModelNamePrefix[] = "model name";
const char kProcessorPrefix[] = "Processor";
std::string cpuinfo;
ReadFileToString(FilePath("/proc/cpuinfo"), &cpuinfo);
DCHECK(!cpuinfo.empty());
ProcCpuInfo* info = new ProcCpuInfo();
StringPairs pairs;
if (!SplitStringIntoKeyValuePairs(cpuinfo, ':', '\n', &pairs)) {
NOTREACHED();
return info;
}
auto model_name = FindFirstProcCpuKey(pairs, kModelNamePrefix);
if (model_name == pairs.end())
model_name = FindFirstProcCpuKey(pairs, kProcessorPrefix);
if (model_name != pairs.end()) {
info->brand =
std::string(TrimWhitespaceASCII(model_name->second, TRIM_ALL));
}
return new std::string();
auto implementer_string = FindFirstProcCpuKey(pairs, "CPU implementer");
if (implementer_string != pairs.end()) {
// HexStringToUInt() handles the leading whitespace on the value.
uint32_t implementer;
HexStringToUInt(implementer_string->second, &implementer);
if (!CheckedNumeric<uint32_t>(implementer)
.AssignIfValid(&info->implementer)) {
info->implementer = 0;
}
}
auto part_number_string = FindFirstProcCpuKey(pairs, "CPU part");
if (part_number_string != pairs.end())
HexStringToUInt(part_number_string->second, &info->part_number);
return info;
}();
return brand;
return info;
}
#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) ||
// defined(OS_LINUX) || defined(OS_CHROMEOS))
......@@ -290,7 +336,10 @@ void CPU::Initialize() {
}
#elif defined(ARCH_CPU_ARM_FAMILY)
#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
cpu_brand_ = *CpuInfoBrand();
const ProcCpuInfo* info = ParseProcCpu();
cpu_brand_ = info->brand;
implementer_ = info->implementer;
part_number_ = info->part_number;
#if defined(ARCH_CPU_ARM64)
// Check for Armv8.5-A BTI/MTE support, exposed via HWCAP2
......
......@@ -70,6 +70,13 @@ class BASE_EXPORT CPU final {
}
bool is_running_in_vm() const { return is_running_in_vm_; }
// The cpuinfo values for ARM cores are from the MIDR_EL1 register, a
// bitfield whose format is described in the core-specific manuals. E.g.,
// ARM Cortex-A57:
// https://developer.arm.com/documentation/ddi0488/h/system-control/aarch64-register-descriptions/main-id-register--el1.
uint8_t implementer() const { return implementer_; }
uint32_t part_number() const { return part_number_; }
// Armv8.5-A extensions for control flow and memory safety.
bool has_mte() const { return has_mte_; }
bool has_bti() const { return has_bti_; }
......@@ -142,6 +149,8 @@ class BASE_EXPORT CPU final {
int stepping_ = 0; // processor revision number
int ext_model_ = 0;
int ext_family_ = 0;
uint32_t part_number_ = 0; // ARM MIDR part number
uint8_t implementer_ = 0; // ARM MIDR implementer identifier
bool has_mmx_ = false;
bool has_sse_ = false;
bool has_sse2_ = false;
......
......@@ -5,6 +5,7 @@
#include "base/cpu.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -190,3 +191,18 @@ TEST(CPU, X86FamilyAndModel) {
EXPECT_EQ(ext_model, 7);
}
#endif // defined(ARCH_CPU_X86_FAMILY)
#if defined(ARCH_CPU_ARM_FAMILY) && \
(defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_CHROMEOS))
TEST(CPU, ARMImplementerAndPartNumber) {
base::CPU cpu;
const std::string& cpu_brand = cpu.cpu_brand();
EXPECT_FALSE(cpu_brand.empty());
EXPECT_EQ(cpu_brand, base::TrimWhitespaceASCII(cpu_brand, base::TRIM_ALL));
EXPECT_GT(cpu.implementer(), 0u);
EXPECT_GT(cpu.part_number(), 0u);
}
#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_LINUX) ||
// defined(OS_ANDROID) || defined(OS_CHROMEOS))
......@@ -7,8 +7,10 @@
#include <stdio.h>
#include <sys/utsname.h>
#include "base/cpu.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
namespace {
......@@ -28,8 +30,26 @@ void ReportKernelVersion() {
}
}
// Per the comment in base/cpu.cc's ParseProcCpu(), unfortunately there is not
// a universally reliable way to examine the CPU part information for all
// cores, so the sampling effect of data collection via UMA will have to
// suffice.
void ReportArmCpu() {
#if defined(ARCH_CPU_ARM_FAMILY)
base::CPU cpu;
// Compose the implementer (8 bits) and the part number (12 bits) into a
// single 20-bit number that can be recorded via UMA.
uint32_t composed = cpu.implementer();
composed <<= 12;
composed |= cpu.part_number();
base::UmaHistogramSparse("Android.ArmCpuPart", composed);
#endif
}
} // namespace
void ReportSeccompSupport() {
ReportKernelVersion();
ReportArmCpu();
}
......@@ -3655,6 +3655,27 @@ Unknown properties are collapsed to zero. -->
<int value="5" label="INSUFFICIENT_RESOURCES"/>
</enum>
<enum name="ArmCpuPart">
<summary>
Defines a subset of ARM CPU (implementer, part-number) pairs composed as a
20-bit number. This is a non-exhaustive enumeration.
</summary>
<int value="269320" label="ARM Cortex-A8"/>
<int value="269321" label="ARM Cortex-A9"/>
<int value="269326" label="ARM Cortex-A17"/>
<int value="269327" label="ARM Cortex-A15"/>
<int value="269335" label="ARM Cortex-R7"/>
<int value="269336" label="ARM Cortex-R8"/>
<int value="269575" label="ARM Cortex-A57"/>
<int value="269576" label="ARM Cortex-A72"/>
<int value="269577" label="ARM Cortex-A73"/>
<int value="269578" label="ARM Cortex-A75"/>
<int value="269579" label="ARM Cortex-A76"/>
<int value="269580" label="ARM Neoverse N1"/>
<int value="269581" label="ARM Cortex-A77"/>
<int value="269633" label="ARM Cortex-A78"/>
</enum>
<enum name="AshAutoNightLightNotificationState">
<summary>
Defines the possible states of the Auto Night Light notification as a result
......@@ -30,6 +30,21 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
</summary>
</histogram>
<histogram name="Android.ArmCpuPart" enum="ArmCpuPart"
expires_after="2022-01-01">
<owner>rsesek@chromium.org</owner>
<owner>clank-security@google.com</owner>
<summary>
Reports information about the ARM CPU of a device. This composes the 8-bit
ARM implementer identifier and the 12-bit part number into a single 20-bit
CPU identifier, ranging from [0x00000,0xFFFFF]. Depending on the CPU
package, processor configuration, and/or kernel version, this may only
report information about the processor on which the metric was collected.
This can happen on heterogeneous-processor SoCs. The metric is logged once
every startup.
</summary>
</histogram>
<histogram name="Android.AutofillAssistant.DropOutReason"
enum="AutofillAssistantDropOutReason" expires_after="2021-06-20">
<owner>mcarlen@chromium.org</owner>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment