ビルドの流れをおさらい
ソースファイル(.cや.h)→コンパイル実行→オブジェクトファイル(.o)、
ライブラリやモジュール(.aや.ld)→リンク実行→実行可能ファイル(.motや.run)
※拡張子は適当
デバッグ時の心構え
・プログラムを詳細に理解する(フローチャートを用意しておくといい)
・特に非効率なのはプログラムコードを理解しない状態で
修正してみてはコンパイル実行の繰り返し
Q1:ビルド実行時、何らかのシンボルが定義されていない系のエラー
→どこの段階でのエラーかをまずは確認する
☆コンパイル段階でのエラーの場合
・タイプミスないか(セミコロン抜け、文字コード、タブ、全角など)
・定義されてないシンボルを参照していないか
・ヘッダーファイルのインクルードが正しくできてるか
・実際のエラーを示す行と問題の行が一致しているとは限らない
☆リンク段階でのエラーの場合
・必要なライブラリやモジュールをリンクできているか
・古いプリコンパイル済みのヘッダーファイルを参照していないか、
疑わしければリビルド(再コンパイル)する
Q2:ビルド実行時、ヘッダーファイルが見つからないエラー
・環境変数INCLUDEは適切に設定できているか
・コンパイルオプションの指定は正しいか
・IDE利用中であれば、IDEのプロジェクトの設定は正しいか
Q3:処理速度が要求を満たしていないとき
・コンパイラの最適化機能で速度優先にする
・配列のループ格納処理をポインタにする
・関数をマクロにできないか検討する
・Cからアセンブラにする(インラインアセンブラもしくはソースファイルごとアセンブラ)
・メモリ定義をレジスタ(register)にする。現場では見たことない
・サイズの小さなデータ型を使う
・シフトを使って計算を速く行う
・短絡評価(演算子の左側の数を評価した段階で式全体の値が
定まらない時のみ右側を評価)を使う
例:if(func()>3 || x>3) ではなく 、
if(x>3 || func()>3) こう書いておけば、
x>3の時はfuncが呼ばれなくて無駄な処理時間を省ける。
Q4:どこかでリセットが掛かっているが原因不明のとき
・リセット要因レジスタに条件付きwriteブレークを張り、リセット要因を特定する
☆ハードウェアリセットの場合
・ウォッチドッグがリセットしてるかも(外部、内部どちらかも判別しておく)
・電源ICの仕様を確認する
☆ソフトウェアリセットの場合
・Resetで飛んでくる処理の先頭にブレークを張って、
止まったときのコールスタック(関数の呼び出し履歴)を確認すると分かることもある
・二分法などを用いてリセット発生させてる処理を特定する
・無限ループに入る→ウォッチドッグがリセットのパターンはよくある
・想定と全然違うところで問題発生してる場合はスタックオーバーフローも疑う
Q5:デバッガ操作前に事前に知っておきたいこと
・ステップ実行、ステップオーバー実行の違い
・ソフトウェアブレークとハードウェアブレークの違い
・コールスタックの見方
・レジスタの見方
・pc(プログラムカウンタ)で現在番地を操作する
Q6:デバッガ接続時は問題ないが、デバッガ未接続時のみ問題が発生する
・デバッガではRAMなどのメモリを事前にクリアしており、問題ない挙動になることがある。
・デバッガではウォッチドッグが無効になっており、問題発生しないことも