c 是一门静态编程语言,能够运用 gcc 或许 clang 进行编译。这儿所说的编译,简略来说便是预编译,编译,汇编,链接的进程。

再细致的进行区分,便是编译器的前端经过 scanner 将原代码文件解析成一个个的 token 流,再经过 parser,一般指递归下降解析器解析成笼统语法树 ast,再转变成中心表明 ir,clang 中便是 llvm ir,gcc 中便是 gimple。然后经过中端优化,也便是机器无关优化,lower 到特定机器相关的汇编代码。

编译到汇编之后,经过汇编器翻译成 .o 的二进制文件,在 linux 中为 elf 格式的目标文件,然后经过链接器进行链接,变成最终的可履行文件。

而预编译阶段,在 C/C 中,便是 include 头文件,并经过文本替换的方法替换源代码中的宏。预编译后的文件,以 .i 结束,此时的文件,包含了 include 中的头文件的内容,一起将宏都进行了替换,能够看成是编译前的一个完好的编译单元 translation unit。编译器在此基础上再进行编译。而 c 中的模版,比如模版打开,模版实例化,都是在编译期间做的工作。

通常在编写 c 模版程序的时分,需求调试模版,或许尝试模版打开都是一个比较麻烦的进程,那是否有一个工具能够辅佐以 gdb 或许 lldb 一样的方法能够以单步盯梢的方法协助你打开模版呢?

凭借 templight 能够达到这样的作用。

从笼统语法树的视点

c 模版打开是在编译期间所做的工作,注定咱们无法经过 gdb 或许 lldb 这样的程序在运转时检查,只能在编译器编译期间检查。

看一个简略的实例程序,经过模版的方法求斐波那契数列,在编译期间就能够直接得出成果。

template <unsigned int N>
struct Fibonacci;
template <>
struct Fibonacci<0> { static const unsigned int value = 0;  };
template <>
struct Fibonacci<1> { static const unsigned int value = 1;  };
template <unsigned int N>
struct Fibonacci {
    static const unsigned int value = Fibonacci<N-1>::value   Fibonacci<N-2>::value;
};
int main(int argc, char *argv[]) {
  return Fibonacci<5>::value;
}

经过 clang 编译运转

# clang   test.cpp
# ./a.out
# echo $? // 看下返回成果

单步盯梢调试c++模版你会吗?
成果符合预期。

同样咱们能够经过 clang 笼统语法树的方式,能够看到在编译器编译时的笼统语法树中,现已对模版进行了实例化

clang   -Xclang -ast-dump -fsyntax-only main.cpp

截取要害部分

