`

Programming in Emacs Lisp笔记(十七) 调试

阅读更多

调试

GNU Emacs中有两个高度器,debug和edebug。第一个是Emacs内建的可以随时使用它;第二个需要借助一些函数才能使用。

debug

假设你编写了用于加1的函数。但函数有个bug。你误将1-输入为1=了。函数定义如下:

(defun triangle-bugged (number)
"Return sum of numbers 1 through NUMBER inclusive."
(let ((total 0))
(while (> number 0)
(setq total (+ total number))
(setq number (1= number))) ; Error here.
total))
当传递4给这个函数时:
(triangle-bugged 4)
在Emacs 21中,将产生一个*Backtrace*缓冲区,并进入这个缓冲区:
---------- Buffer: *Backtrace* ----------
Debugger entered--Lisp error: (void-function 1=)
(1= number)
(setq number (1= number))
(while (> number 0) (setq total (+ total number))
(setq number (1= number)))
(let ((total 0)) (while (> number 0) (setq total ...)
(setq number ...)) total)
triangle-bugged(4)
eval((triangle-bugged 4))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
(重新格式化了一下;调试不会自动折行。可以用q退出调试器)

实际上,像这样简单的bug,'Lisp error'这行告诉了我们如何修改定义。函数1=为'void'。

在Emacs 20中,你将看到:

Symbol's function definition is void: 1=
这与21版中的*Backtrace*缓冲区中的意思是一样的。

假设你还不是很清楚要如何做?你可以阅读完整的回溯信息。

在GNU Emacs 21中,它将自动启动调试器,并将信息放到*Backtrace*缓冲区中;如果有使用Emacs21,可能需要按下面的方法手工启动调试器。

在*Backtrace*中从下向上读;它说明了Emacs是如何出错的。Emacs执行了一个交互式命令C-x C-e(eval-last-sexp),它执行了triangle-bugged语句。上面的每一行显示了Lisp解释器执行内容。

缓冲区的顶部是:

(setq number (1= number))

Emacs试图执行这个语句;依次来执行,它首先执行内部的语句:

(1= number)

这里发生了错误,如错误信息所说:

Debugger entered--Lisp error: (void-function 1=)

你可以修正这个错误,然后重新执行函数定义,再运行测试代码。

debug-on-entry

GNU Emacs 21在函数出错时自动启动了调试器。GNU Emacs 20不会这样做;它只显示一条出错信息。你需要手工启动调试器。

手工启动的好处是在程序没有bug的时候也可以调试。

你可以调用debug-on-entry函数进入调试器。

输入:

M-x debug-on-entry RET triangle-bugged RET

然后,执行下面的语句:

(triangle-bugged 5)
所有版本的Emacs都将产生一个*Backtrace*缓冲区告诉你它将执行triangle-debugged函数:
---------- Buffer: *Backtrace* ----------
Debugger entered--entering a function:
* triangle-bugged(5)
eval((triangle-bugged 5))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
在*Backtrace*缓冲区中输入d。Emacs将执行triangle-bugged的第一行语句;缓冲区看起来如下:
---------- Buffer: *Backtrace* ----------
Debugger entered--beginning evaluation of function call form:
* (let ((total 0)) (while (> number 0) (setq total ...)
(setq number ...)) total)
* triangle-bugged(5)
eval((triangle-bugged 5))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------

现在,再次输入d,连续8次慢慢的输入d,Emacs将执行函数定义的另一个语句。

最后缓冲区看起来如下:

---------- Buffer: *Backtrace* ----------
Debugger entered--beginning evaluation of function call form:
* (setq number (1= number))
* (while (> number 0) (setq total (+ total number))
(setq number (1= number)))
* (let ((total 0)) (while (> number 0) (setq total ...)
(setq number ...)) total)
* triangle-bugged(5)
eval((triangle-bugged 5))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
最后再输入两次d,Emacs将到达错误的位置,*Backtrace*缓冲区顶部的两行将显示:
---------- Buffer: *Backtrace* ----------
Debugger entered--Lisp error: (void-function 1=)
* (1= number)
...
---------- Buffer: *Backtrace* ----------

输入d可以单步执行函数。

可以输入q退出*Backtrace*缓冲区;这将退出跟踪,但并不会退出debug-on-entry。

要退出debug-on-entry,需要调用cancel-debug-on-entry并输入函数名称:

M-x cancel-debug-on-entry RET triangle-bugged RET

debug-on-quit和(debug)

除了debug-on-error或调用debug-on-entry,还有另外两种方法启动debug。

可以通过将变量debug-on-quit设置为t,随时输入C-g(keyboard-quit)来启动debug。这在调试无限循环时很用效。

或者,你可以在代码中插入(debug)以启动调试器,比如:

(defun triangle-bugged (number)
"Return sum of numbers 1 through NUMBER inclusive."
(let ((total 0))
(while (> number 0)
(setq total (+ total number))
(debug) ; Start debugger.
(setq number (1= number))) ; Error here.
total))

edebug源码级的调试器

Edebug是一个源码级的调试器。Edebug通常显示你要调试的源码,并在左边用箭头指出当前执行的行。

你可以单步执行函数,或者快速的执行到断点位置。

下面是tringle-recursively的调试函数:

(defun triangle-recursively-bugged (number)
"Return sum of numbers 1 through NUMBER inclusive.
Uses recursion."

(if (= number 1)
1
(+ number
(triangle-recursively-bugged
(1= number))))) ; Error here.
同样,你可以在函数定义后面使用C-x C-e(eval-last-sexp)安装函数,或者将光标放到定义的内部输入C-M-x(eval-defun)。(缺省情况下,eval-defun命令只在Emacs Lisp或Lisp交互模式下才可以工作。)

但是,为了使用Edebug调试函数,你必须使用另一个命令。可以将停留在函数内部然后输入

M-x edebug-defun RET

这将使Emacs自动加载Edebug。在加载完成后,可以将光标放在下面语句的后面输入C-x C-e(eval-last-sexp):

(triangle-recursively-bugged 3)
将跳到triangle-recursively-bugged的源码,光标被设置在函数if语句所在的开始行。并且,可以在这行的左边看到一个箭头。箭头标明了函数当前执行的位置。(在例子中,我们使用=>代替;在窗口系统中,你可以看到一个实心的三角形)
=>-!-(if (= number 1)
在这里,point的位置显示为-!-。

如果你输入<spc>,point将移到下一个语句;这行将显示如下:</spc>

=>(if -!-(= number 1)

如果继续输入<spc>,point将继续从一个语句移到另一个语句。每次只要语句返回了值,它都会显示到回显区。比如,在point移过number时,你将看到:</spc>

Result: 3 = C-c
这表示number的值为3,它的ASCII值是'control-c'。

你可以继续执行,直到错误的位置。在执行之前,这行如下:

=>        -!-(1= number)))))               ; Error here.

当再次输入<spc>时,将产生错误信息:</spc>

Symbol's function definition is void: 1=

输入q退出Edebug。

要从函数上移除调试的机制,可以重新使用C-x C-e执行函数定义。

Edebug除了跟踪执行外可以做更多的工作。你可以设置它在遇到错误时停止;可以让它显示或修改变量的值;你可以查找出函数被执行了多少次,等等。

评论

相关推荐

    Programming in Emacs Lisp

    Programming in Emacs Lisp英文版

    An Introduction to Programming in Emacs Lisp

    - **标题**:“An Introduction to Programming in Emacs Lisp”(Emacs Lisp编程入门) - **描述**:该资源是基于Emacs官方文档的重编版本,旨在提供更易阅读的字体样式。 #### 知识点详解 ##### 1. Emacs Lisp...

    An Introduction to Programming in Emacs Lisp Second Edition

    - **标题**:“An Introduction to Programming in Emacs Lisp Second Edition” - **描述**:本书是关于Emacs Lisp编程的入门教程,被誉为“经典中的经典”。 该书由Robert J. Chassell撰写,由自由软件基金会出版...

    Robert Chassell:An Introduction to Programming in Emacs Lisp

    ### Robert Chassell:An Introduction to Programming in Emacs Lisp #### 知识点概览 - **Lisp Lists**: 介绍Lisp列表的概念及其在Emacs Lisp中的应用。 - **Lisp Atoms**: 解释Lisp原子的基本概念以及它们在...

    An Introduction to Programming in Emacs Lisp [3.10].chm

    An Introduction to Programming in Emacs Lisp [3.10].chm

    GNU Emacs Lisp编程入门(文本org)

    Programming in Emacs Lisp: An Introduction (美)Robert J.Chassell 著 毛文涛、吕芳 译 洪峰 审校 本书的作者罗伯特·卡塞尔是自由软件基金会的合创人之一,也是理查德·斯托曼博士青年时期结交的挚友,他...

    GNU Emacs Lisp编程入门

    GNU Emacs Lisp编程入门(清晰版) 英文名:An Introduction to Programming in Emacs Lisp

    GNU Emacs Lisp 编程入门

    GNU Emacs Lisp是一种用于GNU Emacs编辑器的编程语言,它是一种功能强大且灵活的编程工具,广泛应用于文本编辑、日历管理、编写邮件、编程以及调试程序等各种环境中。 GNU Emacs Lisp的编写入门从其基础概念讲起,...

    Atom-language-emacs-lisp,emacs lisp和yasnippet支持atom和github。.zip

    在“Atom-language-emacs-lisp.zip”这个压缩包中,主要关注的是对Emacs Lisp的支持。Emacs Lisp是一种用于扩展和定制GNU Emacs编辑器的Lisp方言。它提供了丰富的API和交互式编程环境,使得开发者可以编写自定义的...

    Programming-in-Emacs-LISP.pdf.rar_LISP PDF_emacs lisp_emacs lisp

    GNU emacs Lisp manual This is a very interesting text, useful to write and program in several languages. Emacs is a editor made in Lisp, a artificial intelligence language.

    GNU EMACS lisp编程入门.djvu

    GNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvu

    GNU Emacs Lisp Reference Manual For Emacs Version 22.1 Revision 2.9, April 2007

    这一章介绍了Emacs Lisp中提供的调试工具,如`debug-on-entry`等命令,以及如何使用这些工具来定位和解决问题。 #### 附录 手册还包含了几篇附录,包括了Emacs 21的反新闻、GNU自由文档许可证、GNU通用公共许可证等...

    gnu emacs lisp 编程入门

    本书的英文版原名是《Programming in Emacs Lisp: An Introduction》,由自由软件基金会出版,享有复制与分发的权限。机械工业出版社在2001年5月出版了中文版,ISBN为7-111-08862-6。书籍的定价为38.00元,并承诺如...

    emacs-lisp编程入门

    Emacs Lisp(简称Elisp)是GNU Emacs编辑器的核心编程语言,它允许用户自定义、扩展和控制这个功能丰富的文本编辑器。作为一个强大的脚本语言,Elisp在程序员和Emacs爱好者中广受欢迎,不仅适合初学者,也满足高级...

Global site tag (gtag.js) - Google Analytics