diff --git a/llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test b/llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test @@ -0,0 +1,10 @@ +# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -opcode-name=LEA64r --use-dummy-perf-counters | FileCheck %s + +CHECK: --- +CHECK-NEXT: mode: latency +CHECK-NEXT: key: +CHECK-NEXT: instructions: +CHECK-NEXT: LEA64r +# 'measurements' field should not contain misleading values +CHECK: measurements: [] +CHECK: assembled_snippet: {{[A-Z0-9]+}}{{$}} diff --git a/llvm/tools/llvm-exegesis/lib/LlvmState.h b/llvm/tools/llvm-exegesis/lib/LlvmState.h --- a/llvm/tools/llvm-exegesis/lib/LlvmState.h +++ b/llvm/tools/llvm-exegesis/lib/LlvmState.h @@ -41,9 +41,12 @@ // Factory function. // If `Triple` is empty, uses the host triple. // If `CpuName` is empty, uses the host CPU. - // `Features` is intended for tests. + // If `UseDummyPerfCounters` is set, does not query the kernel + // for event counts. + // `UseDummyPerfCounters` and `Features` are intended for tests. static Expected Create(std::string TripleName, std::string CpuName, - StringRef Features = ""); + StringRef Features = "", + bool UseDummyPerfCounters = false); const TargetMachine &getTargetMachine() const { return *TheTargetMachine; } std::unique_ptr createTargetMachine() const; @@ -86,7 +89,7 @@ createRegNameToRegNoMapping() const; LLVMState(std::unique_ptr TM, const ExegesisTarget *ET, - StringRef CpuName); + const PfmCountersInfo *PCI); const ExegesisTarget *TheExegesisTarget; std::unique_ptr TheTargetMachine; diff --git a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp --- a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp +++ b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp @@ -24,7 +24,8 @@ Expected LLVMState::Create(std::string TripleName, std::string CpuName, - const StringRef Features) { + const StringRef Features, + bool UseDummyPerfCounters) { if (TripleName.empty()) TripleName = Triple::normalize(sys::getDefaultTargetTriple()); @@ -73,16 +74,17 @@ "no Exegesis target for triple " + TripleName, llvm::inconvertibleErrorCode()); } - return LLVMState(std::move(TM), ET, CpuName); + const PfmCountersInfo &PCI = UseDummyPerfCounters + ? ET->getDummyPfmCounters() + : ET->getPfmCounters(CpuName); + return LLVMState(std::move(TM), ET, &PCI); } LLVMState::LLVMState(std::unique_ptr TM, - const ExegesisTarget *ET, const StringRef CpuName) - : TheExegesisTarget(ET), TheTargetMachine(std::move(TM)), + const ExegesisTarget *ET, const PfmCountersInfo *PCI) + : TheExegesisTarget(ET), TheTargetMachine(std::move(TM)), PfmCounters(PCI), OpcodeNameToOpcodeIdxMapping(createOpcodeNameToOpcodeIdxMapping()), RegNameToRegNoMapping(createRegNameToRegNoMapping()) { - PfmCounters = &TheExegesisTarget->getPfmCounters(CpuName); - BitVector ReservedRegs = getFunctionReservedRegs(getTargetMachine()); for (const unsigned Reg : TheExegesisTarget->getUnavailableRegisters()) ReservedRegs.set(Reg); diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.h b/llvm/tools/llvm-exegesis/lib/PerfHelper.h --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.h +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.h @@ -37,6 +37,9 @@ // NOTE: pfm_initialize() must be called before creating PerfEvent objects. class PerfEvent { public: + // Dummy event that does not require access to counters (for tests). + static const char *const DummyEventString; + // http://perfmon2.sourceforge.net/manv4/libpfm.html // Events are expressed as strings. e.g. "INSTRUCTION_RETIRED" explicit PerfEvent(StringRef PfmEventString); @@ -63,6 +66,9 @@ std::string EventString; std::string FullQualifiedEventString; perf_event_attr *Attr; + +private: + void initRealEvent(StringRef PfmEventString); }; // Uses a valid PerfEvent to configure the Kernel so we can measure the @@ -102,6 +108,10 @@ #ifdef HAVE_LIBPFM int FileDescriptor = -1; #endif + bool IsDummyEvent; + +private: + void initRealEvent(const PerfEvent &E); }; } // namespace pfm diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp @@ -44,6 +44,13 @@ #endif } +// Performance counters may be unavailable for a number of reasons (such as +// kernel.perf_event_paranoid restriction or CPU being unknown to libpfm). +// +// Dummy event can be specified to skip interaction with real performance +// counters while still passing control to the generated code snippet. +const char *const PerfEvent::DummyEventString = "not-really-an-event"; + PerfEvent::~PerfEvent() { #ifdef HAVE_LIBPFM delete Attr; @@ -60,6 +67,13 @@ PerfEvent::PerfEvent(StringRef PfmEventString) : EventString(PfmEventString.str()), Attr(nullptr) { + if (PfmEventString != DummyEventString) + initRealEvent(PfmEventString); + else + FullQualifiedEventString = PfmEventString; +} + +void PerfEvent::initRealEvent(StringRef PfmEventString) { #ifdef HAVE_LIBPFM char *Fstr = nullptr; pfm_perf_encode_arg_t Arg = {}; @@ -93,9 +107,15 @@ return FullQualifiedEventString; } -#ifdef HAVE_LIBPFM Counter::Counter(PerfEvent &&E) : Event(std::move(E)){ assert(Event.valid()); + IsDummyEvent = Event.name() == PerfEvent::DummyEventString; + if (!IsDummyEvent) + initRealEvent(E); +} + +#ifdef HAVE_LIBPFM +void Counter::initRealEvent(const PerfEvent &E) { const pid_t Pid = 0; // measure current process/thread. const int Cpu = -1; // measure any processor. const int GroupFd = -1; // no grouping of counters. @@ -106,16 +126,28 @@ errs() << "Unable to open event. ERRNO: " << strerror(errno) << ". Make sure your kernel allows user " "space perf monitoring.\nYou may want to try:\n$ sudo sh " - "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'\n"; + "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'.\n" + << "If you are debugging and just want to execute the snippet " + "without actually reading performance counters, " + "pass --use-dummy-perf-counters command line option.\n"; } assert(FileDescriptor != -1 && "Unable to open event"); } -Counter::~Counter() { close(FileDescriptor); } +Counter::~Counter() { + if (!IsDummyEvent) + close(FileDescriptor); +} -void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); } +void Counter::start() { + if (!IsDummyEvent) + ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); +} -void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); } +void Counter::stop() { + if (!IsDummyEvent) + ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); +} int64_t Counter::read() const { auto ValueOrError = readOrError(); @@ -131,10 +163,15 @@ llvm::Expected> Counter::readOrError(StringRef /*unused*/) const { int64_t Count = 0; - ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count)); - if (ReadSize != sizeof(Count)) - return llvm::make_error("Failed to read event counter", - llvm::errc::io_error); + if (!IsDummyEvent) { + ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count)); + if (ReadSize != sizeof(Count)) + return llvm::make_error("Failed to read event counter", + llvm::errc::io_error); + } else { + Count = 42; + } + llvm::SmallVector Result; Result.push_back(Count); return Result; @@ -143,7 +180,7 @@ int Counter::numValues() const { return 1; } #else -Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {} +void Counter::initRealEvent(const PerfEvent &) {} Counter::~Counter() = default; diff --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h --- a/llvm/tools/llvm-exegesis/lib/Target.h +++ b/llvm/tools/llvm-exegesis/lib/Target.h @@ -59,6 +59,7 @@ unsigned NumIssueCounters; static const PfmCountersInfo Default; + static const PfmCountersInfo Dummy; }; struct CpuAndPfmCounters { @@ -178,6 +179,10 @@ // counters are defined for this CPU). const PfmCountersInfo &getPfmCounters(StringRef CpuName) const; + // Returns dummy Pfm counters which can be used to execute generated snippet + // without access to performance counters. + const PfmCountersInfo &getDummyPfmCounters() const; + // Saves the CPU state that needs to be preserved when running a benchmark, // and returns and RAII object that restores the state on destruction. // By default no state is preserved. diff --git a/llvm/tools/llvm-exegesis/lib/Target.cpp b/llvm/tools/llvm-exegesis/lib/Target.cpp --- a/llvm/tools/llvm-exegesis/lib/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/Target.cpp @@ -9,6 +9,7 @@ #include "LatencyBenchmarkRunner.h" #include "ParallelSnippetGenerator.h" +#include "PerfHelper.h" #include "SerialSnippetGenerator.h" #include "UopsBenchmarkRunner.h" #include "llvm/ADT/Twine.h" @@ -92,8 +93,9 @@ .concat(ModeName) .concat( "' mode, sched model does not define a cycle counter. You " - "can pass --skip-measurements to skip the actual " - "benchmarking.")); + "can pass --benchmark-phase=... to skip the actual " + "benchmarking or --use-dummy-perf-counters to not query " + "the kernel for real event counts.")); } return createLatencyBenchmarkRunner(State, Mode, BenchmarkPhaseSelector, ResultAggMode); @@ -102,8 +104,9 @@ !PfmCounters.UopsCounter && !PfmCounters.IssueCounters) return make_error( "can't run 'uops' mode, sched model does not define uops or issue " - "counters. You can pass --skip-measurements to skip the actual " - "benchmarking."); + "counters. You can pass --benchmark-phase=... to skip the actual " + "benchmarking or --use-dummy-perf-counters to not query the kernel " + "for real event counts."); return createUopsBenchmarkRunner(State, BenchmarkPhaseSelector, ResultAggMode); } @@ -138,6 +141,9 @@ "We shouldn't have dynamic initialization here"); const PfmCountersInfo PfmCountersInfo::Default = {nullptr, nullptr, nullptr, 0u}; +const PfmCountersInfo PfmCountersInfo::Dummy = { + pfm::PerfEvent::DummyEventString, pfm::PerfEvent::DummyEventString, nullptr, + 0u}; const PfmCountersInfo &ExegesisTarget::getPfmCounters(StringRef CpuName) const { assert(llvm::is_sorted( @@ -161,6 +167,10 @@ return *Found->PCI; } +const PfmCountersInfo &ExegesisTarget::getDummyPfmCounters() const { + return PfmCountersInfo::Dummy; +} + ExegesisTarget::SavedState::~SavedState() {} // anchor. namespace { diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -140,6 +140,12 @@ "(default)")), cl::init(exegesis::BenchmarkPhaseSelectorE::Measure)); +static cl::opt + UseDummyPerfCounters("use-dummy-perf-counters", + cl::desc("Do not read real performance counters, use " + "dummy values (for testing)"), + cl::cat(BenchmarkOptions), cl::init(false)); + static cl::opt NumRepetitions("num-repetitions", cl::desc("number of time to repeat the asm snippet"), @@ -412,6 +418,11 @@ } } + // With dummy counters, measurements are rather meaningless, + // so drop them altogether. + if (UseDummyPerfCounters) + Result.Measurements.clear(); + ExitOnFileError(BenchmarkFile, Result.writeYamlTo(State, Ostr)); } } @@ -432,7 +443,8 @@ InitializeAllAsmParsers(); InitializeAllExegesisTargets(); - const LLVMState State = ExitOnErr(LLVMState::Create(TripleName, MCPU)); + const LLVMState State = + ExitOnErr(LLVMState::Create(TripleName, MCPU, "", UseDummyPerfCounters)); // Preliminary check to ensure features needed for requested // benchmark mode are present on target CPU and/or OS.