Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / bolt / lib / Passes / Instrumentation.cpp
blob72adb319d71dc0e437981a31c9ba682523f9b065
1 //===- bolt/Passes/Instrumentation.cpp ------------------------------------===//
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 //===----------------------------------------------------------------------===//
8 //
9 // This file implements the Instrumentation class.
11 //===----------------------------------------------------------------------===//
13 #include "bolt/Passes/Instrumentation.h"
14 #include "bolt/Core/ParallelUtilities.h"
15 #include "bolt/RuntimeLibs/InstrumentationRuntimeLibrary.h"
16 #include "bolt/Utils/CommandLineOpts.h"
17 #include "bolt/Utils/Utils.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/RWMutex.h"
20 #include <stack>
22 #define DEBUG_TYPE "bolt-instrumentation"
24 using namespace llvm;
26 namespace opts {
27 extern cl::OptionCategory BoltInstrCategory;
29 cl::opt<std::string> InstrumentationFilename(
30 "instrumentation-file",
31 cl::desc("file name where instrumented profile will be saved (default: "
32 "/tmp/prof.fdata)"),
33 cl::init("/tmp/prof.fdata"), cl::Optional, cl::cat(BoltInstrCategory));
35 cl::opt<std::string> InstrumentationBinpath(
36 "instrumentation-binpath",
37 cl::desc("path to instumented binary in case if /proc/self/map_files "
38 "is not accessible due to access restriction issues"),
39 cl::Optional, cl::cat(BoltInstrCategory));
41 cl::opt<bool> InstrumentationFileAppendPID(
42 "instrumentation-file-append-pid",
43 cl::desc("append PID to saved profile file name (default: false)"),
44 cl::init(false), cl::Optional, cl::cat(BoltInstrCategory));
46 cl::opt<bool> ConservativeInstrumentation(
47 "conservative-instrumentation",
48 cl::desc("disable instrumentation optimizations that sacrifice profile "
49 "accuracy (for debugging, default: false)"),
50 cl::init(false), cl::Optional, cl::cat(BoltInstrCategory));
52 cl::opt<uint32_t> InstrumentationSleepTime(
53 "instrumentation-sleep-time",
54 cl::desc("interval between profile writes (default: 0 = write only at "
55 "program end). This is useful for service workloads when you "
56 "want to dump profile every X minutes or if you are killing the "
57 "program and the profile is not being dumped at the end."),
58 cl::init(0), cl::Optional, cl::cat(BoltInstrCategory));
60 cl::opt<bool> InstrumentationNoCountersClear(
61 "instrumentation-no-counters-clear",
62 cl::desc("Don't clear counters across dumps "
63 "(use with instrumentation-sleep-time option)"),
64 cl::init(false), cl::Optional, cl::cat(BoltInstrCategory));
66 cl::opt<bool> InstrumentationWaitForks(
67 "instrumentation-wait-forks",
68 cl::desc("Wait until all forks of instrumented process will finish "
69 "(use with instrumentation-sleep-time option)"),
70 cl::init(false), cl::Optional, cl::cat(BoltInstrCategory));
72 cl::opt<bool>
73 InstrumentHotOnly("instrument-hot-only",
74 cl::desc("only insert instrumentation on hot functions "
75 "(needs profile, default: false)"),
76 cl::init(false), cl::Optional,
77 cl::cat(BoltInstrCategory));
79 cl::opt<bool> InstrumentCalls("instrument-calls",
80 cl::desc("record profile for inter-function "
81 "control flow activity (default: true)"),
82 cl::init(true), cl::Optional,
83 cl::cat(BoltInstrCategory));
84 } // namespace opts
86 namespace llvm {
87 namespace bolt {
89 static bool hasAArch64ExclusiveMemop(BinaryFunction &Function) {
90 // FIXME ARMv8-a architecture reference manual says that software must avoid
91 // having any explicit memory accesses between exclusive load and associated
92 // store instruction. So for now skip instrumentation for functions that have
93 // these instructions, since it might lead to runtime deadlock.
94 BinaryContext &BC = Function.getBinaryContext();
95 for (const BinaryBasicBlock &BB : Function)
96 for (const MCInst &Inst : BB)
97 if (BC.MIB->isAArch64Exclusive(Inst)) {
98 if (opts::Verbosity >= 1)
99 outs() << "BOLT-INSTRUMENTER: Function " << Function
100 << " has exclusive instructions, skip instrumentation\n";
101 return true;
104 return false;
107 uint32_t Instrumentation::getFunctionNameIndex(const BinaryFunction &Function) {
108 auto Iter = FuncToStringIdx.find(&Function);
109 if (Iter != FuncToStringIdx.end())
110 return Iter->second;
111 size_t Idx = Summary->StringTable.size();
112 FuncToStringIdx.emplace(std::make_pair(&Function, Idx));
113 Summary->StringTable.append(getEscapedName(Function.getOneName()));
114 Summary->StringTable.append(1, '\0');
115 return Idx;
118 bool Instrumentation::createCallDescription(FunctionDescription &FuncDesc,
119 const BinaryFunction &FromFunction,
120 uint32_t From, uint32_t FromNodeID,
121 const BinaryFunction &ToFunction,
122 uint32_t To, bool IsInvoke) {
123 CallDescription CD;
124 // Ordinarily, we don't augment direct calls with an explicit counter, except
125 // when forced to do so or when we know this callee could be throwing
126 // exceptions, in which case there is no other way to accurately record its
127 // frequency.
128 bool ForceInstrumentation = opts::ConservativeInstrumentation || IsInvoke;
129 CD.FromLoc.FuncString = getFunctionNameIndex(FromFunction);
130 CD.FromLoc.Offset = From;
131 CD.FromNode = FromNodeID;
132 CD.Target = &ToFunction;
133 CD.ToLoc.FuncString = getFunctionNameIndex(ToFunction);
134 CD.ToLoc.Offset = To;
135 CD.Counter = ForceInstrumentation ? Summary->Counters.size() : 0xffffffff;
136 if (ForceInstrumentation)
137 ++DirectCallCounters;
138 FuncDesc.Calls.emplace_back(CD);
139 return ForceInstrumentation;
142 void Instrumentation::createIndCallDescription(
143 const BinaryFunction &FromFunction, uint32_t From) {
144 IndCallDescription ICD;
145 ICD.FromLoc.FuncString = getFunctionNameIndex(FromFunction);
146 ICD.FromLoc.Offset = From;
147 Summary->IndCallDescriptions.emplace_back(ICD);
150 void Instrumentation::createIndCallTargetDescription(
151 const BinaryFunction &ToFunction, uint32_t To) {
152 IndCallTargetDescription ICD;
153 ICD.ToLoc.FuncString = getFunctionNameIndex(ToFunction);
154 ICD.ToLoc.Offset = To;
155 ICD.Target = &ToFunction;
156 Summary->IndCallTargetDescriptions.emplace_back(ICD);
159 bool Instrumentation::createEdgeDescription(FunctionDescription &FuncDesc,
160 const BinaryFunction &FromFunction,
161 uint32_t From, uint32_t FromNodeID,
162 const BinaryFunction &ToFunction,
163 uint32_t To, uint32_t ToNodeID,
164 bool Instrumented) {
165 EdgeDescription ED;
166 auto Result = FuncDesc.EdgesSet.insert(std::make_pair(FromNodeID, ToNodeID));
167 // Avoid creating duplicated edge descriptions. This happens in CFGs where a
168 // block jumps to its fall-through.
169 if (Result.second == false)
170 return false;
171 ED.FromLoc.FuncString = getFunctionNameIndex(FromFunction);
172 ED.FromLoc.Offset = From;
173 ED.FromNode = FromNodeID;
174 ED.ToLoc.FuncString = getFunctionNameIndex(ToFunction);
175 ED.ToLoc.Offset = To;
176 ED.ToNode = ToNodeID;
177 ED.Counter = Instrumented ? Summary->Counters.size() : 0xffffffff;
178 if (Instrumented)
179 ++BranchCounters;
180 FuncDesc.Edges.emplace_back(ED);
181 return Instrumented;
184 void Instrumentation::createLeafNodeDescription(FunctionDescription &FuncDesc,
185 uint32_t Node) {
186 InstrumentedNode IN;
187 IN.Node = Node;
188 IN.Counter = Summary->Counters.size();
189 ++LeafNodeCounters;
190 FuncDesc.LeafNodes.emplace_back(IN);
193 InstructionListType
194 Instrumentation::createInstrumentationSnippet(BinaryContext &BC, bool IsLeaf) {
195 auto L = BC.scopeLock();
196 MCSymbol *Label = BC.Ctx->createNamedTempSymbol("InstrEntry");
197 Summary->Counters.emplace_back(Label);
198 return BC.MIB->createInstrIncMemory(Label, BC.Ctx.get(), IsLeaf,
199 BC.AsmInfo->getCodePointerSize());
202 // Helper instruction sequence insertion function
203 static BinaryBasicBlock::iterator
204 insertInstructions(InstructionListType &Instrs, BinaryBasicBlock &BB,
205 BinaryBasicBlock::iterator Iter) {
206 for (MCInst &NewInst : Instrs) {
207 Iter = BB.insertInstruction(Iter, NewInst);
208 ++Iter;
210 return Iter;
213 void Instrumentation::instrumentLeafNode(BinaryBasicBlock &BB,
214 BinaryBasicBlock::iterator Iter,
215 bool IsLeaf,
216 FunctionDescription &FuncDesc,
217 uint32_t Node) {
218 createLeafNodeDescription(FuncDesc, Node);
219 InstructionListType CounterInstrs = createInstrumentationSnippet(
220 BB.getFunction()->getBinaryContext(), IsLeaf);
221 insertInstructions(CounterInstrs, BB, Iter);
224 void Instrumentation::instrumentIndirectTarget(BinaryBasicBlock &BB,
225 BinaryBasicBlock::iterator &Iter,
226 BinaryFunction &FromFunction,
227 uint32_t From) {
228 auto L = FromFunction.getBinaryContext().scopeLock();
229 const size_t IndCallSiteID = Summary->IndCallDescriptions.size();
230 createIndCallDescription(FromFunction, From);
232 BinaryContext &BC = FromFunction.getBinaryContext();
233 bool IsTailCall = BC.MIB->isTailCall(*Iter);
234 InstructionListType CounterInstrs = BC.MIB->createInstrumentedIndirectCall(
235 std::move(*Iter),
236 IsTailCall ? IndTailCallHandlerExitBBFunction->getSymbol()
237 : IndCallHandlerExitBBFunction->getSymbol(),
238 IndCallSiteID, &*BC.Ctx);
240 Iter = BB.eraseInstruction(Iter);
241 Iter = insertInstructions(CounterInstrs, BB, Iter);
242 --Iter;
245 bool Instrumentation::instrumentOneTarget(
246 SplitWorklistTy &SplitWorklist, SplitInstrsTy &SplitInstrs,
247 BinaryBasicBlock::iterator &Iter, BinaryFunction &FromFunction,
248 BinaryBasicBlock &FromBB, uint32_t From, BinaryFunction &ToFunc,
249 BinaryBasicBlock *TargetBB, uint32_t ToOffset, bool IsLeaf, bool IsInvoke,
250 FunctionDescription *FuncDesc, uint32_t FromNodeID, uint32_t ToNodeID) {
251 BinaryContext &BC = FromFunction.getBinaryContext();
253 auto L = BC.scopeLock();
254 bool Created = true;
255 if (!TargetBB)
256 Created = createCallDescription(*FuncDesc, FromFunction, From, FromNodeID,
257 ToFunc, ToOffset, IsInvoke);
258 else
259 Created = createEdgeDescription(*FuncDesc, FromFunction, From, FromNodeID,
260 ToFunc, ToOffset, ToNodeID,
261 /*Instrumented=*/true);
262 if (!Created)
263 return false;
266 InstructionListType CounterInstrs = createInstrumentationSnippet(BC, IsLeaf);
268 const MCInst &Inst = *Iter;
269 if (BC.MIB->isCall(Inst)) {
270 // This code handles both
271 // - (regular) inter-function calls (cross-function control transfer),
272 // - (rare) intra-function calls (function-local control transfer)
273 Iter = insertInstructions(CounterInstrs, FromBB, Iter);
274 return true;
277 if (!TargetBB || !FuncDesc)
278 return false;
280 // Indirect branch, conditional branches or fall-throughs
281 // Regular cond branch, put counter at start of target block
283 // N.B.: (FromBB != TargetBBs) checks below handle conditional jumps where
284 // we can't put the instrumentation counter in this block because not all
285 // paths that reach it at this point will be taken and going to the target.
286 if (TargetBB->pred_size() == 1 && &FromBB != TargetBB &&
287 !TargetBB->isEntryPoint()) {
288 insertInstructions(CounterInstrs, *TargetBB, TargetBB->begin());
289 return true;
291 if (FromBB.succ_size() == 1 && &FromBB != TargetBB) {
292 Iter = insertInstructions(CounterInstrs, FromBB, Iter);
293 return true;
295 // Critical edge, create BB and put counter there
296 SplitWorklist.emplace_back(&FromBB, TargetBB);
297 SplitInstrs.emplace_back(std::move(CounterInstrs));
298 return true;
301 void Instrumentation::instrumentFunction(BinaryFunction &Function,
302 MCPlusBuilder::AllocatorIdTy AllocId) {
303 if (Function.hasUnknownControlFlow())
304 return;
306 BinaryContext &BC = Function.getBinaryContext();
307 if (BC.isMachO() && Function.hasName("___GLOBAL_init_65535/1"))
308 return;
310 if (BC.isAArch64() && hasAArch64ExclusiveMemop(Function))
311 return;
313 SplitWorklistTy SplitWorklist;
314 SplitInstrsTy SplitInstrs;
316 FunctionDescription *FuncDesc = nullptr;
318 std::unique_lock<llvm::sys::RWMutex> L(FDMutex);
319 Summary->FunctionDescriptions.emplace_back();
320 FuncDesc = &Summary->FunctionDescriptions.back();
323 FuncDesc->Function = &Function;
324 Function.disambiguateJumpTables(AllocId);
325 Function.deleteConservativeEdges();
327 std::unordered_map<const BinaryBasicBlock *, uint32_t> BBToID;
328 uint32_t Id = 0;
329 for (auto BBI = Function.begin(); BBI != Function.end(); ++BBI) {
330 BBToID[&*BBI] = Id++;
332 std::unordered_set<const BinaryBasicBlock *> VisitedSet;
333 // DFS to establish edges we will use for a spanning tree. Edges in the
334 // spanning tree can be instrumentation-free since their count can be
335 // inferred by solving flow equations on a bottom-up traversal of the tree.
336 // Exit basic blocks are always instrumented so we start the traversal with
337 // a minimum number of defined variables to make the equation solvable.
338 std::stack<std::pair<const BinaryBasicBlock *, BinaryBasicBlock *>> Stack;
339 std::unordered_map<const BinaryBasicBlock *,
340 std::set<const BinaryBasicBlock *>>
341 STOutSet;
342 for (auto BBI = Function.getLayout().block_rbegin();
343 BBI != Function.getLayout().block_rend(); ++BBI) {
344 if ((*BBI)->isEntryPoint() || (*BBI)->isLandingPad()) {
345 Stack.push(std::make_pair(nullptr, *BBI));
346 if (opts::InstrumentCalls && (*BBI)->isEntryPoint()) {
347 EntryNode E;
348 E.Node = BBToID[&**BBI];
349 E.Address = (*BBI)->getInputOffset();
350 FuncDesc->EntryNodes.emplace_back(E);
351 createIndCallTargetDescription(Function, (*BBI)->getInputOffset());
356 // Modified version of BinaryFunction::dfs() to build a spanning tree
357 if (!opts::ConservativeInstrumentation) {
358 while (!Stack.empty()) {
359 BinaryBasicBlock *BB;
360 const BinaryBasicBlock *Pred;
361 std::tie(Pred, BB) = Stack.top();
362 Stack.pop();
363 if (llvm::is_contained(VisitedSet, BB))
364 continue;
366 VisitedSet.insert(BB);
367 if (Pred)
368 STOutSet[Pred].insert(BB);
370 for (BinaryBasicBlock *SuccBB : BB->successors())
371 Stack.push(std::make_pair(BB, SuccBB));
375 // Determine whether this is a leaf function, which needs special
376 // instructions to protect the red zone
377 bool IsLeafFunction = true;
378 DenseSet<const BinaryBasicBlock *> InvokeBlocks;
379 for (const BinaryBasicBlock &BB : Function) {
380 for (const MCInst &Inst : BB) {
381 if (BC.MIB->isCall(Inst)) {
382 if (BC.MIB->isInvoke(Inst))
383 InvokeBlocks.insert(&BB);
384 if (!BC.MIB->isTailCall(Inst))
385 IsLeafFunction = false;
390 for (auto BBI = Function.begin(), BBE = Function.end(); BBI != BBE; ++BBI) {
391 BinaryBasicBlock &BB = *BBI;
392 bool HasUnconditionalBranch = false;
393 bool HasJumpTable = false;
394 bool IsInvokeBlock = InvokeBlocks.count(&BB) > 0;
396 for (auto I = BB.begin(); I != BB.end(); ++I) {
397 const MCInst &Inst = *I;
398 if (!BC.MIB->getOffset(Inst))
399 continue;
401 const bool IsJumpTable = Function.getJumpTable(Inst);
402 if (IsJumpTable)
403 HasJumpTable = true;
404 else if (BC.MIB->isUnconditionalBranch(Inst))
405 HasUnconditionalBranch = true;
406 else if ((!BC.MIB->isCall(Inst) && !BC.MIB->isConditionalBranch(Inst)) ||
407 BC.MIB->isUnsupportedBranch(Inst))
408 continue;
410 const uint32_t FromOffset = *BC.MIB->getOffset(Inst);
411 const MCSymbol *Target = BC.MIB->getTargetSymbol(Inst);
412 BinaryBasicBlock *TargetBB = Function.getBasicBlockForLabel(Target);
413 uint32_t ToOffset = TargetBB ? TargetBB->getInputOffset() : 0;
414 BinaryFunction *TargetFunc =
415 TargetBB ? &Function : BC.getFunctionForSymbol(Target);
416 if (TargetFunc && BC.MIB->isCall(Inst)) {
417 if (opts::InstrumentCalls) {
418 const BinaryBasicBlock *ForeignBB =
419 TargetFunc->getBasicBlockForLabel(Target);
420 if (ForeignBB)
421 ToOffset = ForeignBB->getInputOffset();
422 instrumentOneTarget(SplitWorklist, SplitInstrs, I, Function, BB,
423 FromOffset, *TargetFunc, TargetBB, ToOffset,
424 IsLeafFunction, IsInvokeBlock, FuncDesc,
425 BBToID[&BB]);
427 continue;
429 if (TargetFunc) {
430 // Do not instrument edges in the spanning tree
431 if (llvm::is_contained(STOutSet[&BB], TargetBB)) {
432 auto L = BC.scopeLock();
433 createEdgeDescription(*FuncDesc, Function, FromOffset, BBToID[&BB],
434 Function, ToOffset, BBToID[TargetBB],
435 /*Instrumented=*/false);
436 continue;
438 instrumentOneTarget(SplitWorklist, SplitInstrs, I, Function, BB,
439 FromOffset, *TargetFunc, TargetBB, ToOffset,
440 IsLeafFunction, IsInvokeBlock, FuncDesc,
441 BBToID[&BB], BBToID[TargetBB]);
442 continue;
445 if (IsJumpTable) {
446 for (BinaryBasicBlock *&Succ : BB.successors()) {
447 // Do not instrument edges in the spanning tree
448 if (llvm::is_contained(STOutSet[&BB], &*Succ)) {
449 auto L = BC.scopeLock();
450 createEdgeDescription(*FuncDesc, Function, FromOffset, BBToID[&BB],
451 Function, Succ->getInputOffset(),
452 BBToID[&*Succ], /*Instrumented=*/false);
453 continue;
455 instrumentOneTarget(
456 SplitWorklist, SplitInstrs, I, Function, BB, FromOffset, Function,
457 &*Succ, Succ->getInputOffset(), IsLeafFunction, IsInvokeBlock,
458 FuncDesc, BBToID[&BB], BBToID[&*Succ]);
460 continue;
463 // Handle indirect calls -- could be direct calls with unknown targets
464 // or secondary entry points of known functions, so check it is indirect
465 // to be sure.
466 if (opts::InstrumentCalls && BC.MIB->isIndirectCall(*I))
467 instrumentIndirectTarget(BB, I, Function, FromOffset);
469 } // End of instructions loop
471 // Instrument fallthroughs (when the direct jump instruction is missing)
472 if (!HasUnconditionalBranch && !HasJumpTable && BB.succ_size() > 0 &&
473 BB.size() > 0) {
474 BinaryBasicBlock *FTBB = BB.getFallthrough();
475 assert(FTBB && "expected valid fall-through basic block");
476 auto I = BB.begin();
477 auto LastInstr = BB.end();
478 --LastInstr;
479 while (LastInstr != I && BC.MIB->isPseudo(*LastInstr))
480 --LastInstr;
481 uint32_t FromOffset = 0;
482 // The last instruction in the BB should have an annotation, except
483 // if it was branching to the end of the function as a result of
484 // __builtin_unreachable(), in which case it was deleted by fixBranches.
485 // Ignore this case. FIXME: force fixBranches() to preserve the offset.
486 if (!BC.MIB->getOffset(*LastInstr))
487 continue;
488 FromOffset = *BC.MIB->getOffset(*LastInstr);
490 // Do not instrument edges in the spanning tree
491 if (llvm::is_contained(STOutSet[&BB], FTBB)) {
492 auto L = BC.scopeLock();
493 createEdgeDescription(*FuncDesc, Function, FromOffset, BBToID[&BB],
494 Function, FTBB->getInputOffset(), BBToID[FTBB],
495 /*Instrumented=*/false);
496 continue;
498 instrumentOneTarget(SplitWorklist, SplitInstrs, I, Function, BB,
499 FromOffset, Function, FTBB, FTBB->getInputOffset(),
500 IsLeafFunction, IsInvokeBlock, FuncDesc, BBToID[&BB],
501 BBToID[FTBB]);
503 } // End of BBs loop
505 // Instrument spanning tree leaves
506 if (!opts::ConservativeInstrumentation) {
507 for (auto BBI = Function.begin(), BBE = Function.end(); BBI != BBE; ++BBI) {
508 BinaryBasicBlock &BB = *BBI;
509 if (STOutSet[&BB].size() == 0)
510 instrumentLeafNode(BB, BB.begin(), IsLeafFunction, *FuncDesc,
511 BBToID[&BB]);
515 // Consume list of critical edges: split them and add instrumentation to the
516 // newly created BBs
517 auto Iter = SplitInstrs.begin();
518 for (std::pair<BinaryBasicBlock *, BinaryBasicBlock *> &BBPair :
519 SplitWorklist) {
520 BinaryBasicBlock *NewBB = Function.splitEdge(BBPair.first, BBPair.second);
521 NewBB->addInstructions(Iter->begin(), Iter->end());
522 ++Iter;
525 // Unused now
526 FuncDesc->EdgesSet.clear();
529 void Instrumentation::runOnFunctions(BinaryContext &BC) {
530 const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/false,
531 /*IsText=*/false,
532 /*IsAllocatable=*/true);
533 BC.registerOrUpdateSection(".bolt.instr.counters", ELF::SHT_PROGBITS, Flags,
534 nullptr, 0, 1);
536 BC.registerOrUpdateNoteSection(".bolt.instr.tables", nullptr, 0,
537 /*Alignment=*/1,
538 /*IsReadOnly=*/true, ELF::SHT_NOTE);
540 Summary->IndCallCounterFuncPtr =
541 BC.Ctx->getOrCreateSymbol("__bolt_ind_call_counter_func_pointer");
542 Summary->IndTailCallCounterFuncPtr =
543 BC.Ctx->getOrCreateSymbol("__bolt_ind_tailcall_counter_func_pointer");
545 createAuxiliaryFunctions(BC);
547 ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) {
548 return (!BF.isSimple() || BF.isIgnored() ||
549 (opts::InstrumentHotOnly && !BF.getKnownExecutionCount()));
552 ParallelUtilities::WorkFuncWithAllocTy WorkFun =
553 [&](BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocatorId) {
554 instrumentFunction(BF, AllocatorId);
557 ParallelUtilities::runOnEachFunctionWithUniqueAllocId(
558 BC, ParallelUtilities::SchedulingPolicy::SP_INST_QUADRATIC, WorkFun,
559 SkipPredicate, "instrumentation", /* ForceSequential=*/true);
561 if (BC.isMachO()) {
562 if (BC.StartFunctionAddress) {
563 BinaryFunction *Main =
564 BC.getBinaryFunctionAtAddress(*BC.StartFunctionAddress);
565 assert(Main && "Entry point function not found");
566 BinaryBasicBlock &BB = Main->front();
568 ErrorOr<BinarySection &> SetupSection =
569 BC.getUniqueSectionByName("I__setup");
570 if (!SetupSection) {
571 llvm::errs() << "Cannot find I__setup section\n";
572 exit(1);
574 MCSymbol *Target = BC.registerNameAtAddress(
575 "__bolt_instr_setup", SetupSection->getAddress(), 0, 0);
576 MCInst NewInst;
577 BC.MIB->createCall(NewInst, Target, BC.Ctx.get());
578 BB.insertInstruction(BB.begin(), std::move(NewInst));
579 } else {
580 llvm::errs() << "BOLT-WARNING: Entry point not found\n";
583 if (BinaryData *BD = BC.getBinaryDataByName("___GLOBAL_init_65535/1")) {
584 BinaryFunction *Ctor = BC.getBinaryFunctionAtAddress(BD->getAddress());
585 assert(Ctor && "___GLOBAL_init_65535 function not found");
586 BinaryBasicBlock &BB = Ctor->front();
587 ErrorOr<BinarySection &> FiniSection =
588 BC.getUniqueSectionByName("I__fini");
589 if (!FiniSection) {
590 llvm::errs() << "Cannot find I__fini section\n";
591 exit(1);
593 MCSymbol *Target = BC.registerNameAtAddress(
594 "__bolt_instr_fini", FiniSection->getAddress(), 0, 0);
595 auto IsLEA = [&BC](const MCInst &Inst) { return BC.MIB->isLEA64r(Inst); };
596 const auto LEA = std::find_if(
597 std::next(llvm::find_if(reverse(BB), IsLEA)), BB.rend(), IsLEA);
598 LEA->getOperand(4).setExpr(
599 MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *BC.Ctx));
600 } else {
601 llvm::errs() << "BOLT-WARNING: ___GLOBAL_init_65535 not found\n";
605 setupRuntimeLibrary(BC);
608 void Instrumentation::createAuxiliaryFunctions(BinaryContext &BC) {
609 auto createSimpleFunction =
610 [&](StringRef Title, InstructionListType Instrs) -> BinaryFunction * {
611 BinaryFunction *Func = BC.createInjectedBinaryFunction(std::string(Title));
613 std::vector<std::unique_ptr<BinaryBasicBlock>> BBs;
614 BBs.emplace_back(Func->createBasicBlock());
615 BBs.back()->addInstructions(Instrs.begin(), Instrs.end());
616 BBs.back()->setCFIState(0);
617 Func->insertBasicBlocks(nullptr, std::move(BBs),
618 /*UpdateLayout=*/true,
619 /*UpdateCFIState=*/false);
620 Func->updateState(BinaryFunction::State::CFG_Finalized);
621 return Func;
624 // Here we are creating a set of functions to handle BB entry/exit.
625 // IndCallHandlerExitBB contains instructions to finish handling traffic to an
626 // indirect call. We pass it to createInstrumentedIndCallHandlerEntryBB(),
627 // which will check if a pointer to runtime library traffic accounting
628 // function was initialized (it is done during initialization of runtime
629 // library). If it is so - calls it. Then this routine returns to normal
630 // execution by jumping to exit BB.
631 BinaryFunction *IndCallHandlerExitBB =
632 createSimpleFunction("__bolt_instr_ind_call_handler",
633 BC.MIB->createInstrumentedIndCallHandlerExitBB());
635 IndCallHandlerExitBBFunction =
636 createSimpleFunction("__bolt_instr_ind_call_handler_func",
637 BC.MIB->createInstrumentedIndCallHandlerEntryBB(
638 Summary->IndCallCounterFuncPtr,
639 IndCallHandlerExitBB->getSymbol(), &*BC.Ctx));
641 BinaryFunction *IndTailCallHandlerExitBB = createSimpleFunction(
642 "__bolt_instr_ind_tail_call_handler",
643 BC.MIB->createInstrumentedIndTailCallHandlerExitBB());
645 IndTailCallHandlerExitBBFunction = createSimpleFunction(
646 "__bolt_instr_ind_tailcall_handler_func",
647 BC.MIB->createInstrumentedIndCallHandlerEntryBB(
648 Summary->IndTailCallCounterFuncPtr,
649 IndTailCallHandlerExitBB->getSymbol(), &*BC.Ctx));
651 createSimpleFunction("__bolt_num_counters_getter",
652 BC.MIB->createNumCountersGetter(BC.Ctx.get()));
653 createSimpleFunction("__bolt_instr_locations_getter",
654 BC.MIB->createInstrLocationsGetter(BC.Ctx.get()));
655 createSimpleFunction("__bolt_instr_tables_getter",
656 BC.MIB->createInstrTablesGetter(BC.Ctx.get()));
657 createSimpleFunction("__bolt_instr_num_funcs_getter",
658 BC.MIB->createInstrNumFuncsGetter(BC.Ctx.get()));
660 if (BC.isELF()) {
661 if (BC.StartFunctionAddress) {
662 BinaryFunction *Start =
663 BC.getBinaryFunctionAtAddress(*BC.StartFunctionAddress);
664 assert(Start && "Entry point function not found");
665 const MCSymbol *StartSym = Start->getSymbol();
666 createSimpleFunction(
667 "__bolt_start_trampoline",
668 BC.MIB->createSymbolTrampoline(StartSym, BC.Ctx.get()));
670 if (BC.FiniFunctionAddress) {
671 BinaryFunction *Fini =
672 BC.getBinaryFunctionAtAddress(*BC.FiniFunctionAddress);
673 assert(Fini && "Finalization function not found");
674 const MCSymbol *FiniSym = Fini->getSymbol();
675 createSimpleFunction(
676 "__bolt_fini_trampoline",
677 BC.MIB->createSymbolTrampoline(FiniSym, BC.Ctx.get()));
678 } else {
679 // Create dummy return function for trampoline to avoid issues
680 // with unknown symbol in runtime library. E.g. for static PIE
681 // executable
682 createSimpleFunction("__bolt_fini_trampoline",
683 BC.MIB->createDummyReturnFunction(BC.Ctx.get()));
688 void Instrumentation::setupRuntimeLibrary(BinaryContext &BC) {
689 uint32_t FuncDescSize = Summary->getFDSize();
691 outs() << "BOLT-INSTRUMENTER: Number of indirect call site descriptors: "
692 << Summary->IndCallDescriptions.size() << "\n";
693 outs() << "BOLT-INSTRUMENTER: Number of indirect call target descriptors: "
694 << Summary->IndCallTargetDescriptions.size() << "\n";
695 outs() << "BOLT-INSTRUMENTER: Number of function descriptors: "
696 << Summary->FunctionDescriptions.size() << "\n";
697 outs() << "BOLT-INSTRUMENTER: Number of branch counters: " << BranchCounters
698 << "\n";
699 outs() << "BOLT-INSTRUMENTER: Number of ST leaf node counters: "
700 << LeafNodeCounters << "\n";
701 outs() << "BOLT-INSTRUMENTER: Number of direct call counters: "
702 << DirectCallCounters << "\n";
703 outs() << "BOLT-INSTRUMENTER: Total number of counters: "
704 << Summary->Counters.size() << "\n";
705 outs() << "BOLT-INSTRUMENTER: Total size of counters: "
706 << (Summary->Counters.size() * 8) << " bytes (static alloc memory)\n";
707 outs() << "BOLT-INSTRUMENTER: Total size of string table emitted: "
708 << Summary->StringTable.size() << " bytes in file\n";
709 outs() << "BOLT-INSTRUMENTER: Total size of descriptors: "
710 << (FuncDescSize +
711 Summary->IndCallDescriptions.size() * sizeof(IndCallDescription) +
712 Summary->IndCallTargetDescriptions.size() *
713 sizeof(IndCallTargetDescription))
714 << " bytes in file\n";
715 outs() << "BOLT-INSTRUMENTER: Profile will be saved to file "
716 << opts::InstrumentationFilename << "\n";
718 InstrumentationRuntimeLibrary *RtLibrary =
719 static_cast<InstrumentationRuntimeLibrary *>(BC.getRuntimeLibrary());
720 assert(RtLibrary && "instrumentation runtime library object must be set");
721 RtLibrary->setSummary(std::move(Summary));
723 } // namespace bolt
724 } // namespace llvm