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.
208218static 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 << " \n Got " << events << " branch events, expected at least "
495517 << NUM_BRANCHES
496518 << " .\n "
497- " \n The hardware performance counter seems to not be working. Check\n "
498- " that hardware performance counters are working by running\n "
519+ " \n The 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.
547573static 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 ()) {
0 commit comments