解释器里出错打印调用堆栈是怎么实现的?

软件工程师、主攻高级编程语言虚拟机的设计与实现

69 👍 / 6 💬

要看楼主想问的是实现层,目标层,还是混合的调用栈。

下面假想一个场景,用C来实现一个Python解释器。那么C是实现层,Python是目标层。CPython就是这样的一个实例。

假如要打印实现层的调用栈,那么最简单的办法是利用操作系统及其自带的库所提供的功能。

比如在Windows上用CaptureStackBackTrace()

stackoverflow.com/quest

在Unix系上用backtrace()

linux.die.net/man/3/bac

之类的。也可以用比较苦逼的办法,那就是自己手工分析stack pointer与frame pointer然后根据找到的栈帧信息去查找原本的函数是什么。HotSpot JVM里就有自己实现这样的功能。

假如要打印目标层的调用栈,那在一个简易解释器里非常好办。

简易解释器通常不把目标语言的栈帧放在系统栈上,而是自己单独分配和管理一个“解释器栈”。

用上面假想的场景,解释器可能会有C语言声明的结构体来描述栈帧信息,而且会有显式或隐式的链式结构非常容易遍历。例如CPython的PyFrameObject

svn.python.org/projects

这种要如何打印调用栈就不用多说了吧,就遍历一普通的链表而已。

要打印混合的调用栈就比较麻烦一些。

怎样会出现混合的调用栈呢?假如目标层的栈帧也是在系统栈上分配的话,系统栈就会同时包含实现层和目标层两种栈帧,这俩栈就“混合”成一个栈了。HotSpot VM的Java线程的调用栈就是这样。通常要爬这样的调用栈就得自己分析stack pointer与frame pointer然后一层层挖到栈底了。