実行時検査 (RTC) を行うと、開発段階においてネイティブコードアプリケーションの実行時エラー (メモリーアクセスエラー、メモリーリークなど) を自動的に検出できます。メモリーの使用状況も監視できます。Java コードでは、実行時検査を行うことはできません。
この章は次の各節から構成されています。
RTC は、統合的なデバッグ機能であり、コレクタによるパフォーマンスデータの収集時を除けば、実行時にあらゆるデバッグ機能を利用できます。
次に、RTC の機能を簡単に説明します。
メモリーアクセスエラーを検出する
メモリーリークを検出する
メモリー使用に関するデータを収集する
すべての言語で動作する
マルチスレッドコードで動作する
再コンパイル、再リンク、またはメイクファイルの変更が不要である
-g フラグを付けてコンパイルすると、RTC エラーメッセージでのソース行番号の関連性が与えられます。RTC は、最適化 -O フラグによってコンパイルされたプログラムを検査することもできます。-g オプションによってコンパイルされていないプログラムについては、特殊な考慮事項があります。
RTC を実行するには、check コマンドを使用します。
大量のエラーが一度に検出されないようにするには、開発サイクルの初期段階、すなわちプログラムの構成要素となる個々のモジュールを開発する段階で RTC を使用します。この各モジュールを実行する単位テストを作成し、RTC を各モジュールごとに 1 回ずつ使用して検査を行います。これにより、一度に処理するエラーの数が減ります。すべてのモジュールを統合して完全なプログラムにした場合、新しいエラーはほとんど検出されません。エラー数をゼロにしたあとでモジュールに変更を加えた場合にのみ、RTC を再度実行してください。
RTC を使用するには、次の要件を満たす必要があります。
libc を動的にリンクしている。
標準関数 libc malloc、free 、および realloc を利用するか、これらの関数を基づいたアロケータを使用する。RTC では、ほかのアロケータはアプリケーションプログラミングインタフェース (API) で操作します。「実行時検査アプリケーションプログラミングインタフェース」を参照してください。
完全にストリップされていないプログラム。strip -x によってストリップされたプログラムは使用できます。
実行時検査の制限については、「実行時検査の制限」を参照してください。
実行時検査を使用するには、使用したい検査の種類を指定します。
メモリー使用状況とメモリーリークの検査をオンにするには、次を入力します。
(dbx) check -memuse |
MUC か MLC がオンになっている場合、showblock コマンドを実行する、所定のアドレスにおけるヒープブロックに関する詳細情報を表示できます。この詳細情報では、ブロックの割り当て場所とサイズを知ることができます。詳細については、「showblock コマンド」を参照してください。
(dbx) check -access |
メモリーリーク、メモリー使用状況、およびメモリーアクセスの各検査をオンにするには、次のように入力します。
(dbx) check -all |
詳細については、「check コマンド」を参照してください。
(dbx) uncheck -all |
詳細については、「uncheck コマンド」を参照してください。
目的のタイプの RTC を有効にしてテストするプログラムを実行します。この場合、ブレークポイントを設定してもしなくてもかまいません。
プログラムは正常に動作しますが、それぞれのメモリーアクセスが発生する直前にその妥当性チェックが行われるため、動作速度は遅くなります。無効なアクセスを検出すると、dbx はそのエラーの種類と場所を表示します。制御はユーザーに戻ります (dbx 環境変数 rct_auto_continue が on になっている場合を除く (「dbx 環境変数の設定」を参照))。
次に、dbx コマンドを実行します。where コマンドでは現在のスタックトレースを呼び出すことができます。また print を実行すれば変数を確認できます。エラーが致命的でなければ、 cont コマンドでプログラムの処理を続行します。プログラムは次のエラーまたはブレークポイントまで、どちらか先に検出されるところまで実行されます。詳細については、「cont コマンド」を参照してください。
rtc_auto_continue 環境変数が on に設定されている場合、RTC はそのままエラーを求めて自動的に続行されます。検出したエラーは、dbx 環境変数 rtc_error_log_name で指定したファイルにリダイレクトされます (「dbx 環境変数の設定」を参照)。デフォルトログファイル名は、/tmp/dbx.errlog.uniqueid です。
RTC エラーの報告が不要な場合は、suppress コマンドを使用します。詳細については、「suppress コマンド」を参照してください。
次の例は、hello.c と呼ばれるプログラムのメモリーアクセス検査とメモリー使用状況検査をオンにする方法を示しています。
% cat -n hello.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 char *hello1, *hello2; 6 7 void 8 memory_use() 9 { 10 hello1 = (char *)malloc(32); 11 strcpy(hello1, "hello world"); 12 hello2 = (char *)malloc(strlen(hello1)+1); 13 strcpy(hello2, hello1); 14 } 15 16 void 17 memory_leak() 18 { 19 char *local; 20 local = (char *)malloc(32); 21 strcpy(local, "hello world"); 22 } 23 24 void 25 access_error() 26 { 27 int i,j; 28 29 i = j; 30 } 31 32 int 33 main() 34 { 35 memory_use(); 36 access_error(); 37 memory_leak(); 38 printf("%s\n", hello2); 39 return 0; 40 } % cc -g -o hello hello.c % dbx -C hello Reading ld.so.1 Reading librtc.so Reading libc.so.1 Reading libdl.so.1 (dbx) check -access access checking - ON (dbx) check -memuse memuse checking - ON (dbx) run Running: hello (process id 18306) Enabling Error Checking... done Read from uninitialized (rui): Attempting to read 4 bytes at address 0xeffff068 which is 96 bytes above the current stack pointer Variable is ’j’ Current function is access_error 29 i = j; (dbx) cont hello world Checking for memory leaks... Actual leaks report (actual leaks: 1 total size: 32 bytes) Total Num of Leaked Allocation call stack Size Blocks Block Address ========== ====== ========== ======================================= 32 1 0x21aa8 memory_leak < main Possible leaks report (possible leaks: 0 total size: 0 bytes) Checking for memory use... Blocks in use report (blocks in use: 2 total size: 44 bytes Total % of Num of Avg Allocation call stack Size All Blocks Size ========== ==== ====== ====== ======================================= 32 72% 1 32 memory_use < main 12 27% 1 12 memory_use < main execution completed, exit code is 0 |
関数 access_error() は、初期化される前の変数 j を読み取ります。RTC は、このアクセスエラーを非初期化領域からの読み取り (rui) として報告します。
関数 memory_leak() は、終了する前に local を解放 (free()) しません。memory_leak() が終了してしまうと、local がスコープ外になり、行 20 で確保したブロックがリークになります。
プログラムは、常にスコープ内にある大域変数 hello1 と hello2 を使用します。これらの変数はいずれも、使用中ブロック (biu) として報告される割り当て済みメモリーを動的に指します。
アクセス検査では、読み取り、書き込み、割り当て、解放の各操作を監視することによって、プログラムがメモリーに正しくアクセスするかどうかを検査します。
プログラムは、さまざまな方法で間違ってメモリーを読み取ったり、メモリーに書き込んだりすることがあります。このようなエラーをメモリーアクセスエラーといいます。たとえば、ヒープブロックの free() 呼び出しを使用したり、または関数が局所変数にポインタを返したために、プログラムが参照するメモリーブロックの割り当てが解放されている可能性があります。アクセスエラーはプログラムでワイルドポインタの原因になり、間違った出力やセグメント不正など、プログラムの異常な動作を引き起こす可能性があります。メモリーアクセスエラーには、検出が非常に困難なものもあります。
RTC は、プログラムによって使用されているメモリーの各ブロックの情報を追跡するテーブルを管理します。プログラムがメモリー操作を行うと、RTC は関係するメモリーブロックの状態に対してその操作が有効かどうかを判断します。メモリーの状態として次のものがあります。
未割り当て (初期) 状態。メモリーは割り当てられていません。この状態のメモリーはプログラムが所有していないため、読み取り、書き込み、解放のすべての操作が無効です。
割り当て済み/未初期化。メモリーはプログラムに割り当てられていますが、初期化されていません。書き込み操作と解放操作は有効ですが、初期化されていないので読み取りは無効です。たとえば、関数に入るときに、スタック上にメモリーが割り当てられますが、初期化はされません。
読み取り専用。読み取りは有効ですが、書き込みと解放は無効です。
割り当て済み/初期化済み。割り当てられ、初期化されたメモリーに対しては、読み取り、書き込み、解放のすべての操作が有効です。
RTC を使用してメモリーアクセスエラーを見つける方法は、コンパイラがプログラム中の構文エラーを見つける方法と似ています。いずれの場合でも、プログラム中のエラーが発生した位置と、その原因についてのメッセージとともにエラーのリストが生成され、リストの先頭から順に修正していかなければなりません。これは、あるエラーがほかのエラーと関連して連結されたような作用があるためです。連結の最初のエラーが先頭の原因となり、そのエラーを修正することにより、そのエラーから派生したほかの問題も解決されることがあります。
たとえば、初期化されていないメモリーの読み取りにより、不正なポインタが作成されるとします。すると、これが原因となって不正な読み取りと書き込みのエラーが発生し、それがまた原因となってさらに別の問題が発生するというようなことになる場合があります。
メモリーアクセスエラーを検出すると RTC は次の情報を出力します。
エラー |
情報 |
---|---|
type |
エラーの種類。 |
access |
試みられたアクセスの種類 (読み取りまたは書き込み)。 |
size |
試みられたアクセスのサイズ。 |
address |
試みられたアクセスのアドレス |
size |
リークしたブロックのサイズ |
detail |
アドレスについてのさらに詳しい情報。たとえば、アドレスがスタックの近くに存在する場合、現在のスタックポインタからの相対位置が与えられ ます。アドレスが複数存在する場合、一番近いブロックのアドレス、サイズ、相対位置が与えられます。 |
stack |
エラー時の呼び出しスタック (バッチモード)。 |
allocation |
addr がヒープにある場合、もっとも近いヒープブロックの割り当てトレースが与えられます。 |
location |
エラーが発生した位置。行が特定できる場合には、ファイル名、行番号、関数が示されます。行番号がわからないときは関数とアドレスが示されます。 |
代表的なアクセスエラーは次のとおりです。
Read from uninitialized (rui): Attempting to read 4 bytes at address 0xefffee50 which is 96 bytes above the current stack pointer Variable is ”j’ Current function is rui 12 i = j; |
rui (「非初期化メモリーからの読み取り (rui) エラー」を参照)
rua (「非割り当てメモリーからの読み取り (rua) エラー」を参照)
rob (「配列範囲外からの読み込み (rob) エラー」を参照)
wua (「非割り当てメモリーへの書き込み (wua) エラー」を参照)
wro (「読み取り専用メモリーへの書き込み (wro) エラー」を参照)
wob (「配列範囲外メモリーへの書き込み (wob) エラー」を参照)
mar (「境界整列を誤った読み取り (mar) エラー」を参照)
maw (「境界整列を誤った書き込み (maw) エラー」を参照)
duf (「重複解放 (duf) エラー」を参照)
baf (「不正解放 (baf) エラー」を参照)
maf (「境界整列を誤った解放 (maf) エラー」を参照)
oom (「メモリー不足 (oom) エラー」を参照)
SPARC プラットフォームでは、配列境界チェックは行いません。したがって、配列境界侵害はアクセスエラーにはなりません。
メモリーリークとは、プログラムで使用するために割り当てられているが、プログラムのデータ領域中のいずれも指していないポインタを持つ、動的に割り当てられたメモリーブロックを言います。そのようなブロックは、メモリーのどこに存在しているかプログラムにわからないため、プログラムに割り当てられていても使用することも解放することもできません。RTC はこのようなブロックを検知し、報告します。
メモリーリークは仮想メモリーの使用を増やし、一般的にメモリーの断片化を招きます。その結果、プログラムやシステム全体のパフォーマンスが低下する可能性があります。
メモリーリークは、通常、割り当てメモリーを解放しないで、割り当てブロックへのポインタを失うと発生します。メモリーリークの例を次に示します。
void foo() { char *s; s = (char *) malloc(32); strcpy(s, "hello world"); return; /* no free of s. Once foo returns, there is no */ /* pointer pointing to the malloc’ed block, */ /* so that block is leaked. */ } |
リークは、API の不正な使用が原因で起こる可能性があります。
void printcwd() { printf("cwd = %s\n", getcwd(NULL, MAXPATHLEN)); return; /* libc function getcwd() returns a pointer to */ /* malloc’ed area when the first argument is NULL, */ /* program should remember to free this. In this */ /* case the block is not freed and results in leak.*/ } |
メモリーリークを防ぐには、必要のないメモリーは必ず解放します。また、メモリーを確保するライブラリ関数を使用する場合は、メモリーを解放することを忘れないでください。
解放されていないブロックを「メモリーリーク」と呼ぶこともあります。ただし、この定義はあまり使用されません。プログラムが短時間で終了する場合でも、通常のプログラミングではメモリーを解放しないからです。プログラムにそのブロックに対するポインタがある場合、RTC はそのようなブロックはメモリーリークとして報告しません。
mel (「メモリーリーク (mel) エラー」を参照)
air (「レジスタ中のアドレス (air)」を参照)
aib (「ブロック中のアドレス (aib)」を参照)
RTC におけるリーク検出の対象は malloc メモリーのみです。malloc を使用していないプログラムで RTC を行なってもメモリーリークは検出されません。
RTC が「リークの可能性」として報告するエラーには 2 種類あります。1 つは、ブロックの先頭を指すポインタが検知されず、ブロックの内部を指しているポインタが見つかった場合です。これは、ブロック中のアドレス (aib) エラーとして報告されます。このようなブロック内部を指すポインタが見つかった場合は、プログラムに実際にメモリーリークが発生しています。ただし、プログラムによってはポインタに対して故意にそのような動作をさせている場合があり、これは当然メモリーリークではありません。RTC はこの違いを判別できないため、本当にリークが発生しているかどうかはユーザー自身の判断で行う必要があります。
もう 1 つのリークの種類は、ブロックを指すポインタがデータ空間に見つからず、ポインタがレジスタ内に見つかった場合に起こります。このケースは、「レジスタ中のアドレス (air)」エラーとして報告されます。レジスタがブロックを不正に指していたり、古いメモリーポインタが残っている場合には、実際にメモリーリークが発生しています。ただし、コンパイラが最適化のために、ポインタをメモリーに書き込むことなく、レジスタのブロックに対して参照させることがありますが、この場合はメモリーリークではありません。プログラムが最適化され、showleaks コマンドでエラーが報告された場合のみ、リークでない可能性があります。詳細については、「showleaks コマンド」を参照してください。
RTC リーク検査では、標準の libc malloc/free/realloc 関数またはこれらの関数に基づいたアロケータを使用する必要があります。ほかのアロケータについては、「実行時検査アプリケーションプログラミングインタフェース」を参照してください。
メモリーリーク検査がオンの場合、メモリーリークの走査は、テスト中のプログラムが終了する直前に自動的に実行されます。検出されたリークはすべて報告されます。プログラムを、kill コマンドによって強制的に終了しないでください。次に、典型的なメモリーリークエラーによるメッセージを示します。
Memory leak (mel): Found leaked block of size 6 at address 0x21718 At time of allocation, the call stack was: [1] foo() at line 63 in test.c [2] main() at line 47 in test.c |
UNIX プログラムには通常 main 手続き (FORTRAN 77 では MAIN) が存在します。これは、プログラムに対するトップレベルのユーザー関数です。プログラムは exit(3) が呼び出されるか、main から返った時点で終了します。いずれの場合でも、main のすべての局所変数はプログラムが停止するまでスコープから出ず、それらを指す特定のヒープブロックはすべてメモリーリークとして報告されます。
main 内の局所変数に割り当てられているヒープブロックはプログラムでは解放しないのが一般的です。なぜなら、プログラムは終了しようとしており、exit() を呼び出すことなく main から復帰するためです。これらのブロックがメモリーリークとして報告されないようにするには、main 内の最後の実行可能なソース行にブレークポイントを設定することによって、main から復帰する直前でプログラムを停止します。プログラムがそこで停止したとき、RTC の showleaks コマンドを実行すれば、main() とそこで呼び出されるすべての手続きで参照されなくなったヒープブロックのすべてが表示されます。
詳細については、「showleaks コマンド」を参照してください。
リーク検査を有効にすると、プログラムの終了時にリークレポートが自動的に生成されます。kill コマンドでプログラムを終了した場合を除き、リークの可能性がすべて報告されます。レポートの詳細レベルは、dbx 環境変数 rtc_mel_at_exit (「dbx 環境変数の設定」を参照) で制御します。デフォルトで、非冗長リークレポートが生成されます。
レポートは、リークのサイズによってソートされます。実際のメモリーリークが最初に報告され、次に可能性のあるリークが報告されます。詳細レポートには、スタックトレース情報の詳細が示されます。行番号とソースファイルが使用可能であれば、これらも必ず含まれます。
次のメモリーリークエラー情報が、2 種類の報告のどちらにも含まれます。
情報 |
内容の説明 |
---|---|
サイズ |
リークしたブロックのサイズ |
場所 |
リークしたブロックが割り当てられた場所 |
アドレス |
リークしたブロックのアドレス |
スタック |
割り当て時の呼び出しスタック。次によって制約される。check -frames |
次に、対応する簡易メモリーリークレポートを示します。
Actual leaks report (actual leaks: 3 total size: 2427 bytes) Total Num of Leaked Allocation call stack Size Blocks Block Address ========== ====== ========== ======================================= 1852 2 - true_leak < true_leak 575 1 0x22150 true_leak < main Possible leaks report (possible leaks: 1 total size: 8 bytes) Total Num of Leaked Allocation call stack Size Blocks Block Address ========== ====== ========== ======================================= 8 1 0x219b0 in_block < main |
次に、典型的な詳細リークレポートを示します。
Actual leaks report (actual leaks: 3 total size: 2427 bytes) Memory Leak (mel): Found 2 leaked blocks with total size 1852 bytes At time of each allocation, the call stack was: [1] true_leak() at line 220 in "leaks.c" [2] true_leak() at line 224 in "leaks.c" Memory Leak (mel): Found leaked block of size 575 bytes at address 0x22150 At time of allocation, the call stack was: [1] true_leak() at line 220 in "leaks.c" [2] main() at line 87 in "leaks.c" Possible leaks report (possible leaks: 1 total size: 8 bytes) Possible memory leak -- address in block (aib): Found leaked block of size 8 bytes at address 0x219b0 At time of allocation, the call stack was: [1] in_block() at line 177 in "leaks.c" [2] main() at line 100 in "leaks.c" |
showleaks コマンドを使用すると、いつでもリークレポートを要求することができます。このコマンドは、前回の showleaks コマンド以降の新しいメモリーリークを報告するものです。詳細については、「showleaks コマンド」を参照してください。
リークレポートの数が多くなるのを避けるため、RTC は同じ場所で割り当てられたリークを自動的に 1 つにまとめて報告します。1 つにまとめるか、それぞれ各リークごとに報告するかは、number-of-frames-to-match パラメータによって決まります。このパラメータは、-match m オプション (check -leaks コマンドを実行する場合)、または -m オプション (showleaks コマンドを実行する場合) で指定します。呼び出しスタックが 2 つ以上のリークを割り当てる際に m 個のフレームと一致した場合は、リークは 1 つにまとめて報告されます。
次の 3 つの呼び出しシーケンスを考えてみます。
ブロック 1 |
ブロック 2 |
ブロック 3 |
---|---|---|
[1] malloc |
[1] malloc |
[1] malloc |
[2] d() at 0x20000 |
[2] d() at 0x20000 |
[2] d() at 0x20000 |
[3] c() at 0x30000 |
[3] c() at 0x30000 |
[3] c() at 0x31000 |
[4] b() at 0x40000 |
[4] b() at 0x41000 |
[4] b() at 0x40000 |
[5] a() at 0x50000 |
[5] a() at 0x50000 |
[5] a() at 0x50000 |
これらのブロックがすべてメモリーリークを起こす場合、m の値によって、これらのリークを別々に報告するか、1 つのリークが繰り返されたものとして報告するかが決まります。m が 2 のとき、ブロック 1 とブロック 2 のリークは 1 つのリークが繰り返されたものとして報告されます。これは、malloc() の上にある 2 つのフレームが共通しているためです。ブロック 3 のリークは、c() のトレースがほかのブロックと一致しないので別々に報告されます。m が 2 よりも大きい場合、RTC はすべてのリークを別々に報告します (malloc はリークレポートでは表示されません)。
一般に、m の値が小さければリークのレポートもまとめられ、m の値が大きければまとめられたリークレポートが減り、別々のリークレポートが生成されます。
RTC からメモリーリーク報告を受けた場合にメモリーリークを修正する方法についてのガイドラインを次に示します。
リークの修正でもっとも重要なことは、リークがどこで発生したかを判断することです。作成されるリーク報告は、リークが発生したブロックの割り当てトレースを示します。リークが発生したブロックは、ここから割り当てられたことになります。
次に、プログラムの実行フローを見て、どのようにそのブロックを使用したかを調べます。ポインタが失われた箇所が明らかな場合は簡単ですが、それ以外の場合は showleaks コマンドを使用してリークの検索範囲を狭くすることができます。showleaks コマンドは、デフォルトでは前回このコマンドを実行したあとに検出されたリークのみを報告するために使用されます。showleaks を繰り返し実行することにより、ブロックがリークを起こした可能性のある範囲が狭まります。
詳細については、「showleaks コマンド」を参照してください。
メモリー使用状況検査は、使用中のヒープメモリーすべてを確認することができます。この情報によって、プログラムのどこでメモリーが割り当てられたか、またはどのプログラムセクションが大半の動的メモリーを使用しているかを知ることができます。この情報は、プログラムの動的メモリー消費を削減するためにも有効であり、パフォーマンスの向上に役立ちます。
メモリー使用状況検査は、パフォーマンス向上または仮想メモリーの使用制御に役立ちます。プログラムが終了したら、メモリー使用状況レポートを生成できます。メモリー使用情報は、メモリーの使用状況を表示させるコマンド (showmemuse) を使用して、プログラムの実行中に随時取得することもできます。詳細については、「showmemuse コマンド」を参照してください。
メモリー使用状況検査をオンにすると、リーク検査もオンになります。プログラム終了時のリークレポートに加えて、使用中ブロック (biu) レポートも得ることができます。デフォルトでは、使用中ブロックの簡易レポートがプログラムの終了時に生成されます。メモリー使用状況レポートの詳細を制御するには、dbx 環境変数 rtc_biu_at_exit (「dbx 環境変数の設定」 を参照) を使用します。
次に、典型的な簡易メモリー使用状況レポートを示します。
Blocks in use report (blocks in use: 5 total size: 40 bytes) Total % of Num of Avg Allocation call stack Size All Blocks Size ========== ==== ====== ====== ===================================== 16 40% 2 8 nonleak < nonleak 8 20% 1 8 nonleak < main 8 20% 1 8 cyclic_leaks < main 8 20% 1 8 cyclic_leaks < main |
Blocks in use report (blocks in use: 5 total size: 40 bytes) Block in use (biu): Found 2 blocks totaling 16 bytes (40.00% of total; avg block size 8) At time of each allocation, the call stack was: [1] nonleak() at line 182 in "memuse.c" [2] nonleak() at line 185 in "memuse.c" Block in use (biu): Found block of size 8 bytes at address 0x21898 (20.00% of total) At time of allocation, the call stack was: [1] nonleak() at line 182 in "memuse.c" [2] main() at line 74 in "memuse.c" Block in use (biu): Found block of size 8 bytes at address 0x21958 (20.00% of total) At time of allocation, the call stack was: [1] cyclic_leaks() at line 154 in "memuse.c" [2] main() at line 118 in "memuse.c" Block in use (biu): Found block of size 8 bytes at address 0x21978 (20.00% of total) At time of allocation, the call stack was: [1] cyclic_leaks() at line 155 in "memuse.c" [2] main() at line 118 in "memuse.c" The following is the corresponding verbose memory use report: |
showmemuse コマンドを使用すると、メモリー使用状況レポートをいつでも要求できます。
RTC はエラーレポートの数や種類を限定するよう、エラーの抑制機能を備えています。エラーが発生してもそれが抑制されている場合は、エラーは無視され、報告されずにプログラムは継続します。
エラーは suppress コマンド (「suppress コマンド」を参照) で抑止できます。
エラー抑止を取り消すには、unsuppress コマンド (「unsuppress コマンド」を参照) を使用します。
抑止機能は同じデバッグ節内の run コマンドの実行期間中は有効ですが、debug コマンドを実行すると無効になります。
どのエラーを抑止するかを指定する必要があります。次のように、プログラムのどの部分に抑制を適用するかを指定できます。
オプション |
内容の説明 |
---|---|
大域 |
スコープが指定されていないと全体のスコープが対象になり、すべてのプログラムに適用されます。 |
ロードオブジェクト |
共有ライブラリなど、すべてのロードオブジェクトが対象になります。 |
ファイル |
特定のファイルのすべての関数が対象になります。 |
関数 |
特定の関数が対象になります。 |
行 |
特定のソース行が対象になります。 |
アドレス |
特定のアドレスが対象になります。 |
デフォルトで RTC を実行すると、最新のエラーで同じエラーが繰り返し報告されることがなくなります。この機能は、dbx 環境変数 rtc_auto_suppress で制御します。rtc_auto_suppress が on のとき (デフォルト)、特定箇所の特定エラーは最初の発生時にだけ報告され、そのあと同じエラーが同じ場所で発生しても報告が繰り返されることはありません。最新エラーを抑止すると、繰り返し実行するループに 1 つのエラーがあっても、それが何度も報告されることがなく、便利です。
dbx 環境変数 rtc_error_limit では、報告されるエラーの回数を制限します。エラー制限は、アクセスエラーとリークエラーに別々に設定します。たとえば、エラー制限を 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 |
main.cc の行番号 10 での重複解放を抑止します。
suppress duf at main.cc:10 |
関数 bar のすべてのエラー報告を抑止します。
suppress all in bar |
詳細については、「suppress コマンド」を参照してください。
RTC では、-g オプション (記号) を指定してコンパイルを行わなくてもすべてのエラーを検出できます。しかし、非初期化メモリーからの読み取りなど、正確さを保証するのに 記号 (-g) 情報が必要な特定のエラーもあります。このため、a.out の rui や共有ライブラリの rui、aib、air など特定のエラーは、記号情報が取得できない場合は、デフォルトで抑制されます。この動作は、-d オプション (suppress コマンドおよび unsuppress コマンド) を使用することで変更できます。
たとえば、次を実行すると、RTC は記号情報が存在しない (-g オプションを指定しないでコンパイルした) コードについて「非初期化メモリーからの読み取り (rui)」を抑制しません。
unsuppress -d rui |
詳細については、「unsuppress コマンド」を参照してください。
プログラムが大きい場合、エラーの数もそれに従って多くなることが予想されます。このような場合は、このような場合は、suppress コマンドを使用することにより、エラーレポートの数を管理しやすい大きさまで抑制し、一度で修正するエラーを制限します。
たとえば、一度で検出するエラーをタイプによって制限できます。一般的によくあるエラーのタイプは rui、rua、wua に関連したもので、この順序で検出されます。rui エラーはそれほど致命的なエラーではなく、このエラーが検出されてもたいていの場合プログラムは問題なく実行終了します。それに比べて rua と wua エラーは不正なメモリーアドレスにアクセスし、ある種のコーディングエラーを引き起こすため、問題は深刻です。
まず rui と rua エラーを抑制し、wua エラーをすべて修正したあと、もう一度プログラムを実行します。次に rui エラーだけを抑制し、rua エラーをすべて修正したあと、もう一度プログラムを実行します。さらにエラーの抑制をせずに、すべての rui エラーを修正します。最後にプログラムを実行し、エラーがすべて修正されたことを確認してください。
最新のエラー報告を抑止するには、「suppress -last」を実行します。
子プロセスで RTC を実行するには、dbx 環境変数 rtc_inherit を on に設定します。デフォルトでは off になります (「dbx 環境変数の設定」を参照)。
dbx は、親で RTC が有効になっていて、dbx 環境変数 follow_fork_mode が child に設定されている場合、子プロセスの RTC を実行できます (「dbx 環境変数の設定」を参照)。
分岐が発生すると、dbx は子に RTC を自動的に実行します。プログラムが exec() を呼び出すと、exec() を呼び出すプログラムの RTC 設定がそのプログラムに渡ります。
特定の時間に RTC の制御下におくことができるプロセスは 1 つだけです。次に例を示します。
% cat -n program1.c 1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 5 int 6 main() 7 { 8 pid_t child_pid; 9 int parent_i, parent_j; 10 11 parent_i = parent_j; 12 13 child_pid = fork(); 14 15 if (child_pid == -1) { 16 printf("parent: Fork failed\n"); 17 return 1; 18 } else if (child_pid == 0) { 19 int child_i, child_j; 20 21 printf("child: In child\n"); 22 child_i = child_j; 23 if (execl("./program2", NULL) == -1) { 24 printf("child: exec of program2 failed\n"); 25 exit(1); 26 } 27 } else { 28 printf("parent: child’s pid = %d\n", child_pid); 29 } 30 return 0; 31 } |
% cat -n program2.c 1 2 #include <stdio.h> 3 4 main() 5 { 6 int program2_i, program2_j; 7 8 printf ("program2: pid = %d\n", getpid()); 9 program2_i = program2_j; 10 11 malloc(8); 12 13 return 0; 14 } % |
% cc -g -o program1 program1.c % cc -g -o program2 program2.c % dbx -C program1 Reading symbolic information for program1 Reading symbolic information for rtld /usr/lib/ld.so.1 Reading symbolic information for librtc.so Reading symbolic information for libc.so.1 Reading symbolic information for libdl.so.1 Reading symbolic information for libc_psr.so.1 (dbx) check -all access checking - ON memuse checking - ON (dbx) dbxenv rtc_inherit on (dbx) dbxenv follow_fork_mode child (dbx) run Running: program1 (process id 3885) Enabling Error Checking... done RTC reports first error in the parent, program1 Read from uninitialized (rui): Attempting to read 4 bytes at address 0xeffff110 which is 104 bytes above the current stack pointer Variable is ’parent_j’ Current function is main 11 parent_i = parent_j; (dbx) cont dbx: warning: Fork occurred; error checking disabled in parent detaching from process 3885 Attached to process 3886 Because follow_fork_mode is set to child, when the fork occurs error checking is switched from the parent to the child process stopped in _fork at 0xef6b6040 0xef6b6040: _fork+0x0008: bgeu _fork+0x30 Current function is main 13 child_pid = fork(); parent: child’s pid = 3886 (dbx) cont child: In child Read from uninitialized (rui): Attempting to read 4 bytes at address 0xeffff108 which is 96 bytes above the current stack pointer RTC reports an error in the child Variable is ’child_j’ Current function is main 22 child_i = child_j; (dbx) cont dbx: process 3886 about to exec("./program2") dbx: program "./program2" just exec’ed dbx: to go back to the original program use "debug $oprog" Reading symbolic information for program2 Skipping ld.so.1, already read Skipping librtc.so, already read Skipping libc.so.1, already read Skipping libdl.so.1, already read Skipping libc_psr.so.1, already read When the exec of program2 occurs, the RTC settings are inherited by program2 so access and memory use checking are enabled for that process Enabling Error Checking... done stopped in main at line 8 in file "program2.c" 8 printf ("program2: pid = %d\n", getpid()); (dbx) cont program2: pid = 3886 Read from uninitialized (rui): Attempting to read 4 bytes at address 0xeffff13c which is 100 bytes above the current stack pointer RTC reports an access error in the executed program, program2 Variable is ’program2_j’ Current function is main 9 program2_i = program2_j; (dbx) cont Checking for memory leaks... RTC prints a memory use and memory leak report for the process that exited while under RTC control, program2 Actual leaks report (actual leaks: 1 total size: 8 bytes) Total Num of Leaked Allocation call stack Size Blocks Block Address ========== ====== ========== ==================================== 8 1 0x20c50 main Possible leaks report (possible leaks: 0 total size: 0 bytes) execution completed, exit code is 0 |
実行時検査は、影響を受けるメモリーがすでに割り当てられている場合に RUI が検出できなかった例外を伴う接続済みプロセスで機能します。ただし、実行時検査を開始する際、rtcaudit.so を事前に読み込んでおく必要があります。接続先のプロセスが 64 ビットプロセスである場合、次の場所にある 64 ビットの rtcaudit.so を使用します。
64 ビット SPARC プラットフォームの /installation_directory/lib/dbx/sparcv9/runtime/rtcaudit.so
AMD64 プラットフォームの /installation_directory/lib/dbx/amd64/runtime/rtcaudit.so
32 ビットプラットフォームの /installation_directory/lib/dbx/runtime/rtcaudit.so
rtcaudit.so を事前に読み込むには、次のように入力します。
% setenv LD_AUDIT path-to-rtcaudit/rtcaudit.so |
rtcaudit.so を常時読み込んだ状態にせず、必要なときにだけ読み込まれるように環境変数 LD_AUDIT を設定してください。次に例を示します。
% setenv LD_AUDIT... % start-your-application % unsetenv LD_AUDIT |
プロセスに接続したら、RTC を有効にすることができます。
接続したいプログラムがフォークされるか、または別のプログラムによって実行された場合は、LD_AUDIT をフォークを行うメインプログラムに設定する必要があります。LD_AUDIT の設定値は、フォーク先および実行主体を問わず継承されます。
環境変数 LD_AUDIT は 32 ビットプログラムと 64 ビットプログラムの両方に適用されるため、64 ビットプログラムを実行する 32 ビットプログラム用、または 32 ビットプログラムを実行する 64 ビットプログラム用に正しいライブラリを選択することが困難です。Solaris OS のバージョンによっては、環境変数 LD_AUDIT_32 をサポートしているものと環境変数 LD_AUDIT_64 をサポートしているものがあり、それぞれ 32 ビットプログラムと 64 ビットプログラムのみを対象としています。実行している Solaris OS のバージョンで、これらの変数がサポートされているかどうか確認するには、『リンカーとライブラリ』を参照してください。
RTC を修正継続機能とともに使用すると、プログラミングエラーを簡単に分離して修正することができます。修正継続機能を組み合わせて使用すると、デバッグに要する時間を大幅に削減することができます。次に例を示します。
% cat -n bug.c 1 #include stdio.h 2 char *s = NULL; 3 4 void 5 problem() 6 { 7 *s = ’c’; 8 } 9 10 main() 11 { 12 problem(); 13 return 0; 14 } % cat -n bug-fixed.c 1 #include stdio.h 2 char *s = NULL; 3 4 void 5 problem() 6 { 7 8 s = (char *)malloc(1); 9 *s = ’c’; 10 } 11 12 main() 13 { 14 problem(); 15 return 0; 16 } yourmachine46: cc -g bug.c yourmachine47: dbx -C a.out Reading symbolic information for a.out Reading symbolic information for rtld /usr/lib/ld.so.1 Reading symbolic information for librtc.so Reading symbolic information for libc.so.1 Reading symbolic information for libintl.so.1 Reading symbolic information for libdl.so.1 Reading symbolic information for libw.so.1 (dbx) check -access access checking - ON (dbx) run Running: a.out (process id 15052) Enabling Error Checking... done Write to unallocated (wua): Attempting to write 1 byte through NULL pointer Current function is problem 7 *s = ’c’; (dbx) pop stopped in main at line 12 in file "bug.c" 12 problem(); (dbx) #at this time we would edit the file; in this example just copy the correct version (dbx) cp bug-fixed.c bug.c (dbx) fix fixing "bug.c" ...... pc moved to "bug.c":14 stopped in main at line 14 in file "bug.c" 14 problem(); (dbx) cont execution completed, exit code is 0 (dbx) quit The following modules in \Qa.out’ have been changed (fixed): bug.c Remember to remake program. |
修正と継続についての詳細は、「メモリーリーク (mel) エラー」を参照してください。
リーク検出およびアクセスの両方の検査では、共有ライブラリ libc.so 内の標準ヒープ管理ルーチンを使用する必要があります。これは、RTC がプログラム内のすべての割り当てと解放を追跡できるためです。アプリケーションの多くは、独自のメモリー管理ルーチンを malloc() または free() 関数にかぶせて作成するか、最初から作成します。独自のアロケータ (専用アロケータと呼ばれる) を使用すると、RTC はそれらを自動的に追跡できません。したがって、それらの不正な使用によるリークエラーとメモリーアクセスエラーを知ることができません。
ただし、RTC には専用アロケータを使用するための API があります。この API を使用すると、専用アロケータを、標準ヒープアロケータと同様に扱うことができます。API 自体はヘッダーファイル rtc_api.h に入っており、Oracle Solaris Studio ソフトウェアの一部として配布されます。マニュアルページの rtc_api(3x) には、RTC API 入口の詳細が記載されています。
専用アロケータがプログラムヒープを使用しない場合の RTC アクセスエラーレポートには小さな違いがいくつかあります。標準ヒープブロックを参照するメモリーアクセスエラーが発生した場合、エラーレポートには通常、ヒープブロック割り当ての位置が含まれます。専用アロケータがプログラムヒープを使用しない場合、エラーレポートには割り当て項目が含まれない場合があります。
libumem 内のメモリーアロケータを追跡するために RTC API を使用することは、必須ではありません。RTC は libumem ヒープ管理ルーチンに割り込み、それらを対応する libc 関数にリダイレクトします。
bcheck(1) は、dbx の RTC 機能の便利なバッチインタフェースです。これは、dbx のもとでプログラムを実行し、デフォルトにより RTC エラー出力をデフォルトファイルの program.errs に入れます。
bcheck は、メモリーリーク検査、メモリーアクセス検査、メモリー使用状況検査のいずれか、またはこのすべてを実行できます。デフォルトでは、リーク検査だけが実行されます。この使用方法の詳細については、bcheck(1) のマニュアルページを参照してください。
64 ビット Linux OS を実行しているシステムで bcheck ユーティリティーを実行するには、その前に環境変数 _DBX_EXEC_32 を設定する必要があります。
bcheck [-V] [-access | -all | -leaks | -memuse] [-xexec32] [-o logfile] [-q] [-s script] program [args] |
-o logfile オプションを使用すると、ログファイルに別の名前を指定することができます。プログラムの実行前に -s script オプションを使用して、script ファイルに含まれる dbx コマンドを読み取ります。script ファイルには通常、suppress や dbxenv などのコマンドが含まれていて、bcheck によるエラー出力を調整します。
-q オプションは、bcheck を完全な静止状態にして、プログラムと同じ状況になります。これは、スクリプトまたはメイクファイルで bcheck を使用したい場合に便利です。
hello に対してリーク検査だけを実行します。
bcheck hello |
mach に引数 5 を付けてアクセス検査だけを実行します。
bcheck -access mach 5 |
cc に対してメモリー使用状況検査だけを静止状態で実行し、通常の終了状況で終了します。
bcheck -memuse -q cc -c prog.c |
プログラムは、実行時エラーがバッチモードで検出されても停止しません。すべてのエラー出力がエラーログファイル logfile にリダイレクトされます。しかしプログラムは、ブレークポイントを検出するか、またはプログラムが割り込みを受けると停止します。
バッチモードでは、完全なスタックバックトレースが生成されて、エラーログファイルにリダイレクトされます。スタックフレームの数は、dbx 環境変数 stack_max_size によって制御できます。
ファイル logfile がすでに存在する場合、bcheck はそのファイルの内容を消去してから、そこに出力をリダイレクトします。
バッチモードに似たモードを、直接 dbx から有効にすることもできます。具体的には、dbx 環境変数 rtc_auto_continue および rtc_error_log_file_name を設定します (「dbx 環境変数の設定」を参照)。
rtc_auto_continue が on に設定されていると、RTC はそのままエラーを求めて自動的に実行されます。検出したエラーは、dbx 環境変数 rtc_error_log_name で指定したファイルにリダイレクトされます (「dbx 環境変数の設定」を参照)。デフォルトログファイル名は、/tmp/dbx.errlog.uniqueid です。すべてのエラーを端末にリダイレクトするには、rtc_error_log_file_name 環境変数を /dev/tty に設定します。
rtc_auto_continue はデフォルト値は、off です。
プログラム中でエラー検査がオンになっていて、プログラムが実行中の場合、次のエラーが検出されることがあります。
librtc.so と dbx とのバージョンが合いません。エラー検査を休止状態にしました
これは、RTC を接続されたプロセスに使用していて、LD_AUDIT を、各自の Oracle Solaris Studio dbx に添付されたもの以外の rtcaudit.so バージョンに設定した場合に起こる可能性があります。これを修正するには、LD_AUDIT の設定値を変更してください。
パッチエリアが遠すぎます (8M バイトの制限); アクセス検査を休止状態にしました
RTC は、アクセス検査を有効にするためにロードオブジェクトに十分に近いパッチスペースを検出できませんでした。次の「実行時検査の制限」を参照してください。
実行時検査には次の制限があります。
アクセス検査では、ロードオブジェクトにいくつかのシンボル情報が必要です。ロードオブジェクトが完全に削除されている場合、実行時検査ですべてのエラーをキャッチできないことがあります。初期化されていないメモリーからの読み取りエラーは正しくない可能性があるため、抑止されます。この抑止は unsuppress rui コマンドを使用して上書きできます。シンボルオブジェクトのシンボルテーブルを取得するには、ロードオブジェクトを削除する際に -x オプションを使用します。
RTC は、すべての配列範囲外エラーを検出できるわけではありません。静的メモリーおよびスタックメモリーに対する範囲検査は、デバッグ情報なしでは使用できません。
実行時検査では、メモリーアクセス命令を計測してアクセス検査をします。これらの命令は、実行時に SIGSEGV ハンドラによって処理されます。実行時検査には、独自の SIGSEGV ハンドラとシグナル代替スタックが必要なため、SIGSEGV ハンドラまたは SIGALTSTACK ハンドラをインストールしようとしても無視されるか、EINVAL エラーが生成されます。
SIGSEGV ハンドラの呼び出しは入れ子にできません。入れ子にすると、エラー terminating signal 11 SEGSEGV が生成されます。このエラーが表示された場合は、rtc skippatch コマンドを使用して、影響のある関数の計測機構を飛ばします。
既存のすべてのコードを含め、8M バイト以内に十分なパッチ領域がない場合、2 つの問題が発生する可能性があります。
遅延
アクセス検査を有効にすると、dbx は各ロードおよびストア命令をパッチ領域に分岐する分岐命令に置き換えます。この分岐命令の有効範囲は 8M バイトです。デバッグされたプログラムが、置き換えられた特定のロード/ストア命令の 8M バイトのアドレス空間をすべて使いきってしまった場合、パッチ領域を保存する場所がなくなります。この場合、dbx は分岐を使用する代わりにトラップハンドラを呼び出します。トラップハンドラに制御を移行すると、実行速度が著しく (最大 10 倍) 遅くなりますが、8M バイトの制限に悩まされることはなくなります。
V8+ モードでの出力レジスタの上書きの問題
トラップハンドラの制限は、次の両方の状況に該当する場合に、アクセス検査に影響します。
デバッグするプロセスがトラップを使用して検査される。
プロセスが V8+ 命令セットを使用する。
この問題は、V8+ アーキテクチャーでの出力レジスタのサイズと入力レジスタのサイズが異なるために発生します。出力レジスタは 64 ビット長ですが、入力レジスタは 32 ビット長しかありません。トラップハンドラが呼び出されると、出力レジスタが入力レジスタにコピーされ、上位 32 ビットが失われます。そのため、デバッグするプロセスで、出力レジスタの上位 32 ビットを利用する場合に、アクセス検査が有効になると、プロセスが正常に実行しない可能性があります。
32 ビット SPARC ベースのバイナリの作成時に、デフォルトでコンパイラは V8+ アーキテクチャーを使用しますが、-xarch オプションで、V8 アーキテクチャーを使用するように指示することができます。アプリケーションを再コンパイルしてもシステム実行時ライブラリは影響を受けません。
dbx は、トラップを使用して検査すると正常に動作しない次の関数とライブラリは、計測機構を自動的に飛ばします。
server/libjvm.so
client/libjvm.so
`libfsu_isa.so`__f_cvt_real
`libfsu_isa.so`__f90_slw_c4
ただし、計測機構を飛ばすと、不正な RTC エラーが生成されることがあります。
使用しているプログラムに前述のどちらかの状況があてはまり、アクセス検査を有効にするとプログラムの動作が異なってくるようであれば、そのプログラムはトラップハンドラの制限の影響を受けている可能性があります。この制限を回避するには、次の操作を実行します。
rtc skippatch コマンド (「rtc skippatch コマンド」を参照) を使用して、前述の関数とライブラリを使用するプログラム内のコードの計測機構を飛ばします。通常、問題を追跡して関数を特定するのは困難なため、読み込みオブジェクト全体の検査を省略する場合があります。rtc showmap コマンドにより、アドレスごとの計器タイプのマップが表示されます。
64 ビット SPARC V9 の代わりに 32 ビット SPARC V8 を使用します。
可能であれば、すべてのレジスタが 64 ビット長の V9 アーキテクチャーでプログラムを再コンパイルします。
パッチ領域オブジェクトファイルを追加します。
rtc_patch_area シェルスクリプトを使用し、大きな実行可能ファイルや共有ライブラリの中間にリンクできる特別な .o ファイルを作成すれば、パッチ領域を拡大できます。rtc_patch_area(1) マニュアルページを参照してください。
dbx の実行時に 8M バイト制限に達すると、大きすぎる読み込みオブジェクト (メインプログラムや共有ライブラリ) が報告され、その読み込みプロジェクトに必要なパッチ領域値が出力されます。
最適な結果を得るには、実行可能ファイルや共有ライブラリ全体に特別なパッチオブジェクトファイルを均等に分散させ、デフォルトサイズ (8M バイト) かそれよりも小さいサイズを使用します。dbx が必要とする必要値の 10 % から 20 % の範囲を超えてパッチ領域を追加しないでください。たとえば、dbx が a.out に 31M バイトを要求する場合は、rtc_patch_area スクリプトで作成した 8M バイトのオブジェクトファイルを 4 つ追加し、実行可能ファイル内でそれらをほぼ均等に分割します。
dbx の実行時に、実行可能ファイルに明示的なパッチ領域が見つかると、パッチ領域になっているアドレス範囲が出力されるので、リンク回線に正しく指定することができます。
読み込みオブジェクトが大きい場合は、小さい読み込みオブジェクトに分割します。
実行ファイルや大きなライブラリ内のオブジェクトファイルを小さいオブジェクトファイルグループに分割します。それらを小さいパーツにリンクします。大きいファイルが実行可能ファイルの場合、小さい実行可能ファイルと共有ライブラリに分割します。大きいファイルが共有ライブラリの場合、複数の小さいライブラリのセットに再編します。
この方法では、dbx により、異なる共有オブジェクト間でパッチコード用の領域を探すことができます。
パッド .so ファイルを追加します。
この解決方法は、プロセスの起動後に接続する場合にのみ必要です。
実行時リンカーによるライブラリの配置間隔が狭すぎてライブラリ間にパッチ領域を作成できない場合があります。RTC を on にして dbx が実行可能ファイルを起動すると、dbx は実行時リンカーに対して共有ライブラリ間に新たなギャップを挿入するよう指示しますが、実行時検査を有効にして dbx で起動されていないプロセスに接続しても、ライブラリ間が狭すぎて対応できません。
実行時ライブラリ間が狭すぎる場合 (そしてプログラムを dbx で起動できない場合) は、rtc_patch_area スクリプトで共有ライブラリを作成し、ほかの共有ライブラリ間でプログラムにリンクしてください。詳細については、rtc_patch_area(1) マニュアルページを参照してください。
RTC で報告されるエラーは、通常はアクセスエラーとリークの 2 種類があります。
アクセス検査がオンのとき、RTC による検出と報告の対象になるのは次のタイプのエラーです。
意味: 割り当てられたことのないメモリーを解放しようとした。
考えられる原因: free() または realloc() にヒープデータ以外のポインタを渡した。
次に例を示します。
char a[4]; char *b = &a[0]; free(b); /* Bad free (baf) */
意味: すでに解放されているヒープブロックを解放しようとした。
考えられる原因: 同じポインタを使用して free() を 2 回以上呼び出した。C++ では、同じポインタに対して "delete" 演算子を 2 回以上使用した。
次に例を示します。
char *a = (char *)malloc(1); free(a); free(a); /* Duplicate free (duf) */
意味: 境界合わせされていないヒープブロックを解放しようとした。
考えられる原因: free() または realloc() に正しく境界合わせされていないポインタを渡した。malloc によって返されたポインタを変更した。
次に例を示します。
char *ptr = (char *)malloc(4); ptr++; free(ptr); /* Misaligned free */
意味: 適切に境界合わせされていないアドレスからデータを読み取ろうとした。
考えられる原因: ハーフワード、ワード、ダブルワードの境界に合わせられていないアドレスから、それぞれ 2 バイト、4 バイト、8 バイトを読み取った。
次に例を示します。
char *s = “hello world”; int *i = (int *)&s[1]; int j; j = *i; /* Misaligned read (mar) */
意味: 適切に境界合わせされていないアドレスにデータを書き込もうとした。
考えられる原因: ハーフワード、ワード、ダブルワードの境界に合わせられていないアドレスに、それぞれ 2 バイト、4 バイト、8 バイトを書き込んだ。
次に例を示します。
char *s = “hello world”; int *i = (int *)&s[1]; *i = 0; /* Misaligned write (maw) */
意味: 利用可能な物理メモリーより多くのメモリーを割り当てようとした。
考えられる原因: プログラムがこれ以上システムからメモリーを入手できない。oom エラーは、malloc() からの戻り値が NULL かどうか検査していない (プログラミングでよく起きる誤り) ために発生する問題の追跡に役立ちます。
次に例を示します。
char *ptr = (char *)malloc(0x7fffffff); /* Out of Memory (oom), ptr == NULL */
意味: 配列範囲外のメモリーからデータを読み取ろうとした。
考えられる原因: 浮遊ポインタ、ヒープブロックの範囲からあふれ出ている。
次に例を示します。
char *cp = malloc (10); char ch = cp[10];
意味: 存在しないメモリー、割り当てられていないメモリー、マップされていないメモリーからデータを読み取ろうとした。
考えられる原因: ストレイポインタ (不正な値を持つポインタ)、ヒープブロック境界のオーバーフロー、すでに解放されたヒープブロックへのアクセス。
次に例を示します。
char *cp = malloc (10); free (cp); cp[0] = 0;
意味: 初期化されていないメモリーからデータを読み取ろうとした。
考えられる原因: 初期化されていない局所データまたはヒープデータの読み取り。
次に例を示します。
foo() { int i, j; j = i; /* Read from uninitialized memory (rui) */ }
意味: 配列範囲外のメモリーにデータを書き込もうとした。
考えられる原因: 浮遊ポインタ、ヒープブロックの範囲からあふれ出ている。
次に例を示します。
char *cp = malloc (10); cp[10] = 'a';
意味: 読み取り専用メモリーにデータを書き込もうとした。
考えられる原因: テキストアドレスへの書き込み、読み取り専用データセクション (.rodata) への書き込み、読み取り専用として mmap されているページへの書き込み。
次に例を示します。
foo() { int *foop = (int *) foo; *foop = 0; /* Write to read-only memory (wro) */ }
意味: 存在しないメモリー、割り当てられていないメモリー、マップされていないメモリーにデータを書き込もうとした。
考えられる原因: ストレイポインタ (不正な値を持つポインタ)、ヒープブロック境界のオーバーフロー、すでに解放されたヒープブロックへのアクセス。
次に例を示します。
char *cp = malloc (10); free (cp); cp[0] = 0;
リーク検査をオンにしておくと、RTC では次のエラーが報告されます。
意味: メモリーリークの可能性がある。割り当てたブロックの先頭に対する参照はないが、そのブロック内のアドレスに対する参照が少なくとも 1 つある。
考えられる原因: そのブロックの先頭を示す唯一のポインタが増分された。
例 :
char *ptr; main() { ptr = (char *)malloc(4); ptr++; /* Address in Block */ }
意味: メモリーリークの可能性がある。割り当てられたブロックが解放されておらず、そのブロックに対する参照がプログラムのどこにもないが、レジスタには参照がある。
考えられる原因: コンパイラがプログラム変数をメモリーではなくレジスタにだけ保存している場合にこのエラーになる。最適化をオンにしてコンパイラを実行すると、局所変数や関数パラメータにこのような状況がよく発生する。最適化をオンにしていないのにこのエラーが発生する場合は、メモリーリークが疑われる。ブロックを解放する前に、割り当てられたブロックに対する唯一のポインタが範囲外を指定するとメモリーリークになる。
次に例を示します。
if (i == 0) { char *ptr = (char *)malloc(4); /* ptr is going out of scope */ } /* Memory Leak or Address in Register */
意味: 割り当てられたブロックが解放されておらず、そのブロックへの参照がプログラム内のどこにも存在しない。
考えられる原因: プログラムが使用されなくなったブロックを解放しなかった。
次に例を示します。
char *ptr;
ptr = (char *)malloc(1); ptr = 0; /* Memory leak (mel) */