この前はC-u C-u C-M-xでトレーサにかける設定を示しましたが、
今日もトレーサについてです。
Emacs Lispの デバッグ ではトレーサ(M-x trace-function) が超強力です。
しかし、その表示が見辛いという大きな欠点があるため、
どうしても使うのに二の足を踏んでしまいます。
ここで示す設定を加えることで
トレーサの結果を必要な部分だけ
わかりやすく表示するので
使い勝手が格段と向上します。
そのためデバッグが円滑に進みます。
トレーサ表示の問題点は以下の通りです。
- いつ、その関数が呼ばれたのかという情報がない
- トレーサを呼び出したコマンドに関する一連のトレースのみが取り出せない
- 該当部分のトレースを見付けることが困難
- バックトレースがないので、どのような経緯で呼ばれたかも不明
これらの問題はトレース結果を org-mode で表示することですべて解決します。
設定 50trace-org.el(以下のコードと同一)
(require 'trace) (require 'org) ;;;; FIXME trace-functionの結果をorg-modeで表示する設定 (defadvice trace-function-internal (before trace-org-mode (function buffer &rest _) activate) "view-modeのままでも書き込みできるようにする" (with-current-buffer (get-buffer-create (or buffer trace-buffer)) (set (make-local-variable 'inhibit-read-only) t))) ;;; display (defun display-buffer-trace-output (buffer alist) "*trace-output*をorg-mode/view-modeで先程のトレース結果を画面上部で表示" (let (display-buffer-alist) (display-buffer buffer)) (with-selected-window (get-buffer-window buffer) (org-mode) (org-hide-block-all) (view-mode 1) (org-overview) (ignore-errors (goto-char (point-max)) (call-interactively 'previous-line) ;avoid warning (org-show-subtree) (recenter 0)) (selected-window))) (add-to-list 'display-buffer-alist `(,(rx (or "*trace-output")) display-buffer-trace-output)) ;;; 連続したトレースをまとめて表示する (defvar trace-entry-last-time (float-time)) (defvar trace-session-duration 2 "ひとまとまりのtraceを表示する時間単位") (defadvice trace-entry-message (after add-time-and-backtrace (function &rest _) activate) "trace結果をorg形式に整形" (let ((btbuf (generate-new-buffer " trace-backtrace")) (bt (with-output-to-string (backtrace)))) (with-current-buffer btbuf (insert bt) (goto-char 1)) (setq ad-return-value (format "%s** %s\n[[elisp:(view-buffer \"%s\")]]\n%s\n" (if (<= trace-session-duration (- (float-time) trace-entry-last-time)) (format "* Session %s\n" (format-time-string "%X")) "") function ;; (with-output-to-string (backtrace)) (buffer-name btbuf) ad-return-value))) (setq trace-entry-last-time (float-time))) (setq trace-separator "\n") (defun org-ctrl-c-ctrl-c-hook--trace-pp-sexp () (when (and (string= "*trace-output*" (buffer-name)) (string-match "^[|0-9]+ " (thing-at-point 'line))) (pp-display-expression (read (concat "(" (thing-at-point 'line) ")")) "*pp*") t)) (add-hook 'org-ctrl-c-ctrl-c-hook 'org-ctrl-c-ctrl-c-hook--trace-pp-sexp)
実行方法
$ wget http://rubikitch.com/f/50trace-org.el $ emacs -Q -f package-initialize -l 50trace-org.el
たとえば、以下のコードにおいて
M-x hoge 10 200
を実行すると
(defun hoge--a (x) (* 2 x)) (defun hoge--b (x) (* 3 x)) (defun hoge (x y) (interactive "nX: \nnY: ") (message "a=%d b=%d" (hoge--a x) (hoge--b y))) (trace-function 'hoge--a) (trace-function 'hoge--b)
このように表示されます。
Session 08:27:36
hoge--a
#+BEGIN_EXAMPLE
1 -> hoge--a: x=10
1 <- hoge--a: 20
hoge--b
1 -> hoge--b: x=200 1 <- hoge--b: 600 BEGIN_EXAMPLEのところでTABを押すと、 その時点でのバックトレースが見られます。 #+BEGIN_EXAMPLE backtrace() trace-entry-message(hoge--a 1 ((x 10 required))) hoge--a(10) (message "a=%d b=%d" (hoge--a x) (hoge--b y)) hoge(10 200) ad-Orig-call-interactively(hoge t nil) call-interactively(hoge record nil) command-execute(hoge record) execute-extended-command(nil "hoge") ad-Orig-call-interactively(execute-extended-command t nil) call-interactively(execute-extended-command nil nil)
時間情報を加えたことで、再度M-x hogeを実行すると、
その実行に関するトレースのみが表示されます。
本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。