解釋器里出錯列印調用堆棧是怎麼實現的?


要看樓主想問的是實現層,目標層,還是混合的調用棧。

下面假想一個場景,用C來實現一個Python解釋器。那麼C是實現層,Python是目標層。CPython就是這樣的一個實例。

假如要列印實現層的調用棧,那麼最簡單的辦法是利用操作系統及其自帶的庫所提供的功能。
比如在Windows上用CaptureStackBackTrace() http://stackoverflow.com/questions/5693192/win32-backtrace-from-c-code
在Unix繫上用backtrace() http://linux.die.net/man/3/backtrace
之類的。也可以用比較苦逼的辦法,那就是自己手工分析stack pointer與frame pointer然後根據找到的棧幀信息去查找原本的函數是什麼。HotSpot JVM里就有自己實現這樣的功能。

假如要列印目標層的調用棧,那在一個簡易解釋器里非常好辦。
簡易解釋器通常不把目標語言的棧幀放在系統棧上,而是自己單獨分配和管理一個「解釋器棧」。
用上面假想的場景,解釋器可能會有C語言聲明的結構體來描述棧幀信息,而且會有顯式或隱式的鏈式結構非常容易遍歷。例如CPython的PyFrameObject http://svn.python.org/projects/python/tags/r26b3/Include/frameobject.h
這種要如何列印調用棧就不用多說了吧,就遍歷一普通的鏈表而已。

要列印混合的調用棧就比較麻煩一些。
怎樣會出現混合的調用棧呢?假如目標層的棧幀也是在系統棧上分配的話,系統棧就會同時包含實現層和目標層兩種棧幀,這倆棧就「混合」成一個棧了。HotSpot VM的Java線程的調用棧就是這樣。通常要爬這樣的調用棧就得自己分析stack pointer與frame pointer然後一層層挖到棧底了。


如果你是解釋器的設計者

有一個很不錯的方法

所有的對象都帶有一個虛函數print

在複合對象(容器對象)的.print的實現中順便調用包含對象的.print

於是你只需要使用技能—— StackFrame.print()就行啦

完美

(ps:話說回來我的錯誤處理機制用的全局映射表實現是不是略有鬼畜。。


解釋器不是很懂,靜態編譯器(C++)是通過在編譯的時候,在object裡面塞入一些信息(CFI),然後程序一旦crash就會發送信號,在處理那邊通過庫函數調用讀取這個CFI(call frame information)來實現的。


推薦閱讀:

TAG:編程語言 | 編程 | 解釋器 | 編譯原理 | 編譯器 |