常规调试方法
一般Emacs在崩溃或者出现错误的时候,可以用命令 toggle-debug-on-error
来打开Emacs调试器,当Emacs或者插件出错时,Emacs会自动在 *Backtrace*
buffer中显示错误堆栈,方便开发者定位错误。
高级调试方法
Emacs还有一些高级调试方法用于非错误场景的调试,举个例子:
sdcv.el基于posframe来实现翻译窗口的弹出,但是有个小问题,每次EAF网页中弹出翻译窗口的时候,鼠标都会跳到Emacs的左上角,翻译以后要重新移动光标, 非常的不方便。
但是这时候我们往往不知道到底是哪个Emacs插件引入的这种行为, 一个一个的排查非常消耗时,但是我知道Emacs底层API中设置光标位置的函数是 set-mouse-position
, 所以这时候可以调用命令 (debug-on-entry 'set-mouse-position)
来设置当Emacs调用 set-mouse-position
函数的时候弹出堆栈,方便开发者定位Emacs运行时的函数调用堆栈。
这时,只要调用翻译函数,一旦光标被插件移动后,就会弹出类似下面的堆栈信息:
Debugger entered--entering a function:
* set-mouse-position(#<frame emacs@manjaro 0x11bbc30> 0 0)
(progn (set-mouse-position frame 0 0))
(if (and posframe-mouse-banish (not (equal (cdr (mouse-position)) (quote (0 . 0))))) (progn (set-mouse-position frame 0 0)))
posframe--mouse-banish(#<frame emacs@manjaro 0x11bbc30>)
...
通过上面的堆栈信息,定位到是 posframe--mouse-banish
函数来设置的光标位置,最后通过 (setq posframe-mouse-banish nil)
来解决posframe弹出时移动光标的问题。
解决问题后可以通过命令 (cancel-debug-on-entry 'set-mouse-position)
取消运行时调试。
Happy hacking!
19 个赞
cireu
2
可以,不过我习惯用rg找到调用底层函数的插件函数后C-u eval-defun
给那个函数加断点,然后开始单步调试
5 个赞
我比较懒,当知道底层API后,可以直接通过这种方法进入堆栈,简单直接,哈哈哈哈。
关键还是得心里有点谱,知道该在哪儿断点。有时候也可能 print 更合适,一设断点反而把复现条件给破坏了。
3 个赞
我到现在都不知道edebug那个条件断点怎么设。还有以前死都不知道怎么取消instrument,后来才知道只要正常eval-defun
一遍就成了
当时写parinfer的时候调试要疯,这些方法都不是太好用。。。
edebug 有什么经验分享吗?这玩意儿实在不好用。
edebug很好用了,只要不是调各种hook。
找个buffer往里面写日志可能是最通用的办法了。
哈哈 我也是, 原来我跟了哪个老师 就变成什么样了 。。。
请教一下,单步调试的时候可能会进入其他插件引入的函数,除了 emacs -q 外,有什么其他方法避免么。
debug-on-entry只会在你指定的函数上进入debugger啊,“可能会进入其他插件引入的函数”是什么意思?
比如我在 debug 如下函数时,(message "test1")
之后是 (message date)
,但是如果不 emacs -q 的话就会进入下图那个 jit-lock-after-change 的函数中,然后是 pangu-spacing 这个插件,之后单步很久才到 (message date)
这一行
d 很久才到下面这个图(如果 c 的话不小心会 c 错导致重新开始,所以我一般都是 d)
d 一步到下面这个图,但是报错信息比 emacs -q 的少
而如果 emacs -q 后调试后,则不会出现上述状况,直接就 message date 了:
d 一步就到下面
继续 d 一步就到出错位置了,但是这里的出错信息比上面那个能够更清楚的看到是 message date 这里出错了。
Code
(require 'parse-time)
(defun parse-date-string (date-string)
"Parse a date string, such as 2016-12-01"
(let ((date-re (nth 0 parse-time-iso8601-regexp)))
(when (string-match date-re date-string)
(let ((year (match-string 1 date-string))
(month (match-string 2 date-string))
(day (match-string 3 date-string)))
(list day month year)))))
(defun parse-time-from-file-name ()
(let* (
(file-name (file-name-nondirectory buffer-file-name))
(date (parse-date-string (substring file-name 0 10)))
)
(message "test1")
(message date)
(message "test2")
date
)
)
(parse-time-from-file-name)
;;; 命名为 2016-10-29-xxx.el,这个函数就是取这个 date 的
c
的话应该直接到下一个message
,你怕c
错我也没啥好办法……如果不想要你提的那些函数,可以把inhibit-modification-hooks
设成nil
试试。