Last modified: Fri Aug 1 20:20:20 JST 2008
Emacs Lisp で書かれたどんなプログラムにも当てはまることですが、単に

        動かないんですけど

と書かれたメールを受け取っても、作者はどうしてよいかわからないことが
多いものです。ではどんな情報を送れば良いのでしょうか?


        自戒を込めて: 「報告」は目上の人に対して使うことばです。ですか
        ら、「バグを報告する」あなたの日本語は正しい (作者の方が身分が
        上だなんて言ってるんじゃないよ)。しかし、それを受け取った作者が
        「ご報告ありがとう」と言うのはちょっと変です。
        「教えてくださってありがとう」の方が良いのではないかしら。

こちらも参考にしてください: <効果的にバグを報告するには>

1. どんな環境で起こったか                                           例
=========================                         ====================
  何を使っているときに起こった問題ですか?                         gnus

  そのバージョンは?                                             6.14.1
  (または、どこからそれを取得しましたか?)

  それに付随して使っているプログラムは何ですか?   APEL,   FLIM,   SEMI

  それらのバージョンは?                           10.0, 1.13.5, 1.13.7
  (または、どこからそれを取得しましたか?)

  Emacs は何をお使いですか?                                     XEmacs

  そのバージョンは?                                            21.2.26


2. どんな操作を行なったときに起きたか
=====================================
  なるべく詳しく書いてくださいね。


