利用VS Code阅读源码及调试OpenFOAM

1 准备工作

  • win10上安装VSCode
  • VSCode安装remote-wsl插件用于连接Linux子系统,remote-ssh插件用于连接远程Linux服务器
  • 连接到wsl或远程Linux服务器后,安装C/C++插件

以上步骤可以自行搜索了解细节。如果是原生Linux系统,则可直接安装VSCode,然后安装C/C++插件即可。
下图展示连接局域网内的Ubuntu服务器(左下角有提示),并且安装了C/C++插件。

准备工作

2 阅读源码篇

icoFoam求解器为例。

  1. 在VSCode中按win+`键(左上角同~键)打开集成的终端。
  • 加载OpenFOAM环境,拷贝求解器:
    如果终端配置文件中使用的是 source xxx/bashrc. xxx/bashrc,则不需要额外激活OF环境; 如果是通过alias管理多版本OF,则输入对应的alias激活OF环境,如of6

    1
    2
    3
    $ mkdir -p $WM_PROJECT_USER_DIR/solver
    $ cp -r $FOAM_SOLVERS/incompressible/icoFoam $WM_PROJECT_USER_DIR/solver/
    $ cd $WM_PROJECT_USER_DIR/solver/icoFoam
  • 修改Make/files:

    1
    2
    3
    icoFoam.C

    EXE = $(FOAM_USER_APPBIN)/myicoFoam
  • 通过wmake查看真实调用的编译命令,从中提取-I路径用于后续配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
      $ wmake | tee log.wmake
    Making dependency list for source file icoFoam.C
    g++ -std=c++11 -m64 -Dlinux64 -DWM_ARCH_OPTION=64 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wold-style-cast -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -Wno-attributes -O3 -DNoRepository -ftemplate-depth-100 -I/opt/openfoam6/src/finiteVolume/lnInclude -I/opt/openfoam6/src/meshTools/lnInclude -IlnInclude -I. -I/opt/openfoam6/src/OpenFOAM/lnInclude -I/opt/openfoam6/src/OSspecific/POSIX/lnInclude -fPIC -c icoFoam.C -o Make/linux64GccDPInt32Opt/icoFoam.o
    g++ -std=c++11 -m64 -Dlinux64 -DWM_ARCH_OPTION=64 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wold-style-cast -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -Wno-attributes -O3 -DNoRepository -ftemplate-depth-100 -g -I/opt/openfoam6/src/finiteVolume/lnInclude -I/opt/openfoam6/src/meshTools/lnInclude -IlnInclude -I. -I/opt/openfoam6/src/OpenFOAM/lnInclude -I/opt/openfoam6/src/OSspecific/POSIX/lnInclude -fPIC -Xlinker --add-needed -Xlinker --no-as-needed Make/linux64GccDPInt32Opt/icoFoam.o -L/opt/openfoam6/platforms/linux64GccDPInt32Opt/lib \
    -lfiniteVolume -lmeshTools -lOpenFOAM -ldl \
    -lm -o /home/of/OpenFOAM/of-6/platforms/linux64GccDPInt32Opt/bin/myicoFoam

    $ cat log.wmake | sed 's/ /\n/g' | grep '\-I' | sort | uniq | sed 's/\-I//g'
    .
    lnInclude
    /opt/openfoam6/src/finiteVolume/lnInclude
    /opt/openfoam6/src/meshTools/lnInclude
    /opt/openfoam6/src/OpenFOAM/lnInclude
    /opt/openfoam6/src/OSspecific/POSIX/lnInclude

    $ pwd
    /home/of/OpenFOAM/of-6/solver/icoFoam

    可以看到,这里的路径主要都是lnInclude,其中的文件都是软链接,指向各个文件的真实路径。

  1. File → Open Folder,打开最后返回的路径:/home/of/OpenFOAM/of-6/solver/icoFoam

  2. F1Ctrl+Shift+p打开命令面板,然后输入C++ UI,找到C/C++ Configurations (UI),打开。在其中的includePath中添加前面得到的-I路径:

至此,就能实现代码提示和跳转了:

阅读OpenFOAM源码

3 调试篇

以自定义求解器myicoFoam求解器为例。

3.1 单版本调试

参考视频:VS Code调试OpenFOAM

  1. 在VSCode中按win+`键(左上角同~键)打开集成的终端。
  • 加载OpenFOAM环境,拷贝求解器:

    注:假设这里是在~/.bashrc中直接source来激活OpenFOAM环境;对于多版本OF调试,即通过alias激活对应环境的情况,后面再讨论。

    1
    2
    3
    4
    # $HOME/.bashrc设置
    # alias of6=". /opt/openfoam6/etc/bashrc"
    source /opt/openfoam6/etc/bashrc #默认打开终端就激活OF环境
    alias of8=". /opt/openfoam8/etc/bashrc"
    1
    2
    3
    $ mkdir -p $WM_PROJECT_USER_DIR/solver
    $ cp -r $FOAM_SOLVERS/incompressible/icoFoam $WM_PROJECT_USER_DIR/solver/
    $ cd $WM_PROJECT_USER_DIR/solver/icoFoam
  • 修改Make/files:

    1
    2
    3
    icoFoam.C

    EXE = $(FOAM_USER_APPBIN)/myicoFoam
  • 修改Make/options

    1
    2
    3
    4
    5
    6
    7
    8
    EXE_INC = \
    -g \
    -I$(LIB_SRC)/finiteVolume/lnInclude \
    -I$(LIB_SRC)/meshTools/lnInclude

    EXE_LIBS = \
    -lfiniteVolume \
    -lmeshTools

    注意:这里必须额外添加-g选项。

  • 通过wmake查看真实调用的编译命令,从中提取-I指向的路径用于后续配置includePath:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
      $ wmake | tee log.wmake
    Making dependency list for source file icoFoam.C
    g++ -std=c++11 -m64 -Dlinux64 -DWM_ARCH_OPTION=64 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wold-style-cast -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -Wno-attributes -O3 -DNoRepository -ftemplate-depth-100 -g -I/opt/openfoam6/src/finiteVolume/lnInclude -I/opt/openfoam6/src/meshTools/lnInclude -IlnInclude -I. -I/opt/openfoam6/src/OpenFOAM/lnInclude -I/opt/openfoam6/src/OSspecific/POSIX/lnInclude -fPIC -c icoFoam.C -o Make/linux64GccDPInt32Opt/icoFoam.o
    g++ -std=c++11 -m64 -Dlinux64 -DWM_ARCH_OPTION=64 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wold-style-cast -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -Wno-attributes -O3 -DNoRepository -ftemplate-depth-100 -g -I/opt/openfoam6/src/finiteVolume/lnInclude -I/opt/openfoam6/src/meshTools/lnInclude -IlnInclude -I. -I/opt/openfoam6/src/OpenFOAM/lnInclude -I/opt/openfoam6/src/OSspecific/POSIX/lnInclude -fPIC -Xlinker --add-needed -Xlinker --no-as-needed Make/linux64GccDPInt32Opt/icoFoam.o -L/opt/openfoam6/platforms/linux64GccDPInt32Opt/lib \
    -lfiniteVolume -lmeshTools -lOpenFOAM -ldl \
    -lm -o /home/of/OpenFOAM/of-6/platforms/linux64GccDPInt32Opt/bin/myicoFoam

    $ cat log.wmake | sed 's/ /\n/g' | grep '\-I' | sort | uniq | sed 's/\-I//g'
    .
    lnInclude
    /opt/openfoam6/src/finiteVolume/lnInclude
    /opt/openfoam6/src/meshTools/lnInclude
    /opt/openfoam6/src/OpenFOAM/lnInclude
    /opt/openfoam6/src/OSspecific/POSIX/lnInclude

    $ pwd
    /home/of/OpenFOAM/of-6/solver/icoFoam

    $ which myicoFoam
    /home/of/OpenFOAM/of-6/platforms/linux64GccDPInt32Opt/bin/myicoFoam

    注意wmake实际调用的命令中新增了-g选项,说明把该选项写在Make/optionsEXE_INC中是有效的(虽然EXE_INC的原本目的是用来指定includePath)。这里要用到的路径是后面4个lnInclude路径以及pwd返回的icoFoam求解器路径。此外,通过which命令来获取编译得到的myicoFoam路径,因为调试的时候通常是在算例目录下。

