1 | /** |
2 | * Copyright (c) Glow Contributors. See CONTRIBUTORS file. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include "glow/LLVMIRCodeGen/CommandLine.h" |
18 | #include "glow/LLVMIRCodeGen/LLVMIRGen.h" |
19 | #include "glow/Support/Debug.h" |
20 | #include "llvm/Support/Regex.h" |
21 | |
22 | #define DEBUG_TYPE "debug-instrumentation" |
23 | |
24 | using namespace glow; |
25 | |
26 | using llvm::cast; |
27 | using llvm::dyn_cast; |
28 | using llvm::isa; |
29 | |
30 | namespace { |
31 | |
32 | /// Supported types of instrumentations. |
33 | enum FunctionInstrumentationType { |
34 | // Every single LLVM IR instruction |
35 | // of selected functions will be instrumented. |
36 | BODY = 1, |
37 | // Function calls will be wrapped in traces. |
38 | CALL = 2, |
39 | }; |
40 | |
41 | /// Metadata about instrumentation. |
42 | struct InstrumentationMetaInformation { |
43 | // Regular expression of function. |
44 | llvm::Regex funcReg; |
45 | // Instrumentation style. |
46 | FunctionInstrumentationType style; |
47 | // If instrumentation style is CALL, then |
48 | // it allows to specify funciton to call before |
49 | llvm::Function *callBefore; |
50 | // and after. |
51 | llvm::Function *callAfter; |
52 | }; |
53 | |
54 | /// Perform code instrumentation for selected functions. |
55 | /// The syntax is array of sections separated by coma ",". |
56 | /// Every section starts with function regex, then body/call clause. |
57 | /// PE Ex: "func1*:body,func2:call[:before_func2[:after_func2]];..." |
58 | static llvm::cl::list<std::string> llvmIrInstrumentation( |
59 | "llvm-code-debug-trace-instrumentation" , |
60 | llvm::cl::desc( |
61 | "Create trace instructions for functions bodies or function calls" ), |
62 | llvm::cl::ZeroOrMore, llvm::cl::CommaSeparated, |
63 | llvm::cl::cat(getLLVMBackendCat())); |
64 | |
65 | static llvm::cl::opt<std::string> llvmIrInstrPrintoutFuncName( |
66 | "llvm-debug-trace-print-function-name" , |
67 | llvm::cl::desc("Select function that will do prints, function must be with " |
68 | "signature int f(const char *)" ), |
69 | llvm::cl::init("printf" ), llvm::cl::cat(getLLVMBackendCat())); |
70 | |
71 | /// Inserts code to generate logs or traces into the generated LLVM IR to make |
72 | /// debugging easier. |
73 | class DebugInstrumentation { |
74 | public: |
75 | explicit DebugInstrumentation(LLVMIRGen &irgen) : irgen_{irgen} { |
76 | // Bail if there is nothing to be instrumented. |
77 | if (llvmIrInstrumentation.empty()) { |
78 | return; |
79 | } |
80 | |
81 | formatInstrArgD_ = irgen_.emitStringConst( |
82 | irgen_.getBuilder(), "Instruction number %u ,stored value %d\n" ); |
83 | formatInstrArgP_ = irgen_.emitStringConst( |
84 | irgen_.getBuilder(), "Instruction number %u ,stored value %p\n" ); |
85 | formatFuncInArg_ = |
86 | irgen_.emitStringConst(irgen_.getBuilder(), "Function called %s\n" ); |
87 | formatFuncOutArg_ = |
88 | irgen_.emitStringConst(irgen_.getBuilder(), "Function exited %s\n" ); |
89 | |
90 | // Parse input llvm-code-instrumentation option and convert it to |
91 | // InstrumentationMetaInformation vector. |
92 | for (auto §ion : llvmIrInstrumentation) { |
93 | llvm::SmallVector<llvm::StringRef, 4> elements; |
94 | llvm::StringRef(section).split(elements, ":" ); |
95 | InstrumentationMetaInformation funcToInstrument{ |
96 | llvm::Regex(elements[0]), (elements[1] == "body" ? BODY : CALL), |
97 | nullptr, nullptr}; |
98 | |
99 | if (funcToInstrument.style == CALL) { |
100 | if (elements.size() >= 3) { |
101 | funcToInstrument.callBefore = |
102 | irgen_.getModule().getFunction(elements[3]); |
103 | CHECK(funcToInstrument.callBefore) |
104 | << "Cannot find " << elements[3].data() << " function" ; |
105 | } |
106 | if (elements.size() >= 4) { |
107 | funcToInstrument.callAfter = |
108 | irgen_.getModule().getFunction(elements[3]); |
109 | CHECK(funcToInstrument.callAfter) |
110 | << "Cannot find " << elements[3].data() << " function" ; |
111 | } |
112 | } |
113 | funcsToInstrument_.emplace_back(std::move(funcToInstrument)); |
114 | } |
115 | } |
116 | |
117 | void run() { |
118 | // Bail if there is nothing to be instrumented. |
119 | if (llvmIrInstrumentation.empty()) { |
120 | return; |
121 | } |
122 | |
123 | auto *printfF = |
124 | irgen_.getModule().getFunction(llvmIrInstrPrintoutFuncName.getValue()); |
125 | CHECK(printfF) << "Cannot find " << llvmIrInstrPrintoutFuncName.getValue() |
126 | << " function" ; |
127 | |
128 | int64_t traceCounter = 0; |
129 | // Iterating over all functions in the module. |
130 | for (auto &F : irgen_.getModule().functions()) { |
131 | bool instrumentFunctionBody = false; |
132 | // Checking if function's body is requested to be instrumented. |
133 | for (auto &funcToInstrument : funcsToInstrument_) { |
134 | if (!funcToInstrument.funcReg.match(F.getName()) || |
135 | funcToInstrument.style != BODY) { |
136 | continue; |
137 | } |
138 | instrumentFunctionBody = true; |
139 | } |
140 | |
141 | // Getting down to LLVM IR instruction to insert |
142 | // instrumentation traces. |
143 | for (auto &BB : F) { |
144 | for (auto &I : BB) { |
145 | // Skipping instruction that doesn't change memory. |
146 | if (I.getOpcode() == llvm::Instruction::Alloca || |
147 | I.getOpcode() == llvm::Instruction::Ret || |
148 | I.getOpcode() == llvm::Instruction::Unreachable || |
149 | I.getOpcode() == llvm::Instruction::Br) { |
150 | continue; |
151 | } |
152 | // If function body matched to be instrumented above |
153 | // then we iterated over its body adding traces otherwise |
154 | // checking if current instuction is a call and matching |
155 | // on of the function calls to be instrumented. |
156 | if (instrumentFunctionBody && !isa<llvm::CallInst>(&I)) { |
157 | llvm::IRBuilder<> builder(&I); |
158 | if (isa<llvm::StoreInst>(&I)) { |
159 | builder.SetInsertPoint(&BB, ++builder.GetInsertPoint()); |
160 | auto *traceCounterValue = |
161 | builder.getInt64(static_cast<int64_t>(traceCounter++)); |
162 | builder.CreateCall( |
163 | printfF->getFunctionType(), printfF, |
164 | {formatInstrArgP_, traceCounterValue, |
165 | llvm::cast<llvm::StoreInst>(&I)->getPointerOperand()}); |
166 | } else { |
167 | builder.SetInsertPoint(&BB, ++builder.GetInsertPoint()); |
168 | auto *traceCounterValue = |
169 | builder.getInt64(static_cast<int64_t>(traceCounter++)); |
170 | builder.CreateCall(printfF->getFunctionType(), printfF, |
171 | {formatInstrArgD_, traceCounterValue, |
172 | llvm::cast<llvm::Value>(&I)}); |
173 | } |
174 | } else if (auto *CI = llvm::dyn_cast<llvm::CallInst>(&I)) { |
175 | // If current LLVM IR instruction is not a call or the |
176 | // instruction/call name doesn't match requested set of function |
177 | // calls to be instrumented, skipping instrumentation, otherwise |
178 | // wrap function call in traces. |
179 | auto funcToInstrument = std::find_if( |
180 | funcsToInstrument_.begin(), funcsToInstrument_.end(), |
181 | [&](auto &fi) { |
182 | if (CI->getCalledFunction() == nullptr || |
183 | !fi.funcReg.match(CI->getCalledFunction()->getName()) || |
184 | fi.style != CALL) { |
185 | return false; |
186 | } |
187 | return true; |
188 | }); |
189 | |
190 | // If function is in the list to be instumented and |
191 | // custom functions before (and after specified) use them. |
192 | if (funcToInstrument != funcsToInstrument_.end() && |
193 | funcToInstrument->callBefore) { |
194 | llvm::IRBuilder<> builder(CI); |
195 | |
196 | // Args to be used for calling the specialized function. |
197 | llvm::SmallVector<llvm::Value *, 16> argsForInstr; |
198 | for (auto &arg : CI->arg_operands()) { |
199 | argsForInstr.push_back(arg); |
200 | } |
201 | |
202 | builder.CreateCall( |
203 | funcToInstrument->callBefore->getFunctionType(), |
204 | funcToInstrument->callBefore, argsForInstr); |
205 | if (funcToInstrument->callAfter) { |
206 | builder.SetInsertPoint(&BB, ++builder.GetInsertPoint()); |
207 | builder.CreateCall( |
208 | funcToInstrument->callAfter->getFunctionType(), |
209 | funcToInstrument->callAfter, argsForInstr); |
210 | } |
211 | // Otherwise check if function body instrumentation is going |
212 | // or default wrappers for function requested, wrap the |
213 | // function. |
214 | } else if (instrumentFunctionBody || |
215 | funcToInstrument != funcsToInstrument_.end()) { |
216 | instrumentFuntionCall(CI, printfF); |
217 | } |
218 | } |
219 | } |
220 | } |
221 | } |
222 | |
223 | DEBUG_GLOW(llvm::outs() << "LLVM module after instrumentation:\n" ); |
224 | DEBUG_GLOW(irgen_.getModule().print(llvm::outs(), nullptr)); |
225 | } |
226 | |
227 | private: |
228 | // Prints function input parameter values. |
229 | void instrumentFuntionCall(llvm::CallInst *CI, llvm::Function *printfF) { |
230 | if (CI->getCalledFunction() == nullptr || |
231 | (CI->getCalledFunction()->getName() == printfF->getName())) { |
232 | return; |
233 | } |
234 | |
235 | llvm::IRBuilder<> builder(CI); |
236 | |
237 | auto *functionName = irgen_.emitStringConst( |
238 | irgen_.getBuilder(), CI->getCalledFunction()->getName()); |
239 | builder.CreateCall(printfF->getFunctionType(), printfF, |
240 | {formatFuncInArg_, functionName}); |
241 | |
242 | // Iterating over all function args. First agr is function output |
243 | // if function signature is not void. |
244 | #if LLVM_VERSION_MAJOR >= 8 |
245 | for (auto &op : CI->args()) { |
246 | #else |
247 | for (auto &op : CI->arg_operands()) { |
248 | #endif |
249 | llvm::Value *argFormat = nullptr; |
250 | auto type = op.get()->getType(); |
251 | |
252 | // Printing interger values with %d symbol. |
253 | if (type->isIntegerTy() || |
254 | (type->isPointerTy() && dyn_cast<llvm::PointerType>(type) |
255 | ->getElementType() |
256 | ->isIntegerTy())) { |
257 | auto *value = op.get(); |
258 | // If arg is a pointer to integer value get value and print it. |
259 | if (type->isPointerTy()) { |
260 | value = builder.CreateLoad(op.get()); |
261 | } |
262 | |
263 | argFormat = irgen_.emitStringConst(irgen_.getBuilder(), "\targ: %d\n" ); |
264 | builder.CreateCall(printfF->getFunctionType(), printfF, |
265 | {argFormat, value}); |
266 | continue; |
267 | } |
268 | |
269 | // Processing pointers to structure values. |
270 | if (type->isPointerTy()) { |
271 | auto *structType = dyn_cast<llvm::StructType>( |
272 | dyn_cast<llvm::PointerType>(type)->getElementType()); |
273 | if (structType) { |
274 | std::string printStructFuncName = "pretty_print_" ; |
275 | llvm::StringRef structName; |
276 | // Structure types usually start either from struct. or class. |
277 | // stripping it from a name. |
278 | if (structType->getName().startswith("struct." )) { |
279 | structName = structType->getName().slice( |
280 | std::string("struct." ).length(), structType->getName().size()); |
281 | } else if (structType->getName().startswith("class." )) { |
282 | structName = structType->getName().slice( |
283 | std::string("class." ).length(), structType->getName().size()); |
284 | } |
285 | printStructFuncName += structName; |
286 | // Checking if module has print_<Struct\Class name> function to |
287 | // print arg. If not then just jumpring to default printout - ... |
288 | if (auto *printStruct = |
289 | irgen_.getModule().getFunction(printStructFuncName)) { |
290 | builder.CreateCall(printStruct->getFunctionType(), printStruct, |
291 | {op.get()}); |
292 | continue; |
293 | } |
294 | } |
295 | } |
296 | |
297 | argFormat = irgen_.emitStringConst(irgen_.getBuilder(), "\targ: ...\n" ); |
298 | builder.CreateCall(printfF->getFunctionType(), printfF, {argFormat}); |
299 | } |
300 | |
301 | builder.SetInsertPoint(CI->getParent(), ++builder.GetInsertPoint()); |
302 | builder.CreateCall(printfF->getFunctionType(), printfF, |
303 | {formatFuncOutArg_, functionName}); |
304 | } |
305 | |
306 | // Parsed metadata about instrumentation types. |
307 | std::vector<InstrumentationMetaInformation> funcsToInstrument_; |
308 | |
309 | /// LLVMIRGen to be used. |
310 | LLVMIRGen &irgen_; |
311 | |
312 | // Format string for simple line instumentation. |
313 | llvm::Value *formatInstrArgD_ = nullptr; |
314 | // Format string for simple line instumentation. |
315 | llvm::Value *formatInstrArgP_ = nullptr; |
316 | // Format string for trace before function call. |
317 | llvm::Value *formatFuncInArg_ = nullptr; |
318 | // Format string for simple after function call. |
319 | llvm::Value *formatFuncOutArg_ = nullptr; |
320 | }; |
321 | |
322 | } // namespace |
323 | |
324 | void LLVMIRGen::performDebugInstrumentation() { |
325 | DebugInstrumentation debugInstrumentation(*this); |
326 | debugInstrumentation.run(); |
327 | } |
328 | |