利用VSCode阅读C++源码并调试OpenFOAM

创建时间: 2021-01-05 16:52:31
摘要: 本文总结win10系统中利用VS Code连接wsl或远程服务器阅读OpenFOAM源码以及调试OpenFOAM求解器的方法。相关内容对于原生Linux系统也适用。分两篇介绍,阅读源码篇说明如何实现代码提示、跳转,从而高效理解OpenFOAM的代码;调试篇介绍如何利用gdb远程调试OpenFOAM,首先介绍单版本的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

    $ 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:

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

    $ 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激活对应环境的情况,后面再讨论。

    # $HOME/.bashrc设置
    # alias of6=". /opt/openfoam6/etc/bashrc"
    source /opt/openfoam6/etc/bashrc #默认打开终端就激活OF环境
    alias of8=". /opt/openfoam8/etc/bashrc"
    
    $ 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:

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

    EXE_INC = \
        -g \
        -I$(LIB_SRC)/finiteVolume/lnInclude \
        -I$(LIB_SRC)/meshTools/lnInclude
    
    EXE_LIBS = \
        -lfiniteVolume \
        -lmeshTools
    

    备注

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

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

    $ 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个文件所包含的内容如下:

# $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中打开测试算例:

    $ 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文件:

    ...
    "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配置如下:

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

    $ 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

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

    of8-gdb.sh

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

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

    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.

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

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

5 结语

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

参考链接