| //===- RealtimeSanitizer.cpp - RealtimeSanitizer instrumentation *- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file is a part of the RealtimeSanitizer, an LLVM transformation for | |
| // detecting and reporting realtime safety violations. | |
| // | |
| // See also: llvm-project/compiler-rt/lib/rtsan/ | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/IR/Analysis.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/InstIterator.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/Transforms/Utils/ModuleUtils.h" | |
| #include "llvm/Demangle/Demangle.h" | |
| #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" | |
| using namespace llvm; | |
| const char kRtsanModuleCtorName[] = "rtsan.module_ctor"; | |
| const char kRtsanInitName[] = "__rtsan_ensure_initialized"; | |
| static SmallVector<Type *> getArgTypes(ArrayRef<Value *> FunctionArgs) { | |
| SmallVector<Type *> Types; | |
| for (Value *Arg : FunctionArgs) | |
| Types.push_back(Arg->getType()); | |
| return Types; | |
| } | |
| static void insertCallBeforeInstruction(Function &Fn, Instruction &Instruction, | |
| const char *FunctionName, | |
| ArrayRef<Value *> FunctionArgs) { | |
| LLVMContext &Context = Fn.getContext(); | |
| FunctionType *FuncType = FunctionType::get(Type::getVoidTy(Context), | |
| getArgTypes(FunctionArgs), false); | |
| FunctionCallee Func = | |
| Fn.getParent()->getOrInsertFunction(FunctionName, FuncType); | |
| IRBuilder<> Builder{&Instruction}; | |
| Builder.CreateCall(Func, FunctionArgs); | |
| } | |
| static void insertCallAtFunctionEntryPoint(Function &Fn, | |
| const char *InsertFnName, | |
| ArrayRef<Value *> FunctionArgs) { | |
| insertCallBeforeInstruction(Fn, Fn.front().front(), InsertFnName, | |
| FunctionArgs); | |
| } | |
| static void insertCallAtAllFunctionExitPoints(Function &Fn, | |
| const char *InsertFnName, | |
| ArrayRef<Value *> FunctionArgs) { | |
| for (auto &I : instructions(Fn)) | |
| if (isa<ReturnInst>(&I)) | |
| insertCallBeforeInstruction(Fn, I, InsertFnName, FunctionArgs); | |
| } | |
| static PreservedAnalyses rtsanPreservedCFGAnalyses() { | |
| PreservedAnalyses PA; | |
| PA.preserveSet<CFGAnalyses>(); | |
| return PA; | |
| } | |
| static PreservedAnalyses runSanitizeRealtime(Function &Fn) { | |
| insertCallAtFunctionEntryPoint(Fn, "__rtsan_realtime_enter", {}); | |
| insertCallAtAllFunctionExitPoints(Fn, "__rtsan_realtime_exit", {}); | |
| return rtsanPreservedCFGAnalyses(); | |
| } | |
| static PreservedAnalyses runSanitizeRealtimeBlocking(Function &Fn) { | |
| IRBuilder<> Builder(&Fn.front().front()); | |
| Value *Name = Builder.CreateGlobalString(demangle(Fn.getName())); | |
| insertCallAtFunctionEntryPoint(Fn, "__rtsan_notify_blocking_call", {Name}); | |
| return rtsanPreservedCFGAnalyses(); | |
| } | |
| PreservedAnalyses RealtimeSanitizerPass::run(Module &M, | |
| ModuleAnalysisManager &MAM) { | |
| getOrCreateSanitizerCtorAndInitFunctions( | |
| M, kRtsanModuleCtorName, kRtsanInitName, /*InitArgTypes=*/{}, | |
| /*InitArgs=*/{}, | |
| // This callback is invoked when the functions are created the first | |
| // time. Hook them into the global ctors list in that case: | |
| [&](Function *Ctor, FunctionCallee) { appendToGlobalCtors(M, Ctor, 0); }); | |
| for (Function &F : M) { | |
| if (F.empty()) | |
| continue; | |
| if (F.hasFnAttribute(Attribute::SanitizeRealtime)) | |
| runSanitizeRealtime(F); | |
| if (F.hasFnAttribute(Attribute::SanitizeRealtimeBlocking)) | |
| runSanitizeRealtimeBlocking(F); | |
| } | |
| return PreservedAnalyses::none(); | |
| } |