首页 > 编程语言 >【源码解析】C/C++开源代码解析引擎

【源码解析】C/C++开源代码解析引擎

时间:2024-08-21 14:58:00浏览次数:13  
标签:源代码 代码 C++ 编译器 源码 Clang 2.1 解析 LLVM

1. 背景说明

针对Simulink或其他MBD环境的模型生成代码,及其他的外部C/C++代码工程,做相应的后端代码优化处理工作,例如如下场景,

  • 统计代码内的全局变量声明及其内存占用情况;

  • 提取代码内的逻辑判断条件结合Z3 Prover定理证明器进行形式化验证;

  • ...

因此需要对C/C++代码进行语法分析、代码分析、抽象语法树(AST)生成等操作,特此调研开源的C/C++代码解析引擎,并形成此文档。

2. 常见的开源C/C++代码解析引擎

2.1 Clang/LLVM

  • 简介:Clang是一个C、C++和Objective-C的编译器前端,基于LLVM项目。它不仅提供了编译器功能,还提供了丰富的API用于代码分析和工具开发。

  • 功能:Clang提供了抽象语法树(AST)接口、语义分析、代码格式化、重构等功能。

  • 优势:强大的诊断和错误信息、模块化设计、广泛的社区支持。

  • 授权:Apache License v2.0

2.1.1 LLVM项目

LLVM(Low-Level Virtual-Machine)是一个模块化和可重用的编译器和工具链技术项目。最初由克里斯·拉特纳(Chris Lattner)于2000年在伊利诺伊大学厄巴纳-香槟分校的博士项目中开发,现在由LLVM基金会和全球社区维护。LLVM项目包含一组编译器基础设施,分别用于开发前端编译器、中间语言优化和后端代码优化工具。

2.1.1.1 LLVM的核心组件和架构
2.1.1.1.1 LLVM Core

LLVM核心库,提供了中间表示(IR) 的定义、基本块和函数的表示,以及一系列用于操作和分析这些结构的工具。LLVM IR是一种低级、高度优化的语言独立中间表示,可以跨平台编译。

2.1.1.1.2 Clang

Clang是LLVM项目的一个C、C++和Objective-C编译器前端。它提供了代码解析、AST生成和代码分析等功能,旨在提供快速的编译速度和优秀的诊断信息。

2.1.1.1.3 LLVM Optimizer

LLVM优化器利用LLVM IR进行各种优化处理,如常量传播、死代码消除、循环优化等。这些优化可以在编译时(静态优化)或运行时(JIT编译时)进行。

2.1.1.1.4 LLVM Code Generator

LLVM后端部分,它负责将LLVM IR转换为特定架构的机器代码。LLVM支持多种硬件架构,包括x86、ARM、PowerPC、RISC-V等。

2.1.1.1.5 LLVM Runtime Libraries

LLVM项目还提供了一些运行时库,如编译时支持库(libc++、libc++abi等),用于支持生成的代码。

2.1.1.1.6 LLVM工具和实用程序

包括调试器(lldb)、二进制工具(llvm-objdump、llvm-ar等)、静态分析工具(scan-build)等。

2.1.1.2 LLVM的特点
2.1.1.2.1 模块化设计

LLVM的各个部分是高度模块化的,允许开发者根据需求组合使用。可以单独使用Clang作为前端编译器,也可以利用LLVM优化器和代码生成器。

2.1.1.2.2 语言独立性

LLVM IR是一种通用的中间表示,可以用来表示各种高级编程语言的编译结果,这使得LLVM可以支持多种语言前端。

2.1.1.2.3 广泛的硬件支持

LLVM具有广泛的硬件架构支持,这使得他成为各平台开发的理想选择。

2.1.1.2.4 实时编译(JIT)

LLVM支持及时编译(JIT),这使得它适用于动态语言的执行引擎开发和运行时优化。

2.1.1.2.5 开源社区

LLVM是开源项目,有一个活跃的社区贡献者和用户。它被广泛用于学术研究和工业界的编译器开发。

2.1.1.3 LLVM的应用领域
2.1.1.3.1 编译器开发

LLVM被广泛用于开发各种编程语言的编译器,如Rust编译器(rustc)、Julia编译器、Swift编译器等。

