Windowsプログラマーのステップアップシリーズ:『ソフトウェアデバッグ』の6--コンパイラのデバッグサポート

5351 ワード

コンパイラのデバッグサポート
コンパイラは、高度な言語をcpuで認識可能なマシンコードに翻訳するために使用されます.数十年の発展を経て、コンパイラは大きく変化しましたが、デバッグをサポートすることには何の変化もありません.コンパイルされたソフトウェアのエラーをチェックして報告することがコンパイラ設計の主な目標です.コンパイラは、ソースコードとリンクターゲットコードをコンパイルする際に、多くのチェックを行います.これには、コンパイル期間チェックと実行期間チェックが含まれます.
コンパイル期間チェック:コンパイラはコンパイル中にコード内の構文エラーをチェックし、同時に論理エラーと設計欠陥をチェックし、コンパイルエラーまたは警告で報告します.
≪実行期間チェック|Run Time Check|emdw≫:プログラムが実行段階で問題を発見するのを助けるために、コンパイラはコンパイル時にメモリチェックやスタックチェックなどのチェック機能を追加します.コンパイル期間チェックと実行期間チェックについては後述します.コンパイル期間チェックについて説明します.
ソフトウェアはプログラムとドキュメントの集合です.ソースプログラムはコンパイル期間を経て等価アセンブリ言語モジュールにコンパイルされ、その後、アセンブリを経てターゲットプラットフォームcpuと一致するマシンコードモジュールを生成し、最後にリンクを経てマシンコードモジュール中の名前とアドレス参照を解決し、ターゲットプラットフォーム上のオペレーティングシステムの要求フォーマットに合致する実行可能モジュールを生成する.
ターゲットコードを境に,プログラムの構築過程を翻訳とリンクの2段階に分けることができる.
リンク
リンクの主な役割は、コンパイラによって生成された複数のターゲットファイルを、ターゲットプラットフォームで実行できるイメージに合成することです.次のタスクを解決する必要があります.
1:関数呼び出しや変数参照など、ターゲットファイルの外部シンボルを解決します.IDTテーブルとIATテーブルを作成します.
2:外部参照が解決されたターゲットコードのコードセグメントを生成します.
3:読み取り専用データを含むコードセグメントを生成します.
4:リソースデータを含むリソースセグメントを生成します.
5:ベースアドレス再配置テーブルを生成します.このテーブルには、再配置が必要なすべてのアイテムが含まれています.
コンパイルエラーと警告
コンパイル中に、コンパイラがコンパイルされたコードの問題を発見した場合、警告またはエラーの形式でレポートされます.各警告またはエラーのソースと意味をマークするために、コンパイラは一意の識別子を与えます.
エラーIDとソース
各識別子は、1つ以上の大文字で始まり、後に4桁のアラビア数字が続く.文字はレポート情報のコンポーネントを表し、数字はエラーや警告の原因を表す.
次の図はVisualC++のエラーIDのフォーマットとソースです.
同じソースのエラーに対して、異なる範囲にあるIDは通常、致命的なエラー、エラー、警告に分けて異なる重大度レベルに対応します.
次の図は、各種類のエラーID範囲の区分方法です.
コンパイル警告
異なる程度の警告を区別するために、vcは警告情報を4つのレベル:1〜4のレベルに分けた.1級が最も高く、4級が最も低い.コンパイラのコンパイルオプションを構成することで、コンパイル警告を表示する方法を構成できます.
次の図は、コンパイル警告を設定するコマンドラインスイッチです.
コンパイルオプションを使用してコンパイル警告を設定するだけでなく、ソースコードでコンパイラのpragmawarningコマンドでコンパイル警告を制御することもできます.たとえば、次の文は4705番の警告を禁止し、デフォルトに復元します.
#pragmawarning(disable:4705)
#prgamawarning(default:4705)
次の文では、4705番と4034番の警告を禁止し、4385番の警告を1回だけ報告し、4164番の警告をエラーとして処理します.
#pragmawarning(disable:47054034;once4385;error:41164)
すべてのコンパイル警告の現在の設定をpragmawarning(push)を使用して保存し、#pragmawarning(pop)を使用して保存した設定を復元できます.
コンパイル期間チェック
コンパイラはコンパイル時に発見された問題をコンパイル警告とエラーの形式で報告します.コンパイルフェーズで行ったチェックをコンパイル期間チェックと呼びますが、これに対応して実行期間チェックもあります.次の記事では、実行期間チェックについて説明します.コンパイル期間チェックでは,コード中の文法,文法,および少量の意味に関する問題が発見される.2つの一般的なコンパイル期間チェックは、初期化されていないローカル変数とタイプが一致しないことです.
初期化されていないローカル変数
ローカル変数とは、関数内に定義された変数です.1つはスタックに割り当てられ、もう1つはレジスタに割り当てられます.ほとんどのローカル変数はスタックに割り当てられているため、ここではこの場合についてのみ説明します.スタックに割り当てられたローカル変数は、スタックポインタを調整することによって割り当てられ、解放されます.コンパイラがデバッグバージョンをコンパイルすると、割り当てられたローカル変数領域が自動的に固定された0 xcc(x 86システム)に初期化されます.0 xccはINT 3命令のマシンコードであるため、プログラムで明示的に初期化されていない領域を尋ねると、INT 3ソフトウェアが中断する.パブリケーション・バージョンでは、初期化作業が大きなスペースと時間のオーバーヘッドをもたらすことを考慮して、コンパイラがパブリケーション・バージョンでコンパイラを使用していない場合は、上記の作業は行われません.デバッグ・バージョンとパブリケーション・バージョンのこの重要な違いも、デバッグ・バージョンとパブリケーション・バージョンの動作が一致しない原因の1つです.
タイプが一致しません
コンパイラは、意味解析を行うときに変数の比較、割り当てなどの操作をチェックし、潜在的な問題を発見します.シンボル整数とシンボルなし整数を比較する場合や、単一精度浮動小数点数に二重精度浮動小数点数を割り当てる場合に警告されます.同様に、コンパイル関数呼び出しでは、コンパイラが関数プロトタイプに基づいて各パラメータをチェックします.
コンパイル命令
コンパイラコマンドは、ファイルヘッダにifndefを追加するなど、非常に広く使用されています...endif文は、重複する包含を防止します.
ファイルヘッダまたはソースファイルにコンパイラコマンドを追加すると、コンパイラはこれらの文をコンパイルするときにこれらのチェック文を評価します.条件が満たされると、コンパイラは定義したアクションを実行します.
次のようになります.
#ifndef  WIN32
  #error this function only can be used in _win32 platform
