読者です 読者をやめる 読者になる 読者になる

おがさわらなるひこのオープンソースとかプログラミングとか印刷技術とか

おがさわらなるひこ @naru0ga が技術系で興味を持ったりなんだりしたことをたまーに書くブログです。最近はてなダイアリー放置しすぎて記事書くたびにはてな記法忘れるのではてなブログに移行しました。

クリエイティブ・コモンズ・ライセンス
特に断りがない場合は、本ブログの筆者によるコンテンツは クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンスの下に提供されています。

Common Printing Dialog をデバッグしてみよう (その1) [改訂あり]

d:id:naruoga:20090527:1243382202 はこのネタの前フリ。

もう現役プログラマといえなくなってしまったオイラだけど、ついつい勢いでジュンク堂Debug Hacks 刊行記念イベント (d:id:naruoga:20090509:1241887511) に行ってしまい、サインもらうために本買ったのさ。

Debug Hacks -デバッグを極めるテクニック&ツール

Debug Hacks -デバッグを極めるテクニック&ツール

で、最近急速に積ん読が増えてるのを反省して、とりあえず風を通して、Windows プログラマにも共通の知見があったら回りのエンジニアに渡して読んでもらおうなんて思ってたわけ。

けどやっぱりというかなんていうか Linux 本なわけで。しかもかなりディープなカーネルデバッグの本なわけで *1

まあそんなこんなで風通し終えて、デバッグとかトラシューの過程って推理小説みたいで面白いよなー、なんて平凡な感想を持ちつつ本棚にしまおうとして。

いやまてよ、部署で数少ない Linux 技術者としては、ちょっと手を動かして実践してみないとイケてなくね? と思いまして、せっかくだから手を動かした結果を晒してみようというエントリでございます。
こちとら gdb [enter] って押すのも今日初めてな素人なんでツッコミ歓迎ですハイ。

Target

Common Printing Dialog KDE 版。

現象解説

こいつには「CUPS プリントキューが一個もないと KCrash が "Application 'view-dialog' Crashing..." とつぶやきつつ 『ファイルが見つかりません』といって寂しく終了する」というバグとも仕様とも言い難い動作*2がありまして。
まあ実際プリントキューがなかったらプリントダイアログだしてもしょうがないので仕様をどう決めるかってのは議論の余地はあると思うんですけどね。でも Crash するのは論外やろ。
ただ、エラーメッセージが --previewfile で指定したファイルがなかったときと同じというのは、いかなプロトタイプでもちょっと……じゃないかな? と。エラーハンドリングが悪いのかエラーメッセージを手抜きしたのかわかんないけど。

これぐらいの不具合だったらデバッガ出すよりエラーメッセージで grep してソース眺めた方が早いって説もあるけど、そこはあえてデバッガを使うのです。

デバッグオプションつきコンパイル

こんなところでいきなりつまづくところがみっともないです。
だって cmake よくわかんないんだもん。

結局 id:niitsuma さんの d:id:niiitsuma:20071127:1201323141
にずばっと書いてあったので、よしよし……と丸パクリして。

$ cmake -DCMAKE_INSTALL_PREFIX=/usr/bin/kde4 -DCMAKE_BUILD_TYPE=Debug ..
-- Found Qt-Version 4.5.0 (using /usr/bin/qmake)
-- Found X11: /usr/lib/libX11.so
-- Phonon Version: 4.3.0
-- Found KDE 4.2 include dir: /usr/include
-- Found KDE 4.2 library dir: /usr/lib
-- Found the KDE4 kconfig_compiler preprocessor: /usr/bin/kconfig_compiler
-- Found automoc4: /usr/bin/automoc4
-- Found CUPS: -lcups -lgssapi_krb5 -lgnutls -lz -lpthread -lm -lcrypt
CMake Error at kde4-dialog/CMakeLists.txt:7 (PKGCONFIG):
  Unknown CMake command "PKGCONFIG".


-- Configuring incomplete, errors occurred!

あれあれ?

どーやら cmake って生成したファイルをオプション変えたからって上書きしてくれないみたいね >

15分ほど悩んでしまった。

で、結局は build フォルダを消して再ビルドしたらうまく行ったです。

ちゃんとデバッグ版になってるの?

ということで不安になったので gdb で読み込んでみました。
今回は view-dialog の起動時の不具合なので、普通に引数で渡せばおっけーですね。kde4-cpd のようなデーモン的モジュールはプロセスアタッチが必要でしょうけど。

$ gdb kde4-dialog/view-dialog
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) b main
Breakpoint 1 at 0x80510e7: file /home/naruhiko/common-printing-dialog/kde4-dialog/view-dialog.cpp, line 11.
(gdb) 

"b main" でブレークポイントかけたとき、ちゃんとファイル名と行数が出てるから大丈夫なようですね。ぶい。

まーとりあえず最後まで実行してみましょうかってことで、

(gdb) run
Starting program: /home/naruhiko/common-printing-dialog/build/kde4-dialog/view-dialog 
[Thread debugging using libthread_db enabled]
[New Thread 0xb5e3b940 (LWP 15164)]
[Switching to Thread 0xb5e3b940 (LWP 15164)]