|-ClassTemplateDecl 0x1440e8f08 <main.cpp:2:1, line:3:8> col:8 Fibonacci
| |-NonTypeTemplateParmDecl 0x1440e8e08 <line:2:11, col:24> col:24 'unsigned int' depth 0 index 0 N
| |-CXXRecordDecl 0x1440e8e78 <line:3:1, col:8> col:8 struct Fibonacci
| |-ClassTemplateSpecialization 0x1440e9170 'Fibonacci'
| |-ClassTemplateSpecialization 0x144103a00 'Fibonacci'
| |-ClassTemplateSpecializationDecl 0x144104850 <line:11:1, line:15:1> line:12:8 struct Fibonacci definition
| | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial needs_implicit
| | |-TemplateArgument integral 5
| | |-CXXRecordDecl 0x144105868 <col:1, col:8> col:8 implicit struct Fibonacci
| | `-VarDecl 0x1441058f8 <line:13:5, col:79> col:31 referenced value 'const unsigned int' static cinit
| |   `-BinaryOperator 0x144109758 <col:39, col:79> 'unsigned int' ' '
| |     |-ImplicitCastExpr 0x1441096f8 <col:39, col:55> 'unsigned int' <LValueToRValue>
| |     | `-DeclRefExpr 0x1441096c8 <col:39, col:55> 'const unsigned int' lvalue Var 0x144105c90 'value' 'const unsigned int' non_odr_use_constant
| |     `-ImplicitCastExpr 0x144109740 <col:63, col:79> 'unsigned int' <LValueToRValue>
| |       `-DeclRefExpr 0x144109710 <col:63, col:79> 'const unsigned int' lvalue Var 0x144106020 'value' 'const unsigned int' non_odr_use_constant
| |-ClassTemplateSpecializationDecl 0x144105a08 <line:11:1, line:15:1> line:12:8 struct Fibonacci definition
| | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial needs_implicit
| | |-TemplateArgument integral 4
| | |-CXXRecordDecl 0x144105c00 <col:1, col:8> col:8 implicit struct Fibonacci
| | `-VarDecl 0x144105c90 <line:13:5, col:79> col:31 referenced value 'const unsigned int' static cinit
| |   `-BinaryOperator 0x1441094b8 <col:39, col:79> 'unsigned int' ' '
| |     |-ImplicitCastExpr 0x144109458 <col:39, col:55> 'unsigned int' <LValueToRValue>
| |     | `-DeclRefExpr 0x144109428 <col:39, col:55> 'const unsigned int' lvalue Var 0x144106020 'value' 'const unsigned int' non_odr_use_constant
| |     `-ImplicitCastExpr 0x1441094a0 <col:63, col:79> 'unsigned int' <LValueToRValue>
| |       `-DeclRefExpr 0x144109470 <col:63, col:79> 'const unsigned int' lvalue Var 0x1441063b0 'value' 'const unsigned int' non_odr_use_constant
| |-ClassTemplateSpecializationDecl 0x144105da0 <line:11:1, line:15:1> line:12:8 struct Fibonacci definition
| | |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial needs_implicit
| | |-TemplateArgument integral 3
| | |-CXXRecordDecl 0x144105f90 <col:1, col:8> col:8 implicit struct Fibonacci
| | `-VarDecl 0x144106020 <line:13:5, col:79> col:31 referenced value 'const unsigned int' static cinit
| |   `-BinaryOperator 0x144109218 <col:39, col:79> 'unsigned int' ' '
| |     |-ImplicitCastExpr 0x1441091b8 <col:39, col:55> 'unsigned int' <LValueToRValue>
| |     | `-DeclRefExpr 0x144109188 <col:39, col:55> 'const unsigned int' lvalue Var 0x1441063b0 'value' 'const unsigned int' non_odr_use_constant
| |     `-ImplicitCastExpr 0x144109200 <col:63, col:79> 'unsigned int' <LValueToRValue>
| |       `-DeclRefExpr 0x1441091d0 <col:63, col:79> 'const unsigned int' lvalue Var 0x144103cb0 'value' 'const unsigned int' non_odr_use_constant
| `-ClassTemplateSpecializationDecl 0x144106130 <line:11:1, line:15:1> line:12:8 struct Fibonacci definition
|   |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
|   | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
|   | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
|   | |-MoveConstructor exists simple trivial needs_implicit
|   | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
|   | |-MoveAssignment exists simple trivial needs_implicit
|   | `-Destructor simple irrelevant trivial needs_implicit
|   |-TemplateArgument integral 2
|   |-CXXRecordDecl 0x144106320 <col:1, col:8> col:8 implicit struct Fibonacci
|   `-VarDecl 0x1441063b0 <line:13:5, col:79> col:31 referenced value 'const unsigned int' static cinit
|     `-BinaryOperator 0x144106758 <col:39, col:79> 'unsigned int' ' '
|       |-ImplicitCastExpr 0x1441066f8 <col:39, col:55> 'unsigned int' <LValueToRValue>
|       | `-DeclRefExpr 0x1441066c8 <col:39, col:55> 'const unsigned int' lvalue Var 0x144103cb0 'value' 'const unsigned int' non_odr_use_constant
|       `-ImplicitCastExpr 0x144106740 <col:63, col:79> 'unsigned int' <LValueToRValue>
|         `-DeclRefExpr 0x144106710 <col:63, col:79> 'const unsigned int' lvalue Var 0x1440e9420 'value' 'const unsigned int' non_odr_use_constant
|-ClassTemplateSpecializationDecl 0x1440e9170 <line:5:1, line:6:61> col:8 struct Fibonacci definition
| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveConstructor exists simple trivial needs_implicit
| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment exists simple trivial needs_implicit
| | `-Destructor simple irrelevant trivial needs_implicit
| |-TemplateArgument integral 0
| |-CXXRecordDecl 0x1440e9378 <col:1, col:8> col:8 implicit struct Fibonacci
| `-VarDecl 0x1440e9420 <col:23, col:57> col:49 referenced value 'const unsigned int' static cinit
|   `-ImplicitCastExpr 0x1440e94a8 <col:57> 'const unsigned int' <IntegralCast>
|     `-IntegerLiteral 0x1440e9488 <col:57> 'int' 0
|-ClassTemplateSpecializationDecl 0x144103a00 <line:8:1, line:9:61> col:8 struct Fibonacci definition
| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveConstructor exists simple trivial needs_implicit
| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment exists simple trivial needs_implicit
| | `-Destructor simple irrelevant trivial needs_implicit
| |-TemplateArgument integral 1
| |-CXXRecordDecl 0x144103c08 <col:1, col:8> col:8 implicit struct Fibonacci
| `-VarDecl 0x144103cb0 <col:23, col:57> col:49 referenced value 'const unsigned int' static cinit
|   `-ImplicitCastExpr 0x144103d38 <col:57> 'const unsigned int' <IntegralCast>
|     `-IntegerLiteral 0x144103d18 <col:57> 'int' 1
|-ClassTemplateDecl 0x144103ed8 prev 0x1440e8f08 <line:11:1, line:15:1> line:12:8 Fibonacci
| |-NonTypeTemplateParmDecl 0x144103dd8 <line:11:11, col:24> col:24 referenced 'unsigned int' depth 0 index 0 N
| |-CXXRecordDecl 0x144103e48 prev 0x1440e8e78 <line:12:1, line:15:1> line:12:8 struct Fibonacci definition
| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial needs_implicit
| | |-CXXRecordDecl 0x144103f98 <col:1, col:8> col:8 implicit struct Fibonacci
| | `-VarDecl 0x144104040 <line:13:5, col:79> col:31 value 'const unsigned int' static cinit
| |   `-BinaryOperator 0x144104468 <col:39, col:79> '<dependent type>' ' '
| |     |-DependentScopeDeclRefExpr 0x144104250 <col:39, col:55> '<dependent type>' lvalue
| |     `-DependentScopeDeclRefExpr 0x144104430 <col:63, col:79> '<dependent type>' lvalue
| |-ClassTemplateSpecialization 0x1440e9170 'Fibonacci'
| |-ClassTemplateSpecialization 0x144103a00 'Fibonacci'
| |-ClassTemplateSpecialization 0x144104850 'Fibonacci'
| |-ClassTemplateSpecialization 0x144105a08 'Fibonacci'
| |-ClassTemplateSpecialization 0x144105da0 'Fibonacci'
| `-ClassTemplateSpecialization 0x144106130 'Fibonacci'