2.1.1.3.2 工具链和静态分析

LLVM的工具链被用于开发各种代码分析工具、格式化工具和静态分析器。

2.1.1.3.3 实时编译和运行时优化

由于支持JIT,LLVM被用于实时编译和运行时优化,应用在游戏引擎、数据库、浏览器引擎等领域。

2.1.1.3.4 研究和教育

由于模块化和开放性,LLVM常被用于编译器研究和教育领域。

2.1.1.3.5 工业应用

许多公司在器开发工具链中使用LLVM,包括Apple、Google、Microsoft、Sony等。

2.2 GCC(GNU Compiler Collection)

  • 简介:GCC是一个编译器系统,支持多种编译语言,其中包括C和C++。它的GCC内部表示(GIMPLE、RTL等)也可以用于代码分析和优化。

  • 功能:除了编译功能外,GCC还提供了静态分析工具(如GCC Static Analyzer)、插件机制等。

  • 优势:支持广泛的架构和语言,经过长时间验证的工具链。

  • 授权:GPL 2.0 License

2.3 LibClang

  • 简介:LibClang是Clang的C接口,提供了一个相对简单的接口来解析C/C++代码,并访问Clang的AST。

  • 功能:提供了基础的代码解析、符号查找、诊断信息等功能,适合构建简单的代码分析工具。

  • 优势:使用简单,直接获取Clang的强大功能。

  • 授权:Apache License v2.0

2.4 CppCheck

  • 简介:CppCheck是一个静态代码分析工具,专门用于查找C/C++代码中的错误。

  • 功能:检查常见的编程错误、内存泄漏、未定义行为等;

  • 优势:专注于安全性和代码质量,轻量级,易于集成。

  • 授权:GPL-3.0 license

2.5 Ctags

  • 简介:Ctags生成标签文件,该文件记录了源代码中各种标识符的位置。支持C、C++及其他多种语言。

  • 功能:支持符号导航、代码搜索等功能,常与编译器集成。

  • 优势:支持广泛的语言和编译器,简单而高效。

  • 授权:GPL-2.0 license

2.6 Clangd

  • 简介:Clangd是基于Clang的语言服务器协议(LSP)实现,提供了对C/C++代码的智能感知支持。

  • 功能:自动完成、跳转定义、文档查找、诊断信息、代码格式化等。

  • 优势:基于Clang,提供了丰富的功能和良好的编辑器集成支持。

  • 授权:Apache-2.0 license

2.7 Bear

  • 简介:Bear是一个生成Clang编译数据库的工具。它拦截编译命令,生成用于Clang工具链的’json’文件。

  • 功能:主要用于生成编译数据库,以便在Clang工具链中使用。

  • 优势:提供方便的方式为Clang工具链生成编译信息。

  • 授权:GPL-3.0 license

3. ANTLR4与LibClang代码解析的优劣势

此处的代码解析,主要针对于Sysplorer/Sysblock模型生成的C语言代码,以及其他C/C++工程代码。且常见的开源代码解析引擎,相当部分是Clang项目或相关工具链,特此选择ANTLR4与LibClang进行对比。

ANTLR4(Another Tool For Language Recognition)是一个功能强大的解析器生成工具,用于从上下文无关语法(Context-Free Grammars,CFG)生成解析器。ANTLR4特别适合构建语言翻译工具、编译器、解释器等,它支持多种目标编程语言,包括Java、C#、JavaScript、Python等。

ANTLR4和LibClang是两种非常不同的工具,它们都可以用于C/C++代码解析,但在功能、试用场景和优劣势方面有着显著差异。

3.1 ANTLR4

3.1.1 优势

3.1.1.1 通用性强

ANTLR4是一个通用的解析器生成工具,不仅限于C/C++,还可以解析多种自定义语言或数据格式。可以通过编写语法文件(.g4)来定义任意语言的语法和词法规则。

3.1.1.2 灵活性

用户可以完全控制解析的方式和生成的解析树结构。可以为不同语言的特定需求定制解析器,添加特定的语义操作。

3.1.1.3 支持多种目标语言