2021-01-08 20:13:05 更新(点击展开)

更准确的做法应该是通过环境变量WM_COMPILE_OPTION来控制。 即在拷贝求解器后只需要修改Make/files更改一下可执行文件的路径以及名称,然后终端运行WM_COMPILE_OPTION=Debug将默认的Opt编译模式切换成Debug模式。然后wmake即可以调试模式编译求解器。

说明
终端运行echo $WM_COMPILE_OPTION可以发现该变量的值默认为Opt, 其定义的位置在OpenFOAM的环境加载文件中(即$WM_PROJECT_DIR/etc/bashrc中), 可以取值OptDebug以及Prof,分别表示优化(optimised),调试(debug)和分析(profiling)。 wmake会根据变量$WM_COMPILE_OPTION的值来加载不同的选项。 $WM_PROJECT_DIR/wmake/rules/linux64Gcc/c++文件中包含语句include $(DEFAULT_RULES)/c++$(WM_COMPILE_OPTION), 分别可能加载c++Optc++Debugc++Prof文件中的选项设置。这3个文件所包含的内容如下:

1
2
3
4
5
6
7
8
9
10
# $WM_PROJECT_DIR/wmake/rules/linux64Gcc/c++Opt
c++DBUG =
c++OPT = -O3
ROUNDING_MATH = -frounding-math
# $WM_PROJECT_DIR/wmake/rules/linux64Gcc/c++Debug
c++DBUG = -ggdb3 -DFULLDEBUG
c++OPT = -O0 -fdefault-inline
# $WM_PROJECT_DIR/wmake/rules/linux64Gcc/c++Prof
c++DBUG = -pg
c++OPT = -O2