Breakpoint 1, main (argc=1, argv=0xbff4ac24)
    at /home/naruhiko/common-printing-dialog/kde4-dialog/view-dialog.cpp:11
11	int main(int argc, char* argv[]) {
(gdb) cont
Continuing.
ASSERT failure in QVector<T>::operator[]: "index out of range", file /usr/include/qt4/QtCore/qvector.h, line 335

Program received signal SIGABRT, Aborted.
0xb7f2d422 in __kernel_vsyscall ()
(gdb) q
The program is running.  Exit anyway? (y or n) y

……あれ?
なんか違うエラー起きてね?
SIGABRT なんか出してちゃだめだろ。しかも Qt の中で (T_T)。
微妙に蒼くなりながら確認です。
*3

切り分け切り分け

思いつく切り分けは

  • デバッグ版固有か
  • gdb の外でも起きるか
  • プリントキューがある場合でも起きるか
  • 正しく引数を渡した場合でも起きるか

てなとこです。順に確認……しません。
だって Retail 版つくるのめんどくさいんだもん。別の環境で見ます。→見ました。何度も書くけど思いきりクラッシュしてました。

gdb の外では?

さすがにデバッガが外乱要素ってことはないと思うんですが、念のため。

$ kde4-dialog/view-dialog
ASSERT failure in QVector<T>::operator[]: "index out of range", file /usr/include/qt4/QtCore/qvector.h, line 335
KCrash: Application 'view-dialog' crashing...
sock_file=/home/naruhiko/.kde/socket-naruhiko-virtualbox/kdeinit4__0
Warning: connect() failed: : No such file or directory
KCrash cannot reach kdeinit, launching directly.
KCrash failed to exec(), errno = 2

……だよなー。やっぱり。

プリントキューがある場合でも起きるか

これはけっこう大きな違いじゃないかと思ってました。
つまり、デバッグメッセージから見るとベクタの範囲外参照で SEVABRT してます。
わたしゃ Linux のことはよく知りませんが、ふつーベクタの範囲外参照とかいちいちチェックしてたら遅くなるのでサボったりするんじゃないでしょうか?
だから、今回の現象自体が実は「非デバッグ版でエラーがすり抜けるために他のエラーと区別がつかない」という現象なんじゃないかと疑っておるわけです。

という考察は的外れだけどちょこっと当たってもいないこともない。KCrash っていうぐらいだからその場で止まってくれればいいのに、なぜだか処理を継続しちゃうのね。それがファイル名不正のエラーハンドラに合流するってことだと踏んでるんだけど、さて結果は如何に……

ということでプリントキューを一個作ってやると、

$ kde4-dialog/view-dialog
CUPSOptionsBox::sizeHint() =  QSize(18, 18) 
CUPSOptionsBox::sizeHint() =  QSize(18, 18) 
CUPSOptionsBox::sizeHint() =  QSize(18, 18) 
Layout 2: 
plwidth:  181 
pwidth:  412 
Processing group  "General" 
Processing group  "C1L0" 
Processing group  "C1L1" 
Processing group  "C1L2" 
Processing group  "C1L4" 
CPConfMatrixWidget::add( "Application Name" ,  1 ,  1 ); 
CPConfMatrixWidget::add( "General" ,  2 ,  1 ); 
CPConfMatrixWidget::add( "Output Control Extra 4" ,  3 ,  1 ); 
CPConfMatrixWidget::add( "Output Control Extra 2" ,  1 ,  2 ); 
CPConfMatrixWidget::add( "Output Control Extra 1" ,  2 ,  2 ); 
CPConfMatrixWidget::add( "Output Control Common" ,  3 ,  2 ); 
Adding option  "ColorModel" 
Adding option  "Duplex" 
Adding option  "InputSlot" 
Adding option  "PageRegion" 
 ...
Refreshing preset list... 
There are  0  presets. 
Loading presets for printer  "Ricoh-Aficio-MP-7500" 
After loading, there are  0  presets. 
Refreshing preset list... 
There are  0  presets. 

となってちゃんとダイアログが起動します。


おーし、ということはプリントキューが一個も存在しないときにいわゆる境界領域バグを起こしてる可能性が高いですね♪
ちょっと面白くなってきました。「エラーハンドリングのミス」とか「エラーメッセージの手抜き」とかじゃさすがにやる気がしない。

ということで次回は今回エラートラップしてるところからバックトレースしていってエラー箇所を突き止めるなんて話になりますかね。少しワクワクしますね。
しかし私出勤前に徹夜でこのエントリ書いてるので一旦打ち切り。ただでさえ私のブログは一エントリが長すぎると町で評判なので。

*1:当たり前ですがこれは批判ではないです。対象を絞ったから密度が出たということはあるだろうし。

*2:KCrash 君が怒っているので明らかにバグなのですが、あまりにも自然に怒っているので記憶からスルーされてました。エラー検知機構なのに大人しすぎだよ!<KCrash

*3:KCrash でクラッシュ出てるんだからデバッグ版だと死ぬのは当たり前。