ANTLR4支持生成多种目标语言的解析器,包括Java、C#、Python等,便于集成到不同编程语言的项目中。

3.1.1.4 Listener和Visitor模式

ANTLR4提供了方便的Listener和Visitor模式,用于遍历和操作解析树。这使得分析和转换代码变得更加直观和模块化。

3.1.2 劣势

3.1.2.1 手动编写语法文件

需要手动编写C/C++语言的语法文件,这对于复杂的语言(如C++)来说可能非常复杂和困难。要实现完整的C++语法支持是一项巨大的工程。

3.1.2.2 缺乏内置的语义分析

ANTLR4主要关注词法和语法分析,缺乏对C/C++语言特定的语义分析支持,例如符号解析、类型检查等。这需要额外的工作来实现。

3.1.2.3 没有内置的代码补全和错误修正功能

这些功能需要手动实现,与LibClang相比,需要更多的开发工作。

3.2 LibClang

3.2.1 优势

3.2.1.1 原生支持C/C++

LibClang是Clang的C接口,是一个专门为C/C++设计的工具。它完全理解C/C++的复杂语法和语义,支持包括模版、宏等复杂特性。

3.2.1.2 完整的AST生成

提供完整的抽象语法树(AST),精确地表示C/C++代码的结构。可以访问详细的类型信息、作用域、符号等。

3.2.1.3 内置语义分析

提供内置的语义分析功能,如符号解析、类型检查等。这对于开发人员需要深入理解代码语义的工具非常有用。

3.2.1.4 强大的工具集成

LibClang是Clang工具链的一部分,可以与Clang的其他工具(如代码格式化、静态分析、编译器等)紧密集成。

3.2.1.5 支持IDE集成

由于提供了精确的语法和语义信息,LibClang非常适用于实现IDE功能,如代码补全、跳转到定义、代码重构等。

3.2.2 劣势

3.2.2.1 语言限制

LibClang主要支持C、C++和Objective-C。如果需要解析其他语言,LibClang不是合适的工具。

3.2.2.2 复杂性和依赖性

LibClang依赖于Clang的完整工具链,使用和部署可能比其他工具更复杂。

3.2.2.2.1 配合Clang编译环境使用

LibClang是Clang的C接口库,它依赖于Clang的核心库和工具链来解析和处理C/C++代码。

使用LibClang接口通常需要配合Clang编译环境,这包括Clang编译器、相关库和工具,以及正确的编译环境设置。

以下说明了需要Clang编译环境支持的原因:

依赖Clang库

LibClang接口是Clang项目的一部分,它直接依赖于Clang的其他核心库(如’libclangAST’、’libclangParse’等)。这些库提供了解析、语法分析、语义分析等功能。

编译器工具链

LibClang常用于需要精确编译信息的场景,如IDE集成、代码分析等。它通常需要访问编译数据库(‘compile_commands.json’),该文件记录了如何编译项目的每个文件。生成这个文件通常是通过Clang的工具(如’bear’或’cmake’等)实现的。

Clang的特性和优化

Clang提供了一些特定的编译选项和优化(如诊断、代码完成等),这些特定在使用LibClang时也可以受益。

3.2.2.3 非易用的API

虽然LibClang提供了C接口,但操作AST和其他数据结构可能相对复杂。理解Clang的内部数据结构和设计需要一定的学习曲线。

3.3 总结

ANTLR更适合用于需要定制化的解析器开发,尤其是需要支持多种语言或自定义语言的场景。它的灵活性和通用性是其最大的优势,但在处理复杂语言(如C++)是需要额外的工作。

LibClang则是为C/C++量身定制的工具,能够精确地处理C/C++的所有语法特性和语义特性,适用于需要高精度解析和语义分析的工具,如IDE和静态分析器。它的深度和广度使其在C/C++代码解析领域非常强大,但也限制了其适用范围。

3.3.1 适用的开发流程

如果目标是快速、准确地解析C/C++语言代码并获取丰富的语义信息,LibClang是更合适的选择。对于需要高度定制化或跨语言支持的解析需求,且愿意投入较多开发工作量的项目,ANTLR4则提供了更大的灵活性。

4. Clang编译环境的配置与验证

