死循环检测

  • 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 4store i32 0, i32* %1, align 4%2 = load i32, i32* %1, align 4br label %33:                                                ; 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, 21br i1 %9, label %10, label %3, !llvm.loop !310:                                               ; preds = %3store i32 %6, i32* %1, align 4%11 = load i32, i32* %1, align 4%12 = icmp ugt i32 %11, 19br i1 %12, label %13, label %1513:                                               ; 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 %1515:                                               ; preds = %10, %13ret i32 0
}; Function Attrs: nofree nounwind
declare dso_local noundef i32 @printf(i8* nocapture noundef readonly, ...) local_unnamed_addr #1attributes #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 4store i32 0, i32* %loopcount, align 4%1 = alloca i32, align 4store i32 0, i32* %1, align 4%2 = load i32, i32* %1, align 4br label %33:                                                ; 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, 1store 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, 21br i1 %9, label %10, label %3, !llvm.loop !310:                                               ; preds = %3%loadLoopCount1 = load i32, i32* %loopcount, align 4%Cmp = icmp uge i32 %loadLoopCount1, 20              ;这里判断循环次数br i1 %Cmp, label %11, label %1211:                                               ; 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 %1212:                                               ; preds = %10, %11store i32 %6, i32* %1, align 4%13 = load i32, i32* %1, align 4%14 = icmp ugt i32 %13, 19br i1 %14, label %15, label %1715:                                               ; 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 %1717:                                               ; preds = %15, %12ret i32 0
}; Function Attrs: nofree nounwind
declare dso_local noundef i32 @printf(i8* nocapture noundef readonly, ...) local_unnamed_addr #1attributes #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做的一个练习课题,仅供参考。

【LLVM实践】死循环插桩检测相关推荐

  1. 编译器 LLVM Clang原理与实战 制作自己的编译器 source-to-source 源代码转换 编译遍 compile pass 代码插桩

    编译器 LLVM Clang原理与实战 参考1 clang LLVM CMU 教案 深入剖析-iOS-编译-Clang-LLVM LLVM_proj LLVM编程索引 llvm源码浏览带跳转 llvm ...

  2. 效能优化实践:C/C++单元测试万能插桩工具

    作者:mannywang,腾讯安全平台后台开发 研发效能是一个涉及面很广的话题,它涵盖了软件交付的整个生命周期,涉及产品.架构.开发.测试.运维,每个环节都可能影响顺畅.高质量地持续有效交付.在腾讯安 ...

  3. 插桩valgrind_基于动态插桩的CC++内存泄漏检测工具的设计与实现.pdf

    基于动态插桩的CC++内存泄漏检测工具的设计与实现.pdf 第32卷第6期 计 算 机 应 用 研 究 V01.32No.6 20l5年 6月 ApplicationResearchofCompute ...

  4. Android ASM 插桩实践

    上一章知道了如何获取 class 文件,那该如何进行插桩呢?本章告诉你! 什么是 ASM? ASM 是一个字节码操作库,它可以直接修改已经存在的 class 文件或者生成 class 文件. ASM ...

  5. vm磁盘映射 不能启动_iOS 启动优化之Clang插桩实现二进制重排

    前言 原文作者:李斌同学 原文链接:https://juejin.im/post/6844904130406793224 自从抖音团队分享了这篇 抖音研发实践:基于二进制文件重排的解决方案 APP启动 ...

  6. 深入探索编译插桩技术(一、编译基础)

    前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~. 现如今,Gradle + 编译插桩 的应用场景越来越多,无论是 各种性能优化中的插件工具制作 ...

  7. iOS启动优化-二进制重排与Clang插桩

    二进制重排与Clang插桩 背景 优化方案 准备 认识 插件安装 启动优化 操作系统 演进史 进程通信 二进制重排 .Order文件 小节问题 Clang插桩 Clang插桩配置 Clang插桩原理( ...

  8. 吹爆系列:Android 插桩之美,全面掌握~

    作者:阿明的小蝴蝶 插桩 插桩是什么?你在开发中有用过插桩的技术吗? 所谓的插桩就是在代码编译期间修改已有的代码或者生成新代码. 插桩具体在编译的哪个流程介入呢? 插桩的作用与场景 代码生成 代码监控 ...

  9. Android 插桩之美,全面掌握

    链接: https://www.yuque.com/docs/share/b49a3274-17d9-4c87-815a-4a13ab72e4e0 1 插桩 插桩是什么?你在开发中有用过插桩的技术吗? ...

  10. Android AOP之字节码插桩

    背景   本篇文章基于<网易乐得无埋点数据收集SDK>总结而成,关于网易乐得无埋点数据采集SDK的功能介绍以及技术总结后续会有文章进行阐述,本篇单讲SDK中用到的Android端AOP的实 ...

最新文章

  1. VS2013的项目转到VS2010需要修改的
  2. 【Python学习系列三】Windows下Python第三方常用库安装
  3. 最快60秒完成新冠病毒核酸对比 阿里云向社会免费开放基因计算服务
  4. vue 时间戳 格式转化(插件化) - 封装篇
  5. 【入门篇】黑盒测试基础
  6. SQL Server 2005 应用 全文搜索
  7. SqlServer彻底卸载,适用于任何版本,亲测了n次都可用
  8. chartControl控件常用属性总结
  9. JXTA——JAVA P2P网络编程技术(入门篇)
  10. 如何查找Oracle数据库安全漏洞的补丁
  11. 搜集ipv6 DNS服务器信息,IPV6 DNS服务器地址列表
  12. 怎样激火一个儒雅随和的程序员呢
  13. Unity Shader 实现简单的压扁效果
  14. 应用程序无法正常启动0xc00007b的解决(二)
  15. AT32F415 AT32F421 ERTC 时间戳的使用
  16. 专科毕业,从0到1400star,从阮一峰周刊到尤雨溪推荐的开源项目总结
  17. 【ROS-Navigation】—— DWA路径规划算法解析
  18. 旧瓶装新酒系列-Python每日多题(一)
  19. 2022电大国家开放大学网上形考任务-纳税筹划非免费(非答案)
  20. 如何在年薪10万的上班族让自己第二年达到20万以上?

热门文章

  1. kali Linux桌面环境切换
  2. 怎样选择合适的电流继电器
  3. 嵌入式系统开发必读经典书目
  4. c语言各类型数据混合运算
  5. 银行电话营销数据分析
  6. 大数据的核心价值是什么,主要表现在哪几方面?
  7. 二行代码解决全部网页木马(含iframe/script木马)
  8. 《新唐书·李白传》原文及翻译
  9. memcached入门使用
  10. 在程序员职业中,外包的薪资真的更高吗?