Skip to content

Commit 02a8b7b

Browse files
committed
Get CPU microarchs for x86 hybrid cores
1 parent f2d19ca commit 02a8b7b

6 files changed

Lines changed: 241 additions & 110 deletions

File tree

src/CPUs.cc

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44

55
#include <sched.h>
66

7-
#include <fstream>
8-
#include <filesystem>
9-
#include <sstream>
10-
117
#include "log.h"
128

139
using namespace std;
@@ -51,64 +47,6 @@ void CPUs::restore_initial_affinity(pid_t tid) const {
5147
}
5248
}
5349

54-
static vector<CPUs::Group> get_cpu_arch_groups_from_sysfs() {
55-
vector<CPUs::Group> result;
56-
filesystem::path dir_path = "/sys/devices";
57-
if (!filesystem::is_directory(dir_path)) {
58-
return result;
59-
}
60-
for (const auto& entry : filesystem::directory_iterator(dir_path)) {
61-
if (entry.path().filename().string().find("cpu_") != 0) {
62-
continue;
63-
}
64-
ifstream file(entry.path() / "cpus");
65-
if (!file.good()) {
66-
LOG(warn) << "File " << entry.path().string() << "/cpus not found";
67-
continue;
68-
}
69-
ostringstream sstr;
70-
sstr << file.rdbuf();
71-
string s = sstr.str();
72-
size_t dash = s.find('-');
73-
if (dash == string::npos) {
74-
size_t end;
75-
int cpu_index = stoi(s, &end);
76-
if (end != s.size()) {
77-
LOG(warn) << "Bad CPU index";
78-
continue;
79-
}
80-
result.push_back(CPUs::Group{cpu_index, cpu_index + 1});
81-
} else {
82-
size_t end;
83-
int cpu_index = stoi(s.substr(0, dash), &end);
84-
if (end != dash) {
85-
LOG(warn) << "Bad CPU index";
86-
continue;
87-
}
88-
int cpu_index_end = stoi(s.substr(dash + 1), &end);
89-
if (end != dash) {
90-
LOG(warn) << "Bad end CPU index";
91-
continue;
92-
}
93-
result.push_back(CPUs::Group{cpu_index, cpu_index_end + 1});
94-
}
95-
}
96-
return result;
97-
}
98-
99-
std::vector<CPUs::Group> CPUs::cpu_arch_groups() const {
100-
std::vector<CPUs::Group> result = get_cpu_arch_groups_from_sysfs();
101-
if (result.empty()) {
102-
// Assume they're all the same arch.
103-
int configured = sysconf(_SC_NPROCESSORS_CONF);
104-
if (configured < 1) {
105-
FATAL() << "sysconf failed";
106-
}
107-
result.push_back(Group{0, configured});
108-
}
109-
return result;
110-
}
111-
11250
CPUs::CPUs() {
11351
// sched_getaffinity intersects the task's `cpu_mask`
11452
// (/proc/.../status Cpus_allowed_list) with `cpu_active_mask`

src/CPUs.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,13 @@ class CPUs {
1313
public:
1414
static const CPUs& get();
1515

16+
// Returns the CPU indices in the initial affinity mask, in increasing order.
1617
std::vector<int> initial_affinity() const;
1718
static bool set_affinity_to_cpu(int cpu);
1819
// Restore the initial affinity mask to the given tid.
1920
// If unspecified, defaults to this thread.
2021
void restore_initial_affinity(pid_t tid = 0) const;
2122

22-
struct Group {
23-
int start_cpu;
24-
int end_cpu;
25-
};
26-
// Returns a list of CPU groups that all have the same microarchitecture.
27-
// This list covers all available CPUs.
28-
std::vector<Group> cpu_arch_groups() const;
29-
3023
private:
3124
CPUs();
3225

src/PerfCounters.cc

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@
2323
#endif
2424

2525
#include <algorithm>
26+
#include <filesystem>
2627
#include <fstream>
28+
#include <sstream>
2729
#include <limits>
2830
#include <regex>
2931
#include <string>
3032
#include <unordered_set>
3133

34+
#include "CPUs.h"
3235
#include "Flags.h"
3336
#include "Session.h"
3437
#include "Task.h"
@@ -119,7 +122,10 @@ enum CpuMicroarch {
119122
IntelSilvermont,
120123
IntelGoldmont,
121124
IntelTremont,
122-
LastIntel = IntelTremont,
125+
IntelGracemont,
126+
IntelCrestmont,
127+
IntelSkymont,
128+
LastIntel = IntelSkymont,
123129
FirstAMD,
124130
AMDF15 = FirstAMD,
125131
AMDZen,
@@ -205,7 +211,12 @@ struct PmuConfig {
205211
// See Intel 64 and IA32 Architectures Performance Monitoring Events.
206212
// See check_events from libpfm4.
207213
// Match order of CpuMicroarch.
214+
// For the Intel Core lineage, we use the name of the product rather than
215+
// the official name of the microarchitecture. For hybrid products the
216+
// data in this table is for the P-cores of that product. For E-cores
217+
// we use their actual Atom ("mont") microarchitecture.
208218
static const PmuConfig pmu_configs[] = {
219+
{ UnknownCpu, "Unknown", 0, 0, 0, 0, 0 },
209220
{ IntelMerom, "Intel Merom", 0, 0, 0, 100, 0 },
210221
{ IntelPenryn, "Intel Penryn", 0, 0, 0, 100, 0 },
211222
{ IntelNehalem, "Intel Nehalem", 0x5101c4, 0, 0, 100, PMU_TICKS_RCB },
@@ -230,6 +241,9 @@ static const PmuConfig pmu_configs[] = {
230241
{ IntelSilvermont, "Intel Silvermont", 0x517ec4, 0, 0, 100, PMU_TICKS_RCB },
231242
{ IntelGoldmont, "Intel Goldmont", 0x517ec4, 0, 0, 100, PMU_TICKS_RCB },
232243
{ IntelTremont, "Intel Tremont", 0x517ec4, 0, 0, 100, PMU_TICKS_RCB },
244+
{ IntelGracemont, "Intel Gracemont", 0x517ec4, 0, 0, 100, PMU_TICKS_RCB },
245+
{ IntelCrestmont, "Intel Crestmont", 0x517ec4, 0, 0, 100, PMU_TICKS_RCB },
246+
{ IntelSkymont, "Intel Skymont", 0x517ec4, 0, 0, 100, PMU_TICKS_RCB },
233247
{ AMDF15, "AMD Family 15h", 0xc4, 0xc6, 0, 250, PMU_TICKS_TAKEN_BRANCHES },
234248
// 0xd1 == RETIRED_CONDITIONAL_BRANCH_INSTRUCTIONS - Number of retired conditional branch instructions
235249
// 0x2c == INTERRUPT_TAKEN - Counts the number of interrupts taken
@@ -460,6 +474,14 @@ static void do_branches() {
460474
accumulator_sink = accumulator;
461475
}
462476

477+
struct CPUInfo {
478+
// The microarch of the CPU core.
479+
CpuMicroarch microarch;
480+
// Either PERF_TYPE_RAW or some alternative type that should be used
481+
// instead of PERF_TYPE_RAW for raw events on this core.
482+
int raw_perf_event_type;
483+
};
484+
463485
// Architecture specific detection code
464486
#if defined(__i386__) || defined(__x86_64__)
465487
#include "PerfCounters_x86.h"
@@ -469,7 +491,7 @@ static void do_branches() {
469491
#error Must define microarchitecture detection code for this architecture
470492
#endif
471493

472-
static void check_working_counters(perf_event_attrs &perf_attr) {
494+
static void check_working_counters(perf_event_attrs &perf_attr, int pmu_index) {
473495
struct perf_event_attr attr = perf_attr.ticks;
474496
attr.sample_period = 0;
475497
struct perf_event_attr attr2 = perf_attr.cycles;
@@ -494,8 +516,9 @@ static void check_working_counters(perf_event_attrs &perf_attr) {
494516
<< "\nGot " << events << " branch events, expected at least "
495517
<< NUM_BRANCHES
496518
<< ".\n"
497-
"\nThe hardware performance counter seems to not be working. Check\n"
498-
"that hardware performance counters are working by running\n"
519+
"\nThe hardware performance counter seems to not be working on cpu "
520+
<< pmu_index << "\n"
521+
"Check that hardware performance counters are working by running\n"
499522
" "
500523
<< perf_cmdline
501524
<< "\n"
@@ -515,52 +538,59 @@ static void check_working_counters(perf_event_attrs &perf_attr) {
515538
}
516539

517540
// Returns the ticks minimum period.
518-
static uint32_t check_for_bugs(perf_event_attrs &perf_attr) {
541+
// Must be run on the CPU that we're checking for bugs.
542+
static uint32_t check_for_bugs(perf_event_attrs &perf_attr, int pmu_index) {
519543
DEBUG_ASSERT(!running_under_rr());
520544
cpu_improperly_configured = false;
521545

522546
uint32_t min_period = check_for_ioc_period_bug(perf_attr);
523-
check_working_counters(perf_attr);
547+
check_working_counters(perf_attr, pmu_index);
524548
check_for_arch_bugs(perf_attr);
525549
return min_period;
526550
}
527551

528-
static std::vector<CpuMicroarch> get_cpu_microarchs() {
552+
static vector<CPUInfo> get_cpus_info() {
529553
string forced_uarch = lowercase(Flags::get().forced_uarch);
530554
if (!forced_uarch.empty()) {
531555
for (size_t i = 0; i < array_length(pmu_configs); ++i) {
532556
const PmuConfig& pmu = pmu_configs[i];
533557
string name = lowercase(pmu.name);
534558
if (name.npos != name.find(forced_uarch)) {
535559
LOG(info) << "Using forced uarch " << pmu.name;
536-
return { pmu.uarch };
560+
return {
561+
{ pmu.uarch, PERF_TYPE_RAW }
562+
};
537563
}
538564
}
539565
CLEAN_FATAL() << "Forced uarch " << Flags::get().forced_uarch
540566
<< " isn't known.";
541567
}
542-
return compute_cpu_microarchs();
568+
return compute_cpus_info();
543569
}
544570

545571
// Similar to rr::perf_attrs, if this contains more than one element,
546572
// it's indexed by the CPU index.
547573
static std::vector<PmuConfig> get_pmu_microarchs() {
548574
std::vector<PmuConfig> pmu_uarchs;
549-
auto uarchs = get_cpu_microarchs();
575+
auto cpus_info = get_cpus_info();
550576
bool found_working_pmu = false;
551-
for (auto uarch : uarchs) {
577+
for (const auto& info : cpus_info) {
552578
bool found = false;
553-
for (size_t i = 0; i < array_length(pmu_configs); ++i) {
554-
if (uarch == pmu_configs[i].uarch) {
579+
for (const auto& pmu_config : pmu_configs) {
580+
if (info.microarch == pmu_config.uarch) {
555581
found = true;
556-
if (pmu_configs[i].flags & (PMU_TICKS_RCB | PMU_TICKS_TAKEN_BRANCHES)) {
582+
if (pmu_config.flags & (PMU_TICKS_RCB | PMU_TICKS_TAKEN_BRANCHES)) {
557583
found_working_pmu |= true;
558584
}
559-
pmu_uarchs.push_back(pmu_configs[i]);
585+
pmu_uarchs.push_back(pmu_config);
586+
pmu_uarchs.back().event_type = info.raw_perf_event_type;
560587
break;
561588
}
562589
}
563-
DEBUG_ASSERT(found);
590+
if (!found) {
591+
FATAL() << "Can't find PMU config for " << info.microarch;
592+
}
593+
LOG(info) << "Found PMU config for " << info.microarch;
564594
}
565595
if (!found_working_pmu) {
566596
CLEAN_FATAL() << "No supported microarchitectures found.";
@@ -634,8 +664,7 @@ static void init_attributes() {
634664
}
635665
}
636666

637-
bool PerfCounters::support_cpu(int cpu)
638-
{
667+
bool PerfCounters::support_cpu(int cpu) {
639668
// We could probably make cpu=-1 mean whether all CPUs are supported
640669
// if there's a need for it...
641670
DEBUG_ASSERT(cpu >= 0);
@@ -665,7 +694,14 @@ static void check_pmu(int pmu_index) {
665694
return;
666695
}
667696

668-
perf_attr.ticks_min_period = check_for_bugs(perf_attr);
697+
if (perf_attrs.size() > 1 && !CPUs::set_affinity_to_cpu(pmu_index)) {
698+
FATAL() << "Couldn't set affinity to the right PMU";
699+
}
700+
perf_attr.ticks_min_period = check_for_bugs(perf_attr, pmu_index);
701+
if (perf_attrs.size() > 1) {
702+
CPUs::get().restore_initial_affinity();
703+
}
704+
669705
/*
670706
* For maintainability, and since it doesn't impact performance when not
671707
* needed, we always activate this. If it ever turns out to be a problem,
@@ -944,7 +980,7 @@ void PerfCounters::start(Task* t, Ticks ticks_period) {
944980
}
945981

946982
if (!perf_attr.only_one_counter && !running_under_rr()) {
947-
reset_arch_extras<NativeArch>();
983+
reset_arch_extras<NativeArch>(pmu_index);
948984
}
949985

950986
if (perf_attr.activate_useless_counter && !fd_useless_counter.is_open()) {

src/PerfCounters.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class PerfCounters {
199199
#endif
200200

201201
private:
202-
template <typename Arch> void reset_arch_extras();
202+
template <typename Arch> void reset_arch_extras(int pmu_index);
203203

204204
/**
205205
* Use a separate skid_size for recording since we seem to see more skid

src/PerfCounters_aarch64.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,9 @@ static inline void get_cpuinfo_procfs(std::vector<CPUID> &res)
256256
reset();
257257
}
258258

259-
static std::vector<CpuMicroarch> compute_cpu_microarchs() {
260-
std::vector<CPUID> cpuids;
259+
static vector<CPUInfo> compute_cpus_info() {
260+
vector<CPUInfo> result;
261+
vector<CPUID> cpuids;
261262
get_cpuinfo_sysfs(cpuids);
262263
if (cpuids.empty()) {
263264
LOG(warn) << "Unable to read CPU type from sysfs, trying procfs instead.";
@@ -280,13 +281,13 @@ static std::vector<CpuMicroarch> compute_cpu_microarchs() {
280281
}
281282
}
282283
if (single_uarch) {
283-
return { compute_cpu_microarch(cpuid0) };
284+
result.push_back({compute_cpu_microarch(cpuid0), PERF_TYPE_RAW});
285+
return result;
284286
}
285-
std::vector<CpuMicroarch> uarchs;
286287
for (auto &cpuid : cpuids) {
287-
uarchs.push_back(compute_cpu_microarch(cpuid));
288+
result.push_back({compute_cpu_microarch(cpuid), PERF_TYPE_RAW});
288289
}
289-
return uarchs;
290+
return result;
290291
}
291292

292293
static void arch_check_restricted_counter() {
@@ -405,7 +406,7 @@ static void post_init_pmu_uarchs(std::vector<PmuConfig> &pmu_uarchs)
405406
}
406407

407408
template <>
408-
void PerfCounters::reset_arch_extras<ARM64Arch>() {
409+
void PerfCounters::reset_arch_extras<ARM64Arch>(int pmu_index) {
409410
// LL/SC can't be recorded reliably. Start a counter to detect
410411
// any usage, such that we can give an intelligent error message.
411412
struct perf_event_attr attr = perf_attrs[pmu_index].llsc_fail;

0 commit comments

Comments
 (0)