fix を使用すると、デバッグプロセスを停止しないで、編集されたネイティブソースコードを簡単に再コンパイルすることができます。fix コマンドを使用して Java コードを再コンパイルすることはできません。
fix コマンドは、Linux プラットフォームでは使用できません。
この章の内容は次のとおりです。
fix と cont の各機能を使用すると、ソースファイルを修正して再コンパイルし、プログラム全体を作成し直すことなく実行を続けることができます。.o ファイルを更新して、それらをデバッグ中のプログラムに組み込むことにより、再リンクの必要がなくなります。
この機能を使用する利点は次のとおりです。
プログラムをリンクし直す必要がない。
プログラムを dbx に再読み込みする必要がない。
修正した位置からプログラムの実行を再開できる。
構築が進行中の場合は、fix コマンドを使用しないでください。
fix コマンドを使用するには、エディタウィンドウでソースを編集する必要があります。(コードの変更方法については、「fix と cont によるソースの変更」を参照)。変更結果を保存して fix と入力します。fix コマンドについては、「fix コマンド」を参照してください。
fix が実行されると、dbx は適切なコンパイラオプションでコンパイラを呼び出します。変更後のファイルがコンパイルされ、一時共有オブジェクト (.so) ファイルが作成されます。古いファイルと新しいファイルとを比較することによって、修正の安全性を検査する意味上のテストが行われます。
実行時リンカーを使用して新しいオブジェクトファイルが動作中のプロセスにリンクされ、プログラムカウンタが古い関数から新しい関数の同じ行に移動します (その関数が修正中のスタックの一番上にある場合)。さらに、古いファイルのブレークポイントがすべて新しいファイルに移動します。
対象となるファイルがデバッグ情報付きでコンパイルされているかどうかにかかわらず、fix コマンドと cont コマンドを実行できます。ただし、デバッグ情報なしでコンパイルされているファイルの場合には多少の機能制限があります。「fix コマンド」の -g オプションの解説を参照してください。
共有オブジェクト (.so) ファイルの修正は可能ですが、その場合、そのファイルを特別なモードでオープンする必要があります。dlopen 関数の呼び出しで、RTLD_NOW|RTLD_GLOBALまたは RTLD_LAZY|RTLD_GLOBAL のどちらかを使用します。
Oracle Solaris Studio C および C++ コンパイラのプリコンパイル済みヘッダー機能では、再コンパイル時にコンパイラオプションが同じである必要があります。fix コマンドによって、コンパイラオプションがわずかに変更されるため、プリコンパイル済みヘッダーを使用して作成されたオブジェクトファイルでは fix コマンドを使用しないでください。
fix と cont を使用すると、ソースを次の方法で変更できます。
関数の各行を追加、削除、変更する。
関数を追加または削除する。
大域変数および静的変数を追加または削除する。
古いファイルから新しいファイルに関数をマップすると問題が起きることがあります。ソースファイルの編集時にこのような問題の発生を防ぐには、次のことを守ってください。
関数の名前を変更しない。
関数に渡す引数の型を追加、削除、または変更しない。
スタック上で現在アクティブな関数の局所変数の型を追加、削除、または変更しない。
テンプレートの宣言やテンプレートインスタンスを変更しない。C++ テンプレート関数定義の本体でのみ修正可能です。
前述の変更を行う場合は、fix と cont で処理するよりプログラム全体を作り直す方が簡単です。
変更後にソースファイルを再リンクするとき fix コマンドを使用すればプログラム全体を再コンパイルしなくて済みます。引き続きプログラムの実行を続けることができます。
変更をソースファイルに保存します。
dbx プロンプトで fix と入力します。
修正は無制限に行うことができますが、1 つの行でいくつかの修正を行なった場合は、プログラムを作成し直すことを考えてください。fix コマンドは、メモリー内のプログラムのイメージを変更しますが、ディスク上のイメージは変更しません。また修正を行うと、メモリーのイメージは、ディスク上のイメージと同期しなくなります。
fix コマンド は、実行可能ファイル内での変更ではなく、.o ファイルとメモリーイメージの変更だけを行います。プログラムのデバッグを終了したら、プログラムを作成し直して、変更内容を実行可能ファイルにマージする必要があります。デバッグを終了すると、プログラムを作成し直すように指示するメッセージが出されます。
-a 以外のオプションを指定し、ファイル名引数なしで fix コマンドを実行すると、現在変更を行なったソースファイルだけが修正されます。
fix を実行すると、コンパイル時にカレントであったファイルの現在の作業ディレクトリが検索されてからコンパイル行が実行されます。したがってコンパイル時とデバッグ時とでファイルシステム構造が変化すると正しいディレクトリが見つからなくなることがあります。 これを防ぐには、pathmap コマンドを使用します。これは 1 つのパス名から別のパス名までのマッピングを作成するコマンドです。マッピングはソースパスとオブジェクトファイルパスに適用されます。
プログラムの実行を継続するには、cont コマンドを使用します (「cont コマンド」を参照)。
プログラムの実行を再開するには、変更による影響を判断するための次の条件に注意してください。
すでに実行された関数に変更を加えた場合、その変更内容は次のことが起こるまで無効です。
プログラムが再び実行される
その関数が次に呼び出される
変数への単純な変更以上のことを修正した場合は、fix コマンドに続けて run コマンドを使用してください。run コマンドを使用すると、プログラムの再リンクが行われないため処理が速くなります。
呼び出されていない関数に変更を加えた場合、変更内容は、その関数が呼び出されたときに有効になります。
現在実行中の関数に変更を加えた場合、fix コマンドの影響は、変更内容が停止した関数のどの場所に関連しているかによって異なります。
実行済みのコードを変更しても、そのコードは再実行されません。コードを実行するには、現在の関数をスタックからポップし (「pop コマンド」を参照)、変更した関数を呼び出した位置から処理を続けます。取り消すことのできない副作用 (ファイルのオープンなど) が発生しないか、コードの内容をよく理解しておく必要があります。
変更内容がまだ実行されていないコードにある場合は、新しいコードが実行されます。
停止された関数ではなく、現在スタック上にある関数に変更を加えた場合、変更されたコードは、その関数の現在の呼び出しでは使用されません。停止した関数から戻ると、スタック上の古いバージョンの関数が実行されます。
この問題を解決する方法はいくつかあります。
変更したすべての関数がスタックから削除されるまで pop コマンドを実行する。コードを実行して問題が発生しないか確認する。
データ構造を手作業で修正してから (assign コマンドを使用)、実行を続ける。
run コマンドを使用してプログラムを再び実行する。
スタック上の修正された関数にブレークポイントがある場合、このブレークポイントは、新しいバージョンの関数に移動します。古いバージョンが実行される場合、プログラムはこれらの関数で停止しません。
大域変数への変更は、pop コマンドでも fix コマンドでも取り消されません。大域変数に正しい値を手作業で再び割り当てるには、assign コマンドを使用してください (「assign コマンド」を参照)。
次の例は、修正継続機能を使用して簡単なバグを修正する方法を示しています。6 行目で NULL ポインタを逆参照しようとしたときに、セグメンテーションエラーが発生します。
dbx[1] list 1,$ 1 #include <stdio.h> 2 3 char *from = “ships”; 4 void copy(char *to) 5 { 6 while ((*to++ = *from++) != ’\0’); 7 *to = ’\0’; 8 } 9 10 main() 11 { 12 char buf[100]; 13 14 copy(0); 15 printf("%s\n", buf); 16 return 0; 17 } (dbx) run Running: testfix (process id 4842) signal SEGV (no mapping at the fault address) in copy at line 6 in file “testfix.cc” 6 while ((*to++ = *from++) != ’\0’); |
14 行目を 0 ではなく buf をコピー (copy) するように変更し、fix を実行します。
14 copy(buf); <=== modified line (dbx) fix fixing “testfix.cc” ..... pc moved to “testfix.cc”:6 stopped in copy at line 6 in file “testfix.cc” 6 while ((*to++ = *from++) != ’\0’) |
ここでプログラムを続行しても、NULL ポインタがスタックをプッシュしているためセグメント例外が返されます。pop コマンドを使用して、スタックフレームを 1 つ上がってください。
(dbx) pop stopped in main at line 14 in file “testfix.cc” 14 copy(buf); |
ここでプログラムを続行すると、プログラムは実行されますが、大域変数 from がすでに増分されているため正しい値が出力されません。assign コマンドを使用しないと、プログラムは ships と表示すべきところを hips と表示します。assign コマンド を使用して大域変数を復元し、次に cont コマンドを使用してください。 プログラムは次のように正しい値を表示します。
(dbx) assign from = from-1 (dbx) cont ships |
場合によってはソースファイルだけでなくヘッダ (.h) ファイルも変更することがあります。 変更したヘッダファイルをインクルードしている、プログラム内のすべてのソースファイルから、それらのヘッダファイルをアクセスするには、そのヘッダファイルをインクルードしているすべてのソースファイルのリストを引数として fix コマンドに渡す必要があります。ソースファイルのリストを指定しなければ、主要 (現在の) ソースファイルだけが再コンパイルされ、変更したヘッダファイルは主要ソースファイルにしかインクルードされず、プログラムのほかのソースには変更前のヘッダファイルがインクルードされたままになります。
C++ テンプレート定義は直接修正できないので、これらのファイルはテンプレートインスタンスで修正します。テンプレート定義ファイルを変更しなかった場合に日付チェックを上書きするには、-f オプションを使用します。