[llvm] b02e2ed - [llvm-exegesis] Make possible to execute snippets without perf counters

Pavel Kosov via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 6 03:09:47 PDT 2023


Author: Pavel Kosov
Date: 2023-04-06T13:08:48+03:00
New Revision: b02e2ed7ac71137430fc9fc8b32bed344f5e7fef

URL: https://github.com/llvm/llvm-project/commit/b02e2ed7ac71137430fc9fc8b32bed344f5e7fef
DIFF: https://github.com/llvm/llvm-project/commit/b02e2ed7ac71137430fc9fc8b32bed344f5e7fef.diff

LOG: [llvm-exegesis] Make possible to execute snippets without perf counters

Performance counters may be unavailable due to various reasons (such as
access restriction via sysctl properties or the CPU model being unknown
to libpfm). On the other hand, for debugging llvm-exegesis itself it is
still useful to be able to run generated code snippets to ensure that
the snippet does not crash at run time.

The --use-dummy-perf-counters command line option makes llvm-exegesis
behave just as usual except for using fake event counts instead of asking
the kernel for actual values.

~~

Huawei RRI, OS Lab

Reviewed By: courbet

Differential Revision: https://reviews.llvm.org/D146301

Added: 
    llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test

Modified: 
    llvm/tools/llvm-exegesis/lib/LlvmState.cpp
    llvm/tools/llvm-exegesis/lib/LlvmState.h
    llvm/tools/llvm-exegesis/lib/PerfHelper.cpp
    llvm/tools/llvm-exegesis/lib/PerfHelper.h
    llvm/tools/llvm-exegesis/lib/Target.cpp
    llvm/tools/llvm-exegesis/lib/Target.h
    llvm/tools/llvm-exegesis/llvm-exegesis.cpp

Removed: 
    