可以看到c++Debug文件中开启了-ggdb3,类似于-g选项,用于开启编译功能。
c++Prof中开启-pg选项,编译后的程序可以使用gperf之类的工具进行性能分析。
而默认的c++Opt-O3则最大限度地优化程序。

  1. 拷贝测试算例,并通过File → Open Folder在VSCode中打开测试算例:

    1
    2
    3
    4
    5
    6
    $ mkdir -p $WM_PROJECT_USER_DIR/run
    $ run
    $ cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity .
    $ cd cavity
    $ pwd
    /home/of/OpenFOAM/of-6/run/cavity
  2. Ctrl+Shift+D打开左侧的调试面板(Run),点击create a launch.json file创建launch.json文件

    调试设置

  • 修改launch.json文件:

    1
    2
    3
    4
    ...
    "program": "/home/of/OpenFOAM/of-6/platforms/linux64GccDPInt32Opt/bin/myicoFoam",
    ...
    "stopAtEntry": true,

    program的值即为自定义求解器的路径,通过which myicoFoam获得,而stopAtEntry设为true能在main函数处默认设置一个断点。

  • F1Ctrl+Shift+p打开命令面板,然后输入C++ UI,找到C/C++ Configurations (UI),打开。在其中的includePath中添加前面得到的-I路径以及求解器源码路径(注意与前一节有点差别)。

    调试过程中添加includePath

  1. 生成网格,按F5键开始调试。

3.2 多版本调试

以上是单OpenFOAM版本,实际场景中经常会安装多个版本,通过alias在各个版本之间切换,那以上调试方法还适用吗?

通过各种尝试折腾,终于想到了一个巧妙的解决方案,即通过包装一个新的gdb脚本来实现,具体方案如下。

讨论两个版本的情况:

  • $HOME/.bashrc配置如下:

    1
    2
    alias of6=". /opt/openfoam6/etc/bashrc"
    alias of8=". /opt/openfoam8/etc/bashrc"
  • 创建一个目录专门用于存放脚本,比如~/OFdebug

    1
    2
    3
    4
    $ mkdir -p ~/OFdebug
    $ cd OFdebug
    $ touch of6-gdb.sh && chmod +x of6-gdb.sh
    $ touch of8-gdb.sh && chmod +x of8-gdb.sh

    两个脚本的内容分别为:

    of6-gdb.sh

    1
    2
    3
    #!/bin/bash
    . /opt/openfoam6/etc/bashrc
    /usr/bin/gdb "$@" # 将脚本接受的参数传给gdb命令

    of8-gdb.sh

    1
    2
    3
    #!/bin/bash
    . /opt/openfoam8/etc/bashrc
    /usr/bin/gdb "$@" # 将脚本接受的参数传给gdb命令
  • 修改launch.json文件,通过miDebuggerPath指定编译器自定义的gdb脚本路径,比如使用of6来调试:

    1
    2
    3
    ...
    "miDebuggerPath": "/home/of/OFdebug/of6-gdb.sh",
    ...
  • 调试cavity算例:

    1
    2
    3
    4
    5
    6
    of6 # 激活环境
    run
    cd cavity
    foamCleanTutorials # 清理算例
    blockMesh # 生成网格
    # ... 开始调试

4 相关参考

在研究gdb调试之前,主要尝试的就是gdbOF[1],能方便地将求解过程中的变量导出到文件,具体应用可以看[2]的介绍。目前OpenFOAMwiki上介绍适配的最高版本是OF5.x。

gdbOF is an attachable to GNU debugger (gdb) tool that includes macros to debug OpenFOAM’s solvers and applications in an easier way.

另一个角度,[3]介绍了如何使用gdbserver调试。

使用 VS Code 调试远程服务器上的 OpenFOAM 代码。


花了不少时间折腾,写作不易,希望能帮到学习OpenFOAM的小伙伴!


  1. ContribgdbOF 点击跳转 ↩︎

  2. 用GdbOF追踪laplacian函数在OpenFOAM中的定义过程 点击跳转 ↩︎

  3. VSCode远程调试OpenFOAM 点击跳转 ↩︎