【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 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实践】死循环插桩检测相关推荐
- 编译器 LLVM Clang原理与实战 制作自己的编译器 source-to-source 源代码转换 编译遍 compile pass 代码插桩
编译器 LLVM Clang原理与实战 参考1 clang LLVM CMU 教案 深入剖析-iOS-编译-Clang-LLVM LLVM_proj LLVM编程索引 llvm源码浏览带跳转 llvm ...
- 效能优化实践:C/C++单元测试万能插桩工具
作者:mannywang,腾讯安全平台后台开发 研发效能是一个涉及面很广的话题,它涵盖了软件交付的整个生命周期,涉及产品.架构.开发.测试.运维,每个环节都可能影响顺畅.高质量地持续有效交付.在腾讯安 ...
- 插桩valgrind_基于动态插桩的CC++内存泄漏检测工具的设计与实现.pdf
基于动态插桩的CC++内存泄漏检测工具的设计与实现.pdf 第32卷第6期 计 算 机 应 用 研 究 V01.32No.6 20l5年 6月 ApplicationResearchofCompute ...
- Android ASM 插桩实践
上一章知道了如何获取 class 文件,那该如何进行插桩呢?本章告诉你! 什么是 ASM? ASM 是一个字节码操作库,它可以直接修改已经存在的 class 文件或者生成 class 文件. ASM ...
- vm磁盘映射 不能启动_iOS 启动优化之Clang插桩实现二进制重排
前言 原文作者:李斌同学 原文链接:https://juejin.im/post/6844904130406793224 自从抖音团队分享了这篇 抖音研发实践:基于二进制文件重排的解决方案 APP启动 ...
- 深入探索编译插桩技术(一、编译基础)
前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~. 现如今,Gradle + 编译插桩 的应用场景越来越多,无论是 各种性能优化中的插件工具制作 ...
- iOS启动优化-二进制重排与Clang插桩
二进制重排与Clang插桩 背景 优化方案 准备 认识 插件安装 启动优化 操作系统 演进史 进程通信 二进制重排 .Order文件 小节问题 Clang插桩 Clang插桩配置 Clang插桩原理( ...
- 吹爆系列:Android 插桩之美,全面掌握~
作者:阿明的小蝴蝶 插桩 插桩是什么?你在开发中有用过插桩的技术吗? 所谓的插桩就是在代码编译期间修改已有的代码或者生成新代码. 插桩具体在编译的哪个流程介入呢? 插桩的作用与场景 代码生成 代码监控 ...
- Android 插桩之美,全面掌握
链接: https://www.yuque.com/docs/share/b49a3274-17d9-4c87-815a-4a13ab72e4e0 1 插桩 插桩是什么?你在开发中有用过插桩的技术吗? ...
- Android AOP之字节码插桩
背景 本篇文章基于<网易乐得无埋点数据收集SDK>总结而成,关于网易乐得无埋点数据采集SDK的功能介绍以及技术总结后续会有文章进行阐述,本篇单讲SDK中用到的Android端AOP的实 ...
最新文章
- VS2013的项目转到VS2010需要修改的
- 【Python学习系列三】Windows下Python第三方常用库安装
- 最快60秒完成新冠病毒核酸对比 阿里云向社会免费开放基因计算服务
- vue 时间戳 格式转化(插件化) - 封装篇
- 【入门篇】黑盒测试基础
- SQL Server 2005 应用 全文搜索
- SqlServer彻底卸载,适用于任何版本,亲测了n次都可用
- chartControl控件常用属性总结
- JXTA——JAVA P2P网络编程技术(入门篇)
- 如何查找Oracle数据库安全漏洞的补丁
- 搜集ipv6 DNS服务器信息,IPV6 DNS服务器地址列表
- 怎样激火一个儒雅随和的程序员呢
- Unity Shader 实现简单的压扁效果
- 应用程序无法正常启动0xc00007b的解决(二)
- AT32F415 AT32F421 ERTC 时间戳的使用
- 专科毕业,从0到1400star,从阮一峰周刊到尤雨溪推荐的开源项目总结
- 【ROS-Navigation】—— DWA路径规划算法解析
- 旧瓶装新酒系列-Python每日多题(一)
- 2022电大国家开放大学网上形考任务-纳税筹划非免费(非答案)
- 如何在年薪10万的上班族让自己第二年达到20万以上?