次: Sending Patches, 前: Understanding Bug Reporting, 上: Bugs
バグレポートを送る最良の方法は、電子メイルでEmacs保守チーム `bug-gnu-emacs@gnu.org'に送ることです。 (重要な改良の提案などもここに送ってください)。
他から出されたバグレポートが読みたければ、 ニュースグループ`gnu.emacs.bug'で読めます。 ただし、傍観者として見る場合には、見たものについて批判するべきではない、 ということを承知しておいてください。 バグレポートの目的はEmacs保守チームに情報を提供することです。 傍観者は、この目的に干渉しない限りは、歓迎します。 特に、大量のデータが添付されているバグレポートもありますので、 傍観者はそのことを非難すべきではありません。
ネットニュース経由でバグレポートを投稿しないでください。 ネットニュースよりもメイルのほうが送り手のメイルアドレスが確実にわかり 信頼できます。 もっと情報が必要なときには、メイルで問い合わせる必要があるかも知れません。
電子メイルを送れない場合には、 紙や他の機械可読な媒体で下記へ送ってください。
GNU Emacs Bugs Free Software Foundation 59 Temple Place, Suite 330 Boston, MA 02111-1307 USA
バグを修正するとは約束できません。 しかし、重大なバグや、醜いバグや、簡単に直せるバグなら、直したいと思います。
Emacsのバグレポートを送るのに便利な方法の1つは、 コマンドM-x report-emacs-bugsを使うことです。 このコマンドはメイルバッファ(see Sending Mail)を開いて、 自動的に重要な情報の一部を書き込みます。 しかし、必要な情報をすべて入れられるわけではありませんから、 以下の指針を読んでそれに従い、 メッセージを送るまえに重要な情報を自分で打ち込んでください。
保守チームがバグの調査を開始するためには、 以下のすべてがバグレポートに含まれている必要があります。
バージョン番号を調べるには、M-x emacs-version <RET>と打つ。 このコマンドが動作しないようなら、 GNU Emacsではないエディタを使っているようなので、 どこか別のところへバグを報告する。
configure
コマンドの引数。
これらの変更については正確に記してほしい。 英語での説明では不十分。 ソースのコンテキストdiffを送ること。
独自のファイルを追加したり、別のマシンに移植するのも、 ソースの変更にあたる。
ファイルをまったく訪問せずに問題が再現可能なら、ぜひ教えてほしい。 そのほうがデバッグがずっと楽になる。 どうしてもファイルが必要なら、必ずその内容が正確にわかるようにすること。 たとえば、行末に空白文字が付いているかどうかとか、 バッファの最終行に改行文字があるかどうかが 問題になることは頻繁にある (最終行に改行があるかどうかで何か違いがあるべきではないのだが、 もし違いが生じるようならそれもバグといえる)。
Emacsへの入力を正確に記録する簡単な方法は、 ドリブルファイルに書くことである。 ドリブルファイルを開始するには、 Emacsを実行開始した直後に、M-:か バッファ`*scratch*'でつぎのLisp式を実行する。
(open-dribble-file "~/dribble")
それ以降はEmacsプロセスが終了するまで、 Emacsはすべての入力をドリブルファイルにコピーする。
TERM
の値)、
(すべてのマシンで同じとは限らないので)
/etc/termcapファイル中の当該端末のtermcapの定義すべて、
および、Emacsが実際に端末に送った出力。
端末への出力を収集するには、Emacsを実行開始した直後に、M-:か バッファ`*scratch*'でつぎのLisp式を実行する。
(open-termscript "~/termscript")
それ以降、Emacsはプロセスが終了するまでのすべての端末出力の写しを 指定されたtermscriptファイルに書き出す。 Emacsが起動するときに問題が起きるのなら、 上の式を.emacsファイルに入れて、 Emacsが最初に画面を開くときに一緒にtermscriptファイルも 書き始めるようにする。
ただし、端末に依存したバグは、 そのバグの出る端末なしで直すことは難しいことが多く、 ときとして不可能であることも承知しておいてほしい。
もちろん、Emacsが致命的なシグナルを受け取るのなら、それは誰にでもわかる。 しかし、バグが正しくないテキストだとすると、 保守チームにはどこが正しくないのかわからない可能性がある。 そういう可能性のある書き方はやめてほしい。
遭遇する問題が致命的なシグナルだとしても、はっきりとそう書くべきである。 たとえば、Emacsのソースが一部違っている版だったとか、 システムのCライブラリのバグに遭遇したといった 奇妙なことに出会ったとしよう(実話!)。 あなたが使っているEmacsはクラッシュするが、保守チームのほうでは何ともない。 クラッシュするといってもらえれば、 保守チームのほうで実行してクラッシュしなければバグが再現しないとわかる。 しかしそういってもらえないと、 バグが再現したのかどうかさえわからずに、 試してみた結果からは何の結論も得られない。
エラーメッセージの文面を正確に報告するには、 `*Message*'バッファからメッセージをバグレポートにコピーする。 一部ではなく、全体をコピーしてほしい。
エラーのバックトレースを取得するには、エラーが発生するよりまえにLisp式
(setq debug-on-error t)
を評価する
(つまり、まずこのLisp式を実行して、それからエラーを再現させる)。
すると、エラーが起きたときにLispデバッガが実行され、
デバッガがバックトレースを表示する。
このデバッガのバックトレース出力を、バグレポートにコピーする。
このやり方は、バグを再現できるときだけ使える。 再現できない場合は、最低限、エラーメッセージだけでもすべてコピーする。
-q
を指定して初期化ファイルのロードを抑制して)
個人のファイル.emacsをロードせずに起動したEmacsでも
エラーが再現するかどうか調べる。
これでエラーが再現しないなら、
エラーの再現に必要なので、ロードしたすべてのプログラムの内容を正確に報告する。
開発中のソースの行番号とユーザーが入手するソースの行番号とは同じではない。 あなたが使っているバージョンのソースの何行目が、 開発中のソースの何行目に対応しているか調べるのは余分な手間であり、 正確にはわからないかもしれない。
ただし、バグの原因を示すために追加情報を集める場合には、 追加情報をいつ集めるかをよく考える必要がある。
たとえば、多くの人はバックトレースだけを送ってくるが、 それ単体ではあまり役に立たない。 引数の記録つきの単純なバックトレースでは、 GNU Emacsの内部で何が起きているかについての情報はほとんどない。 というのは、バックトレースに表示される引数のほとんどは Lispオブジェクトへのポインタだから。 それらのポインタの値そのものは、なんら重要ではない。 重要なのは、ポインタが指している先のオブジェクトの内容 (そしてその内容もまたポインタであることが多い)。
役に立つ情報を提供するには、 Lispオブジェクトの値をLispの記法で示す必要がある。 スタックの底付近にある数個のフレームについて、 Lispオブジェクトであるような各変数に対してこれを行ってほしい。 デバッガは単なる整数だと思うので、 どの変数がLispオブジェクトであるかはソースを見てほしい。
変数の値をLispの記法で示すには、まず、その値をプリントしてから、
GDBのユーザー定義コマンドpr
を使ってLispオブジェクトをLispの記法で
表示させる。
(別のデバッガを使わなければならない場合は、
オブジェクトを引数として関数debug_print
を呼び出す)。
コマンドpr
は、ファイル.gdbinitで定義されており、
(コアダンプではなく)実行中のプロセスをデバッグするときだけ使える。
Lispでエラーが発生したときにEmacsを中断してGDBに戻るようにするには、
Fsignal
にブレークポイントを設定する。
実行中のLisp関数の簡素な一覧を表示するには、
GDBのコマンドxbacktrace
を打つ。
Lisp関数の引数を調べたい場合には、
スタック上を上に移動していき関数Ffuncall
のフレームに到達するごとに、
つぎのようなGDBコマンドを打つ。
p *args pr
関数が受け取った最初の引数を出力するには、つぎのようにする。
p args[1] pr
2番目以降の引数でも同様に出力できる。
Ffuncall
の引数nargs
は、
Ffuncall
が受け取った引数の個数を表す。
この個数は、Lisp関数自身とその関数に対する引数とを合わせた数。
ファイル.gdbinitは、
データタイプやLispオブジェクトの中身を調べるのに役立つコマンド類を定義する。
それらのコマンドの名前は`x'で始まる。
これらのコマンドはpr
より下位のレベルで動作し使い難いが、
コアダンプをデバックしたり、
Emacsが致命的なシグナルを受理したときのように
pr
がうまく動かないときでも使える。
こうして調べた結果、Emacsがシステムコールの中で固まっているとわかったら、 Emacsを再度止めて、システムコールの引数を調べる。 そしてバグレポートには、ソース中でのシステムコールの正確な位置と、 引数が何だったかを正確に記入する。
Emacsが無限ループしているのなら、ループの始まりと終りを調べる。 もっとも簡単にこれを調べるには、GDBのコマンド`finish'を使う。 このコマンドを使うたびに、1つのスタックフレームから抜けるまで Emacsは実行を継続する。 戻ってこなくなるまで、繰り返し`finish'を打つ。 戻ってこないのは、そのフレームで無限ループが起こっているからである。
ここでEmacsを再度停止し、 戻ってこなくなったフレームにちょうど戻るまで、 繰り返し`finish'を使う。 つぎに、`next'を使ってそのフレーム内で1ステップずつ実行する。 こうすれば、ループがどこで始まりどこで終るかわかる。 さらに、ループ内で使われているデータを調べて、 ループが終るべきところでなぜ終らないかを追求してみてほしい。 これらの情報すべてを、バグレポートに含める。
以下には、バグレポートに必要ないものをあげておきます。
バグに出会った人はしばしば、入力をどう変えるとバグが出なくなるとか、 あるいは、相変わらず出るといったことを探求するのに時間をかける。
これは時間がかかるわりには、役に立たない。 というのは、保守チームがデバッグを行うときには、 デバッガのもとでブレークポイントを設定しながらバグの出る1つの例を 実行するのであって、何通りもの例から帰納的に推論するわけではない。 だから、別の例を探すのに時間をかけたりしないでほしい。
もちろん、もとの例のかわりに使えるもっと簡単な例がみつかれば、 それは役に立つ。 簡単な例なら、出力中のエラーもみつけやすくなり、 デバッガを使って実行するにも短い時間ですむ。
ただし、単純化は必須ではない。 もし単純化できなかったり、単純化する時間がなければ、 もとの例のままでよいので、バグレポートを出してほしい。
ある特別な種類のバグについては、システムコールトレースは非常に役立つが、 多くの場合はほとんど有用な情報は得られない。 したがって、多くの人がシステムコールのトレースこそクラッシュに 関する情報を報告するのに欠かせないものだと思っているらしいのは、 不思議である。 これはたぶん、ソースコードやデバッグ用シンボルのないプログラムを デバッグした経験から生まれた習慣だろう。
ほとんどのプログラムでは、システムコールのトレースより、
バックトレースのほうがずっとずっと役に立つ。
Emacsでさえ、単純なバックトレースのほうが有用である。
しかし、十分な情報を提供するには、バックトレースの補記として、
変数の値を表示しpr
でLispオブジェクトとしても表示する(上記参照)。
バグに対する修正は、よい品質のものなら有用である。 しかし、修正が正しいことを示すテスト例などの バグレポートに必要な情報を省かないでほしい。 修正に問題があるとわかって別のやり方でバグをつぶすかもしれないし、 報告された修正がまったく理解できないこともありえる。 そして、どんなバグを修正しようとしているのかわからない、あるいは、 その修正がなぜ改良になるのかわからなければ、 その修正を採用するわけにいかない。
こういう予想は、たいていはまちがっている。 専門家でさえ、まずデバッガで事実を調べない限り、正しい予想はできない。