使用libclang分析cpp代码
文章目录
背景
libclang是llvm的一个工具,可以使用其提供的接口分析C++代码,并得到源码中期望的数据。
在之前的一个项目中,需要分析获取每个日志宏的字符串信息以及该条日志宏所在的函数。比如下面的例子,需要获取到this is a log
这个字符串,以及它所在的函数Test::test
。字符串可以通过正则表达式匹配到,但日志宏所在的函数Test::test
就很难分析出来,因此需要借助libclang来获取到这些信息。
|
|
使用
libclang实际是个dll,在安装完llvm后,可以在其bin目录下拿到。libclang提供了多种语言的binding,简单起见,使用了python的binding。直接使用命令pip install clang
即可安装python的binding,然后将libclang.dll的路径加入到系统环境变量后,便可使用python分析C++代码了。
下面是一个简单的例子。
|
|
|
|
执行完上面这个例子后会输出解析后的整个语法树,整个语法树非常大,从下图中可以看到真正的main函数只占一小部分。
源码的所有信息都在这颗语法树上,语法树是个多叉树,我们按照树的遍历算法遍历整棵树便能拿到我们想要的任何信息。
在上面的程序中,只输出了每个节点kind
值和spelling
值,kind
值代表当前节点的类型,比如main
函数节点的类型是FUNCTION_DECL
,而调用printf
函数时,对应节点的类型是CALL_EXPR
。spelling
翻译过来就是拼写,可以理解为当前节点的名称,在上图中可以分别看到main
函数节点的拼写是main
,调用的printf
函数的拼写是printf
。
更多的详细信息可以在llvm的官方文档中查看,目前我们只需要了解kind
和spelling
这两个信息即可实现当前的需求。
注意事项
在使用部分,仅举了一个比较简单的例子进行说明,在对一个比较大的工程分析时,会遇到几个问题。
- 工程选项中会配置附加包含目录,在使用libclang编译代码时,如果没有指定这些附加目录,那么libclang将会编译错误,编译错误后会导致语法树有缺失。
- 工程选项中会配置一些预定义宏,使用libclang时如果没有指定,那么也会编译错误,进而导致语法树缺失。
- libclang分为32位和64位的版本,32位的用来编译32位的代码,64位的编译64位的代码,不可混用。
- libclang只支持utf8编码的源码文件,在MSVC中一般使用GBK,同时还会在GBK中使用中文字符串,这会导致libclang编译错误,建议全部使用英文字符串。注意,不影响注释中的中文。
- MSVC的语法与clang的语法有些不兼容,某些代码MSVC可以编译通过,但clang却不行,这需要修改代码。
针对上面的前两个问题,在调用index.parse
方法时,可以将附加包含目录和预定义宏作为参数的形式传递进去。一个例子如下:
|
|
使用-I
参数指定附加包含路径,使用-D
参数指定预定义宏,然后作为一个列表传递给parse函数。
如果编译有错,可以通过下面这种方式获取到对应的错误信息。
|
|