10.6 运行时符号信息
使用过Go语言都会注意到程序panic时会有traceback,不仅有函数调用,还有文件名和行号等信息,这个是如何做到的呢?
编译器在编译的时候会生成一些额外信息,运行时符号信息就是这样生成的。以traceback信息中的行号为例,编译器在编译的时候会记录下函数地址对应的源文件行号,也就是pc->line
的一张表,后面简称pcln,其中pc是program counter的缩写。
如果将pcln的概念推广,其它不仅仅可以记录行号,还可以记录下许多其它信息。比如pcsp表,记录栈的大小;或者记录下栈内的存活的变量信息,这样可以方便垃圾回收的处理。将pcln概念进行推广,是一个pc-value的表,其中value可以是文件名或行号或其它任何信息。
每个函数都可以拥有一些元数据和PC-Value表。运行时符号信息由编译器在编译的时候生成,存放在可执行文件中,当程序被执行时,这张表被加载到内存,用于程序运行时辅助Go的运行时库执行一些处理。
一个函数符号表的形式就是一张program counter的查找表,形式类式于:
N pc0 func0 pc1 func1 pc2 func2 ... pc(N-1) func(N-1) pcN
表中包含记录项数信息N,接下来表的每一项,由一个pc和一个函数元数据指针构成。给定一个pc值,通过二分搜索可以很快地查找到对应的值。每个funcN值是它在函数符号表的偏移值,函数符号表是一个如下结构体的数组:
struct Func
{
uintptr entry; // start pc
int32 name; // name (offset to C string)
int32 args; // size of arguments passed to function
int32 frame; // size of function frame, including saved caller PC
int32 pcsp; // pcsp table (offset to pcvalue table)
int32 pcfile; // pcfile table (offset to pcvalue table)
int32 pcln; // pcln table (offset to pcvalue table)
int32 nfuncdata; // number of entries in funcdata list
int32 npcdata; // number of entries in pcdata list
};
每个函数元数据中,存储了函数的入口地址,pc-value表包含了当前栈指针偏移,文件信息,行号信息。
除了每个函数的元数据,还有一个每个函数的元数据指针以及每个函数的pc-value表。Go汇编中使用了一些伪指令:
FUNCDATA $2, $sym(SB)
表明funcdata中的第2项是一个指向sym的指针。类似地
PCDATA $3, $45
表明当前program counter偏移3关联的值是45。
在运行时,运行时库可以通过Func,给定一个偏移来检索funcdata,或者通过program counter,给定一个偏移来检索pcdata。
在内存中,结构体Func紧接着就是pcdata表和funcdata表。