OpenBoot 3.x コマンド・リファレンスマニュアル

第 6 章 デバッグ

OpenBoot では、Forth 言語逆コンパイラ、マシン言語逆アセンブラ、レジスタ表示用コマンド、シンボリックデバッガ、ブレークポイント関係コマンド、Forth ソースレベルデバッガ、高水準言語パッチ機能、例外追跡を含むデバッグ用ツールを提供しています。この章では、IEEE 規格 1275-1994 に指定された機能について説明します。

Forth 言語逆コンパイラの使用方法

組み込み Forth 言語逆コンパイラを使用して、どのような定義済み Forth ワードのソースコードでも表示できます。


ok see old-name

このコマンドは、old-name のソースの (ソースコメントなしの) リストを表示します。

see(see) と対をなしています。つまり、(see) の機能は Forth ワードの逆コンパイルを行い、その実行トークンはスタックから取り出されます。次の例を参照してください。


ok ' old-name (see)

(see)see と同じ書式のリストを生成します。


ok see see
: see   
   " ["] (see) catch if    
      drop 
   then  
; 
ok see (see)
defer (see) is 
: (f0018a44)   
   40 rmargin ! dup dup (f00189c4) dup (f0018944) (f0018980) (f0018658) 
   ??cr 
; 
ok f0018a44 (see)
: (f0018a44)   
   40 rmargin ! dup dup (f00189c4) dup (f0018944) (f0018980) (f0018658) 
   ??cr 
; 

上記のリストは、次のことを示しています。

Forth アセンブラ言語に実装されているワードについては、see は Forth アセンブラのリストを表示します。たとえば、dup を逆コンパイルすると、次のように表示されます。


ok see dup
code dup 
f0008c98     sub         %g7, 8, %g7
f0008c9c     stx         %g4, [%g0 + %g7]
f0008ca0     ld          [%g5], %l0
f0008ca4     jmp         %l0, %g2, %g0
f0008ca8     add         %g5, 4, %g5

逆アセンブラの使用方法

組み込み逆アセンブラはメモリーの内容を、対応するアセンブラ言語に翻訳します。

表 6-1に、メモリーの内容を対応するオペコードに逆アセンブルするコマンドを示します。

表 6-1 逆アセンブラコマンド

コマンド  

スタックダイアグラム 

説明 

+dis

( -- ) 

最後に逆アセンブルを終了したところから逆アセンブルを継続します。 

dis

( addr -- ) 

指定されたアドレスから逆アセンブルを開始します。 

dis は指定する任意のアドレスから、メモリーの内容の逆アセンブルを開始します。システムは次の場合に中断します。

中断したら、逆アセンブルを停止することも、+dis コマンドを使用して最後に逆アセンブルが停止したところから逆アセンブルを継続することもできます。

メモリーアドレスは通常は 16 進で示されますが、シンボルテーブルがある場合は、メモリーアドレスは可能なかぎりシンボルで表示されます。

レジスタの表示

プログラムがクラッシュしたり、ユーザーが中止したり、あるいはブレークポイントに遭遇した結果、プログラムの実行途中でユーザーインタフェースに入ってしまうことがあります。(ブレークポイントについては、「ブレークポイント」で説明します。) こういった場合には、ユーザーインタフェースは自動的にすべての CPU データレジスタの値をバッファー領域に保存します。デバッグの目的のためにこれらの値は調べたり、変更することができます。

SPARC レジスタ

表 6-2に SPARC のレジスタコマンドを示します。

表 6-2 SPARC レジスタコマンド

コマンド  

スタックダイアグラム 

説明 

%g0%g7

( -- value ) 

指定されたグローバルレジスタの値を返します。 

%i0%i7

( -- value ) 

指定された入力レジスタの値を返します。 

%l0%l7

( -- value ) 

指定されたローカルレジスタの値を返します。 

%o0%o7

( -- value ) 

指定された出力レジスタの値を返します。 

%pc %npc %y

( -- value ) 

指定されたレジスタの値を返します。 

%f0%f31

( -- value ) 

指定された浮動小数点レジスタの値を返します。 

.fregisters

( -- ) 

%f0 から %f31 までの値を表示します。

.locals

( -- ) 

ilo レジスタの値を表示します。

.registers

( -- ) 

プロセッサレジスタの値を表示します。 