下面介绍在Visual Studio开发环境中配置Clang编译器的基本说明。

4.1 在Visual Studio中使用Clang编译器

Visual Studio支持使用不同的编译器,包括Clang。在Windows上,Clang通常作为LLVM工具链的一部分安装,可以使用VS的“工具集”(toolset)来切换到Clang编译环境。

4.1.1 安装Clang编译环境

  1. 下载并安装LLVM,确保Clang工具链已安装;

  2. 将LLVM的bin目录添加到系统的PATH环境变量中,以便Visual Studio可以找到’clang-cl.exe’;

  3. 或者使用Visual Studio Installer配置Clang编译环境,详情参考,Visual Studio 项目中的 Clang/LLVM 支持

图 4-1 Visual Studio安装程序的Clang环境

4.1.2 配置Visual Studio使用Clang编译环境

4.1.2.1 创建或打开项目

创建一个新的Visual Studio项目或打开现有项目。

4.1.2.2 更改项目的工具集
  1. 打开项目的“属性”窗口;

  2. 在“配置属性”下,选择“常规”;

  3. 在“平台工具集”(Platform Toolset)下拉菜单中选择LLVM(clang-cl)或者其他表示Clang的选项。

图 4-2 切换至Clang编译器

4.1.2.3 配置编译和链接器设置

在“C/C++”和“链接器”设置下,可以配置与Clang相关的选项,这些设置与使用MSVC编译器时类似。

图 4-3 Clang编译环境的"C/C++"设置

图 4-4 Clang编译环境的"链接器"设置

4.1.2.4 编译和运行

配置完成后,可以像平常一样编译和运行项目。Clang编译器会负责代码的编译和链接。

4.1.2.5 注意事项
  • 兼容性:Clang可能对一些特定的MSVC扩展不完全兼容,尤其是涉及Windows特定API和特性时。需要测试和调整代码以确保兼容性。

  • 诊断信息:Clang提供了不同的诊断和告警信息,可以在项目设置中进一步定制和调整。

  • 经过验证,MSVC编译环境下可正常编译和运行LibClang程序,如下:

    • Microsoft Visual Studio 2019,版本11.32;

    • LLVM工具集,版本1.8。

4.2 Visual Studio工程项目验证示例

4.2.1 代码解析的C代码头文件

以下为执行代码解析的C代码头文件,header_de.h。

#pragma once

typedef char MwbChar;
typedef int MwbInt;
typedef unsigned int MwbUInt;
typedef float MwbFloat;
typedef double MwbDouble;
typedef int MwbBool;
typedef size_t MwbSize;
typedef signed char MwbInt8;
typedef unsigned char MwbUInt8;
typedef short MwbInt16;
typedef unsigned short MwbUInt16;
typedef int MwbInt32;
typedef unsigned int MwbUInt32;

double gFloat = 0.0f;

struct model81SdModel81 {
  MwbDouble m_sample;
  MwbInt m_errorCode;
  MwbSolveStage m_solveStage;
  MwbSolveStepStage m_solveStepStage;
  MwbDouble m_startTime;
  MwbInt m_timeTickCount;
  MwbDouble m_curTime;
};


struct model81IdModel81U {
  struct Bus1 inport;
  Enum1 inport1;
  MwbInt16 inport2;
};

如上代码段所示,待解析的C代码头文件包含如下信息,类型定义typedef语句、变量定义语句,以及结构体声明语句。

4.2.2 使用LibClang解析C代码示例

#include <iostream>
#include <clang-c/Index.h>  // This is libclang.

#define FILE_PATH  "C:\\Users\\TR\\source\\repos\\Clang\\header_de.h"

// 输出变量类型
void PrintVarDecl(CXCursor cursor, CXCursorKind cursor_type)
{
    // 变量名
    CXString name = clang_getCursorSpelling(cursor);
    // 变量类型
    CXType type = clang_getCursorType(cursor);
    // 变量类型名
    CXString type_name = clang_getTypeSpelling(type);
    // 游标类型名
    CXString cursor_type_name = clang_getCursorKindSpelling(cursor_type);

    // 游标位置
    CXSourceLocation location = clang_getCursorLocation(cursor);
    CXFile file;
    unsigned int line, column;
    clang_getFileLocation(location, &file, &line, &column, NULL);
    // 文件名称
    CXString file_name = clang_getFileName(file);

    printf("Cursor:%s, Type:%s[%s], Location:%s %d:%d\n",
           clang_getCString(name),
           clang_getCString(type_name),
           clang_getCString(cursor_type_name),
           clang_getCString(file_name), line, column);

    // 资源释放
    clang_disposeString(name);
    clang_disposeString(type_name);
    clang_disposeString(cursor_type_name);
    clang_disposeString(file_name);
}

