[ 前のページ ]
[ 次のページ ]
[ 目次 ]
[ 索引 ]
[ DOC Home ]
本章では,特殊な場合のデバッグ方法について説明します。
省略時の設定では,プログラムの実行速度を向上させるためにコードを最適化するコンパイラが多くあります。 最適化は,実行時に一度だけ評価されればすむよう不変式がDO ループから除かれる,メモリ記憶位置のいくつかがプログラム内の異なる場所で異なる変数に割り当てられる, いくつかの変数がデバッグ中にアクセスしなくてすむように削除される, などの方法で行われます。
最終的には,デバッグ時に実行されているコードは,画面モードのソース・ ディスプレイ(第7.4.1 項を参照)で表示されるソース・コードやソース・リスト・ファイルに記述されているソース・ コードとは一致しないことがあります。
最適化されたコードをデバッグする際の問題を回避するために,多くのコンパイラではコンパイル時に/NOOPTIMIZE ,またはそれに等しいコマンド修飾子を指定することができます。 この修飾子を指定すると,コンパイラによる最適化がほとんど抑止され, それによって最適化のために発生するソース・ コードと実行可能なコードとの相違を減らすことができます。
このオプションが利用できない場合,または最適化されているコードのデバッグが明らかに必要な場合は, 本章を参照してください。本章では,最適化されたコードのデバッグ方法と, 最適化されたコードの典型的な例を使用して混乱の潜在的な原因を示します。 また,最適化されたコードをデバッグするときに, このような混乱を減らす目的で使用する機能についても説明します。
この機能を十分活かした上で,最適化されたコードのデバッグ効率を向上させるためには, 使用している言語コンパイラの最新版を入手しなければなりません。 必要なコンパイラのバージョンについては,コンパイラのリリース・ ノートやその他のマニュアルなどを参照してください。
また最適化されたコードをデバッグするためには,イメージのサイズの増加に対応するため, 通常の必要ディスク領域に比べて,約1/3の領域が余分に必要になります。
最適化されたコードをデバッグする場合,定義済みディスプレイのINSTなどの画面モード機械語命令ディスプレイを使用して, プログラムのデコード済み命令ストリーム( 第7.4.4 項を参照)を表示します。機械語命令ディスプレイは,実際に実行されているコードを表示します。
画面モードでは,KP7を押すとSRC表示とINST表示が比較しやすいように並べて表示されます。 または,コンパイラが生成した機械語コード・リストを調べることもできます。
さらに,プログラムを命令レベルで実行し,命令を検査するには,第4.3節で説明されている方法を使用してください。
これらの方法を使用すると,実行可能なコード・レベルで何が起きているかを明確にし, ソース・ディスプレイとプログラム動作の相違を解決することができます。
コンパイラは,実行中のさまざまな時点で変数を永久にまたは一時的に削除することによってコードを最適化することがあります。 たとえば,最適化によってアクセスできなくなった変数X を検査しようとすると,デバッガは次のメッセージのいずれかを表示します。
%DEBUG-W-UNALLOCATED, entity X was not allocated in memory (was optimized away) %DEBUG-W-NOVALATPC, entity X does not have a value at the current PC
次のPascalの例は,この状況が発生する様子を示しています。
PROGRAM DOC(OUTPUT); VAR X,Y: INTEGER; BEGIN X := 5; Y := 2; WRITELN(X*Y); END.
このプログラムを/NOOPTIMIZE,またはそれに等しい修飾子を付けてコンパイルすると, デバッグの時に次のような正常な動作が得られます。
$ PASCAL/DEBUG/NOOPTIMIZE DOC $ LINK/DEBUG DOC $ DEBUG/KEEP . . . DBG> RUN DOC . . . DBG> STEP stepped to DOC\%LINE 5 5: X := 5; DBG> STEP stepped to DOC\%LINE 6 6: Y := 2; DBG> STEP stepped to DOC\%LINE 7 7: WRITELN(X*Y); DBG> EXAMINE X,Y DOC\X: 5 DOC\Y: 2 DBG>
このプログラムを/OPTIMIZE,またはそれに等しい修飾子を付けてコンパイルすると,X とYの値は初期値の割り当て以降は変更されないため,コンパイラはX*Y を計算して値(10)を保存し,XとYの記憶域を割り当てません。 そのため,デバッグの開始後,STEPコマンドは第5行ではなく直接第7 行に移ります。さらに,XやYを検査することはできません。
$ PASCAL/DEBUG/OPTIMIZE DOC $ LINK/DEBUG DOC $ DEBUG/KEEP . . . DBG> RUN DOC . . . DBG> EXAMINE X,Y %DEBUG-W-UNALLOCATED, entity X was not allocated in memory (was optimized away) DBG> STEP stepped to DOC\%LINE 7 7: WRITELN(X*Y); DBG>
VAXプロセッサでは,最適化されたプログラム内でどんな値が使用されているかを調べるには,EXAMINE/OPERAND .%PC コマンドを使用し, 現在のPC値の機械語コードをすべてのオペランドの値とシンボルを含めて表示します。 たとえば,次の行はPC値がWRITELN文にある場合の最適化されたコードを表示します。
DBG> STEP stepped to DOC\%LINE 7 7: WRITELN(X*Y); DBG> EXAMINE/OPERAND .%PC DOC\%LINE 7: PUSHL S^#10 DBG>
これに対して,次の行はWRITELN文の最適化されていないコードを表示します。
DBG> STEP stepped to DOC\%LINE 7 7: WRITELN(X*Y); DBG> EXAMINE/OPERAND .%PC DOC\%LINE 7: MOVL S^#10,B^-4(FP) B^-4(FP) 2146279292 contains 62914576 DBG>
最適化の手法の中には,ソース・コードで指定されたシーケンスとは異なるシーケンスで操作を行うという方法をとるものがあります。 場合によってはコードがすべて削除されてしまうこともあります。
その結果,デバッガが表示するソース・コードと,実際に実行されているコードが完全には対応しなくなります。
例を使用して説明するために,Fortranプログラムのソース・コードのセグメントを示します。 これはコンパイラ・リストや画面モードのソース表示に表示されるものです。 このコード・セグメントは配列Aの最初の10個の要素を1/X の値に設定します。
Line Source Code ---- ----------- 5 DO 100 I=1,10 6 A(I) = 1/X 7 100 CONTINUE
最適化は次の手順で行われます。コンパイラがソース・プログラムを処理するとき,X の逆数はソース・コードで指定されている10回ではなく,1回だけ計算すればよいと判断されます。X の値はDOループの中では変化しないからです。 コンパイラはこのように,次に示すコード・セグメントと等しい最適化されたコードを作成します。
Line Optimized Code Equivalent ---- ------------------------- 5 TEMP = 1/X DO 100 I=1,10 6 A(I) = TEMP 7 100 CONTINUE
コンパイラの機能によって,移動したコードがループの第1行と関連したり(VAX システムで共通),元の行番号を維持したり(Alphaシステムで共通) することがあります。
相違が発生しても,その相違は表示されるソース行を見るだけでは明らかではありません。 さらに,Xがゼロのために1/Xの計算が失敗したとき,ソース表示を入念に調べることによって, 除算がまったく含まれていないソース行でゼロによる除算が行われたことが明らかになります。
このようなソース・コードと実行可能なコードの明らかな不一致は,最適化されたプログラムをデバッグするときにしばしば見られます。 前の例のようなループから抜ける場合のコード動作が原因になるだけでなく, 他の多くの最適化の手法も原因になります。
セマンティク・ステップ実行(Alphaシステムのみで使用可能)を行うことにより, 最適化されたコードをステップ実行するとき,混乱を少なくすることができます。 セマンティク・ステップ実行モードは,従来の行単位ステップ・ モード,命令単位ステップ・モードを補足するものです。 セマンティク・ステップ実行に使用するコマンドとして,SET STEP SEMANTIC_EVENTとSTEP/SEMANTIC_EVENTの2種類のコマンドがあります。
最適化されたコードをステップ実行するときの問題として,明らかなソース・ プログラムの位置が前後に「動き回り」,同じ行が何度も現れるということがあります。 実際,STEP LINEモードでコードを前方向に実行している場合でも,STEP コマンドのたびに同じ命令が繰り返されることもあります。
この問題は,セマンティク・イベントの注釈命令によって対応できます。 セマンティク・イベントは,次の2つの理由で重要です。
セマンティク・イベントは,以下のいずれかになります。
ただしセマンティク・イベントは,必ずしもイベント割り当て,制御の転送, 呼び出しに限られるものではありません。例外として次のようなものがあります。
このようなルーチンにステップ移動するときは,次のいずれかを行う。
SET STEP SEMANTIC_EVENTコマンドは,省略時のステップ実行モードをセマンティクとして確立します。
STEP/SEMANTIC_EVENTコマンド(セマンティク・モードになっているときは単にSTEP でも同等)は,次のセマンティク・イベントが割り当て,制御の転送, 呼び出しのいずれであっても,そのイベントにブレークポイントをセットします。 そのイベントに移るまで,実行が進みます。さまざまな行/ 文があれば,そのうちのいくつかがプロセスを阻害しない方法で実行されます。 セマンティク・イベントに移る(つまりそのイベントに対応する命令に移ったが, まだ実行されていない状態)と,実行が停止します(STEP/LINE を使用しているときに次の行に移るのと同様)。
次に示すCプログラムdoct2の内容は,最適化の場合の注意事項を示しています。
#include <stdio.h> #include <stdlib.h> int main(unsigned argc, char **argv) { int w, x, y, z=0; x = atoi(argv[1]); printf("%d\n", x); x = 5; y = x; if (y > 2) { /* always true */ printf("y > 2"); } else { printf("y <= 2"); } if (z) { /* always false */ printf("z"); } else { printf("not z"); } printf("\n"); }
次の2つの例は,最適化したdoct2プログラムによって,行単位でステップ実行した場合の例と, セマンティク・イベントによりステップ実行した場合の例で, 上記と対照をなしています。
$ doct2:=$sys$disk:[]doct2 $ doct2 6 Debugger Banner and Version Number Language:: Module: Doct2: GO to reach DBG> go break at routine DOCT2\main 654: x = atoi(argv[1]); DBG> step stepped to DOCT2\main\%LINE 651 651: int main(unsigned argc, char **argv) { DBG> step stepped to DOCT2\main\%LINE 654 654: x = atoi(argv[1]); DBG> step stepped to DOCT2\main\%LINE 651 651: int main(unsigned argc, char **argv) { DBG> step stepped to DOCT2\main\%LINE 654 654: x = atoi(argv[1]); DBG> step stepped to DOCT2\main\%LINE 655 655: printf("%d\n", x); DBG> step stepped to DOCT2\main\%LINE 654 654: x = atoi(argv[1]); DBG> step stepped to DOCT2\main\%LINE 655 655: printf("%d\n", x); DBG> step 6 stepped to DOCT2\main\%LINE 661 661: printf("y > 2"); DBG> step y > 2 stepped to DOCT2\main\%LINE 671 671: printf("not z"); DBG> step not z stepped to DOCT2\main\%LINE 674 674: printf("\n"); DBG> step stepped to DOCT2\main\%LINE 675 675: } DBG> step 'Normal successful completion' DBG>
$ doct2:=$sys$disk:[]doct2 $ doct2 6 Debugger Banner and Version Number Language:: Module: Doct2: GO to reach DBG> set step semantic_event DBG> go break at routine DOCT2\main 654: x = atoi(argv[1]); DBG> step stepped to DOCT2\main\%LINE 654+8 654: x = atoi(argv[1]); DBG> step stepped to DOCT2\main\%LINE 655+12 655: printf("%d\n", x); DBG> step 6 stepped to DOCT2\main\%LINE 661+16 661: printf("y > 2"); DBG> step y > 2 stepped to DOCT2\main\%LINE 671+16 671: printf("not z"); DBG> step not z stepped to DOCT2\main\%LINE 674+16 674: printf("\n"); DBG> step stepped to DOCT2\main\%LINE 675+24 675: } DBG> step stepped to DOCT2\__main+104 DBG> step 'Normal successful completion' DBG>
セマンティク・ステップ実行の動作は,行単位のステップ実行と比べてはるかに滑らかで, 直線的な方法になります。またセマンティク・ステップ実行では, 問題のある重要な箇所で停止します。一般的に,最適化したコードを行単位でステップ実行する場合に特徴的な, コードが前後に「動き回」ることによる混乱は, セマンティク・ステップ実行の場合,激減するかまたは完全になくなります。 ソース・プログラムの順序変更により,実行特性が向上することもありますが, 通常フローは上から下に向かって移動します。
ステップ実行の密度は,行単位のステップ実行とセマンティク・ステップ実行とで異なります。 行単位の方が大きくなることもあり,セマンティクの方が大きくなることもあります。 たとえば,セマンティクの性質によりセマンティク・ イベントを構成する文は,その文が完全に最適化されている場合, セマンティク・ステップ実行で出てくることはありません。このように, セマンティク・リージョンは複数の行にまたがっており,最適化された行はスキップされるということになります。
コンパイラは,1つの式の値は2つの出現の間では変化しないものと判断し, その値をレジスタに保存することがあります。このような場合,コンパイラは次の出現では値の再計算は行わず, レジスタに保存されている値が有効であるとみなします。
プログラムをデバッグしている間に,式中の変数の値を変更するためにDEPOSIT コマンドを使用しても,レジスタに保存されている対応する値は変更されません。 したがって,処理を続行する場合,式中の変更された値ではなくレジスタの値が使用され, 予期しない結果になることがあります。
さらに,非静的変数(第3.4.3項を参照)の値がレジスタに保存されている場合, メモリ内にあるその値は通常,無効になります。 したがって,このような状況下では,変数に対してEXAMINE コマンドを入力すると誤った値が表示されることがあります。
最適化の方法の中に,VAXプロセッサの条件コードが設定される方法を利用したものが1 つあります。次のPascalのソース・コードを例に考えてみます。
X := X + 2.5; IF X < 0 THEN . . .
分岐するかどうかを判断するためにXの新しい値をテストする代わりに, 最適化されたコードではXに2.5が加えられたあとの条件コード設定に判断の基準が置かれます。 したがって,IF文にブレークポイントを設定してX に異なる値を格納しようとすると,条件コードはもはやXの値を反映しなくなるため, 意図した結果を得ることができなくなります。すなわち,分岐の判定は変数に格納された値に関係なく行われます。
ここでも,期待する結果を得るために,EXAMINE/OPERAND .%PCコマンドを使用して正しい格納位置を明確にすることができます。
最適化してコンパイルするとき,コンパイラが変数を,別個に割り当てることのできるいくつかの部分変数に「分割」して, その変数で存在期間分割による分析を実行することがあります。 その結果,元の変数が,別々の時間に別々の場所( 場合によってはレジスタ,場合によってはメモリ, 場合によってはどこにもない)に置かれているということも可能になります。 異なる部分変数を使用すれば,それぞれの変数を同時にアクティブにすることもできます。
Alphaプロセッサでは, EXAMINEコマンドを使用すると,プログラムのどの場所で変数が定義されているかが表示されます。 変数が不適切な値のとき, この位置の情報により,変数の値がどこで割り当てられているか判断することができます。 また/DEFINITION S修飾子により,この位置の数を省略時の5 から別の値に変更できるようになっています。
存在期間分割による分析は,スカラ変数およびパラメータのみに適用されます。 配列,レコード,構造体,その他の集合体には適用されません。
次の例は,存在期間分割処理の例を示しています。最初の例は,小さなC プログラムですが,ここでは,左の段の数字が行番号を表しています。
385 doct8 () { 386 387 int i, j, k; 388 389 i = 1; 390 j = 2; 391 k = 3; 392 393 if (foo(i)) { 394 j = 17; 395 } 396 else { 397 k = 18; 398 } 399 400 printf("%d, %d, %d\n", i, j, k); 401 402 }
最適化されているプログラムで,デバッグのために,コンパイル,リンク, 実行を行うとき,次のようなダイアログが表示されます。
$ run doct8
. . . DBG> step/into stepped to DOCT8\doct8\%LINE 391 391: k = 3; DBG> examine i %W, entity 'i' was not allocated in memory (was optimized away) DBG> examine j %W, entity 'j' does not have a value at the current PC DBG> examine k %W, entity 'k' does not have a value at the current PC
変数iのメッセージが,変数j,kのメッセージと異なることに注意してください。 変数iは,メモリ(レジスタ,コア, その他)にまったく割り当てられていないため,その値を再度テストする意味はありません。 比較のために見てみると,jとkには, この時点で「現在のPC」に値がありません。これ以降のプログラムの中で出てきます。
もう1行ステップ実行すると,次のようになります。
DBG> step stepped to DOCT8\doct8\%LINE 385 385: doct8 () {
これを見ると,1ステップ戻っているように見えます。最適化(スケジューリング) したコードで共通の現象です。この問題については,第14.1.2項の「セマンティク・ ステップ実行モード」の項で説明しています。 さらにステップ実行を続けると,次のようになります。
DBG> step 5 stepped to DOCT8\doct8\%LINE 391 391: k = 3; DBG> examine k %W, entity 'k' does not have a value at the current PC DBG> step stepped to DOCT8\doct8\%LINE 393 393: if (foo(i)) { DBG> examine j %W, entity 'j' does not have a value at the current PC DBG> examine k DOCT8\doct8\k: 3 value defined at DOCT8\doct8\%LINE 391
ここではjがまだ定義されていませんが,kには3という値があります。 この値は391行目で割り当てられているものです。
ソースでは,kの前にjに値が割り当てられている(390行目) ため,これはすでに表示されていなければなりません。ここでも最適化( スケジューリング)したコードで共通の現象が発生しています。
DBG> step stepped to DOCT8\doct8\%LINE 390 390: j = 2;
ここでjの値が表示されます。
DBG> examine j %W, entity 'j' does not have a value at the current PC DBG> step stepped to DOCT8\doct8\%LINE 393 393: if (foo(i)) { DBG> examine j DOCT8\doct8\j: 2 value defined at DOCT8\doct8\%LINE 390
400行目のprint文までスキップし,jの値をテストします。
DBG> set break %line 400 DBG> g break at DOCT8\doct8\%LINE 400 400: printf("%d, %d, %d\n", i, j, k); DBG> examine j DOCT8\doct8\j: 2 value defined at DOCT8\doct8\%LINE 390 value defined at DOCT8\doct8\%LINE 394
jの値を定義した位置が複数あります。IF節で選択されるパスにより, どちらかの位置が適用されます。この機能を使用すれば,変数が明らかに不適切な値の場合, その場所を調べることができ,同時に値がどこから来ているのかについても調べることができます。
次の例のようにSHOW SYMBOL/ADDRESSコマンドを使用すると,あるシンボルの存在期間分割情報を表示することができます。
DBG> show symbol/address j data DOCT8\doct8\j between PC 131128 and 131140 PC definition locations are at: 131124 address: %R3 between PC 131144 and 131148 PC definition locations are at: 131140 address: %R3 between PC 131152 and 131156 PC definition locations are at: 131124 address: %R3 between PC 131160 and 131208 PC definition locations are at: 131124, 131140 address: %R3
変数jには,4つの存在期間セグメントがあります。PCアドレスは, イメージのリンク結果を示し,同時にソース・プログラムの行番号との対応が, コメントにより示されます。
OpenVMS AlphaシステムとOpenVMS VAXシステムとではサポートする存在期間分割に, 概念上大きな違いがあります。Alphaシステムの場合,デバッガは, どの割り当てとどの定義が変数の表示値を規定するかについて,トラックして報告します。 この補足情報を使用することにより,コードのモーションなどの最適化の効果のうちいくつかを処理できるようになります。 変数値がプログラムの予期しない場所から来ているような場合も,その効果を処理できるようになります。
◆EXAMINE/DEFINITIONSコマンド(Alphaのみ)
存在期間分割変数の場合,EXAMINEコマンドは,アクティブな存在期間の値を表示するだけでなく, 存在期間の定義位置も表示します。定義位置とは, 存在期間が初期値を受け取る場所を表します。定義位置が1つの場合, それが唯一の位置になります。
存在期間の初期値が複数の位置から来ている場合,定義位置も複数になります。 前の例では,プログラムがprintf,で停止したときに jをテストすると次のようになります。
DBG> examine j DOCT8\doct8\j: 2 value defined at DOCT8\doct8\%LINE 390 value defined at DOCT8\doct8\%LINE 394
この場合,393行目の式がTUREかどうかにより,値が390行目と394行目のどちらから来るか決まるため, jの存在期間には,2つの定義位置があることになります。
省略時の場合,変数の内容がテストされるときに最大で5つの定義位置が表示されます。 定義位置の数を指定するときは,次の例のように/DEFINITIONS= n修飾子を使用します。
DBG> EXAMINE/DEFINITIONS=74 FOO
省略形は/DEFIになります。
省略時の定義数を5以外の数にしたい場合,次のようなコマンド定義を使用します。
DBG> DEFINE/COMMAND E = "EXAMINE/DEFINITIONS=100"
/DEFINITIONS修飾子に100が設定されていて,テストしている存在期間分割変数に120 の定義位置がある場合,デバッガは,指定に従って100個表示しますが, 同時に次のようなレポートも表示します。
there are 20 more definition points
デバッグ・セッションの間,デバッガは端末の画面を入出力(I/O)のために使用します。1 台の端末を使用して,画面の大部分または全体を使用するような画面用プログラムのデバッグを行う場合, デバッガの入出力はプログラムの入出力に対して上書きしたり上書きされたりします。
1台の端末をプログラムの入出力とデバッガの入出力のために使用すると, 画面モードでデバッグし画面用プログラムが実行時ライブラリ(RTL) 画面管理(SMG$)ルーチンのどれかを呼び出している場合はさらに複雑になります。 これは,デバッガの画面モードもSMGルーチンを呼び出すからです。 このような場合,デバッガとユーザ・プログラムは同じSMGペーストボードを共用し, 一層の混乱を引き起こします。
画面用プログラムをデバッグする際のこのような問題を回避するために, デバッガの入出力をプログラムの入出力から分離するために次の方法のいずれかを使用します。
TTD1:が現在の端末であり,そこからデバッグを開始するものとします。 デバッガの入出力は端末TTD2:に表示し,TTD1:はプログラムの入出力専用にするものとします。
次の手順に従ってください。
以降の操作はすべてTTD1:で行う。
$ ALLOCATE TTD2:
$ DEFINE DBG$INPUT TTD2: $ DEFINE DBG$OUTPUT TTD2:
DBG$INPUTおよびDBG$OUTPUTは,デバッガの入力装置および出力装置をそれぞれ指定する。 省略時には,これらの論理名はそれぞれSYS$INPUT およびSYS$OUTPUTになる。DBG$INPUTおよびDBG$OUTPUTをTDD2:に割り当てることによって, デバッガ・コマンドおよびデバッガの出力をTDD2: に表示できるようになる。
$ SHOW DEVICE/FULL TTD2:
装置タイプがunknownのとき,システム管理者またはLOG_IO特権かPHY_IO 特権を持つユーザは次の例のような方法でシステムに対して端末タイプを認識させる必要がある。 この例では,端末をVT200としている。
$ SET TERMINAL/PERMANENT/DEVICE=VT200 TTD2:
$ DEBUG/KEEP . . . DBG> RUN prog-name
デバッガの入出力をTTD2:で観察することができる。
$ DEALLOCATE TTD2:
適切に保護されているシステムでは,ある端末から別の端末を占有することができないように, 端末が保護されています。
必要な保護を設定するために,システム管理者または必要な特権を持つユーザは次の例のような手順に従う必要があります。
この例では,TTD1:が現在の端末(デバッガを起動する端末),TTD2:がデバッガの入出力を表示するために占有する端末です。
TTD1:とTTD2:がLAT(ローカル・エリア・トランスポート)を介してシステムに接続されている場合は, ステップ2に進む。
$ SET PROCESS/PRIV=LOG_IO $ SET TERMINAL/NOHANG/PERMANENT $ LOGOUT/NOHANG
$ SET ACL/OBJECT_TYPE=DEVICE/ACL=(IDENT=[PROJ,JONES],ACCESS=READ+WRITE) TTD2: 【1】 $ SET PROTECTION=WORLD:RW/DEVICE TTD2: 【2】
1つの同じデバッグ・セッションの中で,ソース・コードが異なる言語で記述されたモジュールをデバッグすることができます。 言語固有の動作について, 混乱を避けるために注意すべきことを中心に説明します。
どの言語の場合でも,デバッグの際には次を参照してください。
プログラムをデバッガの制御下に置くとき,デバッガは現在の言語 をメイン・プログラムが含まれているモジュール(通常は,イメージ遷移アドレスを含んでいるルーチン) を記述している言語に設定します。 現在の言語は,その時点で識別されます。次に例を示します。
$ DEBUG/KEEP Debugger Banner and Version Number DBG> RUN prog-name Language: PASCAL, Module: FORMS DBG>
現在の言語の設定は,デバッガ・コマンドで指定した名前,演算子,式をデバッガがどう解析し, 解釈するかを決定します。変数の型の設定,配列やレコードの構文, 整数データの省略時の基数,大文字/小文字の区別などの解釈も含まれます。 言語の設定によって,ユーザ・プログラムに関連するデータの表示方法も決まります。
多くのプログラムでは,メイン・プログラムの言語とは別の言語で記述されたモジュールが含まれています。 混乱をできるだけ少なくするために, 省略時の設定ではデバッガの言語は,別の言語で記述されたモジュールの中で実行が一時停止しても, デバッグ・セッションを通してメイン・プログラムの言語に設定されたまま変わりません。
このようなモジュールでのシンボリック・デバッグの利点を最大に活用するためには,SET LANGUAGE コマンドを使用してデバッグ・コンテキストを別の言語のデバッグ・ コンテキストに設定します。たとえば次のコマンドは, デバッガがシンボルや式などをCOBOL言語の規則に従って解釈するように設定します。
DBG> SET LANGUAGE COBOL
VAXプロセッサでは,SET LANGUAGEコマンドは次のキーワードを受け付けます。
ADA | BASIC | BLISS | C |
C_PLUS_PLUS | COBOL | DIBOL | FORTRAN |
MACRO | PASCAL | PLI | RPG |
SCAN | UNKNOWN |
Alphaプロセッサでは,SET LANGUAGEコマンドは次のキーワードを受け付けます。
ADA | AMACRO | BASIC | BLISS |
C | C_PLUS_ PLUS | COBOL | FORTRAN |
MACRO | MACRO64 | PASCAL | PLI |
UNKNOWN |
さらに,サポートされていない言語で記述されたプログラムをデバッグする場合,SET LANGUAGE UNKNOWN コマンドを指定することができます。サポートされていない言語でもデバッガを最大限活用できるようにするために,SET LANGUAGE UNKNOWN コマンドはデバッガがデータ形式と演算子の組み合わせを広く受け付けるようにします。 これらの中には,サポートされている言語の少数にだけ固有のものも含まれます。 言語がUNKNOWNに設定された場合に認識される演算子や構造については, デバッガのオンライン・ ヘルプに示します(HELP Languageと入力します)。
ここでは,各言語でデバッグを行う場合に注意すべき相違点を説明します。SET LANGUAGE コマンドの影響を受ける相違点およびその他の相違点, たとえば,言語固有の初期化コードや定義済みのブレークポイントなどが挙げられています。
ここに挙げられているものはすべてではありません。詳細については,デバッガのオンライン・ ヘルプ(HELP Languageと入力する)および使用している言語のドキュメントを参照してください。
整数データを入力したり表示したりするための省略時の基数は,ほとんどの言語では10 進数です。
VAXプロセッサでは,BLISSおよびMACROの場合は例外であり, これらは16進数を省略時の基数とします。
Alphaプロセッサでは,BLISS, MACRO-32およびMACRO-64 の場合は例外であり,これらは16進数を省略時の基数とします。
新しい省略時の基数を設定するには,SET RADIXコマンドを使用します。
いくつかのデバッガ・コマンドや構造は言語式を評価します。
これらのコマンドを処理するとき,第4.1.6 項で説明されているように,デバッガは現在の言語の構文および現在の基数に基づいて言語式を評価します。 デバッガは,(コマンドを入力するときではなく) 実行のたびに,WHEN節やDO節の式の構文をチェックし, その後これらを評価します。
演算子は言語によって大きく異なりますので注意してください。たとえば, 次の2つのコマンドはそれぞれPascalおよびFortranで記述された同じ意味の式を評価しています。
DBG> SET WATCH X WHEN (Y < 5) ! Pascal DBG> SET WATCH X WHEN (Y .LT. 5) ! FORTRAN
言語がPASCALに設定されていて,最初のPascalのコマンドを入力したとします。 ここでFortranのルーチン内の命令をステップ実行して言語をFortran に設定し,処理を再開します。言語がFortranに設定されている間, デバッガは式(Y < 5)を評価することができません。その結果,無条件のウォッチポイントを設定します。 ウォッチポイントが検出されると,< 演算子に対する構文エラーが返されます。
このような矛盾は,デバッガ・コマンド・プロシージャやデバッガ初期化ファイル内で言語式を評価するコマンドを実行する場合にも発生します。
言語がBLISSに設定された場合,デバッガは変数名または他のアドレス式を含む言語式を別の言語が設定された場合とは異なるように処理します。 詳細については第4.1.6項を参照してください。
配列要素やレコード構成要素(適用できる場合)を表す文法は,言語によって異なります。
たとえば,ある言語では配列要素を区切るために大括弧([])を使用し, 別の言語では括弧(( ))を使用します。
ある言語ではゼロを基底とする配列を持ち,また別の言語では,次の例のように1 を基底とする配列を持ちます。
DBG> EXAMINE INTEGER_ARRAY PROG2\INTEGER_ARRAY (1,1): 27 (1,2): 31 (1,3): 12 (2,1): 15 (2,2): 22 (2,3): 18 DBG>
ある言語(PascalやAdaなど)では,特定の配列宣言によって配列が何を基底にするかが決まります。
C言語では,名前や言語式の大文字/小文字が区別されます。名前や言語式を指定する場合, ソース・コードに記述されているとおりに指定しなければなりません。 たとえば,言語がCに設定されているとき,次の2つのコマンドは等しくありません。
DBG> SET BREAK SCREEN_IO\%LINE 10 DBG> SET BREAK screen_io\%LINE 10
多くのプログラムでは,プログラムがデバッガの制御下に置かれたとき, NOTAT MAINメッセージが表示されます。次に例を示します。
$ DEBUG/KEEP Debugger Banner and Version Number DBG> RUN prog-name Language: ADA, Module: MONITOR Type GO to reach main program DBG>
NOTATMAINメッセージは,メイン・プログラムの先頭の前でプログラムが一時停止していることを知らせます。 これによって,デバッガの制御下でいくつかの初期化コードを実行し, チェックすることが可能になります。
初期化コードはコンパイラによって作成され,LIB$INITIALIZEという名前の特別なPSECT に置かれます。たとえば,Adaパッケージの場合,初期化コードはパッケージ本体( 変数を初期化するための文を含む)に属します。 Fortranプログラムの場合は,初期化コードは/CHECK=UNDERFLOW修飾子または/CHECK=ALL 修飾子を指定したときに必要とされるハンドラを宣言します。
NOTATMAINメッセージは,初期化コードをデバッグしたくない場合,GOコマンドを入力すると直ちにメイン・ プログラムの先頭から実行できることを知らせます。 このとき,ユーザは他のプログラムのデバッグを起動するときと同じ場面にいます。GO コマンドを再び入力すると,プログラムの実行が開始されます。
タスキング・プログラムの場合,プログラムがデバッガの制御下に置かれると, タスキング例外イベントに関連づけられた2つのブレークポイントが自動的に設定されます。 これらのブレークポイントは,SET LANGUAGEコマンドの影響を受けません。 これらのブレークポイントは,適切な実行ライブラリが存在すると, デバッガの初期化の間に自動的に設定されます。
これらの定義済みのブレークポイントを示すには,SHOW BREAKコマンドを入力します。 次に例を示します。
DBG> SHOW BREAK Predefined breakpoint on ADA event "EXCEPTION_TERMINATED" for any value Predefined breakpoint on ADA event "DEPENDENTS_EXCEPTION" for any value DBG>
VAXシステムでは,STEP/OVERコマンドを使用すると,Fortran実行時ライブラリ(RTL) ルーチンを飛び越さずに,その中でステップ実行することがあります。Digital Fortran プログラムをデバッグしているときに, READ,WRITE,PRINTなどのFortran入出力操作が見つかった場合,Fortran コンパイラは,Fortran RTLルーチンを呼び出し,入出力操作を終了させます。RTL ルーチンを呼び出すときSTEPコマンドを使用すると,デバッガはこのルーチンを飛び越さずにその中でステップ実行し, 次のエラー・メッセージを表示します。
%DEBUG-W-NOSCRLIN, no source line for address nnnnnnnn
通常デバッガは,次に実行するRET命令を探すことによってルーチンを飛び越しますが,Fortran コンパイラは,明示的にRET命令を実行せずに,標準的でない方法を使用して,Fortran RTL 入出力ルーチンで使用されている一時文字列の割り当てをスタックから解除します。 これがその原因になります。
エラー・メッセージから回復するときは,STEPコマンドを何度か出して, セッション内の希望するコード行に戻ります。問題の再発を防ぐために, プログラムを変更して,入出力文の前に,関数の結果を格納する一時変数を含むように書き換えます。 たとえば次のようにします。
CHARACTER*23 c1, c2 c1 = c2() ! c1 is the temporary variable WRITE (*) c1 END C CHARACTER*23 FUNCTION c2() c2 = 'ABCDEFGHIJKLMNOPQRSTUVW' RETURN END
デバッガは,起動時に一定量のメモリを割り当て,ユーザのプログラムとスタックを共有します。 ユーザ・プロセスの例外により,リソースの浪費やスタックの破損が発生した場合, デバッガは制御を失うことがあり,その場合デバッグ・ セッションも異常終了します。
スタック破損のメッセージや,重大なエラーについての警告が出たら,このような事態になることも考えておかなければなりません。 どちらの場合も, デバッグ・セッションの完全性が保証されなくなります。
次のいずれかの手段を試してみます。
条件ハンドラは,例外が発生した場合にオペレーティング・システムが実行するプロシージャです。
例外には,ハードウェア条件(算術演算でのオーバフローやメモリ・アクセス違反など) ,またはシグナル通知されたソフトウェア的な例外(ファイルが見つからないためにシグナル通知された例外など) が含まれます。
オペレーティング・システムやデバッガ,またはユーザ・プログラムによって設定された種々の条件ハンドラ, たとえば,1次ハンドラ,呼び出しフレーム・ ハンドラ(アプリケーションで宣言されたもの)などが,どのように, またどんな順序で起動されるかは,オペレーティング・システムの規則によって指定されます。 デバッガを使用する場合の条件処理については, 第14.5.3項を参照してください。 条件処理についての一般的な説明については,『OpenVMS Run-Time Library Routines Volume』を参照してください。
例外ハンドラおよび条件ハンドラのデバッグ・ツールには次のものがあります。
SET BREAK/EXCEPTIONまたはSET TRACE/EXCEPTIONコマンドを入力すると, デバッガはユーザ・プログラムによって作成された例外をブレークポイントまたはトレースポイントとして扱うようになります。SET BREAK /EXCEPTIONコマンドの結果,ユーザのプログラムが例外を作成した場合は, デバッガは実行を一時停止し,例外が生じたことと実行が一時停止した行を報告し, コマンド入力を要求するプロンプトを表示します。次に例を示します。
DBG> SET BREAK/EXCEPTION DBG> GO . . . %SYSTEM-F-INTDIV, arithmetic trap, integer divide by zero at PC=0000066C, PSL=03C00022 break on exception preceding TEST\%LINE 13 6: X := 3/Y; DBG>
例外ブレークポイント(例外トレースポイント)は,例外を処理するための条件ハンドラをユーザ・ プログラムが持っている場合でも検出されます。SET BREAK/EXCEPTION コマンドは,ハンドラが実行可能になる前およびその結果, 例外が破棄される前にブレークポイントを発生させます。 例外ブレークポイントがなければハンドラは実行され,例外を破棄するハンドラがない場合にだけ, デバッガが制御を得ることができます。 第14.5.2項および第14.5.3項を参照してください。
次のコマンド行は,例外が発生した場所を示すために使用すると便利です。 このコマンドを使用すると,デバッガは一連のアクティブな呼び出しと例外ブレークポイントでのPC 値を自動的に表示します。
DBG> SET BREAK/EXCEPTION DO (SET MODULE/CALLS; SHOW CALLS)
画面モードのDO表示を作成して,デバッガが実行に割り込みをかけた場合にSHOW CALLS コマンドを実行することもできます。次に例を示します。
DBG> DISPLAY CALLS DO (SET MODULE/CALLS; SHOW CALLS)
SET TRACE/EXCEPTIONコマンドで設定される例外トレースポイントは,例外ブレークポイントにアドレス式の指定を持たないGO コマンドが続くものと似ています。
例外ブレークポイントは例外トレースポイントを取り消します。また,その逆も同様です。
例外ブレークポイントまたは例外トレースポイントを取り消すには,それぞれCANCEL BREAK/EXCEPTION コマンドまたはCANCEL TRACE/EXCEPTIONコマンドを使用します。
例外ブレークポイントが検出されると,アプリケーションで宣言された条件ハンドラが起動される前に実行が一時停止します。 ブレークポイントからGO ,STEP,またはCALLの各コマンドで実行を再開する場合,動作は次のようになります。
次のFORTRANの例では,例外ブレークポイントで条件ハンドラの存在をどのようにして判断するか, また,ブレークポイントで入力されたSTEPコマンドがどのようにしてハンドラ内の命令をステップ実行するかを示しています。
例外ブレークポイントでは,SHOW CALLコマンドがSYS$QIOWルーチン呼び出しの間に例外が発生したことを知らせます。
DBG> SET BREAK/EXCEPTION DBG> GO . . . %SYSTEM-F-SSFAIL, system service failure exception, status=0000013C, PC=7FFEDE06, PSL=03C00000 break on exception preceding SYS$QIOW+6 DBG> SHOW CALLS module name routine name line rel PC abs PC SYS$QIOW 00000006 7FFEDE06 *EXC$MAIN EXC$MAIN 23 0000003B 0000063B DBG>
VAXプロセッサでは,次のSHOW STACKコマンドは,SYS$QIOW ルーチンではハンドラが宣言されていないことを示しています。 呼び出しスタックの1レベル下で,EXC$MAINルーチンが「SSHAND」という名前のハンドラを宣言しています。
DBG> SHOW STACK stack frame 0 (2146296644) condition handler: 0 SPA: 0 S: 0 mask: ^M<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11> PSW: 0020 (hexadecimal) saved AP: 2146296780 saved FP: 2146296704 saved PC: EXC$MAIN\%LINE 25 . . . stack frame 1 (2146296704) condition handler: SSHAND SPA: 0 S: 0 mask: ^M<r11> PSW: 0000 (hexadecimal) saved AP: 2146296780 saved FP: 2146296760 saved PC: SHARE$DEBUG+2217 . . .
この例外ブレークポイントでSTEPコマンドを入力すると,条件ハンドラ「SSHAND 」内の命令を直接ステップ実行することができます。
DBG> STEP stepped to routine SSHAND 2: INTEGER*4 FUNCTION SSHAND (SIGARGS, MECHARGS) DBG> SHOW CALLS module name routine name line rel PC abs PC *SSHAND SSHAND 2 00000002 00000642 ----- 上記の条件ハンドラは例外0000045Cで呼び出された。 %SYSTEM-F-SSFAIL, system service failure exception, status=0000013C, PC=7FFEDE06, PSL=03C00000 ----- 例外メッセージの終わり。 SYS$QIOW 00000006 7FFEDE06 *EXC$MAIN EXC$MAIN 23 0000003B 0000063B DBG>
デバッガは,可能な場合には条件ハンドラのアドレスを名前としてシンボル化します。 ただし,ある言語では,アプリケーションで宣言された条件ハンドラが起動される前に, 例外はまず実行時ライブラリ(RTL)ルーチンによって処理されるので注意が必要です。 このような場合,最初の条件ハンドラのアドレスはRTL 共用可能イメージのアドレスからのオフセットにシンボル化されます。
プログラムをデバッガとともに実行する場合,次の条件ハンドラのうち少なくとも1 つが,プログラムの実行によって発生した例外を処理するために起動されます。 複数の場合は次のリストの順番に起動されます。
ハンドラは,次の3つの状態コードのうち1つを「Condition Handling Facility」へ戻します。
条件処理についてさらに詳しい説明は,『OpenVMS Programming Concepts Manual』を参照してください。
プログラムをデバッガとともに実行する場合,1次ハンドラはデバッガです。 したがって,デバッガが例外を処理する最初の機会を持ちます。例外がデバッガによって引き起こされたものであるかどうかは関係ありません。
SET BREAK/EXCEPTIONコマンドまたはSET TRACE/EXCEPTIONコマンドを入力した場合, デバッガはユーザ・プログラムによって引き起こされた例外でブレーク( トレース)します。ブレーク(トレース)処理は,アプリケーションで宣言されたハンドラが起動される前に行われます。
SET BREAK/EXCEPTIONコマンドまたはSET TRACE/EXCEPTIONコマンドを入力していない場合,1 次ハンドラはユーザ・プログラムによって引き起こされた例外を再シグナル通知します。
2次ハンドラは,特別な目的で使用され,本書で説明しているようなプログラムに対しては適用されません。
ユーザ・プログラムの各ルーチンで,条件ハンドラを設定することができます。 これらは呼び出しフレーム・ハンドラとして知られます。オペレーティング・ システムは,現在実行中のルーチンからこれらのハンドラの検索を開始します。 ルーチンにハンドラが設定されていない場合,呼び出しスタックの次のルーチンによって設定されたハンドラを検索します。 このようにして, 必要ならばメイン・プログラムまで検索します。
呼び出しフレーム・ハンドラの起動後,ハンドラは次のうち1つの処理を行います。
これらのハンドラはデバッガによって制御されます。これらのハンドラは, アプリケーションで宣言されたハンドラが例外を処理していなければ, デバッガに最終的に制御を戻し,DBG>プロンプトを表示します。そうでない場合は, デバッグ・セッションは終了し,DCLコマンド・インタプリタに制御が引き渡されます。
最終ハンドラは,呼び出しスタックの最終フレームであり,これら2つのハンドラのうち最初に起動されるハンドラです。 次の例は,処理されていない例外が例外ブレークポイントから最終ハンドラに伝えられ場合, どのように処理されるかを示しています。
DBG> SET BREAK/EXCEPTION DBG> GO . . . %SYSTEM-F-INTDIV, arithmetic trap, integer divide by zero at PC=0000066C, PSL=03C00022 break on exception preceding TEST\%LINE 13 6: X := 3/Y; DBG> GO %SYSTEM-F-INTDIV, arithmetic trap, integer divide by zero at PC=0000066C, PSL=03C00022 DBG>
この例では,最初のINTDIVメッセージは1次ハンドラによって発行されます。2 つ目のメッセージは最終ハンドラによって発行され,その後最終ハンドラによりDBG> プロンプトが表示されています。
ラスト・チャンス・ハンドラは,呼び出しスタックの破損のために最終ハンドラが制御を得ることができなかった場合にだけ起動されます。 次に例を示します。
DBG> DEPOSIT %FP = 10 DBG> GO . . . %SYSTEM-F-ACCVIO, access violation, reason mask=00, virtual address=0000000A, PC=0000319C, PSL=03C00000 %DEBUG-E-LASTCHANCE, stack exception handlers lost, re-initializing stack DBG>
キャッチオール・ハンドラはオペレーティング・システムの一部であり, ラスト・チャンス・ハンドラが制御を得ることができなかった場合に起動されます。 キャッチオール・ハンドラはレジスタのダンプを作成します。 デバッガがユーザ・プログラムを制御している場合に起動されることはありません。 デバッガを使用せずにプログラムを実行しているとき,プログラムでエラーが検出されると起動されます。
デバッグ・セッションの間,レジスタ・ダンプが現れDCLレベル($)に戻った場合は, 弊社のサポート要員にご連絡ください。
例外がシグナル通知された場合,デバッガは次の例外関連の組み込みシンボルを設定します。
シンボル | 説明 |
---|---|
%EXC_FACILITY | 現在の例外を発行したファシリティの名前 |
%EXC_NAME | 現在の例外の名前 |
%ADAEXC_NAME | 現在のAda例外の名前 (Adaプログラムの場合のみ) |
%EXC_NUMBER | 現在の例外の番号 |
%EXC_SEVERITY | 現在の例外の重大度コード |
これらのシンボルを次のように使用することができます。
次に,これらのシンボルの使用例を示します。WHEN句の条件式は言語固有ですので注意してください。
DBG> EVALUATE %EXC_NAME 'ACCVIO' DBG> SET TRACE/EXCEPTION WHEN (%EXC_NAME = "ACCVIO") DBG> EVALUATE %EXC_FACILITY 'SYSTEM' DBG> EVALUATE %EXC_NUMBER 12 DBG> EVALUATE/CONDITION_VALUE %EXC_NUMBER %SYSTEM-F-ACCVIO, access violation, reason mask=01, virtual address=FFFFFF30, PC=00007552, PSL=03C00000 DBG> SET BREAK/EXCEPTION WHEN (%EXC_NUMBER = 12) DBG> SET BREAK/EXCEPTION WHEN (%EXC_SEVERITY .NE. "I" .AND. %EXC_SEVERITY .NE. "S")
終了ハンドラは,イメージがシステム・サービス$EXITを要求したり,実行が終了したりしたときに呼び出されるプロシージャです。 ユーザ・プログラムは1 つまたは複数の終了ハンドラを宣言することができます。デバッガは常に自分自身の終了ハンドラを宣言します。
プログラム終了時,アプリケーションで宣言された終了ハンドラがすべて実行されたあとにデバッガの終了ハンドラが実行されます。
アプリケーションで宣言された終了ハンドラをデバッグするには,次の手順に従ってください。
終了ハンドラが実行されると,ブレークポイントが有効になり,制御がデバッガに戻る。 続いてコマンドの入力を要求するプロンプトが表示される。
SHOW EXIT_HANDLERSコマンドは,ユーザ・プログラムで宣言した終了ハンドラを表示します。 終了ハンドラのルーチンは,呼び出された順番に表示されます。 ルーチン名は可能であればシンボルとして表示されます。そうでない場合はアドレスが表示されます。 デバッガの終了ハンドラは表示されません。 次に例を示します。
DBG> SHOW EXIT_HANDLERS exit handler at STACKS\CLEANUP exit handler at BLIHANDLER\HANDLER1 DBG>
プログラムは,非同期システム・トラップ(AST)を明示的に使用したり, システムサービス,またはアプリケーションで定義されたASTルーチンを呼び出す実行時ライブラリ(RTL) ルーチンを呼び出すことによって,暗黙に使用したりできます。 第14.7.1項では, ユーザのプログラムから発行されたASTの実行要求を禁止したり許可にしたりすることによってデバッグを行う方法を説明します。
ASTドライブ式プログラムのデバッグは混乱することがあります。デバッガが実行を続けている間( コマンドを処理したり,例外をトレースしたり, 情報を表示したりしている間)に,デバッグ中のプログラムからの割り込みが発生し, 処理されないことがあるからです。
省略時の設定では,ASTの実行要求はプログラムが実行されている間は許可されています。DISABLE AST コマンドはプログラムが実行されている間のAST の実行要求を禁止し,このような割り込みの発生をキューに登録します。
ASTの実行要求は,デバッガの実行中は常に禁止されます。
静的ウォッチポイントが有効な場合,デバッガは,システム・サービス呼び出しの直前に, 静的ウォッチポイント,AST,スレッド切り替えをオフにします。 デバッガは,システム・サービス呼び出しが終了した直後に再起動します。 詳細については,SET WATCHコマンドの項を参照してください。ENABLE AST コマンドは,待ち状態にあるASTの実行要求も含め,ASTの実行要求を再び許可します。SHOW AST コマンドは,ASTの実行要求が禁止されているか許可されているかを示します。
CALLコマンドで呼び出されたルーチンの実行中,ASTの実行要求を制御するには,/[NO]ASC 修飾子を使用します。CALL/ASTコマンドは,呼び出されたルーチン内のAST の実行要求を許可します。CALL/NOASTコマンドは, 呼び出されたルーチン内のASTの実行要求を禁止します。CALLコマンドで/AST も/NOASTも指定しない場合は,前にDISABLE ASTコマンドを入力していないかぎり,AST の実行要求は許可されます。
OpenVMS Alphaシステムの場合,デバッガは,変換されたイメージのデバッグをサポートしてしません。 変換されたイメージをデバッグする必要があるときは,Delta/XDelta デバッガを使用してください。このデバッガの詳細については, 『OpenVMS Delta/XDelta Debugger Manual』を参照してください。
同期化または通信を実行する一部のプログラムでは,デバッグで問題が発生することがあります。 次の場合には,ユーザ・アプリケーションはHIB (ハイバネート)状態のままになります。
この問題を回避するには,省略時のモードやマルチプロセス・モードではなく, 制限された1プロセス・モードを使用して,これらのアプリケーション・ プログラムをデバッグします。1プロセス・モードを設定するには, 次のコマンドを使用します。
$ DEFINE DBG$PROCESS NONE
OpenVMSシステムでは,デバッガはインライン・ルーチンのデバッグをサポートしません。 インライン・ルーチンをデバッグしようとすると,次の例に示すように, デバッガはルーチンにアクセスできないことを示すメッセージを出力します。
%DEBUG-E-ACCESSR, no read access to address 00000000
この問題を回避するには,/NOOPTIMIZE修飾子を使用してプログラムをコンパイルします。
[ 前のページ ]
[ 次のページ ]
[ 目次 ]
[ 索引 ]
[ DOC Home ]