[sanitizer] Improve FreeBSD ASLR detection
[llvm-project.git] / llvm / tools / llvm-exegesis / lib / BenchmarkRunner.cpp
blob03e7ccc26f46360cf7f37010050b5145fb0d1c02
1 //===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include <array>
10 #include <memory>
11 #include <string>
13 #include "Assembler.h"
14 #include "BenchmarkRunner.h"
15 #include "Error.h"
16 #include "MCInstrDescView.h"
17 #include "PerfHelper.h"
18 #include "Target.h"
19 #include "llvm/ADT/ScopeExit.h"
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/ADT/Twine.h"
23 #include "llvm/Support/CrashRecoveryContext.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/MemoryBuffer.h"
27 #include "llvm/Support/Program.h"
29 namespace llvm {
30 namespace exegesis {
32 BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
33 InstructionBenchmark::ModeE Mode)
34 : State(State), Mode(Mode), Scratch(std::make_unique<ScratchSpace>()) {}
36 BenchmarkRunner::~BenchmarkRunner() = default;
38 namespace {
39 class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
40 public:
41 FunctionExecutorImpl(const LLVMState &State,
42 object::OwningBinary<object::ObjectFile> Obj,
43 BenchmarkRunner::ScratchSpace *Scratch)
44 : State(State), Function(State.createTargetMachine(), std::move(Obj)),
45 Scratch(Scratch) {}
47 private:
48 Expected<int64_t> runAndMeasure(const char *Counters) const override {
49 auto ResultOrError = runAndSample(Counters);
50 if (ResultOrError)
51 return ResultOrError.get()[0];
52 return ResultOrError.takeError();
55 static void
56 accumulateCounterValues(const llvm::SmallVector<int64_t, 4> &NewValues,
57 llvm::SmallVector<int64_t, 4> *Result) {
58 const size_t NumValues = std::max(NewValues.size(), Result->size());
59 if (NumValues > Result->size())
60 Result->resize(NumValues, 0);
61 for (size_t I = 0, End = NewValues.size(); I < End; ++I)
62 (*Result)[I] += NewValues[I];
65 Expected<llvm::SmallVector<int64_t, 4>>
66 runAndSample(const char *Counters) const override {
67 // We sum counts when there are several counters for a single ProcRes
68 // (e.g. P23 on SandyBridge).
69 llvm::SmallVector<int64_t, 4> CounterValues;
70 int Reserved = 0;
71 SmallVector<StringRef, 2> CounterNames;
72 StringRef(Counters).split(CounterNames, '+');
73 char *const ScratchPtr = Scratch->ptr();
74 const ExegesisTarget &ET = State.getExegesisTarget();
75 for (auto &CounterName : CounterNames) {
76 CounterName = CounterName.trim();
77 auto CounterOrError = ET.createCounter(CounterName, State);
79 if (!CounterOrError)
80 return CounterOrError.takeError();
82 pfm::Counter *Counter = CounterOrError.get().get();
83 if (Reserved == 0) {
84 Reserved = Counter->numValues();
85 CounterValues.reserve(Reserved);
86 } else if (Reserved != Counter->numValues())
87 // It'd be wrong to accumulate vectors of different sizes.
88 return make_error<Failure>(
89 llvm::Twine("Inconsistent number of values for counter ")
90 .concat(CounterName)
91 .concat(std::to_string(Counter->numValues()))
92 .concat(" vs expected of ")
93 .concat(std::to_string(Reserved)));
94 Scratch->clear();
96 auto PS = ET.withSavedState();
97 CrashRecoveryContext CRC;
98 CrashRecoveryContext::Enable();
99 const bool Crashed = !CRC.RunSafely([this, Counter, ScratchPtr]() {
100 Counter->start();
101 this->Function(ScratchPtr);
102 Counter->stop();
104 CrashRecoveryContext::Disable();
105 PS.reset();
106 if (Crashed) {
107 std::string Msg = "snippet crashed while running";
108 #ifdef LLVM_ON_UNIX
109 // See "Exit Status for Commands":
110 // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
111 constexpr const int kSigOffset = 128;
112 if (const char *const SigName = strsignal(CRC.RetCode - kSigOffset)) {
113 Msg += ": ";
114 Msg += SigName;
116 #endif
117 return make_error<SnippetCrash>(std::move(Msg));
121 auto ValueOrError = Counter->readOrError(Function.getFunctionBytes());
122 if (!ValueOrError)
123 return ValueOrError.takeError();
124 accumulateCounterValues(ValueOrError.get(), &CounterValues);
126 return CounterValues;
129 const LLVMState &State;
130 const ExecutableFunction Function;
131 BenchmarkRunner::ScratchSpace *const Scratch;
133 } // namespace
135 Expected<InstructionBenchmark> BenchmarkRunner::runConfiguration(
136 const BenchmarkCode &BC, unsigned NumRepetitions, unsigned LoopBodySize,
137 ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,
138 bool DumpObjectToDisk) const {
139 InstructionBenchmark InstrBenchmark;
140 InstrBenchmark.Mode = Mode;
141 InstrBenchmark.CpuName = std::string(State.getTargetMachine().getTargetCPU());
142 InstrBenchmark.LLVMTriple =
143 State.getTargetMachine().getTargetTriple().normalize();
144 InstrBenchmark.NumRepetitions = NumRepetitions;
145 InstrBenchmark.Info = BC.Info;
147 const std::vector<MCInst> &Instructions = BC.Key.Instructions;
149 InstrBenchmark.Key = BC.Key;
151 // If we end up having an error, and we've previously succeeded with
152 // some other Repetitor, we want to discard the previous measurements.
153 struct ClearBenchmarkOnReturn {
154 ClearBenchmarkOnReturn(InstructionBenchmark *IB) : IB(IB) {}
155 ~ClearBenchmarkOnReturn() {
156 if (Clear)
157 IB->Measurements.clear();
159 void disarm() { Clear = false; }
161 private:
162 InstructionBenchmark *const IB;
163 bool Clear = true;
165 ClearBenchmarkOnReturn CBOR(&InstrBenchmark);
167 for (const std::unique_ptr<const SnippetRepetitor> &Repetitor : Repetitors) {
168 // Assemble at least kMinInstructionsForSnippet instructions by repeating
169 // the snippet for debug/analysis. This is so that the user clearly
170 // understands that the inside instructions are repeated.
171 const int MinInstructionsForSnippet = 4 * Instructions.size();
172 const int LoopBodySizeForSnippet = 2 * Instructions.size();
174 SmallString<0> Buffer;
175 raw_svector_ostream OS(Buffer);
176 if (Error E = assembleToStream(
177 State.getExegesisTarget(), State.createTargetMachine(),
178 BC.LiveIns, BC.Key.RegisterInitialValues,
179 Repetitor->Repeat(Instructions, MinInstructionsForSnippet,
180 LoopBodySizeForSnippet),
181 OS)) {
182 return std::move(E);
184 const ExecutableFunction EF(State.createTargetMachine(),
185 getObjectFromBuffer(OS.str()));
186 const auto FnBytes = EF.getFunctionBytes();
187 llvm::append_range(InstrBenchmark.AssembledSnippet, FnBytes);
190 // Assemble NumRepetitions instructions repetitions of the snippet for
191 // measurements.
192 const auto Filler = Repetitor->Repeat(
193 Instructions, InstrBenchmark.NumRepetitions, LoopBodySize);
195 object::OwningBinary<object::ObjectFile> ObjectFile;
196 if (DumpObjectToDisk) {
197 auto ObjectFilePath = writeObjectFile(BC, Filler);
198 if (Error E = ObjectFilePath.takeError()) {
199 InstrBenchmark.Error = toString(std::move(E));
200 return InstrBenchmark;
202 outs() << "Check generated assembly with: /usr/bin/objdump -d "
203 << *ObjectFilePath << "\n";
204 ObjectFile = getObjectFromFile(*ObjectFilePath);
205 } else {
206 SmallString<0> Buffer;
207 raw_svector_ostream OS(Buffer);
208 if (Error E = assembleToStream(
209 State.getExegesisTarget(), State.createTargetMachine(),
210 BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS)) {
211 return std::move(E);
213 ObjectFile = getObjectFromBuffer(OS.str());
216 const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
217 Scratch.get());
218 auto NewMeasurements = runMeasurements(Executor);
219 if (Error E = NewMeasurements.takeError()) {
220 if (!E.isA<SnippetCrash>())
221 return std::move(E);
222 InstrBenchmark.Error = toString(std::move(E));
223 return InstrBenchmark;
225 assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
226 for (BenchmarkMeasure &BM : *NewMeasurements) {
227 // Scale the measurements by instruction.
228 BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
229 // Scale the measurements by snippet.
230 BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
231 InstrBenchmark.NumRepetitions;
233 if (InstrBenchmark.Measurements.empty()) {
234 InstrBenchmark.Measurements = std::move(*NewMeasurements);
235 continue;
238 assert(Repetitors.size() > 1 && !InstrBenchmark.Measurements.empty() &&
239 "We're in an 'min' repetition mode, and need to aggregate new "
240 "result to the existing result.");
241 assert(InstrBenchmark.Measurements.size() == NewMeasurements->size() &&
242 "Expected to have identical number of measurements.");
243 for (auto I : zip(InstrBenchmark.Measurements, *NewMeasurements)) {
244 BenchmarkMeasure &Measurement = std::get<0>(I);
245 BenchmarkMeasure &NewMeasurement = std::get<1>(I);
246 assert(Measurement.Key == NewMeasurement.Key &&
247 "Expected measurements to be symmetric");
249 Measurement.PerInstructionValue = std::min(
250 Measurement.PerInstructionValue, NewMeasurement.PerInstructionValue);
251 Measurement.PerSnippetValue =
252 std::min(Measurement.PerSnippetValue, NewMeasurement.PerSnippetValue);
256 // We successfully measured everything, so don't discard the results.
257 CBOR.disarm();
258 return InstrBenchmark;
261 Expected<std::string>
262 BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
263 const FillFunction &FillFunction) const {
264 int ResultFD = 0;
265 SmallString<256> ResultPath;
266 if (Error E = errorCodeToError(
267 sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath)))
268 return std::move(E);
269 raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
270 if (Error E = assembleToStream(
271 State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns,
272 BC.Key.RegisterInitialValues, FillFunction, OFS)) {
273 return std::move(E);
275 return std::string(ResultPath.str());
278 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
280 } // namespace exegesis
281 } // namespace llvm