################################################################################
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
index 0000000000000..471b9f89578a4
--- /dev/null
+++ b/llvm/test/tools/llvm-exegesis/X86/latency/dummy-counters.test
@@ -0,0 +1,12 @@
+# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -mode=latency -opcode-name=LEA64r --use-dummy-perf-counters | FileCheck %s
+# TODO Support non-Linux systems
+# REQUIRES: exegesis-can-execute-x86_64, system-linux
+
+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.cpp b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp
index 8be4876b5be31..6c3d8ff963034 100644
--- a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp
+++ b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp
@@ -24,7 +24,8 @@ namespace exegesis {
 
 Expected<LLVMState> 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 @@ Expected<LLVMState> LLVMState::Create(std::string TripleName,
         "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<const TargetMachine> 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/LlvmState.h b/llvm/tools/llvm-exegesis/lib/LlvmState.h
index 6039bdb658de4..137ba1b5a54fa 100644
--- a/llvm/tools/llvm-exegesis/lib/LlvmState.h
+++ b/llvm/tools/llvm-exegesis/lib/LlvmState.h
@@ -41,9 +41,12 @@ class LLVMState {
   // 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<LLVMState> Create(std::string TripleName, std::string CpuName,
-                                    StringRef Features = "");
+                                    StringRef Features = "",
+                                    bool UseDummyPerfCounters = false);
 
   const TargetMachine &getTargetMachine() const { return *TheTargetMachine; }
   std::unique_ptr<LLVMTargetMachine> createTargetMachine() const;
@@ -86,7 +89,7 @@ class LLVMState {
   createRegNameToRegNoMapping() const;
 
   LLVMState(std::unique_ptr<const TargetMachine> TM, const ExegesisTarget *ET,
-            StringRef CpuName);
+            const PfmCountersInfo *PCI);
 
   const ExegesisTarget *TheExegesisTarget;
   std::unique_ptr<const TargetMachine> TheTargetMachine;

diff  --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp
index 8a0b399dc982b..4bf748523cf27 100644
--- a/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp
+++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp
@@ -44,6 +44,13 @@ void pfmTerminate() {
 #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(PerfEvent &&Other)
 
 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 @@ StringRef PerfEvent::getPfmEventString() const {
   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 @@ Counter::Counter(PerfEvent &&E) : Event(std::move(E)){
     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 @@ int64_t Counter::read() const {
 llvm::Expected<llvm::SmallVector<int64_t, 4>>
 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<llvm::StringError>("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<llvm::StringError>("Failed to read event counter",
+                                                 llvm::errc::io_error);
+  } else {
+    Count = 42;
+  }
+
   llvm::SmallVector<int64_t, 4> Result;
   Result.push_back(Count);
   return Result;
@@ -143,7 +180,7 @@ Counter::readOrError(StringRef /*unused*/) const {
 int Counter::numValues() const { return 1; }
 #else
 
-Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {}
+void Counter::initRealEvent(const PerfEvent &) {}
 
 Counter::~Counter() = default;
 
@@ -155,6 +192,11 @@ int64_t Counter::read() const { return 42; }
 
 llvm::Expected<llvm::SmallVector<int64_t, 4>>
 Counter::readOrError(StringRef /*unused*/) const {
+  if (IsDummyEvent) {
+    llvm::SmallVector<int64_t, 4> Result;
+    Result.push_back(42);
+    return Result;
+  }
   return llvm::make_error<llvm::StringError>("Not implemented",
                                              llvm::errc::io_error);
 }

diff  --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.h b/llvm/tools/llvm-exegesis/lib/PerfHelper.h
index 506917f16cdcb..f3e2b1b6b9522 100644
--- a/llvm/tools/llvm-exegesis/lib/PerfHelper.h
+++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.h
@@ -37,6 +37,9 @@ void pfmTerminate();
 // 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 @@ class PerfEvent {
   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 @@ class Counter {
 #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/Target.cpp b/llvm/tools/llvm-exegesis/lib/Target.cpp
index 4717f9f7fa8d3..268d99e96d62e 100644
--- 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 @@ ExegesisTarget::createBenchmarkRunner(
               .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 @@ ExegesisTarget::createBenchmarkRunner(
         !PfmCounters.UopsCounter && !PfmCounters.IssueCounters)
       return make_error<Failure>(
           "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 @@ static_assert(std::is_trivial_v<PfmCountersInfo>,
               "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 @@ const PfmCountersInfo &ExegesisTarget::getPfmCounters(StringRef CpuName) const {
   return *Found->PCI;
 }
 
+const PfmCountersInfo &ExegesisTarget::getDummyPfmCounters() const {
+  return PfmCountersInfo::Dummy;
+}
+
 ExegesisTarget::SavedState::~SavedState() {} // anchor.
 
 namespace {

diff  --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h
index 9f21122a03b03..3cc36457134fd 100644
--- a/llvm/tools/llvm-exegesis/lib/Target.h
+++ b/llvm/tools/llvm-exegesis/lib/Target.h
@@ -59,6 +59,7 @@ struct PfmCountersInfo {
   unsigned NumIssueCounters;
 
   static const PfmCountersInfo Default;
+  static const PfmCountersInfo Dummy;
 };
 
 struct CpuAndPfmCounters {
@@ -178,6 +179,10 @@ class ExegesisTarget {
   // 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/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
index 06ae924bb9b26..ce028eb008929 100644
--- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
+++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -140,6 +140,12 @@ static cl::opt<exegesis::BenchmarkPhaseSelectorE> BenchmarkPhaseSelector(
             "(default)")),
     cl::init(exegesis::BenchmarkPhaseSelectorE::Measure));
 
+static cl::opt<bool>
+    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<unsigned>
     NumRepetitions("num-repetitions",
                    cl::desc("number of time to repeat the asm snippet"),
@@ -412,16 +418,24 @@ static void runBenchmarkConfigurations(
       }
     }
 
+    // With dummy counters, measurements are rather meaningless,
+    // so drop them altogether.
+    if (UseDummyPerfCounters)
+      Result.Measurements.clear();
+
     ExitOnFileError(BenchmarkFile, Result.writeYamlTo(State, Ostr));
   }
 }
 
 void benchmarkMain() {
-  if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure) {
+  if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure &&
+      !UseDummyPerfCounters) {
 #ifndef HAVE_LIBPFM
     ExitWithError(
-        "benchmarking unavailable, LLVM was built without libpfm. You can pass "
-        "--skip-measurements to skip the actual benchmarking.");
+        "benchmarking unavailable, LLVM was built without libpfm. You can "
+        "pass --benchmark-phase=... to skip the actual benchmarking or "
+        "--use-dummy-perf-counters to not query the kernel for real event "
+        "counts.");
 #else
     if (exegesis::pfm::pfmInitialize())
       ExitWithError("cannot initialize libpfm");
@@ -432,7 +446,8 @@ void benchmarkMain() {
   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.


        


More information about the llvm-commits mailing list