【LLVM实践】死循环插桩检测

死循环检测

  • LLVM介绍
  • FunctionPass
  • 集成到Clang工具中
  • 后记

本篇文章的目的是通过在循环体里插桩代码,用以检测循环是否发生了不可预期的死循环。

#include "stdio.h"
int main()
{
    int i = 0;
    for(; i < 21; i++)
    {
        printf("%d \n", i);
    }
    return 0;
}

输出

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
many loop count 21 

自动判断是否超出了预期的循环次数。

LLVM介绍

LLVM是新一代的编译工具,强大的模块化支持,让程序员很方便的介入到编译过程中的各个方面,本篇文章主要利用函数处理阶段来插桩部分代码。

FunctionPass

FunctionPass是LLVM在处理函数代码生成时,会调用的Pass模块。本篇文章中直接在LLVM自带的HelloWorldPass里加入我们的代码。

PreservedAnalyses HelloWorldPass::run(Function &F,
                                      FunctionAnalysisManager &AM) {
  LoopInfo *LI = &AM.getResult<LoopAnalysis>(F);
  errs() << F.getName() << " loopcount:" <<  LI->getTopLevelLoopsVector().size() << "\n";
  Type *intType = Type::getInt32Ty(F.getContext());
  std::vector<Type *> printfArgsTypes({Type::getInt8PtrTy(F.getContext())});
  FunctionType *printfType = FunctionType::get(intType, printfArgsTypes, true);
  auto printfFunc = F.getEntryBlock().getModule()->getOrInsertFunction("printf", printfType);
  for(auto &l : LI->getTopLevelLoopsVector())
  {
    auto pp = l->getLoopPredecessor();
    if(pp)
    {
      IRBuilder<> builderInit(pp->getFirstNonPHI());      
      auto pLoopCount = builderInit.CreateAlloca(builderInit.getInt32Ty(), nullptr, "loopcount");
      builderInit.CreateStore(ConstantInt::get(builderInit.getInt32Ty(), 0), pLoopCount);

      auto pB = l->getHeader();
      builderInit.SetInsertPoint(pB->getFirstNonPHI());
      auto *addL = builderInit.CreateLoad(builderInit.getInt32Ty(), pLoopCount, "loadLoopCount");
      auto *one = ConstantInt::get(builderInit.getInt32Ty(), 1);
      auto *pRest = builderInit.CreateAdd(addL, one, "inc loopCount");
      builderInit.CreateStore(pRest, pLoopCount);
      auto pBExit = l->getExitBlock();
      if(pBExit != nullptr)
      {
        builderInit.SetInsertPoint(pBExit->getFirstNonPHI());
        auto *addL = builderInit.CreateLoad(builderInit.getInt32Ty(), pLoopCount, "loadLoopCount");
        auto *n100W = ConstantInt::get(builderInit.getInt32Ty(), 20);
        auto *pCmp = cast<ICmpInst>(builderInit.CreateCmp(CmpInst::Predicate::ICMP_UGE, addL, n100W, "Cmp"));// builderInit.CreateCmp();
        //auto pAssert = BasicBlock::Create(pBExit->getContext(), "assert");
        Instruction *Brnch =
          SplitBlockAndInsertIfThen(pCmp, pCmp->getNextNode(), false);

      builderInit.SetInsertPoint(Brnch);
            

      Value *puts_params[] = {
        builderInit.CreateGlobalStringPtr("many loop count %d \n", "loopttip"),
        addL
      };
      builderInit.CreateCall(printfFunc, puts_params, "printlog");
       // auto pIfThen = SplitBlockAndInsertIfThen(pCmp, pBExit->getFirstNonPHI(), false, nullptr, (DominatorTree*)nullptr, nullptr, pAssert);
       // builderInit.Insert(pIfThen, "ifthen");
        //pBExit->getModule()->getOrInsertFunction()
        //builderInit.CreateSelect(pCmp, )
      }     
      
    }
  }
 
  return PreservedAnalyses::none();
}

生成的IR对比

; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@.str = private unnamed_addr constant [5 x i8] c"%d \0A\00", align 1
@loopttip = private unnamed_addr constant [21 x i8] c"many loop count %d \0A\00", align 1

; Function Attrs: nofree nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  %2 = load i32, i32* %1, align 4
  br label %3

3:                                                ; preds = %0, %3
  %4 = phi i32 [ %2, %0 ], [ %6, %3 ]
  %5 = phi i32 [ 0, %0 ], [ %8, %3 ]
  %6 = add i32 %4, 1
  %7 = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i32 noundef %5)
  %8 = add nuw nsw i32 %5, 1
  %9 = icmp eq i32 %8, 21
  br i1 %9, label %10, label %3, !llvm.loop !3