// 遍历AST节点的回调函数
// Visit Children,任何cursor都有一种类型enum CXCursorKind,表示cursor的本质
CXChildVisitResult visitor(CXCursor cursor, CXCursor parent, CXClientData client_data)
{
    CXCursorKind eType = clang_getCursorKind(cursor);
    PrintVarDecl(cursor, eType);

    return CXChildVisit_Recurse;
}

int main()
{
    CXIndex index = clang_createIndex(0, 0);
    // 解析成功后,获取经过解析的抽象语法树(AST),可以遍历和检查该语法树
    CXTranslationUnit unit = clang_parseTranslationUnit(index,
                             FILE_PATH,
                             nullptr, 0, nullptr, 0,
                             CXTranslationUnit_None);

    if(nullptr == unit)
    {
        std::cerr << "Unable to parse translation unit. Qutting." << std::endl;
        return -1;
    }

    // 指向抽象语法树(AST)的指针成为Cursors
    CXCursor cursor = clang_getTranslationUnitCursor(unit);
    // 调用回调函数
    clang_visitChildren(cursor, visitor, nullptr);

    // 资源释放
    clang_disposeTranslationUnit(unit);
    clang_disposeIndex(index);

    return 0;
}
4.2.2.1 LibClang接口解析的通用流程

使用LibClang解析源码并遍历抽象语法树AST的通用流程包括以下步骤:

4.2.2.1.1 初始化和配置
  • 创建索引:创建一个索引对象,通常一个编译器示例对应一个索引对象;

  • 设置库路径(可选):在使用Python绑定时,可能需要指定libclang库的路径。

    • 在Windows环境使用相关函数时需要链接到LibClang库,需要链接以下库文件(包含在LLVM工具集内):

      • lib

        • 静态库文件,链接该库可以使用LibClang提供的所有API。

      • dll

        • 动态链接库,需要在运行时确保该文件在可执行文件路径内。

4.2.2.1.2 解析源代码
  • 生成翻译单元:使用clang_parseTranslationUnit()函数解析源代码文件,生成翻译单元(Translation Unit)。这代表一个独立的解析结果,通常对应一个源代码及其包含的头文件。

4.2.2.1.3 获取根游标
  • 获取根游标:翻译单元的根游标(Cursor)代表了整个翻译单元的根节点,可以通过clang_getTranslationUnitCursor()获取。

4.2.2.1.4 遍历AST
  • 设置回调函数:定义一个回调函数来处理每个节点。这通常是通过检查节点的类型来过滤和处理感兴趣的节点,例如变量声明、函数定义等。

  • 遍历节点:使用clang_visitChildren()函数从根游标开始递归遍历整个AST,该函数会调用回调函数来处理每个子节点。

4.2.2.1.5 处理节点
  • 获取节点信息:在回调函数中,可以使用相关API获取节点的详细信息,包括类型、名称、位置等。

4.2.2.1.6 释放资源
  • 释放翻译单元和索引:在完成解析和遍历后,释放分配的资源,以防止内存泄漏。

4.2.2.2 执行结果

上述示例代码的执行结果如下:

图 4-5 示例代码执行结果

如图 4-5所示,可获取类型定义TypedefDecl、变量定义VarDecl、结构体定义StructDecl,以及结构体变量定义FieldDecl等类型,及其所在的文件及行列。

5. 调研结论

如3.3.3所示,LLVM工具集是模块化和可重用的编译器和工具链技术项目,LibClang作为Clang的C接口,原生支持C/C++语法与语义,能够精确处理C/C++所有语法特性与语义特性,且Visual Studio 2019环境可原生支持编译和运行Clang环境,适用于需要高精度解析和语义分析的工具。

