10.6 运行时符号信息

使用过Go语言都会注意到程序panic时会有traceback,不仅有函数调用,还有文件名和行号等信息,这个是如何做到的呢?

编译器在编译的时候会生成一些额外信息,运行时符号信息就是这样生成的。以traceback信息中的行号为例,编译器在编译的时候会记录下函数地址对应的源文件行号,也就是pc->line的一张表,后面简称pcln,其中pc是program counter的缩写。

如果将pcln的概念推广,其它不仅仅可以记录行号,还可以记录下许多其它信息。比如pcsp表,记录栈的大小;或者记录下栈内的存活的变量信息,这样可以方便垃圾回收的处理。将pcln概念进行推广,是一个pc-value的表,其中value可以是文件名或行号或其它任何信息。

每个函数都可以拥有一些元数据和PC-Value表。运行时符号信息由编译器在编译的时候生成,存放在可执行文件中,当程序被执行时,这张表被加载到内存,用于程序运行时辅助Go的运行时库执行一些处理。

一个函数符号表的形式就是一张program counter的查找表,形式类式于:

  1. N pc0 func0 pc1 func1 pc2 func2 ... pc(N-1) func(N-1) pcN

表中包含记录项数信息N,接下来表的每一项,由一个pc和一个函数元数据指针构成。给定一个pc值,通过二分搜索可以很快地查找到对应的值。每个funcN值是它在函数符号表的偏移值,函数符号表是一个如下结构体的数组:

  1. struct Func
  2. {
  3. uintptr entry; // start pc
  4. int32 name; // name (offset to C string)
  5. int32 args; // size of arguments passed to function
  6. int32 frame; // size of function frame, including saved caller PC
  7. int32 pcsp; // pcsp table (offset to pcvalue table)
  8. int32 pcfile; // pcfile table (offset to pcvalue table)
  9. int32 pcln; // pcln table (offset to pcvalue table)
  10. int32 nfuncdata; // number of entries in funcdata list
  11. int32 npcdata; // number of entries in pcdata list
  12. };

每个函数元数据中,存储了函数的入口地址,pc-value表包含了当前栈指针偏移,文件信息,行号信息。

除了每个函数的元数据,还有一个每个函数的元数据指针以及每个函数的pc-value表。Go汇编中使用了一些伪指令:

  1. FUNCDATA $2, $sym(SB)

表明funcdata中的第2项是一个指向sym的指针。类似地

  1. PCDATA $3, $45

表明当前program counter偏移3关联的值是45。

在运行时,运行时库可以通过Func,给定一个偏移来检索funcdata,或者通过program counter,给定一个偏移来检索pcdata。

在内存中,结构体Func紧接着就是pcdata表和funcdata表。