1 | //===- EntryExitInstrumenter.cpp - Function Entry/Exit Instrumentation ----===// |
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 | #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" |
10 | #include "llvm/Analysis/GlobalsModRef.h" |
11 | #include "llvm/IR/DebugInfoMetadata.h" |
12 | #include "llvm/IR/Dominators.h" |
13 | #include "llvm/IR/Function.h" |
14 | #include "llvm/IR/Instructions.h" |
15 | #include "llvm/IR/Intrinsics.h" |
16 | #include "llvm/IR/Module.h" |
17 | #include "llvm/IR/Type.h" |
18 | #include "llvm/InitializePasses.h" |
19 | #include "llvm/TargetParser/Triple.h" |
20 | #include "llvm/Pass.h" |
21 | #include "llvm/Transforms/Utils.h" |
22 | |
23 | using namespace llvm; |
24 | |
25 | static void insertCall(Function &CurFn, StringRef Func, |
26 | BasicBlock::iterator InsertionPt, DebugLoc DL) { |
27 | Module &M = *InsertionPt->getParent()->getParent()->getParent(); |
28 | LLVMContext &C = InsertionPt->getParent()->getContext(); |
29 | |
30 | if (Func == "mcount" || |
31 | Func == ".mcount" || |
32 | Func == "llvm.arm.gnu.eabi.mcount" || |
33 | Func == "\01_mcount" || |
34 | Func == "\01mcount" || |
35 | Func == "__mcount" || |
36 | Func == "_mcount" || |
37 | Func == "__cyg_profile_func_enter_bare" ) { |
38 | Triple TargetTriple(M.getTargetTriple()); |
39 | if (TargetTriple.isOSAIX() && Func == "__mcount" ) { |
40 | Type *SizeTy = M.getDataLayout().getIntPtrType(C); |
41 | Type *SizePtrTy = PointerType::getUnqual(C); |
42 | GlobalVariable *GV = new GlobalVariable(M, SizeTy, /*isConstant=*/false, |
43 | GlobalValue::InternalLinkage, |
44 | ConstantInt::get(Ty: SizeTy, V: 0)); |
45 | CallInst *Call = CallInst::Create( |
46 | Func: M.getOrInsertFunction(Name: Func, |
47 | T: FunctionType::get(Result: Type::getVoidTy(C), Params: {SizePtrTy}, |
48 | /*isVarArg=*/false)), |
49 | Args: {GV}, NameStr: "" , InsertBefore: InsertionPt); |
50 | Call->setDebugLoc(DL); |
51 | } else { |
52 | FunctionCallee Fn = M.getOrInsertFunction(Name: Func, RetTy: Type::getVoidTy(C)); |
53 | CallInst *Call = CallInst::Create(Func: Fn, NameStr: "" , InsertBefore: InsertionPt); |
54 | Call->setDebugLoc(DL); |
55 | } |
56 | return; |
57 | } |
58 | |
59 | if (Func == "__cyg_profile_func_enter" || Func == "__cyg_profile_func_exit" ) { |
60 | Type *ArgTypes[] = {PointerType::getUnqual(C), PointerType::getUnqual(C)}; |
61 | |
62 | FunctionCallee Fn = M.getOrInsertFunction( |
63 | Name: Func, T: FunctionType::get(Result: Type::getVoidTy(C), Params: ArgTypes, isVarArg: false)); |
64 | |
65 | Instruction *RetAddr = CallInst::Create( |
66 | Func: Intrinsic::getDeclaration(M: &M, id: Intrinsic::returnaddress), |
67 | Args: ArrayRef<Value *>(ConstantInt::get(Ty: Type::getInt32Ty(C), V: 0)), NameStr: "" , |
68 | InsertBefore: InsertionPt); |
69 | RetAddr->setDebugLoc(DL); |
70 | |
71 | Value *Args[] = {&CurFn, RetAddr}; |
72 | CallInst *Call = |
73 | CallInst::Create(Func: Fn, Args: ArrayRef<Value *>(Args), NameStr: "" , InsertBefore: InsertionPt); |
74 | Call->setDebugLoc(DL); |
75 | return; |
76 | } |
77 | |
78 | // We only know how to call a fixed set of instrumentation functions, because |
79 | // they all expect different arguments, etc. |
80 | report_fatal_error(reason: Twine("Unknown instrumentation function: '" ) + Func + "'" ); |
81 | } |
82 | |
83 | static bool runOnFunction(Function &F, bool PostInlining) { |
84 | // The asm in a naked function may reasonably expect the argument registers |
85 | // and the return address register (if present) to be live. An inserted |
86 | // function call will clobber these registers. Simply skip naked functions for |
87 | // all targets. |
88 | if (F.hasFnAttribute(Kind: Attribute::Naked)) |
89 | return false; |
90 | |
91 | StringRef EntryAttr = PostInlining ? "instrument-function-entry-inlined" |
92 | : "instrument-function-entry" ; |
93 | |
94 | StringRef ExitAttr = PostInlining ? "instrument-function-exit-inlined" |
95 | : "instrument-function-exit" ; |
96 | |
97 | StringRef EntryFunc = F.getFnAttribute(Kind: EntryAttr).getValueAsString(); |
98 | StringRef ExitFunc = F.getFnAttribute(Kind: ExitAttr).getValueAsString(); |
99 | |
100 | bool Changed = false; |
101 | |
102 | // If the attribute is specified, insert instrumentation and then "consume" |
103 | // the attribute so that it's not inserted again if the pass should happen to |
104 | // run later for some reason. |
105 | |
106 | if (!EntryFunc.empty()) { |
107 | DebugLoc DL; |
108 | if (auto SP = F.getSubprogram()) |
109 | DL = DILocation::get(Context&: SP->getContext(), Line: SP->getScopeLine(), Column: 0, Scope: SP); |
110 | |
111 | insertCall(CurFn&: F, Func: EntryFunc, InsertionPt: F.begin()->getFirstInsertionPt(), DL); |
112 | Changed = true; |
113 | F.removeFnAttr(Kind: EntryAttr); |
114 | } |
115 | |
116 | if (!ExitFunc.empty()) { |
117 | for (BasicBlock &BB : F) { |
118 | Instruction *T = BB.getTerminator(); |
119 | if (!isa<ReturnInst>(Val: T)) |
120 | continue; |
121 | |
122 | // If T is preceded by a musttail call, that's the real terminator. |
123 | if (CallInst *CI = BB.getTerminatingMustTailCall()) |
124 | T = CI; |
125 | |
126 | DebugLoc DL; |
127 | if (DebugLoc TerminatorDL = T->getDebugLoc()) |
128 | DL = TerminatorDL; |
129 | else if (auto SP = F.getSubprogram()) |
130 | DL = DILocation::get(Context&: SP->getContext(), Line: 0, Column: 0, Scope: SP); |
131 | |
132 | insertCall(CurFn&: F, Func: ExitFunc, InsertionPt: T->getIterator(), DL); |
133 | Changed = true; |
134 | } |
135 | F.removeFnAttr(Kind: ExitAttr); |
136 | } |
137 | |
138 | return Changed; |
139 | } |
140 | |
141 | namespace { |
142 | struct PostInlineEntryExitInstrumenter : public FunctionPass { |
143 | static char ID; |
144 | PostInlineEntryExitInstrumenter() : FunctionPass(ID) { |
145 | initializePostInlineEntryExitInstrumenterPass( |
146 | *PassRegistry::getPassRegistry()); |
147 | } |
148 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
149 | AU.addPreserved<GlobalsAAWrapperPass>(); |
150 | AU.addPreserved<DominatorTreeWrapperPass>(); |
151 | } |
152 | bool runOnFunction(Function &F) override { return ::runOnFunction(F, PostInlining: true); } |
153 | }; |
154 | char PostInlineEntryExitInstrumenter::ID = 0; |
155 | } |
156 | |
157 | INITIALIZE_PASS_BEGIN( |
158 | PostInlineEntryExitInstrumenter, "post-inline-ee-instrument" , |
159 | "Instrument function entry/exit with calls to e.g. mcount() " |
160 | "(post inlining)" , |
161 | false, false) |
162 | INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) |
163 | INITIALIZE_PASS_END( |
164 | PostInlineEntryExitInstrumenter, "post-inline-ee-instrument" , |
165 | "Instrument function entry/exit with calls to e.g. mcount() " |
166 | "(post inlining)" , |
167 | false, false) |
168 | |
169 | FunctionPass *llvm::createPostInlineEntryExitInstrumenterPass() { |
170 | return new PostInlineEntryExitInstrumenter(); |
171 | } |
172 | |
173 | PreservedAnalyses |
174 | llvm::EntryExitInstrumenterPass::run(Function &F, FunctionAnalysisManager &AM) { |
175 | if (!runOnFunction(F, PostInlining)) |
176 | return PreservedAnalyses::all(); |
177 | PreservedAnalyses PA; |
178 | PA.preserveSet<CFGAnalyses>(); |
179 | return PA; |
180 | } |
181 | |
182 | void llvm::EntryExitInstrumenterPass::printPipeline( |
183 | raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) { |
184 | static_cast<PassInfoMixin<llvm::EntryExitInstrumenterPass> *>(this) |
185 | ->printPipeline(OS, MapClassName2PassName); |
186 | OS << '<'; |
187 | if (PostInlining) |
188 | OS << "post-inline" ; |
189 | OS << '>'; |
190 | } |
191 | |