#endif

上のチェック文はWIN 32が定義されているかどうかをチェックし、定義されていない場合はエラーメッセージを表示します.
次の文は、コンパイラのバージョンを確認するために使用します.
#if _MSC_VER<1300
   #error Compiler version not supported by windows ddk;
#endif

#ifまたは#ifdef、#ifndef命令判定の条件式は、#defin文で定義してもよいし、プロジェクト属性であるPreprocessorDefinitionsで定義した記号を使用してもよい.次のようになります.
寸法すんぽう
コンパイル期間チェックを追加するもう1つの方法は、コンパイラがより多くの問題を検出するのに役立つように、コードに寸法情報を追加することです.
寸法の一般的な適用は、関数のプロトタイプを宣言するときに、特定のシンボルを使用して関数のパラメータと戻り値を寸法することです.VS 2005からvsは、コンパイラや他の分析ツールがソースコードのセキュリティ問題を発見するのを助けるために、標準寸法言語SAL(standardannotationlanguage)というメカニズムを導入した.SALマーク記号はsal.hファイルで定義します.関数パラメータと戻り値を記述するために使用されるクラスと、バッファロケータと呼ばれるクラスと、高度なロケータと呼ばれるクラスの2つに分類されます.
バッファマークアップ
バッファロケータは、ポインタ、バッファ長、および値を返す方法を含む関数パラメータと戻り値を記述するために使用されます.1つのバッファロケータは、1つのパラメータを記述することができ、1つのパラメータにも1つのバッファロケータしかありません.しかし、1つのバッファ寸法子は、パラメータのある態様の特徴を記述するためにそれぞれ使用される複数の要素を含むことができる.複数の要素を下線を接続して、完全なバッファ寸法子を構成します.
バッファマーカーには、次の図のような複数の要素が構成されています.
例は次のとおりです.
void func1(
   _in HWND hwnd,// 。 。
   _in_opt DHC hdc,// , 。
   _inout char*p// , 。
   );

インスタンス2
   HRESULT func2( size_t cb,_deref_bcount(cb) T**ppv);

そのうち_derefは、パラメータppvポインタの間接階層を記述するために使用される.このとき*ppvはバッファへのポインタを格納します._bcount(cb)は*ppvが指すバッファの大きさである.含まれていないためfullまたは_partしたがって、このバッファは使用前に初期化されません.
vcが提供するライブラリ関数を表示するときにsalタグを含む関数によく遭遇します.上記の説明を経て理解するのは難しいことではないと信じています.
別のクラスの寸法子は、高度な寸法子です.名前の通り、バッファロケータよりも高度に機能し、バッファロケータが表現できない制約や制限を記述します.
次の表に示します.
例を見てください.
   _success (return ==true) bool fun3( _out_ecount(MAX_PATH) char*buff,char*path);

上の寸法はfunc 3関数が成功したときの戻り値がtrueであることを意味します.関数が正常に返されたときにbuffが指すバッファのみが0を終端とする合法的な文字列です.
例2:
   _checkReturn _bcount_opt(size) void* _cdecl malloc(
         _in size_t size);

_checkReturnは、呼び出し元が戻り値をチェックすべきであることを示します._bcount_opt(size)は、戻り値の長さがsizeバイトであるが、空である可能性があることを示す.パラメータsizeは入力に使用します.呼び出し元によって初期化されます.
コンパイル期間チェックについて説明します.次の記事では、ライブラリの実行と実行期間チェックについて説明します.
以上の内容は「ソフトウェアデバッグ」の張銀奎著を参考にした.誤りがあれば,指摘を惜しまないでください.ありがとう!
2013、3、25浙江杭州