llvm libtooling 遍历变量,函数,类

背景

由于项目需要,需要解析cpp代码,经调查llvm,clang前端有这样的库可以使用,找到libtooling,功能强大,有完善的文档,于是写一系列的文章介绍libtooling库的使用,

安装llvm

欢迎关注专栏

llvm clang

前置知识

AST是什么:

LLVM Clang AST是指Clang编译器前端生成的抽象语法树(Abstract Syntax Tree)
解析的代码,被表示成树状结构,libtooling从抽象语法树解析
 

使用clang命令输出AST

clang -Xclang -ast-dump -fsyntax-only test.cc

test.cc内容

#include <stdio.h>
void test(int a, int b) {
  int c = a + b -3
}

clang输出

`-FunctionDecl 0x7fc46e075608 <line:2:1, line:4:1> line:2:6 test 'void (int, int)'
  |-ParmVarDecl 0x7fc46e075488 <col:11, col:15> col:15 used a 'int'
  |-ParmVarDecl 0x7fc46e075508 <col:18, col:22> col:22 used b 'int'
  `-CompoundStmt 0x7fc46e075880 <col:25, line:4:1>
    `-DeclStmt 0x7fc46e075868 <line:3:3, col:19>
      `-VarDecl 0x7fc46e075730 <col:3, col:18> col:7 c 'int' cinit
        `-BinaryOperator 0x7fc46e075848 <col:11, col:18> 'int' '-'
          |-BinaryOperator 0x7fc46e075808 <col:11, col:15> 'int' '+'
          | |-ImplicitCastExpr 0x7fc46e0757d8 <col:11> 'int' <LValueToRValue>
          | | `-DeclRefExpr 0x7fc46e075798 <col:11> 'int' lvalue ParmVar 0x7fc46e075488 'a' 'int'
          | `-ImplicitCastExpr 0x7fc46e0757f0 <col:15> 'int' <LValueToRValue>
          |   `-DeclRefExpr 0x7fc46e0757b8 <col:15> 'int' lvalue ParmVar 0x7fc46e075508 'b' 'int'
          `-IntegerLiteral 0x7fc46e075828 <col:18> 'int' 3

其中FunctionDecl就是函数节点,VarDecl是变量节点

其它:关于FrontendAction、ASTConsumer、RecursiveASTVisitor的介绍在这里

https://blog.youkuaiyun.com/weixin_43837016/article/details/140134391

代码

以下代码使用libtooling 遍历变量,函数,类等

#include <clang/AST/AST.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/Tooling/Tooling.h>

#include <iostream>

using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
using namespace llvm;
using namespace std;

const char *toolOverview = "demo";

static cl::OptionCategory optCat("demo Options");

bool checkIfSystem(const Decl *D)
{
    if (!D) {
        return true;
    }
    
    ASTContext & context = D->getASTContext();
    SourceManager &srcMgr = context.getSourceManager();
    SourceLocation srcLoc = D->getLocation();
    
    SourceLocation beginSrcLoc = D->getBeginLoc();
    SourceLocation expansionLoc = srcMgr.getExpansionLoc(beginSrcLoc);
    if (srcMgr.isInSystemHeader(expansionLoc)) {
        return true;
    }

    if (srcMgr.getFileCharacteristic(srcLoc) == SrcMgr::C_System) {
        return true;
    }

    if (srcMgr.getFileCharacteristic(srcLoc) == SrcMgr::C_ExternCSystem) {
        return true;
    }

    return false;
}

class MyVisitor : public RecursiveASTVisitor<MyVisitor> {
public:
    explicit MyVisitor() {}

    bool VisitVarDecl(VarDecl *VD) {
        if (checkIfSystem(VD)) {
            return true;
        }
        std::cout << "Variable name: " << VD->getName().str() << std::endl;

        return true;
    }

    bool VisitFunctionDecl(const FunctionDecl * FD) {
        if (checkIfSystem(FD)) {
            return true;
        }
        std::cout << "FunctionDecl name: " << FD->getName().str() << std::endl;

        return true;
    }

    bool VisitCXXRecordDecl(CXXRecordDecl *CXXRD) {
        if (checkIfSystem(CXXRD)) {
            return true;
        }
        std::cout << "CXXRecordDecl name: " << CXXRD->getName().str() << std::endl;

        return true;
    }
};

class MyConsumer : public ASTConsumer {
    
public:
    explicit MyConsumer(ASTContext *Context) : Context(Context) {}

    void HandleTranslationUnit(ASTContext &Context) override {
        MyVisitor visitor;
        visitor.TraverseDecl(Context.getTranslationUnitDecl());
    }

private:
    ASTContext *Context;
};

class MyAction : public ASTFrontendAction {
public:
    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef) override {
        return std::make_unique<MyConsumer>(&CI.getASTContext());

    }

};

int main(int argc, const char *argv[])
{

    auto OptionsParserExpected = CommonOptionsParser::create(argc, argv, optCat, cl::OneOrMore, toolOverview);
    if (!OptionsParserExpected) {
        llvm::errs() << "Failed to parse options: " << toString(OptionsParserExpected.takeError()) << "\n";
        return 1;
    }
    
    CommonOptionsParser &OptionsParser = *OptionsParserExpected;
    ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());
    Tool.run(newFrontendActionFactory<MyAction>().get());
    

    return 0;
}

cmake文件:

cmake_minimum_required(VERSION 3.4.3)
project(demo2)
set(CMAKE_CXX_COMPILER "clang++")
find_package(Clang REQUIRED)    
include_directories(${Clang_INCLUDE_DIRS} ${LLVM_MAIN_INCLUDE_DIR})  

add_executable(demo2
demo2.cpp
  )

target_link_libraries(demo2
  PUBLIC
  clangAST
  clangASTMatchers
  clangBasic
  clangFrontend
  clangSerialization
  clangTooling
  )

被解析的cpp代码:

#include <string>
#include <iostream>
using namespace std;

class Test {
public:
	void p() {};
};


int main(int argc, const char *argv[]){
	string s = "abc";
	return 0;
}

打印结果:


CXXRecordDecl name: Test
FunctionDecl name: p
FunctionDecl name: main
Variable name: argc
Variable name: argv
Variable name: s

引用:

Clang AST

### 关于 LLVM Slicer 工具 LLVM 是一个模块化和可重用的编译器以及工具链技术集合,支持多种编程语言并提供丰富的功能扩展接口。对于切片分析(slicing analysis),LLVM 社区提供了多个基于其基础设施构建的相关工具和技术实现。 #### 1. **LLVM-Slicer 的背景** LLVM 并未在其官方文档中直接提及名为 `llvm-slicer` 的独立工具[^1]。然而,在学术界和开源社区中存在一些第三方开发的插件或项目,它们利用 LLVM 提供的基础框架实现了程序依赖图(Program Dependence Graph, PDG)生成、控制流分析等功能,并进一步用于代码切片操作。这些工具通常作为 LLVM Pass 或外部脚本形式发布。 #### 2. **获取与安装** 由于 `llvm-slicer` 不属于 LLVM 官方发行版的一部分,因此需要通过以下方式寻找合适的资源: - 访问 GitHub 等开源平台搜索关键词 “LLVM slicer”。例如,某些研究团队可能已经公开其实现细节及其源码链接[^2]。 - 如果目标是执行静态切片,则可以考虑使用其他成熟的解决方案如 ROSE 编译器框架或者 Dyninst 动态 instrumentation 库来完成似任务。 #### 3. **基本概念说明** ##### (a). 静态 vs 动态切片 - **静态切片**是指仅依据给定输入程序结构而无需实际运行即可得出的结果;它主要依靠语法树遍历算法配合数据流向追踪机制达成目的。 - **动态切片**则需结合具体测试案例的实际执行路径来进行更精确地裁剪处理。 ##### (b). 控制依赖关系 & 数据依赖关系 为了有效实施软件切片过程,必须清楚理解两种核心依赖型: - *Control Dependency*: 描述分支条件如何影响后续指令序列的选择; - *Data Dependency*: 表达变量赋值语句之间存在的因果联系。 以下是简单的 C++ 示例展示上述定义的应用场景: ```cpp int main() { int a = 0; if(a > 5){ printf("Greater than five\n"); } } ``` 在此例子当中,“printf”调用就完全受控于前面那个比较表达式的评估结果——即形成了典型的控制依存现象。 #### 4. **典型应用场景举例** 假设我们希望从大型 legacy system 中提取特定函数的功能片段以便单独调试验证。此时借助专门设计好的 llvm-based slicing tools 就显得尤为重要了。下面给出一段伪代码表示整个流程概览: ```bash $ clang -emit-llvm -c source.c -o source.bc # Step A: Generate bitcode file. $ opt -load path/to/slicer.so -slice <criteria> source.bc -o sliced.bc # Step B: Apply custom pass performing slice operation based on user-defined criteria. $ llc sliced.bc -o output.s # Optional step converting back into assembly language form if necessary. ``` 注意这里 `-load` 参数后面接的是自定义 so 文件位置地址,代表加载预先编写完毕的那个包含切片逻辑的核心组件。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值