主要难点在于操作AST和其他数据结构可能相对复杂,需要一定的学习和理解过程。

如果需要高度定制化的词法与语法解析流程,可考虑ANTLR4解析器生成工具。

标签:源代码,代码,C++,编译器,源码,Clang,2.1,解析,LLVM
From: https://blog.csdn.net/weixin_42932294/article/details/141389776

相关文章

  • ast.literal_eval替代eval将字符串形式的表达式解析为 Python 对象
    如果一个字符串表示一个列表,你可以使用Python的ast.literal_eval方法将其转换为真正的列表。ast.literal_eval是一个安全的方法,可以将字符串形式的表达式解析为Python对象。这里是一个例子:importast#字符串形式的列表str_list="[1,2,3,4,5]"#将字符串解析......
  • java线程池任务执行过程 | java线程池原理探究 | 线程池源码
    目录一、线程池的使用二、线程池的创建2.1构造方法及参数2.2拒绝策略2.2.1AbortPolicy(直接抛出异常)2.2.2 CallerRunsPolicy(将任务交给调用者处理)2.2.3 DiscardOldestPolicy(弹出队列中等待最久的任务)2.2.4 DiscardPolicy(无操作)2.2.5自定义拒绝策略(实现Rejected......
  • 源码解析之为何要用ConcurrentHashMap
    为什么要用ConcurrentHashMap?ConcurrentHashMap是JUC包下的一个线程安全的HashMap类,我们都知道多线程的场景下要用ConcurrentHashMap来代替HashMap使用,有没有想过为什么不能用HashMap,为什么能用ConcurrentHashMap呢?下面我通过走源码的方式,带大家看一看其中的一些细节!HashMapmap......
  • 基于SSM的小区物业管理系统2【附源码+文档】
    ......
  • 域名泛解析是什么?如何设置?
    在当今数字化的时代,网站建设和网络运营对于企业和个人来说都变得至关重要。而在这个过程中,域名的管理和配置起着关键作用。其中,域名泛解析是一个重要的概念,它可以为网站的运营和管理带来诸多便利。一、域名泛解析是什么?域名泛解析,也称为域名通配符解析,是指将一个域名的所有子域......
  • DevEco Studio 调试三方库源码
    有相关的官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs-V5/faqs-app-debugging-26-V5实操:将编译好的三方库文件和符号文件整理好在工程中添加对库文件的使用,一般是将库文件放到libs/arm64-v8a下点击顶栏的选项,Run->EditConfigurations,进入到R......
  • (附源码)基于springboot的清华逸景闲置房租赁系统的设计与实现-计算机毕设 09065
    基于springboot的清华逸景闲置房租赁系统的设计与实现目 录摘要1绪论1.1选题背景与意义1.2国内外研究现状1.3系统开发创新之处1.4论文结构与章节安排2系统分析2.1可行性分析2.2 系统功能分析2.2.1功能性分析2.2.2非功能性分析2.3 系统用例......
  • 平衡二叉树、B树、B+树、红黑树解析
    目录有序二叉树平衡二叉树构造平衡二叉树RRLLRLLR平衡二叉树的优缺点:2-3-4树红黑树B树B+树B树、B+树、红黑树的应用有序二叉树关于有序二叉树的详解以及Ja......
  • Android开发 - BluetoothClient 类处理蓝牙全过程连接与数据解析
    BluetoothClient是什么BluetoothClient类通常用于蓝牙应用中,特别是与蓝牙设备进行通信的客户端管理。通常用于在Android应用中进行蓝牙通信。它帮助你处理与蓝牙连接、设备发现、数据传输等等任务BluetoothClient的使用环境连接到蓝牙设备:通过BluetoothClient,你可以......
  • Android开发 - Handler 类处理线程通信与任务调度解析
    什么是Handler类是处理线程间通信和任务调度的一个重要工具,用于在不同的线程之间传递消息和执行任务使用场景线程间通信:在子线程中执行任务后,更新主线程(UI线程)的界面。任务调度:安排在将来某个时间点执行的任务。基本工作原理消息队列:每个线程(包括主线程)都有一个......