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

关注者
123
被浏览
3523
要看楼主想问的是实现层,目标层,还是混合的调用栈。

下面假想一个场景,用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然后一层层挖到栈底了。