3. バックトレースの採取
=======================
  これはもっとも重要です。


  a. 問題が起きた後でキー入力ができる場合
  ---------------------------------------
     *scratch* バッファに

     (setq debug-on-error t)

     と書いて、`)' の後ろで C-j をタイプしてください。

  b. ハングアップする (普通のキー入力はできないが C-g キーは効く) 場合
  --------------------------------------------------------------------
     *scratch* バッファに

     (setq debug-on-quit t)

     と書いて、`)' の後ろで C-j をタイプしてください。

  c. ハングアップしたままで止められない、または Emacs が即死する場合
  ------------------------------------------------------------------
     この場合はどうしようもありません。ありのままをお知らせください。

        しかし、おそらくそれは Emacs 本体のバグです。Emacs Lisp で書か
        れたどんなプログラムを走らせても、Emacs が固まったり死んでしまっ
        てはいけないことになっているのです。

  d. エラーは起こらないが、望んだ動作をしない場合
  -----------------------------------------------
     プログラムが不完全である、故意にエラーを起こさないようになってい
     る、または報告されない種類のエラーであることが考えられます。この
     場合はバックトレースを採取できないかもしれませんが、一応 a か b
     と後述の方法を試してみてください。


  さて、上記 a または b の準備をしておくと、エラーが起きたとき、または
  C-g キーで中断させた場合に *Backtrace* という名前のバッファが現れま
  す。そうしたら、その内容をメールに張り付けて送りましょう。でも

    !! ちょっと待ってください !!

  あなたにもう少し頑張っていただくことによって、問題解決までの時間を大
  幅に短縮できるかもしれません。それと、バックトレースには ascii 以外
  の文字が含まれていることが多々ありますから、そのままメールで送ると事
  故の原因になる可能性が大です。

    バックトレースに ascii 以外の文字が含まれているかもしれない
    ときは MIME の別パートにするか、いったん binary (または
    *noconv*) でファイルにセーブ (※) してから添付ファイルとして
    送信することを心掛けましょう。
    大きい場合は gzip などで圧縮するのも良いでしょう。

  (※) *Backtrace* のバッファで
       M-x set-buffer-file-coding-system       ...(Emacs 20 や XEmacs)
       M-x set-file-coding-system              ...(Mule 2.3 など)
       で binary か *noconv* を指定してから
       M-x write-file (または C-x C-w) で適当なファイル名を指定します。

  例えばこんな *Backtrace* が現れたとします。

  Signaling: (wrong-type-argument char-or-string-p nil)
    article-date-ut(local nil)
    article-date-local()

  これが意味するのは、エラーが起きたのは article-date-ut という関数の
  中である、ということです。しかしこれだけではその関数の何が悪いのかを
  的確に知ることは困難です。エラーの原因を特定しにくい例をもう一つ。

  Debugger entered--Lisp error: (error "Stack overflow in regexp matcher")
    signal(error ("Stack overflow in regexp matcher"))
    byte-code("\301 \210\302^H@^HA\"\207" [err ...
    wl-message-buffer-display([elmo-shimbun-folder [0 0 0 0 0 0 0] shimbun

  これは byte-code(...) という関数が wl-message-buffer-display という
  関数から呼ばれてエラーが起きているのですが、このように byte compile
  された関数が現れても、それだけの情報では、それが何で、どこで定義され
  ているかを知ることは事実上不可能です。

  そこで、主に一つ目の article-date-ut を例に、エラーの詳細を知るため
  の方法を書きましょう。二つ目のような場合でも、同様の方法でもっと人間
  が判読しやすい情報を得ることができます。

  まず article-date-ut という関数がどこで定義されているかを調べてくだ
  さい。二つ目の例については wl-message-buffer-display という関数を調
  べることから始めてください。

  M-x describe-function
  Describe function: article-date-ut

  (M-x describe-function は [Help]f または C-h f キーで代用できます。)

  すると、以下のような画面が現れて

  `article-date-ut' is an interactive compiled Lisp function
    -- loaded from "/usr/local/lib/xemacs/packages/lisp/gnus/gnus-art.elc"

  gnus-art.elc というファイルで定義されていることがわかります。そうし
  たら

  M-x load-file
  Load file: /usr/local/lib/xemacs/packages/lisp/gnus/gnus-art.el

  とタイプして gnus-art.el (gnus-art.elc ではありません) を読み込み、
  さらに

  M-x abort-recursive-edit

  とタイプしてから、もう一度同じエラーを起こしてみてください。すると今
  度は次のような *Backtrace* が現れます。

  Signaling: (wrong-type-argument char-or-string-p nil)
    insert(nil)
    (let ((buffer-read-only nil)) (when (setq date-pos ...) (goto-char
    (save-restriction (article-narrow-to-head) (when (or ... ... ...)
    (save-excursion (save-restriction (article-narrow-to-head) (when .
    (if (and date (not ...)) (save-excursion (save-restriction ... ...
    (when (and date (not ...)) (save-excursion (save-restriction ... .
    (let* ((header ...) (date ...) (inhibit-point-motion-hooks t) bfac
    article-date-ut(local nil)
    article-date-local()

  ここまでくればしめたものです。ですが念には念を入れて、エラーを起こし
  ている関数 insert が何者かを調べてみましょう。

  `insert' is a built-in function

  これは Emacs の組み込み関数ですので、これ以上の探求は不要です。しか
  しそれが別の *.elc ファイルで定義されているものだったら、エラーを起
  こす関数が built-in になるまで、そしてなるべく byte-code(...) のよう
  なものが現れなくなるまで、以下を繰り返してください。

  M-x load-file
  Load file: *.el    (*.elc ではありません)

  M-x abort-recursive-edit

  (エラーを起こす操作)

  このようにして採取したバックトレースは、作者を大いに助けてあげること
  ができるはずです。

  さてここでもう一つ、よくある特別なバックトレースの例をあげておきます。

  Signaling: (invalid-function (macro lambda (entity) (list (quote are
    mail-header-date([mime-buffer-entity [0 0 0 0 0 0 0] 33 ((type . t
    article-date-ut(local nil)
    article-date-local()

  これは `article-date-ut' で使われている `mail-header-date' という関数
  がおかしいと言っているのですが、このように

  Signaling: (invalid-function (macro ...

  という文句が出た場合、その直接の原因になっている `mail-header-date'
  を M-x describe-function で調べると

  `mail-header-date' is a compiled Lisp macro
    -- loaded from "nnheader.elc"

  これは普通の関数ではなく「マクロ」であることがわかります。これの意味
  するのは、`mail-header-date' というマクロを使っている gnus-art.el を
  コンパイルする際に、あらかじめ `mail-header-date' が定義されている
  nnheader.el(c) を load する必要があったのに行なわれていなかった、と
  いうことです。

    ;; 関連する *.el ファイルを M-x load-file してバックトレースを
    ;; 採り直そうとするとエラーが起きなくなってしまう場合があります
    ;; が、そのこと自体がマクロが未定義のままコンパイルしてしまった
    ;; ことの証明になります。

  もしあなたが標準的な方法で make した結果このようなエラーに遭遇したの
  ならば、おそらくその責任はプログラムの作者にあります。しかし、あなた
  が gnus-art.el を手作業で M-x byte-compile-file した、などの場合には
  適正な方法で make し直せば問題を回避できる可能性がありますので、まず
  はご自分で試してみてください。
  あるいは、もしあなたがコンパイル済みのパッケージをお使いならば、最後
  の手段としてパッケージを作成した責任者の方に、バックトレースを添えて
  問い合わせてみるのも良いかもしれません。


4. バックトレースを採取するときに注意すること
=============================================
  一度 *Backtrace* という名前のバッファが現れると、以後はエラーが起き
  ても再びバックトレースを採取することができない状態になっています。
  これができるように元の状態に戻してやるためのコマンドが

  M-x abort-recursive-edit

  で、最近の Emacs では C-] というキーに標準で割り当てられています。
  あるいは *Backtrace* バッファで q をタイプしても有効かもしれません。
  これらの状態はモードラインの右端の方に鍵括弧 [...] が表示されている
  かどうかで知ることができます。


  バックトレースを取得し終わって普通の状態に戻したいときは、まずモード
  ラインの右端に鍵括弧が表示されていない状態にしてから *scratch* バッ
  ファで

     (setq debug-on-error nil)

  または

     (setq debug-on-quit nil)

  を eval (行末で C-j をタイプ) し、さらに *.el ファイルを load-file
  してしまった場合には *.elc ファイルを load-file するか、または

  M-x load-library
  Load library: gnus-art

  などとタイプしてください。これは gnus-art.elc を load-file するのと
  同じことです。


  これまでに述べたように *scratch* バッファは Emacs Lisp のプログラム
  を評価する場合に大変大事なものですので、間違っても text-mode などに
  してしまうことはなさりませんよう。


  詳細なバックトレースを採るために *.el ファイルを load しようにも、そ
  れがインストールされていない場合もあるでしょう。しかし、それが確実に
  動作することが確認できるまでは、*.elc とは別の場所でも構いませんから
  残しておくことを強くお勧めします。


  なお、古い Emacs では M-x describe-function を使って、ある関数が定義
  されているのがどのファイルなのかを調べることができない場合があります。
  そういう場合は、それらしい *.el ファイルを grep などで探すしか手は無
  いかもしれません。