简略说一下笼统语法树的构成

单步盯梢调试c++模版你会吗?
每一行描绘的都是一个 ast node 的信息,比如上图中的榜首行,

  • ClassTemplateSpecializationDecl 便是 clang 中笼统语法树的一个 node 名称,从字面意思能够看出,这便是一个模版特化声明。 <line:8:1, line:9:61> 表明的是该声明在源代码文件中的 location。能够持续看下该声明的内容,ast 中列出了该 struct 中隐式声明的相关构造和析构函数。
  • TemplateArgument 表明模版参数,是 integral 类型的节点
  • CXXRecordDecl 在 clang ast 中用来表明 struct 或许 class

clang 笼统语法树的展示便是一种层级结构,能形象的表明树的结构方式。在笼统语法树的最终,咱们能够看到有五次模版特化的进程。

既然编译器现已做好了这些工作,那是否能够从编译器的视点,在编译阶段,让模版打开和特化的进程一步一步呈现出来呢。templight 就做了这些工作。

运用 templight

Templight(https://github.com/mikael-s-persson/templight) is a Clang-based tool to profile the time and memory consumption of template instantiations and to perform interactive debugging sessions to gain introspection into the template instantiation process.

templight 是基于 clang 开发的,无法独立编译和运转,有必要基于 clang 源码树的基础上完结编译。

编译

我这儿选取了 llvm-14 的版别进行编译的。假如选取其他的 llvm 版别,先看下 templight 当时版别是否兼容,假如不兼容,编译会通不过,因为 llvm api 版别迭代之间改变较大,很多时分不能兼容老版别。

首要下载 llvm 14

git clone http://gitlab.alibaba-inc.com/AliOSCompiler/clang_llvm.git -b llvmorg-14.0.6

然后 clone templight

cd clang_llvm
cd clang/tools
git clone https://github.com/mikael-s-persson/templight.git

留意,templight 放在 llvm 源码树的 clang/tools 目录下,一起需求在 clang/tools/CMakeLists.txt 中将 templight 加入编译

add_clang_subdirectory(templight)

因为我选取的是 llvm-14,需求将 templight 回退到与 14 兼容的 commit id

cd templgiht
git reset --hard b4fdd78d8d66ee955394403e8e4d9e05f688bf6e

回到 llvm 源码的根目录,进行编译

mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS=clang ../llvm
make -j12

这儿只 enable 了 clang,其他的暂时不需求。llvm 也支撑 ninja 的方法进行编译,在生成装备时,运用 ninja 即可

cmake -G ninja -DLLVM_ENABLE_PROJECTS=clang ../llvm

编译完结后,会在 build/bin 目录下生成 templight 和 templight 这两个可履行文件。其实这两个文件,也是 clang driver 程序,仅仅增加了 templight 的功能的 clang 编译器。能够运用 -v 来检查版别信息

$ ./templight   --version
clang version 14.0.6 (https://github.com/llvm/llvm-project.git f28c006a5895fc0e329fe15fead81e37457cb1d1)
Target: arm64-apple-darwin22.6.0
Thread model: posix
InstalledDir: /opt/source_code/llvm-project/build-templight/bin/.

usage

运用 --help 能够检查协助信息。

$ ./templight --help
Templight options (USAGE: templight [[-Xtemplight [templight option]]|[options]] <inputs>)
  --stdout             - Output template instantiation traces to standard output.
  --memory             - Profile the memory usage during template instantiations.
  --safe-mode          - Output Templight traces without buffering,
                         not to lose them at failure (note: this will
                         distort the timing profiles due to file I/O latency).
  --ignore-system      - Ignore any template instantiation coming from
                         system-includes (-isystem).
  --profiler           - Start an interactive Templight debugging session.
  --debugger           - Start an interactive Templight debugging session.
  --output=<string>    - Write Templight profiling traces to <file>.
  --blacklist=<string> - Use regex expressions in <file> to filter out undesirable traces.
...... (后面的内容是 clang 的协助信息)

咱们在编译时,能够运用 templight 的 --profiler--debugger 这两个选项,在运用 templight 进行编译时,进入交互式形式,能检查模版打开的进程。templight 的参数传递,有必要经过 -Xtemplight 的方法,这相似于 clang 传递参数给 linker,有必要运用 -Xlinker, 或许 gcc 传递给 ld 有必要运用 -Wl 一样,也便是说,运用这两个调试的参数,需求这样来传递参数

-Xtemplight --profiler -Xtemplight --debugger

留意:templight 的这个功能,是针对具体的 translation unit 的。也便是说,假如有很多 cpp 文件,你相对其间某一个文件运用 templight 的交互形式,就只对这一个文件运用上面这两个参数进行编译。在编译期间,当编译到这个文件时,就会进入到交互形式,其他文件的编译保持不变。 下面咱们对上面那个斐波那契程序来做一个小试验。

运用 templight 对 main.cpp 进行编译。

templight   -Xtemplight --profiler -Xtemplight --debugger main.cpp

留意,假如是 mac 电脑,这儿的装备与 linux 稍有不同,在 mac 中 sysroot 默许与 linux 是不同的,需求手动指定。这儿假如不知道 sysroot 参数,能够运用 mac 电脑上已安装的 clang 运用 -v 参数编译一下,检查编译参数。通常如下

templight   -Xtemplight --profiler -Xtemplight --debugger -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -I/usr/local/include main.cpp

因为咱们运用了 --profiler--debugger 参数,编译时会进入交互界面。

Welcome to the Templight debugger!
Begin by entering 'run' after setting breakpoints.
(tdb) b Fibonacci
Breakpoint 0 for Fibonacci
(tdb) r
Entering template instantiation of Fibonacci<5>
  at main.cpp|19|10 (Memory usage: 0)

templight 中的指令与 gdb 和 lldb 中相似。b 也便是 break 设置断点。r 表明履行编译。

介绍一下相关的指令操作:

  • break <template name>,表明在模版类或许模版函数的实例化的当地设置断点,编译器会在实例化该模版的时分停止
  • r/run,表明持续履行编译进程内
  • info break 检查断点信息,会显现一切断点信息及其索引
  • delete <breakpoint index> 删去断点
  • kill/quit 表明退出 templight 调试进程,编译器编译当时文件指导编译使命完结退出
  • step/s, step into the template instantiation,该操作会进入到嵌套实例化的模版
  • next/n, skip to the end of the current template instantiation,该操作会跳过嵌套实例化的模版
  • backtrace/bt,打印当时嵌套模版实例化的进程
  • setmode [verbose | quiet], verbose 会打印详细输出,而 quiet 表明静默输出

其他参数能够检查文档(github.com/mikael-s-pe…)

下面咱们经过上述的例子来展示一下 templight 的这些功能。

(tdb) s
Leaving  template instantiation of Fibonacci<5>
  at main.cpp|19|10 (Memory usage: 0)
(tdb) whois Fibonacci
Found Fibonacci
  at main.cpp|12|8
(tdb) s
Entering template instantiation of Fibonacci<5>
  at main.cpp|19|10 (Memory usage: 0)

s 进入模版实例化 Fibonacci<5>,一起显现了该模版实例化的 location 的位置

(tdb) s
Entering template instantiation of Fibonacci<4>
  at main.cpp|13|39 (Memory usage: 0)
    static const unsigned int value = Fibonacci<N-1>::value   Fibonacci<N-2>::value;

这是一个嵌套模版实例化,进入了到了 Fibonacci<4> 这个实例化的模版,再 step 会进入到 Fibonacci<3>

Entering template instantiation of Fibonacci<3>
  at main.cpp|13|39 (Memory usage: 0)
    static const unsigned int value = Fibonacci<N-1>::value   Fibonacci<N-2>::value;

经过 backtrace 能够检查整个模版实例化嵌套的进程

(tdb) bt
template instantiation of Fibonacci<3> at main.cpp|13|39
template instantiation of Fibonacci<4> at main.cpp|13|39
template instantiation of Fibonacci<5> at main.cpp|19|10

quit 退出交互形式,编译器持续编译直到完结编译。

(tdb) quit
Templight debugging session has ended. Goodbye!

好了,以上便是我今日的共享,期望对你有所协助。假如发现文中有不对的当地,欢迎指正。