dbx コマンドによるデバッグ | ![]() ![]() ![]() ![]() ![]() |
第 9 章
実行時検査
実行時検査 (RTC) を使用すると、開発段階でアプリケーションの実行時エラーを自動的に検出できます。RTC では、メモリーアクセスエラーおよびメモリーリークエラーなどの実行時エラーを検出して、メモリーの使用状況を監視できます。
- 概要
- RTC の使用
- メモリーアクセスエラーの検出 (SPARC のみ)
- メモリーリークの検査
- メモリー使用状況検査の使用
- エラーの抑止
- 子プロセスにおける RTC の実行
- 接続されたプロセスへの RTC の使用
- RTC での修正継続機能の使用
- 実行時検査アプリケーションプログラミング インタフェース
- バッチモードでの RTC の使用
- トラブルシューティングのヒント
注 - アクセス検査は SPARC でのみ利用可能です。
概要
RTC は、統合的なデバッグ機能であり、標本コレクタによるパフォーマンスデータの収集時を除けば、実行時にあらゆるデバッグ機能を利用できます。
- メモリーアクセスエラーを検出する
- メモリーリークを検出する
- メモリー使用に関するデータを収集する
- すべての言語で動作する
- システムライブラリなど、ソースを持たないコードで動作する
- マルチスレッドコードで動作する
- 再コンパイル、再リンク、またはメークファイルの変更が不要である
-g
フラグを付けてコンパイルすると、RTC エラーメッセージでのソース行番号の関連性が与えられます。RTC は、最適化-O
フラグによってコンパイルされたプログラムを検査することもできます。-g
オプションによってコンパイルされていないプログラムについては、特殊な考慮事項があります。RTC を実行するには、
dbx
コマンドを入力するか、「デバッグ」ウィンドウや「実行時検査」ウィンドウを利用します。Sun WorkShop オンラインヘルプの「デバッグウィンドウの使い方」の「実行時検査の開始」を参照してください。RTC を使用する場合
大量のエラーが一度に検出されないようにするには、RTC を開発サイクルの初期の段階で使用します。この段階では、プログラムの構成要素となる個々のモジュールを開発します。この各モジュールを実行する単位テストを作成し、RTC を各モジュールごとに 1 回ずつ使用して検査を行います。これにより、一度に処理するエラーの数が減ります。すべてのモジュールを統合して完全なプログラムにした場合、新しいエラーはほとんど検出されません。エラー数をゼロにした後でモジュールに変更を加えた場合にのみ、RTC を再度実行してください。
RTC の必要条件
- Sun のコンパイラを使用してコンパイルされたプログラム
libc
を動的にリンクしている。libc
の標準関数malloc
、free
、realloc
を利用するか、これらの関数を基にアロケータを使用します。RTC では、他のアロケータはアプリケーションプログラミングインタフェース (API) で操作します。「実行時検査アプリケーションプログラミング インタフェース」を参照してください。- 完全にストリップされていないプログラム。
strip -x
によってストリップされたプログラムは使用できます。制限事項
RTC は、8Mバイトより大きなプログラムのテキスト領域およびデータ領域を処理しません。詳細については「RTC の 8M バイト制限」を参照してください。
実行可能イメージに特殊ファイルを挿入すると、8Mバイトより大きなプログラムのテキスト領域とデータ領域を処理することができます。
RTC の使用
実行時検査を使用するには、使用したい検査の種類を指定します。
メモリー使用状況とメモリーリーク検査を有効化
メモリーの使用状況とメモリーリークの検査を有効にするには、次のように入力します。
(dbx)
check -memuse
メモリーの使用状況とメモリーリークの検査は、「デバッグ」ウィンドウでも有効にできます。Sun WorkShop オンラインヘルプの「デバッグウィンドウの使い方」の「すべてのメモリーリークの表示」を参照してください。
MUC か MLC がオンになっている場合、
showblock
コマンドを実行する、所定のアドレスにおけるヒープブロックに関する詳細情報を表示できます。この詳細情報では、ブロックの割り当て場所とサイズを知ることができます。詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「showblock コマンド」を参照してください。メモリーアクセス検査を有効化
メモリーアクセス検査だけをオンにするには、次のように入力します。
(dbx)
check -access
メモリーアクセスの検査も「デバッグ」ウィンドウで有効にできます。詳細については、Sun WorkShop オンラインヘルプの「「デバッグウィンドウの使い方」の「メモリーアクセス検査を有効にする」を参照してください。
すべての RTC を有効化
メモリーリーク、メモリー使用状況、およびメモリーアクセスの各検査をオンにするには、次のように入力します。
(dbx)
check -all
詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「check コマンド」を参照してください。
RTC を無効化
(dbx)
uncheck -all
詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「uncheck コマンド」を参照してください。
プログラムを実行
目的のタイプの RTC を有効にしてテストするプログラムを実行します。この場合、ブレークポイントを設定してもしなくてもかまいません。
プログラムは普通に実行されますが、すべてのメモリーアクセスがその妥当性を確認するために検査されるので、実行速度は遅くなります。無効なアクセスがあると
dbx
によって検出され、そのタイプとエラーの発生場所が表示されます。制御はユーザーに戻ります (dbx
環境変数rct_auto_continue
がon
になっている場合を除きます (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「rtc_auto_continue 環境変数」参照))。
次に、
dbx
コマンドを実行します。where
コマンドでは現在のスタックトレースを呼び出すことができます。またcont
コマンドでプログラムの処理を続行します。プログラムは次のエラーまたはブレークポイントまで、どちらか先に検出されるところまで実行されます。詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「cont コマンド」を参照してください。
rtc_auto_continue
がon
に設定されている場合、RTC はそのままエラーを求めて自動的に続行されます。検出したエラーは、dbx
環境変数rtc_error_log_name
で指定したファイルにリダイレクトされます (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「rtc_error_log _file_name 環境変数」を参照してください)。
RTC エラーの報告が不要な場合は、
suppress
コマンドを使用します。詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「suppress コマンド」を参照してください。次の例は、
hello.c
と呼ばれるプログラムのメモリーアクセス検査とメモリー使用状況検査をオンにする方法を示しています。
関数
access_error()
は、初期化される前の変数j
を読み取ります。RTC は、このアクセスエラーを非初期化領域からの読み取り (rui
) として報告します。関数
memory_leak()
は、終了する前にlocal
を解放 (free()
) しません。memory_leak()
が終了してしまうと、local
がスコープ外になり、行 20 で確保したブロックがリークになります。プログラムは、常にスコープ内にある大域変数
hello1
とhello2
を使用します。これらの変数はいずれも、使用中ブロック (biu
) として報告される割り当て済みメモリーを動的に指します。メモリーアクセスエラーの検出 (SPARC のみ)
RTC では、読み取り、書き込み、メモリー解放の各操作を監視することによって、プログラムがメモリーに正しくアクセスするかどうかを検査します。
プログラムは、さまざまな方法で間違ってメモリーを読み取ったり、メモリーに書き込んだりすることがあります。このようなエラーをメモリーアクセスエラーといいます。たとえば、ヒープブロックの
free()
呼び出しを使用して、または関数がローカル変数にポインタを返したために、プログラムが参照するメモリーブロックの割り当てが解放されている可能性があります。アクセスエラーはプログラムでワイルドポインタの原因になり、間違った出力やセグメント不正など、プログラムの異常な動作を引き起こす可能性があります。メモリーアクセスエラーには、検出が非常に困難なものもあります。RTC は、プログラムによって使用されているメモリーの各ブロックの情報を追跡するテーブルを管理します。プログラムがメモリー操作を行うと、RTC は関係するメモリーブロックの状態に対してその操作が有効かどうかを判断します。メモリーの状態として次のものがあります。
- 未割り当て (初期) 状態。メモリーは割り当てられていません。この状態のメモリーはプログラムが所有していないため、読み取り、書き込み、解放のすべての操作が無効です。
- 割り当て済み/未初期化。メモリーはプログラムに割り当てられていますが、初期化されていません。書き込み操作と解放操作は有効ですが、初期化されていないので読み取りは無効です。たとえば、関数に入るときに、スタック上にメモリーが割り当てられますが、初期化はされません。
- 読み取り専用。読み取りは有効ですが、書き込みと解放は無効です。
- 割り当て済み/初期化済み。割り当てられ、初期化されたメモリーに対しては、読み取り、書き込み、解放のすべての操作が有効です。
アクセスエラーを見つけるには、アクセス検査をオンにし、プログラムにアクセスエラーのリストを作成させるのが一番簡単な方法です。 RTC を使用してメモリーアクセスエラーを見つける方法は、コンパイラがプログラム中の構文エラーを見つける方法と似ています。いずれの場合でも、プログラム中のエラーが発生した位置と、その原因についてのメッセージとともにエラーのリストが生成され、リストの先頭から順に修正していかなければなりません。これは、あるエラーがほかのエラーと関連して連結されたような作用があるためです。連結の最初のエラーが先頭の原因となり、そのエラーを修正することにより、そのエラーから派生した他の問題も解決されることがあります。たとえば、初期化されていないメモリーの読み取りにより、不正なポインタが作成されるとします。すると、これが原因となって不正な読み取りと書き込みのエラーが発生し、それがまた原因となってさらに別の問題が発生するというようなことになる場合があります。
メモリーアクセスエラーの報告
メモリーアクセスエラーを検出すると RTC は次の情報を出力します。
非初期化領域からの読み取り (rui):4 バイト読み取り を アドレス 0xefffee50 で しようとしましたそれは 96 バイト 現スタックポインタより上 です変数は 'j' です。現関数 :rui12 i = j;メモリーアクセスエラー
rui
(「非初期化メモリーからの読み取り (rui) エラー」参照)rua
(「非割り当てメモリーからの読み取り (rua) エラー」参照)wua
(「非割り当てメモリーへの書き込み (wua) エラー」参照)wro
(「読み取り専用メモリーへの書き込み (wro) エラー」参照)mar
(「境界整列を誤った読み取り (mar) エラー」参照)maw
(「境界整列を誤った書き込み (maw) エラー」参照)duf
(「重複解放 (duf) エラー」参照)baf
(「不正解放 (baf) エラー」参照)maf
(「境界整列を誤った解放 (maf) エラー」参照)oom
(「メモリー不足 (oom) エラー」参照)
注 - RTC では、アレイ境界チェックは行いません。したがって、アレイ境界侵害はアクセスエラーにはなりません。
メモリーリークの検査
メモリーリークとは、プログラムで使用するために割り当てられているが、プログラムのデータ領域中のいずれも指していないポインタをもつ、動的に割り当てられたメモリーブロックを言います。そのようなブロックは、メモリーのどこに存在しているかプログラムにわからないため、プログラムに割り当てられていても使用することも解放することもできません。RTC はこのようなブロックを検知し、報告します。
メモリーリークは仮想メモリーの使用を増やし、一般的にメモリーの断片化を招きます。その結果、プログラムやシステム全体のパフォーマンスが低下する可能性があります。
メモリーリークは、通常、割り当てメモリーを解放しないで、割り当てブロックへのポインタを失うと発生します。メモリーリークの例を以下に示します。
voidfoo(){char *s;s = (char *) malloc(32);strcpy(s, "hello world");return; /*s
が解放されていない。foo
が戻るとき、malloc
されたブロックを指しているポインタが存在しないため、ブロックはリークする*/}リークは、API の不正な使用が原因で起こる可能性があります。
voidprintcwd(){printf("cwd = %s\n", getcwd(NULL, MAXPATHLEN));return; /*libc
の関数getcwd()
は、最初の引数が NULL の場合malloc
された領域へのポインタを返す。プログラムは、これを解放する必要がある。この場合、ブロックが解放されていないため、結果的にリークになる。*/}メモリーリークを防ぐには、必要のないメモリーは必ず解放します。また、メモリーを確保するライブラリ関数を使用する場合は、メモリーを解放することを忘れないでください。
解放されていないブロックを「メモリーリーク」と呼ぶこともあります。ただし、この定義はあまり使用されません。プログラムが短時間で終了する場合でも、通常のプログラミングではメモリーを解放しないからです。プログラムにそのブロックに対するポインタがある場合、RTC はそのようなブロックはメモリーリークとして報告しません。
メモリーリーク検査の使用
maf
(「メモリーリーク (mel) エラー」参照)maf
(「レジスタ中のアドレス (air)」参照)maf
(「ブロック中のアドレス (aib)」参照)
注 - RTC のリーク検査を実行するには、標準のlibc
のmalloc、free、realloc
のいずれか、またはこれらの関数にもとづいた割り当て関数を使用する必要があります。
リークの可能性
RTC が「リークの可能性」として報告するエラーには 2 種類あります。1 つは、ブロックの先頭を指すポインタが検知されず、ブロックの内部を指しているポインタが見つかった場合です。これは、ブロック中のアドレス (
aib
) エラーとして報告されます。このようなブロック内部を指すポインタが見つかった場合は、プログラムに実際にメモリーリークが発生しています。ただし、プログラムによってはポインタに対して故意にそのような動作をさせている場合があり、これは当然メモリーリークではありません。RTC はこの違いを判別できないため、本当にリークが発生しているかどうかはユーザー自身の判断で行う必要があります。
もう 1 つのリークの種類は、レジスタ中のアドレス (
air
) エラーとして報告されるリークです。これは、ある領域を指すポインタがデータ空間中には存在せず、レジスタ内に存在する場合です。レジスタがブロックを不正に指していたり、古いメモリーポインタが残っている場合には、実際にメモリーリークが発生しています。ただし、コンパイラが最適化のために、ポインタをメモリーに書き込むことなく、レジスタのブロックに対して参照させることがありますが、この場合はメモリーリークではありません。プログラムが最適化され、showleaks
コマンドでエラーが報告された場合のみ、リークでない可能性があります。詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「showleaks コマンド」を参照してください。
注 - RTC リーク検査では、標準libc malloc/free/realloc
関数またはアロケータをこれらの関数に基いて使用する必要があります。ほかのアロケータについては、「RTC での修正継続機能の使用」を参照してください。
リークの検査
メモリーリーク検査がオンの場合、メモリーリークの走査は、テスト中のプログラムが終了する直前に自動的に実行されます。検出されたリークはすべて報告されます。プログラムを、
kill
コマンドによって強制的に終了してはなりません。次に、典型的なメモリーリークエラーによるメッセージを示します。
メモリーリーク (mel):
0x21718
大きさ 1024 バイト のリークのあるブロックをアドレスに発見
割り当て時のスタックの状態
foo
[1]() 行番号 63
"test.c""
[2] main() 行番号 47test.c
"「デバッグ」ウィンドウで、コールスタック場所のハイパーテキストリンクをクリックすると、エディタウィンドウのソースコード行に移動できます。
プログラムには通常
main
(FORTRAN 77 ではMAIN
) 手続きが存在します。プログラムはexit
(3) が呼び出されるか、main
から返った時点で終了します。いずれの場合でも、main
のすべての局所変数はプログラムが停止するまでスコープから出ず、それらを指す特定のヒープブロックはすべてメモリーリークとして報告されます。main() に割り当てられているヒープブロックはプログラムでは解放しないのが一般的です。これらのヒープブロックはプログラムが停止するまでスコープ内に残り、プログラムの停止後オペレーティングシステムによって自動的に解放されるためです。main() に割り当てられたブロックがメモリーリークとして報告されないようにするには、
main()
が終了する直前にブレークポイントを設定しておきます。プログラムがそこで停止したとき、RTC のshowleaks
コマンドを実行すれば、main() とそこで呼び出されるすべての手続きで参照されなくなったヒープブロックのすべてが表示されます。詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「showleaks コマンド」を参照してください。
または、「デバッグ」ウィンドウでもメモリーリークを検査できます。Sun WorkShop オンラインヘルプの「デバッグウィンドウの使い方」の「すべてのメモリーリークの表示」を参照してください。
メモリーリークの報告を理解する
リーク検査を有効にすると、プログラムの終了時にリークレポートが自動的に生成されます。
kill
コマンドでプログラムを終了した場合を除き、リークの可能性がすべて報告されます。レポートの詳細レベルは、dbx
環境変数rtc_mel_at_exit
(Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「rtc_mel_at_exit 環境変数」参照) か、「デバッグオプション」ダイアログの「自動リーク報告」オプション (Sun WorkShop オンラインヘルプの「デバッグウィンドウの使い方」の「自動リーク報告の生成」参照) で制御します。デフォルトで、非冗長リークレポートが生成されます。レポートは、リークのサイズによってソートされます。実際のメモリーリークが最初に報告され、次に可能性のあるリークが報告されます。詳細レポートには、スタックトレース情報の詳細が示されます。行番号とソースファイルが使用可能であれば、これらも必ず含まれます。
次のメモリーリークエラー情報が、2 種類の報告のどちらにも含まれます。
リークしたブロックが割り当てられた場所 リークしたブロックのアドレス リークしたブロックのサイズ 割り当て時の呼び出しスタック。 check -frames
によって制約される
「実行時検査」ウィンドウに表示される非冗長レポートでは、エラー情報がテーブルにカプセル化されますが、冗長レポートでは、エラーごとに独立したエラーメッセージが表示されます。どちらもソースコードにおけるエラー位置までのハイパーテキストリンクを備えています。
リークレポートの生成
showleaks
コマンドを使用すると、いつでもリークレポートを要求することができます。このコマンドは、前回のshowleaks
コマンド以降の新しいメモリーリークを報告するものです。詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「showleaks コマンド」を参照してください。リークレポート
リークレポートの数が多くなるのを避けるため、RTC は同じ場所で割り当てられたリークを自動的に 1 つにまとめて報告します。1 つにまとめるか、それぞれ各リークごとに報告するかは、一致フレーム数引数によって決まります。
この引数は、check -leaks
コマンドを実行する際は-match
m オプション、showleaks
コマンドを実行する際は-m
オプションで指定します。呼び出しスタックが 2 つ以上のリークを割り当てる際に m 個のフレームと一致した場合は、リークは 1 つにまとめて報告されます。以下の 3 つの呼び出しシーケンスを考えてみます。
これらのブロックがすべてメモリーリークを起こす場合、m の値によって、これらのリークを別々に報告するか、1 つのリークが繰り返されたものとして報告するかが決まります。m が 2 のとき、ブロック 1 とブロック 2 のリークは 1 つのリークが繰り返されたものとして報告されます。これは、
malloc()
の上にある 2 つのフレームが共通しているためです。ブロック 3 のリークは、c()
のトレースがほかのブロックと一致しないので別々に報告されます。m が 2 より大きい場合、RTC はすべてのリークを別々に報告します (malloc
はリークレポートでは表示されません)。一般に、m の値が小さければリークのレポートもまとめられ、m の値が大きければまとめられたリークレポートが減り、別々のリークレポートが生成されます。
メモリーリークの修正
RTC からメモリーリーク報告を受けた場合にメモリーリークを修正する方法についてのガイドラインを以下に示します。
- リークの修正で最も重要なことは、リークがどこで発生したかを判断することです。作成されるリーク報告は、リークが発生したブロックの割り当てトレースを示します。リークが発生したブロックは、ここから割り当てられたことになります。次に、プログラムの実行フローを見て、どのようにそのブロックを使用したかを調べます。
ポインタが失われた箇所が明らかな場合は簡単ですが、それ以外の場合は
showleaks
コマンドを使用してリークの検索範囲を狭くすることができます。showleaks
コマンドは、デフォルトでは前回このコマンドを実行した後に検出されたリークのみを報告するため、showleaks
を繰り返し実行することにより、ブロックがリークを起こした可能性のある範囲が狭まります。詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「showleaks コマンド」を参照してください。
メモリー使用状況検査の使用
メモリ使用状況検査は、使用中のヒープメモリーすべてを確認することができます。この情報によって、プログラムのどこでメモリーが割り当てられたか、またはどのプログラムセクションが大半の動的メモリーを使用しているかを知ることができます。この情報は、プログラムの動的メモリー消費を削減するためにも有効であり、パフォーマンスの向上に役立ちます。
メモリー使用状況検査は、パフォーマンス向上または仮想メモリーの使用制御に役立ちます。プログラムが終了したら、メモリー使用状況レポートを生成できます。メモリー使用情報は、メモリーの使用状況を表示させるコマンドを使用して、プログラムの実行中に随時入手することもできます。詳細については、Sun WorkShop オンラインヘルプのdbx コマンドの使い方」の「shownenyse コマンド」を参照してください。
メモリー使用状況検査をオンにすると、リーク検査もオンになります。プログラム終了時のリークレポートに加えて、使用中ブロック (
biu
) レポートも得ることができます。デフォルトでは、使用中ブロックの簡易レポートがプログラムの終了時に生成されます。これは、dbxenv
変数rtc_biu_at_exit
によって指定できます。(Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「rtc_biu_at_exit 環境変数」参照) か、「デバッグオプション」ダイアログの「自動リーク報告」オプション (Sun WorkShop オンラインヘルプの「デバッグウィンドウの使い方」の「自動リーク報告の生成」参照) で制御します。
showmemuse
コマンドを使用すると、メモリー使用状況レポートをいつでも要求できます。エラーの抑止
RTC はエラーレポートの数や種類を限定するよう、エラーの抑制機能を備えています。エラーが発生してもそれが抑制されている場合は、エラーは無視され、報告されずにプログラムは継続します。
エラーは
suppress
コマンド (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「suppress コマンド」参照) で抑止できます。最新のエラー報告を抑止するには、「実行時検査」ウィンドウの「最後に報告されたリークの抑止」ボタン (Sun WorkShop オンラインヘルプの「デバッグウィンドウの使い方」の「最後に報告されたエラーの抑止」参照) を使用します。
エラー抑止を取り消すには、
unsuppress
コマンド (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「unsuppress コマンド」参照) を使用します。抑止機能は同じデバッグ節内の
run
コマンドの実行期間中は有効ですが、debug
コマンドを実行すると無効になります。抑止のタイプ
スコープと種類による抑制
最新エラーの抑止
デフォルトで RTC を実行すると、最新のエラーで同じエラーが繰り返し報告されることがなくなります。この機能は、
dbx
環境変数rtc_auto_suppress
で制御します。rtc_auto_suppress
がon
のとき (デフォルト)、特定個所の特定エラーは最初の発生時にだけ報告され、その後同じエラーが同じ場所で発生しても報告が繰り返されることはありません。最新エラーを抑止すると、繰り返し実行するループに 1 つのエラーがあっても、それが何度も報告されることがなく、便利です。
エラー報告回数の制限
dbx
環境変数rtc_error_limt
では、報告されるエラーの回数を制御します。エラー制限は、アクセスエラーとリークエラーに別々に設定します。たとえば、エラー制限を 5 に設定すると、プログラムの終了時のリークレポートと、showleaks
コマンドの実行ごとに、アクセスエラーとリークエラーがそれぞれ最高で 5 回報告されます。デフォルトは 1000 です。
エラー抑止の例
次の例では、
main.cc
はファイル名、foo
とbar
は関数を示し、a.out
は実行可能ファイルの名前を示します。割り当てが関数
foo
で起こったメモリーリークは報告しません。
suppress mel in foo
libc.so.1
から割り当てられた使用中のブロック報告を抑止します。
suppress biu in libc.so.1
a.out
の非初期化機能からの読み取りを抑止します。
suppress rui in a.out
ファイル
main.cc
の非割り当てメモリーからの読み取りを報告しません。
suppress rua in main.cc
suppress duf at main.cc:10
関数
bar
のすべてのエラー報告を抑止します。
suppress all in bar
詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「suppress コマンド」を参照してください。
デフォルトの抑止
RTC では、
-g
オプション (記号) を指定してコンパイルを行わなくてもすべてのエラーを検出できます。しかし、非初期化メモリーからの読み取りなど、正確さを保証するのに 記号 (-g
) 情報が必要な特定のエラーもあります。このため、a.out
のrui
や 共有ライブラリのrui
,aib
,air
など特定のエラーは、記号情報が取得できない場合は、デフォルトで抑制されます。この動作は、suppress
やunsuppress
コマンドの-d
オプションを使用することで変更できます。
たとえば、以下を実行すると、RTC は記号情報が存在しない (
-g
オプションを指定しないでコンパイルした) コードについて「非初期化メモリーからの読み取り (rui
)」を抑制しません。
unsuppress -d rui
詳細については、Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「unsuppress コマンド」を参照してください。
抑止によるエラーの制御
プログラムが大きい場合、エラーの数もそれに従って多くなることが予想されます。このような場合は、
suppress
コマンドを使用することにより、エラーレポートの数を管理しやすい大きさまで抑制し、一度で修正するエラーを制限します。抑制するエラーの数を徐々に減らしながら、この動作を繰り返してください。たとえば、一度で検出するエラーをタイプによって制限できます。一般的によくあるエラーのタイプは
rui、rua、wua
に関連したもので、この順序で検出されます。rui
エラーはそれほど致命的なエラーではなく、このエラーが検出されてもたいていの場合プログラムは問題なく実行終了します。それに比べてrua
とwua
エラーは不正なメモリーアドレスにアクセスし、ある種のコーディングエラーを引き起こすため、問題は深刻です。まず
rui
とrua
エラーを抑制し、wua
エラーをすべて修正した後、もう一度プログラムを実行します。次にrui
エラーだけを抑制し、rua
エラーをすべて修正した後、もう一度プログラムを実行します。さらにエラーの抑制をせずに、すべてのrui
エラーを修正します。最後にプログラムを実行し、エラーがすべて修正されたことを確認してください。最新のエラー報告を抑止するには、「
suppress -last
」を実行します (または「実行時検査」ウィンドウの「最後に報告されたエラーの抑止」ボタンをクリックします)。子プロセスにおける RTC の実行
子プロセスで RTC を実行するには、
dbx
環境変数rtc_inherit
をon
に設定します。デフォルトではoff
になります (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「rtc_inherit 環境変数」参照)。
親で RTC が有効になっていて、
dbx
環境変数follow_fork_mode
がchild
に設定されているときにdbx
を実行すると子プロセスの RTC を実行できます (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「follow_fork_mode 環境変数」参照)。分岐が発生すると、
dbx
は子に RTC を自動的に実行します。プログラムがexec ()
を呼び出すと、exec ()
を呼び出すプログラムの RTC 設定がそのプログラムに渡ります。特定の時間に RTC の制御下におくことができるプロセスは 1 つだけです。次に例を示します。
接続されたプロセスへの RTC の使用
RTC は接続されたプロセスに機能します。ただし、接続されたプロセスに RTC を使用するには、
librtc.so
を事前に読み込んでおく必要があります。たとえば、librtc.so
は、Sun WorkShop のlib
ディレクトリにインストールされます。したがって、製品を/opt
にインストールすると、librtc.so
は/opt/SUNWspro/lib
にインストールされます。
librtc.so
を事前に読み込むには、次のように入力します。
%
setenv LD_PRELOAD
path-to-librtc/librtc.so
librtc.so
を常時読み込んだ状態にせず、必要なときにだけ読み込まれるようにLD_PRELOAD
を設定してください。次に例を示します。
% setenv LD_PRELOAD...
% start-your-application% unsetenv LD_PRELOAD
接続したいプログラムがフォークされるか、または別のプログラムによって実行された場合は、
LD_PRELOAD
をフォークを行うメインプログラムに設定する必要があります。LD_PRELOAD
の設定値はfork/exec
を通して継承されます。RTC での修正継続機能の使用
RTC を修正継続機能とともに使用すると、プログラミングエラーを簡単に分離して修正することができます。修正継続機能を組み合わせて使用すると、デバッグに要する時間を大幅に削減することができます。次に例を示します。
修正と継続についての詳細は、ëÊ 11 èÕを参照してください。
実行時検査アプリケーションプログラミング
インタフェースリーク検出およびアクセスの両方の検査では、共有ライブラリ
libc.so
内の標準ヒープ管理ルーチンを使用する必要があります。これは、RTC がプログラム内のすべての割り当てと開放を追跡できるためです。アプリケーションの多くは、独自のメモリー管理ルーチンをmalloc/free
にかぶせて作成したり、または最初から作成します。独自のアロケータ (専用アロケータと呼ばれる) を使用すると、RTC はそれらを自動的に追跡できません。したがって、それらの不正な使用によるリークエラーとメモリーアクセスエラーを知ることができません。ただし、RTC には専用アロケータを使用するための API があります。この API を使用すると、専用アロケータを、標準ヒープアロケータと同様に扱うことができます。API 自体はヘッダーファイル
rtc_api.h
に入っており、WorkShop の一部として出されます。マニュアルページのrtc_api(3x)
には、RTC API 入口の詳細が記載されています。専用アロケータがプログラムヒープを使用しない場合の RTC アクセスエラーレポートには小さな違いがいくつかあります。エラーレポートに、割り当て項目は含まれません。
バッチモードでの RTC の使用
bcheck(1)
は、dbx
の RTC 機能の便利なバッチインタフェースです。これは、dbx
のもとでプログラムを実行し、デフォルトにより RTC エラー出力をデフォルトファイルのprogram.errs
に入れます。
bcheck
は、メモリーリーク検査、メモリーアクセス検査、メモリー使用状況検査のいずれか、またはこのすべてを実行できます。デフォルトでは、リーク検査だけが実行されます。この使用方法の詳細については、bcheck
(1) のマニュアルページを参照してください。
bcheck
構文
bcheck
の構文は次のとおりです。
bcheck
[-access
| -all
| -leaks | -memuse
][
-o
logfile] [-q]
script
[-s]
program[
args]
-o
logfile オプションを使用すると、ログファイルに別の名前を指定することができます。-s
script オプションはプログラムの実行前に script を実行します。ファイル script に含まれるdbx
コマンドを読み取ることができます。script ファイルには通常、suppress
やdbxenv
などのコマンドが含まれていて、bcheck
によるエラー出力を調整します。
-g
オプションは、bcheck
を完全な静止状態にして、プログラムと同じ状況になります。これは、スクリプトまたはメークファイルでbcheck
を使用したい場合に便利です。
bcheck
構文
bcheck hello
mach
に引数5
を付けてアクセス検査だけを実行します。
bcheck -access mach 5
cc
に対してメモリー使用状況検査だけを静止状態で実行し、通常の終了状況で終了します。
bcheck -memuse -q cc -c prog.c
プログラムは、実行時エラーがバッチモードで検出されても停止しません。すべてのエラー出力がエラーログファイル logfile にリダイレクトされます。しかしプログラムは、ブレークポイントを検出するか、またはプログラムが割り込みを受けると停止します。
バッチモードでは、完全なスタックバックトレースが生成されて、エラーログファイルにリダイレクトされます。スタックフレームの数は、
dbxenv
変数stack_max_size
によって指定できます。ファイル logfile がすでに存在する場合、
bcheck
はそのファイルの内容を消去してから、そこに出力をリダイレクトします。
dbx
からバッチモードを直接有効化バッチモードに似たモードは、
dbx
環境変数rtc_auto_continue
とrtc_error_log_file_name
から直接有効にできます (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「rtc_auto_continue 環境変数」と「rtc_error_log_file_name 環境変数」参照)。
rtc_auto_continue
がon
に設定されていると、RTC はそのままエラーを求めて自動的に実行されます。検出したエラーは、dbx
環境変数rtc_error_log_name
で指定したファイルにリダイレクトされます (Sun WorkShop オンラインヘルプの「dbx コマンドの使い方」の「rtc_error_log_file_name 環境変数」参照)。デフォルトログファイル名は、/tmp/dbx.errorlog.
uniqueid です。すべてのエラーを端末にリダイレクトするには、rtc_error_log_file_name
環境変数を/dev/tty
に設定します。
rtc_auto_continue
はデフォルトで off です。トラブルシューティングのヒント
プログラム中でエラー検査がオンになっていて、プログラムが実行中の場合、次のエラーが検出されることがあります。
librtc.so と dbx とのバージョンが合いません。; エラー検査を休止状態にしま したこれは、RTC を接続されたプロセスに使用していて、
LD_PRELOAD
を、各自の Sun WorkShopdbx
に添付されたもの以外のlibrtc.so
バージョンに設定した場合に起こる可能性があります。これを修正するには、LD_PRELOAD
の設定値を変更してください。
パッチエリアが遠すぎます (8MB の制限); アクセス検査を休止状態にしましたRTC は、アクセス検査を有効にするためにロードオブジェクトに十分に近いパッチスペースを検出できませんでした。この章に後述される「rtc_patch_area」の項を参照してください。
RTC の 8M バイト制限
アクセス検査を実行するために、
dbx
の RTC 機能は各ロードおよびストア命令を、パッチ領域への分岐命令と置き換えます。この分岐命令の有効範囲は 8Mバイトです。これは、デバッグされたプログラムが、置き換えられた特定のロード/ストア命令の 8Mバイトのアドレス空間をすべて使いきってしまった場合、パッチ領域を保存する場所がなくなることを意味します。RTC がメモリーへのすべてのロードおよびストアにまったく割り込めない場合、RTC は正確な情報を提供できないので完全に無効になります。リークの検査は影響を受けません。
この制約にぶつかった場合、
dbx
は何らかの処置を施します。その結果、問題が修正できれば続行しますが、問題が修正できない場合は、エラーメッセージを表示しアクセス検査を終了します。8M バイトの制限値に達したら、以下の対策をとってください。
- 64 ビット SPARC V9 の代わりに 32 ビット SPARC V8 を使用します。
- トラップを使用します。
- UltraSparc ハードウェアでは、ブランチではなくトラップハンドラを呼び出す機能が
dbx
に追加されました。トラップハンドラへの制御の移行には非常に時間がかかりますが (最大で 10 分)、8M バイトの制限がなくなります。トラップハンドラを有効にするには、dbxrtc_use_traps
環境変数を使用します (「dbxenv コマンドでの dbx 環境変数の設定」参照)。ハードウェアが UltraSparc であれば、この変数は 32 ビットプログラムと 64 ビットプログラムで使用できます。ハードウェアを検査するには、システムコマンドuname -m
を使用し、結果がsun4u
であるかを確認します。- パッチ領域オブジェクトファイルを追加します。
rtc_patch_area
シェルスクリプトを使用し、大きな実行可能ファイルや共有ライブラリの中間にリンクできる特別な.o
ファイルを作成すれば、パッチ領域を拡大できます。rtc_patch_area
(1) マニュアルページを参照してください。dbx
の実行時に 8M バイト制限に達すると、大きすぎる読み込みオブジェクト (メインプログラムや共有ライブラリ) が報告され、その読み込みプロジェクトに必要なパッチ領域値が出力されます。- 最適な結果を得るには、実行可能ファイルや共有ライブラリ全体に特別なパッチオブジェクトファイルを均等に分散させ、デフォルトサイズ (8M バイト) かそれよりも小さいサイズを使用します。dbx が必要とする必要値の 10 % から 20 % の範囲を超えてパッチ領域を追加しないでください。たとえば、
dbx
が.out
に 31M バイトを要求する場合は、rtc_patch_area
スクリプトで作成したそれぞれのサイズが 8M バイトのオブジェクトファイルを 4 つ追加し、実行可能ファイル内でそれらをほぼ均等に分割します。dbx
の実行時に、実行可能ファイルに明示的なパッチ領域が見つかると、パッチ領域になっているアドレス範囲が出力されるので、リンク回線に正しく指定することができます。- 読み込みオブジェクトが大きい場合は、小さい読み込みオブジェクトに分割します。
- パッド
.so
ファイルを追加します。
- この処置は、プロセスの起動後に接続する場合にのみ必要です。
- 実行時リンカーによるライブラリの配置間隔が狭すぎてライブラリ間にパッチ領域を作成できない場合があります。RTC を on にして
dbx
が実行可能ファイルを起動すると、dbx
は実行時リンカーに対して共用ライブラリ間に新たなギャップを挿入するよう指示しますが、実行時検査を有効にしてdbx
で起動されていないプロセスに接続しても、ライブラリ間が狭すぎて対応できません。- この場合 (そしてプログラムを
dbx
で起動できない場合) は、rtc_patch_area
スクリプトで共有ライブラリを作成し、他の共有ライブラリ間でプログラムにリンクしてください。詳細については、rtc_patch_area
(1) マニュアルページを参照してください。RTC エラー
RTC で報告されるエラーは、通常はアクセスエラーとリークの 2 種類があります。
アクセスエラー
アクセス検査がオンのとき、RTC による検出と報告の対象になるのは次のタイプのエラーです。
不正解放 (
baf
) エラー意味 : 割り当てられたことのないメモリーを解放しようとした。
考えられる原因 :
free()
またはrealloc()
にヒープデータ以外のポインタを渡した。例 : char a[4]; char *b = &a[0]; free(b); /* 不正解放 (baf) */重複解放 (
duf
) エラー意味 : すでに解放されているヒープブロックを解放しようとした。
考えられる原因 : 同じポインタを使用して
free()
を 2 回以上呼び出した。C++ では、同じポインタに対して "delete" 演算子を 2 回以上使用した。char *a = (char *)malloc(1); free(a); free(a); /* 重複解放 (duf) */境界整列を誤った解放 (
maf
) エラー意味 : 境界合わせされていないヒープブロックを解放しようとした。
考えられる原因 :
free()
またはrealloc()
に正しく境界合わせされていないポインタを渡した。malloc
によって返されたポインタを変更した。char *ptr = (char *)malloc(4); ptr++; free(ptr); /* 境界整列を誤った解放 (maf) */境界整列を誤った読み取り (
mar
) エラー意味 : 適切に境界合わせされていないアドレスからデータを読み取ろうとした。
考えられる原因 : ハーフワード、ワード、ダブルワードの境界に合わせられていないアドレスから、それぞれ 2 バイト、4 バイト、8 バイトを読み取った。
char *s = "hello world"; int *i = (int *)&s[1]; int j; j = *i; /* 境界整列を誤った読み取り (mar) */境界整列を誤った書き込み (
maw
) エラー意味 : 適切に境界合わせされていないアドレスにデータを書き込もうとした。
考えられる原因 : ハーフワード、ワード、ダブルワードの境界に合わせられていないアドレスに、それぞれ 2 バイト、4 バイト、8 バイトを書き込んだ。
char *s = "hello world"; int *i = (int *)&s[1]; *i = 0; /* 境界整列を誤った書き込み (maw) */メモリー不足 (
oom
) エラー意味 : 利用可能な物理メモリーより多くのメモリーを割り当てようとした。
考えられる原因 : プログラムがこれ以上システムからメモリーを入手できない。
oom
エラーは、malloc()
からの戻り値がNULL
かどうか検査していない (プログラミングでよく起きる誤り) ために発生する問題の追跡に役立ちます。char *ptr = (char *)malloc(0x7fffffff); /* メモリー不足 (oom), ptr == NULL */非割り当てメモリーからの読み取り (
rua
) エラー意味 : 存在しないメモリー、割り当てられていないメモリー、マップされていないメモリーからデータを読み取ろうとした。
考えられる原因 : ストレイポインタ (不正な値を持つポインタ)、ヒープブロック境界のオーバーフロー、すでに解放されたヒープブロックへのアクセス。
char c, *a = (char *)malloc(1); c = a[1]; /* 非割り当てメモリーからの読み取り (rua) */非初期化メモリーからの読み取り (
rui
) エラー意味 : 初期化されていないメモリーからデータを読み取ろうとした。
考えられる原因 : 初期化されていない局所データまたはヒープデータの読み取り。
foo() { int i, j; j = i; /* 非初期化メモリーからの読み取り (rui) */ }読み取り専用メモリーへの書き込み (
wro
) エラー考えられる原因 : テキストアドレスへの書き込み、読み取り専用データセクション
(
.rodata
) への書き込み、読み取り専用としてmmap
されているページへの書き込み。foo() { int *foop = (int *) foo; *foop = 0;/* 読み取り専用メモリーへの書き込み (wro) */ }非割り当てメモリーへの書き込み (
wua
) エラー意味 : 存在しないメモリー、割り当てられていないメモリー、マップされていないメモリーにデータを書き込もうとした。
考えられる原因 : ストレイポインタ、ヒープブロック境界のオーバーフロー、すでに解放されたヒープブロックへのアクセス。
char *a = (char *)malloc(1); a[1] = `\0';/* 非割り当てメモリーへの書き込み (wua) */メモリーリークエラー
リーク検査をオンにしておくと、RTC では次のエラーが報告されます。
ブロック中のアドレス (
aib
)意味: メモリーリークの可能性がある。割り当てたブロックの先頭に対する参照はないが、そのブロック内のアドレスに対する参照が少なくとも 1 つある。
考えられる原因: そのブロックの先頭を示す唯一のポインタが増分された。
char *ptr;
main()
{ ptr = (char *)malloc(4); ptr++; /* Address in Block */ }レジスタ中のアドレス (
air
)意味: メモリーリークの可能性がある。割り当てられたブロックが解放されておらず、そのブロックに対する参照がプログラムのどこにもないが、レジスタには参照がある。
考えられる原因: コンパイラがプログラム変数をメモリーではなくレジスタにだけ保存している場合にこのエラーになる。最適化をオンにしてコンパイラを実行すると、ローカル変数や関数パラメタにこのような状況がよく発生する。最適化をオンにしていないのにこのエラーが発生する場合は、メモリーリークが疑われる。ブロックを解放する前に、割り当てられたブロックに対する唯一のポインタが範囲外を指定するとメモリーリークになる。
if (i == 0) { char *ptr = (char *)malloc(4); /* ptr is going out of scope */ } /* Memory Leak or Address in Register */メモリーリーク (
mel
) エラー意味 : 割り当てられたブロックが解放されておらず、そのブロックへの参照がプログラム内のどこにも存在しない。
考えられる原因 : プログラムが使用されなくなったブロックを解放しなかった。
ptr = (char *)malloc(1);
ptr = 0;
/* メモリーリーク (mel) */
サン・マイクロシステムズ株式会社 Copyright information. All rights reserved. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |