Doxygen 终于可以正确生成函数调用图了!


大家都知道, Doxygen 可以用于提取代码的注释生成项目的文档,只要注释满足其规定的格式。但我更喜欢利用它生成类继承图(inheritance diagram)和函数调用图(callgraph),通过他们能够加快对代码的理解。

以 MySQL 代码为例, Doxygen 生成的类继承图和调用图分别是这样的:

/img/2021-02-16-doxygen_innodb_classhandler__inherit__graph.png
handler (存储引擎)类继承图
/img/2021-02-16-doxygen_mysql_ha_recover_callgraph.png
ha_recover() crash recovery 函数调用图

之前工作中交接了几个 C++ 项目,由于还不熟悉,就想用 Doxygen 来生成这两种图来加快熟悉代码,但结果却生成失败了。

几经排查,最后发现只要代码中包含2个以上的 using namespace xxx; 语句,调用图就无法生成。比如下面这样一个简单的类实现,就无法生成调用关系图。

#pragma once

namespace demo {
    class Test {
    public:
        void foo();
        void bar();
    };
}
test.h 代码
#include <iostream>
#include "test.h"

using namespace demo;
using namespace std;

void Test::foo()
{
    bar();
}

void Test::bar()
{
    return;
}

int main()
{
    Test test;
    test.foo();
    return 0;
}
test.cpp 代码

虽然这种直接包含命名空间的用法不推荐,但是在已有项目中却很常见,这就意味着 Doxygen 基本不可用了。

由于当时没有时间,就先给官方报了一个 Issue ,等待他们解决。

没想到几个月之后,官方也还没有解决,而最近正好有点时间,就重新去分析这个 bug ,看如何解决。解决过程虽然花了不少时间,但总结起来就几点:

  1. 对比正常生成调用图和无法生成调用图的两种输出,再 grep 差异信息定位到生成 callgraph 图的相关代码;

  2. 添加 printf 调试信息,找出 callgraph 元数据的来源,即 Doxygen 怎么得到调用关系的,最终找到实现在 code.l (C族语言的 flex 实现,用于解析工程源码)中,其中基于 flex 自己实现了词法解析;

  3. 学习 flex 的使用方法;

最后终于找到问题的根源在于, Doxygen 在解析 using namespace xxx; 指令的时候,错误地把文件中其后的代码包围在 xxx 命名空间中( pushScope ),从而把函数的绝对命名空间弄错,就没法解析出正确的调用关系了。

提了 PR 修复了之后, Doxygen 就能够正常地生成调用图了,前面的测试代码就能正确地生成如下的函数调用图了。

/img/2021-02-16-doxygen_fix_test_callgraph.png
Test::foo() 的调用图

目前(2021.02)这个 PR 已经合并到 master 了,但是还没有发布,需要再等一等。

C++ 

也可以看看

comments powered by Disqus