10:                                               ; preds = %3
  store i32 %6, i32* %1, align 4
  %11 = load i32, i32* %1, align 4
  %12 = icmp ugt i32 %11, 19
  br i1 %12, label %13, label %15

13:                                               ; preds = %10
  %14 = call i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([21 x i8], [21 x i8]* @loopttip, i64 0, i64 0), i32 %11)
  br label %15

15:                                               ; preds = %10, %13
  ret i32 0
}

; Function Attrs: nofree nounwind
declare dso_local noundef i32 @printf(i8* nocapture noundef readonly, ...) local_unnamed_addr #1

attributes #0 = { nofree nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { nofree nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 2}
!2 = !{!"clang version 15.0.0 (https://github.com/llvm/llvm-project.git a4d2943bf6b350ea62745ab25c7dcc2f1d17a470)"}
!3 = distinct !{!3, !4, !5}
!4 = !{!"llvm.loop.mustprogress"}
!5 = !{!"llvm.loop.unroll.disable"}

; ModuleID = '../llvm-test/test.ll.out'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@.str = private unnamed_addr constant [5 x i8] c"%d \0A\00", align 1
@loopttip = private unnamed_addr constant [21 x i8] c"many loop count %d \0A\00", align 1
@loopttip.1 = private unnamed_addr constant [21 x i8] c"many loop count %d \0A\00", align 1

; Function Attrs: nofree nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #0 {
  %loopcount = alloca i32, align 4
  store i32 0, i32* %loopcount, align 4
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  %2 = load i32, i32* %1, align 4
  br label %3

3:                                                ; preds = %3, %0
  %4 = phi i32 [ %2, %0 ], [ %6, %3 ]
  %5 = phi i32 [ 0, %0 ], [ %8, %3 ]
  %loadLoopCount = load i32, i32* %loopcount, align 4;这里自增循环次数
  %"inc loopCount" = add i32 %loadLoopCount, 1
  store i32 %"inc loopCount", i32* %loopcount, align 4
  %6 = add i32 %4, 1
  %7 = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i32 noundef %5)
  %8 = add nuw nsw i32 %5, 1
  %9 = icmp eq i32 %8, 21
  br i1 %9, label %10, label %3, !llvm.loop !3

10:                                               ; preds = %3
  %loadLoopCount1 = load i32, i32* %loopcount, align 4
  %Cmp = icmp uge i32 %loadLoopCount1, 20				;这里判断循环次数
  br i1 %Cmp, label %11, label %12

11:                                               ; preds = %10
  %printlog = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @loopttip.1, i32 0, i32 0), i32 %loadLoopCount1)
  br label %12

12:                                               ; preds = %10, %11
  store i32 %6, i32* %1, align 4
  %13 = load i32, i32* %1, align 4
  %14 = icmp ugt i32 %13, 19
  br i1 %14, label %15, label %17

15:                                               ; preds = %12
  %16 = call i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([21 x i8], [21 x i8]* @loopttip, i64 0, i64 0), i32 %13)
  br label %17

17:                                               ; preds = %15, %12
  ret i32 0
}

; Function Attrs: nofree nounwind
declare dso_local noundef i32 @printf(i8* nocapture noundef readonly, ...) local_unnamed_addr #1

attributes #0 = { nofree nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { nofree nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 2}
!2 = !{!"clang version 15.0.0 (https://github.com/llvm/llvm-project.git a4d2943bf6b350ea62745ab25c7dcc2f1d17a470)"}
!3 = distinct !{!3, !4, !5}
!4 = !{!"llvm.loop.mustprogress"}
!5 = !{!"llvm.loop.unroll.disable"}

集成到Clang工具中

//clang/lib/CodeGen/BackendUtil.cpp
void EmitAssemblyHelper::RunOptimizationPipeline(
    BackendAction Action, std::unique_ptr<raw_pwrite_stream> &OS,
    std::unique_ptr<llvm::ToolOutputFile> &ThinLinkOS) {
    ....
     PB.registerScalarOptimizerLateEPCallback(
     [](FunctionPassManager &FPM, OptimizationLevel Level) {
       FPM.addPass(HelloWorldPass());
     });
    ....
    }

后记

本篇文章只是初试LLVM做的一个练习课题,仅供参考。

你可能感兴趣的:(c++,LLVM)