.window

( window# -- ) 

w .locals と同じ。指定されたウィンドウを表示します。

ctrace

( -- ) 

C サブルーチンを示す復帰スタックを表示します。 

set-pc

( new-value -- ) 

%pc に new-value を、 %npc に (new-value+4) をそれぞれ設定します。

to regname

( new-value -- ) 

上記のうちの任意のレジスタに格納された値を変更します。 new-value to regname の形式で使用してください。

w

( window# -- ) 

現在のウィンドウを、 %ix、%lx、または %ox を表示するために設定します。

表 6-3 SPARC V9 レジスタコマンド

コマンド 

スタック ダイアグラム 

説明 

%fprs

%asi

%pstate

%tl-c

%pil

%tstate %tt

%tba

%cwp %cansave

%canrestore

%otherwin

%wstate

%cleanwin

( -- value ) 

指定されたレジスタの値を返します。 

.pstate

( -- ) 

プロセッサ状態レジスタを特定書式で表示します。 

.ver

( -- ) 

バージョンレジスタを特定書式で表示します。 

.ccr

( -- ) 

%ccr レジスタを特定書式で表示します。

.trap-registers

( -- ) 

トラップレジスタを表示します。 

これらのレジスタの値はすべて保存され、to で変更できます。値の確認や変更が終わったら、go コマンドを使用してプログラムの実行を継続できます。保存したレジスタの値 (変更したものを含めて) は、(コピーして) CPU に戻され、保存されたプログラムカウンタによって指定された位置から実行が再開されます。

to を使用して %pc を変更する場合は、%npc も変更する必要があります。(set-pc の方が両レジスタを自動的に変更するので簡単です。)

SPARC V9 システムでは、N が現在のウィンドウの場合、N-1 は呼び出し元のウィンドウを指定し、N-2 は呼び出し元の呼び出し元を指定します。

ブレークポイント

ユーザーインタフェースは、スタンドアロンプログラムの開発とデバッグの支援用として、ブレークポイント機能を備えています。(オペレーティングシステムのもとで実行されるプログラムは、一般的にこの機能は使用しないで、オペレーティングシステムのもとで動作するほかのデバッガを使用します。) ブレークポイント機能では、テストプログラムを停止させたい場所で停止することができます。プログラムの実行が停止した後は、レジスタまたはメモリーを調べたり、変更できるほか、ブレークポイントを新たに設定またはクリアすることができます。プログラムの実行は go コマンドで再開できます。

表 6-4に、プログラム実行の制御、監視用のブレークポイントコマンドを示します

表 6-4 ブレークポイントコマンド

コマンド 

スタックダイアグラム 

説明 

+bp

( addr -- ) 

指定されたアドレスにブレークポイントを追加します。 

-bp

( addr -- ) 

指定されたアドレスからブレークポイントを削除します。 

--bp

( -- )  

最後に設定されたブレークポイントを削除します。 

.bp

( -- ) 

現在設定されているすべてのブレークポイントを表示します。 

.breakpoint

( -- ) 

ブレークポイントが発生したときに、指定された処理を実行します。このワードは、実行させたい任意の処理を実行するように変更できます。たとえば、ブレークポイントごとにレジスタを表示するには、 ['] .registers to .breakpoint と入力します。デフォルト処理は .instruction です。複数の処理を実行させるには、実行させたいすべての処理を呼び出す 1 つの定義を作成し、次にそのワードを .breakpoint に読み込みます。

.instruction

( -- ) 

最後に現れたブレークポイントのアドレスとオペコードを表示します。 

.step

( -- ) 

シングルステップで実行になったときに指定された処理を実行します (.breakpoint を参照)。

bpoff

( -- ) 

すべてのブレークポイントを削除します。 

finish-loop

( -- ) 

このループの終わりまで実行します。 

go

( -- ) 

ブレークポイントから処理を継続します。これを利用して、 go を発行する前にプロセッサのプログラムカウンタを設定することにより、任意のアドレスに移ることができます。

gos

( n -- ) 

go を n 回実行します。

hop

( -- ) 

(step コマンドに似ています。) サブルーチン呼び出しを 1 つの命令として取り扱います。

hops

( n -- ) 

hop を n 回実行します。

return

( -- ) 

このサブルーチンの終わりまで実行します。 

returnl

( -- )  

このリーフサブルーチンの終わりまで実行します。 

skip

( -- ) 

現在の命令をスキップします (実行しません)。 

step

( -- ) 

1 命令を 1 つずつ実行します。 

steps

( n -- ) 

step を n 回実行します。

till

( addr -- ) 

指定されたアドレスが現れるまで実行します。+bp go と等価です。

ブレークポイントを使用してプログラムをデバッグするには、次の手順に従います。

  1. テストプログラムをメモリーへ読み込みます。

  2. 詳細は、第 5 章「プログラムの読み込みと実行」を参照してください。各レジスタの値が自動的に初期化されます。

  3. (省略可能) ダウンロードされたプログラムを逆アセンブルして、ファイルが正しく読み込まれているかどうかを確認します。

  4. step コマンドを使用してテストプログラムを 1 命令ずつ実行します。

  5. さらに、ブレークポイントを設定し、実行したり (たとえば、addr +bp および go コマンドを実行します)、ほかの方法で実行することもできます。

Forth ソースレベルデバッガ

ソースレベルデバッガでは、Forth プログラムのシングルステップ実行およびトレースが可能です。各実行ステップが 1 つの Forth ワードに対応します。

表 6-5にこのデバッガのコマンドを示します。

表 6-5 Forth ソースレベルデバッガコマンド

コマンド 

説明 

c

"Continue (継続)"。シングルステップ実行からトレースに切り替え、デバッグ中のワードの実行の残り部分をトレースします。 

d

"Down a level ( 1 レベルダウン)"。今表示された名前のワードをデバッグ対象としてマークし、次にそのワードを実行します。 

u

"Up a level ( 1 レベルアップ)"。デバッグ中のワードからデバッグ対象のマークを取り消します。その呼び出し元をデバッグ対象としてマークし、それまでデバッグされていたワードの実行を終了します。 

f

下位の Forth インタプリタを起動します。通常はこのインタプリタで Forth コマンドを実行できます。このインタプリタが ( resume ) で終了されると、制御がデバッガの f コマンドが実行された位置に戻ります。

g

"Go"。デバッガをオフに設定し、プログラムの実行を継続します。 

q

"Quit (終了)"。デバッグ中のワードとそのすべての呼び出し元の実行を強制終了させ、制御をコマンドインタプリタに戻します。 

s

"see"。デバッグ中のワードを逆アセンブルします。 

$

スタックの一番上の address,len をテキスト文字列として表示します。 

h

"Help"。シンボリックデバッガのマニュアルを表示します。 

?

"Short Help"。簡略なシンボリックデバッガのマニュアルを表示します。 

debug name

指定された Forth ワードをデバッグ対象としてマークします。以降は、name を実行しようとするたびに、必ず Forth ソースレベルデバッガを起動します。 debug の実行後は、 debug-off でデバッグがオフされるまではシステムの実行速度が落ちることがあります。 ( . などの基本 Forth ワードはデバッグしないでください。)

(debug

(debug は、入力ストリームから名前を取り出すのでなく、スタックから実行トークンを取り出す点を除いて debug と同じです。

debug-off

Forth ソースレベルデバッガをオフにします。以降、ワードのデバッグは行われません。 

resume

下位インタプリタを終了し、制御をデバッガのステップ実行に戻します (この表の f コマンドを参照)。

stepping

Forth ソースレベルデバッガを "シングルステップ (実行) モード" に設定し、デバッグ中のワードを 1 ステップずつ対話的に実行できるようにします。シングルステップモードはデフォルトです。 

tracing

Forth ソースレベルデバッガを "トレースモード" に設定します。このモードは、デバッグ中のワードの実行をトレースし、その間そのワードが呼び出す各ワードの名前とスタックの内容を表示します。 

<space-bar>

今表示されたワードを実行し、次のワードのデバッグに移ります。 

すべての Forth ワードはそれぞれに、「コンポーネント」ワードと呼べる 1 つまたは複数の一連のワードとして定義されています。指定されたワードをデバッグしている間に、デバッガは、そのワードの各「コンポーネント」ワードを実行中にスタックの内容に関する情報を表示します。各コンポーネントワードを実行する直前に、デバッガはスタックの内容と、実行されようとしているコンポーネントワードの名前を表示します。

トレースモードでは、そのコンポーネントワードがそこで実行され、プロセスは次のコンポーネントワードに引き継がれます。

ステップモード (デフォルト) では、ユーザーがデバッガの実行動作を制御します。各コンポーネントワードの実行前に、ユーザーはプロンプトで表 6-5にあるキー操作のどれかを求められます。

patch(patch) の使用方法

OpenBoot では、アセンブル済みの Forth ワードの定義を高水準の Forth 言語を使用して変更できます。変更は、通常、該当するソースコードで行われるのに対して、patch 機能はデバッグ時に見つけられなかった誤りを迅速に修正する手段を提供します。

patch は次に示す各情報から入力ストリームを読みます。

たとえば、次の例を考えてください。この例では、ワード test が数値 555 に置き換えられます。


ok : patch-me  test 0 do  i . cr  loop ;
ok patch 555 test patch-me
ok see patch-me
: patch-me
	h# 555 0 do
		i . cr
	loop
;

patch を使用するときは、十分注意して正しいワードを選択し、置き換える必要あります。置き換えようとするワードがターゲットワード内で数回使用され、ターゲットワード内で最初に出てくるのものでなく、何番目かに出てくるワードである場合には、とくに注意が必要です。そのような場合は、なんらかの回避手段が必要です。


ok : patch-me2  dup dup dup ( This third dup should be drop) ;
ok : xx dup ;
ok patch xx dup patch-me2
ok patch xx dup patch-me2
ok patch drop dup patch-me2
ok see patch-me2
: patch-me2
	xx xx drop
;

patch のもう 1 つの用途は、パッチするワードに、完全に廃棄する必要がある特定の機能がある場合です。そのような場合は、機能を削除する最初のワードに対してワード exit をパッチします。たとえば、次のような定義をもつワードを考えてみてください。


ok : foo good bad unneeded ;

この例では、bad の機能は誤っており、unneeded の機能は廃棄する必要があります。foo をパッチしようとするとき、最初は、ワード right 内に exit を使用すれば unneeded を実行できなくできると期待して、次のように入力するかも知れません。


ok : right this that exit ;
ok patch right bad foo 

残念ながら、exit はそれが入っているワード、この場合 right の実行を終了させてしまいます。foo の正しいパッチ方法は次のようになります。


ok : right this that ;
ok patch right bad foo 
ok patch exit unneeded foo 

(patch) は、その引数をスタックから得る点を除いて patch と同じです。(patch) のスタックダイアグラムは次のとおりです。

( new-n1 num1? old-n2 num2? xt -- )

ただし、

たとえば、次の例を考えてみてください。ここでは、数値 555 を test に置き換えて、最初の patch の例の効果を逆にしています。


ok see patch-me
: patch-me
	h# 555 0 do
		i . cr
	loop
;
ok ["] test false 555 true ["] patch-me (patch)
ok see patch-me
: patch-me
	test 0 do
		i . cr
	loop
;

ftrace の使用方法

ftrace コマンドは、最後の例外割り込み時に実行されていた Forth ワード処理を表示します。次に ftrace の例を示します。


ok : test1 1 ! ; 
ok : test2 1 test1 ; 
ok test2 
Memory address not aligned
ok ftrace 
! 		 Called from test1 	at ffeacc5c
test1 		 Called from test2 	at ffeacc6a
(ffe8b574) 	Called from (interpret 	at ffe8b6f8
execute 			Called from catch 	at ffe8a8ba
	ffefeff0
	0	
	ffefebdc
catch 			 Called from (fload) 	at ffe8ced8
	0
(fload) 			Called from interact 	at ffe8cf74
execute 	Called from catch 	at ffe8a8ba
	ffefefd4
	0		
	ffefebdc
catch 		Called from (quit 	at ffe8cf98

上の例では、test2test1 を呼び出し、test1 は境界に合わないアドレスに値を格納しようとします。その結果、Memory address not aligned という例外が発生します。

ftrace の出力の 1 行目は、例外を発生させた最後のコマンドを示しています。2 行目以降は、その後のコマンドが呼び出されようとしていたメモリーアドレスを示しています。

最後の数行は、通常どの ftrace の出力とも同じですが、これは、それが Forth インタプリタが入力ストリームからワードを解釈するときに有効な呼び出し処理であるからです。