printk() デバッグとエミュレータデバッグ

某社デバイスの組込 Linux で、Ethernet ドライバがパケットを取りこぼしている疑惑の調査を継続しています。正確に言うと、ifconfig で Rx パケットドロップが報告される訳ですが。
いままでに分かったことは、

  • カーネル内のコアネットワークレイヤに受信パケットを引き渡す段階で、中身の変なパケットを渡しているため、コアネットワークレイヤでそれを蹴られている

ということです。
実際にここまで突き止めるのも結構メンドーで、誰がカウンタをインクリメントしているのか調べるのに苦労してしまった。エミュレータ(あるいは ICE)を使って、ハードウェア・ウォッチポイントを張れば一発なのですが。
さらに、上記問題まで分かった後、関数呼び出しを遡っていこうと思ったのですが、最近(?)の Linux カーネルは、やたらとオブジェクト指向ばりの動的束縛(dynamic binding)が多く、簡単には呼び出し元が分かりませんorz これも、カーネルデバッガがあれば、スタックトレース(バックトレース)が得られるのですが。
という訳で、ようやく重い腰を上げてエミュレータをセットアップしました。最近は寄る年波もあり、新しいことを勉強するのがしんどいのですが、それではイカンですな。
実はやってみるとそれほど難しいことはなく、手順は以下の通り。(プラットフォームに依存すると思いますが。)

  1. デバッグ用のエミュレータをターゲットに接続し、free run させておく。
  2. U-Boot から Linux をブートさせる。
  3. Linux をロードし終わった後のブート中(ま、ブートが終わってからでも良し)にデバッガからターゲットを halt する。
  4. 上記でブートした Linux はたぶん uImage とかだけど、ビルド時に作った vmlinux を探す。
  5. デバッガから vmlinux のシンボル情報だけをロードする。(load symbolic information とかなんとやら)
  6. デバッガに、ソースコードのあるパスを教えてやる。

ここまでで普通、実行箇所のソースが表示されると思います。ま、デバッガにも依ると思いますが。(私は、Eclipse ベースのプロプリエタリデバッガを使用)
後は、問題の箇所にブレークポイント(ソフトウェアブレークポイントで良い)をしかけ、そこに引っかかったらスタックトレースを見ることができます。問題は、私の環境だと、親関数がインライン関数を呼んでいると、親関数の本当の実行箇所がよく分からないことですが。。。デバッガ固有の問題かも。
でもとりあえず、デバッグ環境は printk() だけに比べて大幅に改善しました。来週も頑張ってデバッグしよう。