blob: 975f27469eda49c100facafca49946d19c3109c1 [file] [edit]
/*
* Copyright (C) 2026 The Android Open Source Project
*
* 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.
*/
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "perfetto/ext/base/cpu_info.h"
#include "perfetto/ext/base/cpu_info_features_allowlist.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/utils.h"
namespace perfetto {
namespace base {
namespace {
// Key for default processor string in /proc/cpuinfo as seen on arm. Note the
// uppercase P.
const char kDefaultProcessor[] = "Processor";
// Key for processor entry in /proc/cpuinfo. Used to determine whether a group
// of lines describes a CPU.
const char kProcessor[] = "processor";
// Key for CPU implementer in /proc/cpuinfo. Arm only.
const char kImplementer[] = "CPU implementer";
// Key for CPU architecture in /proc/cpuinfo. Arm only.
const char kArchitecture[] = "CPU architecture";
// Key for CPU variant in /proc/cpuinfo. Arm only.
const char kVariant[] = "CPU variant";
// Key for CPU part in /proc/cpuinfo. Arm only.
const char kPart[] = "CPU part";
// Key for CPU revision in /proc/cpuinfo. Arm only.
const char kRevision[] = "CPU revision";
// Key for feature flags in /proc/cpuinfo. Arm calls them Features,
// Intel calls them Flags.
const char kFeatures[] = "Features";
const char kFlags[] = "Flags";
std::string ReadFile(const std::string& path) {
std::string contents;
if (!base::ReadFile(path, &contents))
return "";
return contents;
}
} // namespace
std::vector<CpuInfo> ParseCpuInfo(std::string proc_cpu_info) {
std::vector<CpuInfo> cpus;
std::string processor = "unknown";
std::optional<uint32_t> cpu_index;
std::optional<uint32_t> implementer;
std::optional<uint32_t> architecture;
std::optional<uint32_t> variant;
std::optional<uint32_t> part;
std::optional<uint32_t> revision;
uint64_t features = 0;
uint32_t next_cpu_index = 0;
auto flush_cpu = [&] {
if (cpu_index.has_value()) {
CpuInfo cpu{};
cpu.processor = processor;
cpu.cpu_index = *cpu_index;
cpu.implementer = implementer;
cpu.architecture = architecture;
cpu.variant = variant;
cpu.part = part;
cpu.revision = revision;
cpu.features = features;
#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
if (cpu.implementer && cpu.part) {
std::string cpuid =
base::Uint64ToHexStringNoPrefix(cpu.implementer.value()) +
base::Uint64ToHexStringNoPrefix(cpu.part.value());
if (cpu.variant) {
cpuid += base::Uint64ToHexStringNoPrefix(cpu.variant.value());
if (cpu.revision) {
cpuid += base::Uint64ToHexStringNoPrefix(cpu.revision.value());
}
}
base::StringCopy(cpu.arm_cpuid, cpuid.c_str(), sizeof(cpu.arm_cpuid));
}
#endif // PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
cpus.emplace_back(std::move(cpu));
next_cpu_index++;
}
cpu_index = std::nullopt;
implementer = std::nullopt;
architecture = std::nullopt;
variant = std::nullopt;
part = std::nullopt;
revision = std::nullopt;
features = 0;
};
for (base::StringSplitter lines(
std::move(proc_cpu_info), '\n',
base::StringSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS);
lines.Next();) {
std::string line(lines.cur_token(), lines.cur_token_size());
if (line.empty() && cpu_index.has_value()) {
flush_cpu();
continue;
}
auto splits = base::SplitString(line, ":");
if (splits.size() != 2)
continue;
std::string key =
base::StripSuffix(base::StripChars(splits[0], "\t", ' '), " ");
std::string value = base::StripPrefix(splits[1], " ");
if (key == kDefaultProcessor) {
processor = value;
} else if (key == kProcessor) {
cpu_index = base::StringToUInt32(value);
} else if (key == kImplementer) {
implementer = base::CStringToUInt32(value.data(), 16);
} else if (key == kArchitecture) {
architecture = base::CStringToUInt32(value.data(), 10);
} else if (key == kVariant) {
variant = base::CStringToUInt32(value.data(), 16);
} else if (key == kPart) {
part = base::CStringToUInt32(value.data(), 16);
} else if (key == kRevision) {
revision = base::CStringToUInt32(value.data(), 10);
} else if (key == kFeatures || key == kFlags) {
for (base::StringSplitter ss(value.data(), ' '); ss.Next();) {
for (size_t i = 0; i < base::ArraySize(kCpuInfoFeatures); ++i) {
if (strcmp(ss.cur_token(), kCpuInfoFeatures[i]) == 0) {
static_assert(base::ArraySize(kCpuInfoFeatures) < 64);
features |= 1ull << i;
}
}
}
}
}
flush_cpu();
return cpus;
}
std::vector<CpuInfo> ReadCpuInfo() {
return ParseCpuInfo(ReadFile("/proc/cpuinfo"));
}
} // namespace base
} // namespace perfetto