dbx コマンドによるデバッグ

第 9 章 実行時検査


注 -

アクセス検査は SPARC でのみ利用可能です。


実行時検査 (RTC) を使用すると、開発段階でアプリケーションの実行時エラーを自動的に検出できます。RTC では、メモリーアクセスエラーおよびメモリーリークエラーなどの実行時エラーを検出して、メモリーの使用状況を監視できます。

この章は次の各節から構成されています。

概要

RTC はコレクタを除く、ブレークポイントの設定や変数の検査などのデバッグ機能とともに使用することができます。

次に、RTC の機能を簡単に説明します。

-g フラグを付けてコンパイルすると、RTC エラーメッセージでのソース行番号の関連性が与えられます。RTC は、最適化 -O フラグによってコンパイルされたプログラムを検査することもできます。-g オプションによってコンパイルされていないプログラムについては、特殊な考慮事項があります。

RTC の詳細については、オンラインヘルプを参照してください。

RTC を使用する場合

大量のエラーが一度に検出されないようにするには、RTC を開発サイクルの初期の段階で使用します。この段階では、プログラムの構成要素となる個々のモジュールを開発します。この各モジュールを実行する単位テストを作成し、RTC を各モジュールごとに 1 回ずつ使用して検査を行います。これにより、一度に処理するエラーの数が減ります。すべてのモジュールを統合して完全なプログラムにした場合、新しいエラーはほとんど検出されません。エラー数をゼロにした後でモジュールに変更を加えた場合にのみ、RTC を再度実行してください。

必要条件

RTC を使用するには、次の要件を満たす必要があります。

制限事項

RTC は、8Mバイトより大きなプログラムのテキスト領域およびデータ領域を処理しません。

実行可能イメージに特殊ファイルを挿入すると、8Mバイトより大きなプログラムのテキスト領域とデータ領域を処理することができます。

RTC の使用

実行時検査を使用するには、使用したい検査の種類を指定します。

必要な検査モードを有効にするには、-C オプションを付けて dbx を起動します。


% dbx -C program_name

-C フラグは、RTC ライブラリの初期読み込みを強制的に実行するものです。dbx-C オプションを付けずに起動して、検査を有効にした場合、RTC ライブラリは、次に run コマンドを発行したときに読み込まれます。これにより、プログラムで必要な共有ライブラリが再読み込みされる可能性があります。-C フラグを使用すると、この再読み込みを避けることができます。


注 -

必要な検査の種類を指定してから、プログラムを実行するようにしてください。


メモリーの使用状況およびメモリーリークの検査をオンにするには、次のように入力します。


(dbx) check -memuse

メモリーアクセス検査だけをオンにするには、次のように入力します。


(dbx) check -access

メモリーリーク、メモリー使用状況、およびメモリーアクセスの各検査をオンにするには、次のように入力します。


(dbx) check -all

RTC 全体をオフにするには、次のように入力します。


(dbx) uncheck -all

検査したいプログラムを実行してください。ブレークポイントは同時に使用できます。

プログラムは正常に実行されますが、処理速度は遅くなります。これは、メモリーアクセスが起こる直前ごとに妥当性が検査されるためです。dbx は、無効なアクセスを検出すると、エラーのタイプと位置を表示します。dbxenvrtc_auto_continueon に設定されていない限り、制御はユーザーに返されます。ここで、現在のスタックトレースを表示するための where や、変数を調べるための print などの dbx コマンドを実行できます。エラーが重大エラーでなければ、cont コマンドによってプログラムを実行し続けることができます。プログラムは、次のエラーまたはブレークポイントのいずれかが検出されるまで続行されます。

rtc_auto_continueon に設定されている場合、RTC はエラーを検出し、自動的に実行し続けます。dbxenvrtc_error_log_file_name が設定されている場合には、結果はそのファイルにリダイレクトされます。

suppress コマンドを使用すると、RTC エラーの報告を制限することができます。プログラムは、次のエラーまたはブレークポイントのいずれかが検出されるまで続けられます。

ブレークポイントの設定や変数の検査など、通常のデバッグ動作をすべて実行することができます。cont コマンドは、別のエラーまたはブレークポイントが見つかるまで、あるいはプログラムが終了するまでプログラムを実行し続けます。

次の例は、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
hello のシンボル情報を読んでいます
rtld /usr/lib/ld.so.1 のシンボル情報を読んでいます
librtc.so のシンボル情報を読んでいます
libc.so.1 のシンボル情報を読んでいます
libdl.so.1 のシンボル情報を読んでいます
(dbx) check -access
アクセス検査 - ON
(dbx) check -memuse
メモリー使用状況検査 - ON
(dbx) run
実行中: hello 
(プロセス id 18306)
実行時検査を有効にしています...終了
非初期化領域からの読み取り (rui):
4 バイト読み取り を アドレス 0xeffff068 で しようとしました
    それは 96 バイト 現スタックポインタより上 です
変数は 'j' です。
現関数 :access_error
   29           i = j;
(dbx) cont

メモリーリーク検査中...

実際のリークの報告       (実際のリーク:         1  合計サイズ:      32 バイト)

合計    ブロック リーク    割り当て呼出しスタック
サイズ    数     ブロック
                 アドレス
======  ====== ==========  =======================================
    32       1    0x21aa8  memory_leak < main 
 

起こり得るリークの報告  (起こり得るリーク:       0  合計サイズ       0 バイト)
メモリー使用状況検査中...

ブロック使用量の報告     (ブロック使用量:       2  合計サイズ:      44 バイト)
 

  合計  割合 ブロック 平均   割り当て呼出しスタック
 サイズ  %     数   サイズ
======= ==== ====== ======  =======================================
     32  72%      1     32  memory_use < main 
     12  27%      1     12  memory_use < main 
 
実行完了。終了コードは、0 です

関数 access_error() は、初期化される前の変数 j を読み取ります。RTC は、このアクセスエラーを非初期化領域からの読み取り (rui) として報告します。

関数 memory_leak() は、終了する前に local を解放 (free()) しません。 memory_leak() が終了してしまうと、local がスコープ外になり、行 20 で確保したブロックがリークになります。

プログラムは、常にスコープ内にある大域変数 hello1hello2 を使用します。これらの変数はいずれも、使用中ブロック (biu) として報告される割り当て済みメモリーを動的に指します。

メモリーアクセスエラーの検出 (SPARC のみ)

RTC では、読み取り、書き込み、メモリー解放の各操作を監視することによって、プログラムがメモリーに正しくアクセスするかどうかを検査します。

プログラムは、さまざまな方法で間違ってメモリーを読み取ったり、メモリーに書き込んだりすることがあります。このようなエラーをメモリーアクセスエラーといいます。たとえば、ヒープブロックの free() 呼び出しを使用して、または関数がローカル変数にポインタを返したために、プログラムが参照するメモリーブロックの割り当てが解放されている可能性があります。アクセスエラーはプログラムでワイルドポインタの原因になり、間違った出力やセグメント不正など、プログラムの異常な動作を引き起こす可能性があります。メモリーアクセスエラーには、検出が非常に困難なものもあります。

RTC は、プログラムによって使用されているメモリーの各ブロックの情報を追跡するテーブルを管理します。プログラムがメモリー操作を行うと、RTC は関係するメモリーブロックの状態に対してその操作が有効かどうかを判断します。メモリーの状態として次のものがあります。

アクセスエラーを見つけるには、アクセス検査をオンにし、プログラムにアクセスエラーのリストを作成させるのが一番簡単な方法です。 RTC を使用してメモリーアクセスエラーを見つける方法は、コンパイラがプログラム中の構文エラーを見つける方法と似ています。いずれの場合でも、プログラム中のエラーが発生した位置と、その原因についてのメッセージとともにエラーのリストが生成され、リストの先頭から順に修正していかなければなりません。これは、あるエラーがほかのエラーと関連して連結されたような作用があるためです。連結の最初のエラーが先頭の原因となり、そのエラーを修正することにより、そのエラーから派生した他の問題も解決されることがあります。たとえば、初期化されていないメモリーの読み取りにより、不正なポインタが作成されるとします。すると、これが原因となって不正な読み取りと書き込みのエラーが発生し、それがまた原因となってさらに別の問題が発生するというようなことになる場合があります。

メモリーアクセスエラーの報告

メモリーアクセスエラーを検出すると RTC は次の情報を出力します。

種類 

エラーの種類。 

アクセス 

試みられたアクセスの種類 (読み込みまたは書き込み)。 

サイズ 

試みられたアクセスのサイズ。 

アドレス 

試みられたアクセスのアドレス 

詳細 

アドレスについてのさらに詳しい情報。たとえば、アドレスがスタックの近くに存在する場合、現在のスタックポインタからの相対位置が与えられます。アドレスが複数存在する場合、一番近いブロックのアドレス、サイズ、相対位置が与えられます。 

スタック 

エラー時の呼び出しスタック (バッチモード)。 

割り当て 

addr がヒープにある場合、最も近いヒープブロックの割り当てトレースが与えられます。

場所 

エラーが発生した位置。行が特定できる場合には、ファイル名、行番号、関数が示されます。行番号が分からないときは関数とアドレスが示されます。 

代表的なアクセスエラーは次のとおりです。


非初期化領域からの読み取り (rui):
4 バイト読み取り を アドレス 0xeffff794 で しようとしました
    それは 100 バイト 現スタックポインタより上 です
変数は 'y' です。
現関数 :sub
  41           x = y;

メモリーアクセスエラー

RTC は、以下のメモリーアクセスエラーを検出します。

各エラーと例の詳細については、この章の「RTC エラー」の項を参照してください。

メモリーリークの検査

メモリーリークとは、プログラムで使用するために割り当てられているが、プログラムのデータ領域中のいずれも指していないポインタをもつ、動的に割り当てられたメモリーブロックを言います。そのようなブロックは、メモリーのどこに存在しているかプログラムにわからないため、プログラムに割り当てられていても使用することも解放することもできません。RTC はこのようなブロックを検知し、報告します。

メモリーリークは仮想メモリーの使用を増やし、一般的にメモリーの断片化を招きます。その結果、プログラムやシステム全体のパフォーマンスが低下する可能性があります。

メモリーリークは、通常、割り当てメモリーを解放しないで、割り当てブロックへのポインタを失うと発生します。メモリーリークの例を以下に示します。


void
foo()
{
    char *s;
    s = (char *) malloc(32);
 
    strcpy(s, "hello world");
 
    return; /* s が解放されていない。foo が戻るとき、malloc されたブロック
               を指しているポインタが存在しないため、ブロックはリークする*/
}

リークは、API の不正な使用が原因で起こる可能性があります。


void
printcwd()
{

    printf("cwd = %s¥n", getcwd(NULL, MAXPATHLEN));

    return; /* libc の関数 getcwd() は、最初の引数が NULL の場合 malloc
               された領域へのポインタを返す。プログラムは、これを解放する必要
               がある。この場合、ブロックが解放されていないため、結果的にリー
               クになる。*/
}

メモリーリークを防ぐには、必要のないメモリーは必ず解放します。また、メモリーを確保するライブラリ関数を使用する場合は、メモリーを解放することを忘れないでください。

解放されていないブロックを「メモリーリーク」と呼ぶこともあります。ただし、この定義はあまり使用されません。プログラムが短時間で終了する場合でも、通常のプログラミングではメモリーを解放しないからです。プログラムにそのブロックに対するポインタがある場合、RTC はそのようなブロックはメモリーリークとして報告しません。

メモリーリーク検査の使用


注 -

RTC のリーク検査を実行するには、標準の libcmalloc、free、realloc のいずれか、またはこれらの関数にもとづいた割り当て関数を使用する必要があります。


RTC は、以下のメモリーリークエラーを検出できます。

各エラーと例の詳細については、この章に後述される「RTC エラー」の項を参照してください。

リークの可能性

RTC が「リークの可能性」として報告するエラーには 2 種類あります。1 つは、ブロックの先頭を指すポインタが検知されず、ブロックの内部を指しているポインタが見つかった場合です。これは、ブロック中のアドレス (aib) エラーとして報告されます。このようなブロック内部を指すポインタが見つかった場合は、プログラムに実際にメモリーリークが発生しています。ただし、プログラムによってはポインタに対して故意にそのような動作をさせている場合があり、これは当然メモリーリークではありません。RTC はこの違いを判別できないため、本当にリークが発生しているかどうかはユーザー自身の判断で行う必要があります。

もう 1 つのリークの種類は、レジスタ中のアドレス (air) エラーとして報告されるリークです。これは、ある領域を指すポインタがデータ空間中には存在せず、レジスタ内に存在する場合です。レジスタがブロックを不正に指していたり、古いメモリーポインタが残っている場合には、実際にメモリーリークが発生しています。ただし、コンパイラが最適化のために、ポインタをメモリーに書き込むことなく、レジスタのブロックに対して参照させることがありますが、この場合はメモリーリークではありません。プログラムが最適化され、showleaks コマンドでエラーが報告された場合のみ、リークでない可能性があります。


注 -

RTC リーク検査では、標準 libc malloc/free/realloc 関数またはアロケータをこれらの関数に基いて使用する必要があります。ほかのアロケータについては、「RTC での修正継続機能の使用」を参照してください。


リークの検査

メモリーリーク検査がオンの場合、メモリーリークの走査は、テスト中のプログラムが終了する直前に自動的に実行されます。検出されたリークはすべて報告されます。プログラムを、kill コマンドによって強制的に終了してはなりません。次に、典型的なメモリーリークエラーによるメッセージを示します。


メモリーリーク (mel):
大きさ 1024 バイト のリークのあるブロックをアドレス 0x20c20 に発見
割り当て時のスタックの状態
      [1] _getcwd() at 0xef6be224
      [2] printcwd() 行番号 7 "printcwd.c"
      [3] main() 行番号 13 "printcwd.c"

呼び出しスタック位置のハイパーテキストリンクをクリックすると、ソースコードの該当する行がエディタウィンドウに表示されます。

プログラムには通常 main (FORTRAN 77 では MAIN) 手続きが存在します。プログラムは exit(3) が呼び出されるか、main から返った時点で終了します。いずれの場合でも、main のすべての局所変数はプログラムが停止するまでスコープから出ず、それらを指す特定のヒープブロックはすべてメモリーリークとして報告されます。

main() に割り当てられているヒープブロックはプログラムでは解放しないのが一般的です。これらのヒープブロックはプログラムが停止するまでスコープ内に残り、プログラムの停止後オペレーティングシステムによって自動的に解放されるためです。main() に割り当てられたブロックがメモリーリークとして報告されないようにするには、main() が終了する直前にブレークポイントを設定しておきます。プログラムがそこで停止したとき、RTC の showleaks コマンドを実行すれば、main() とそこで呼び出されるすべての手続きで参照されなくなったヒープブロックのすべてが表示されます。

メモリーリークの報告を理解する

リーク検査機能をオンにすると、プログラムの終了時に自動リークレポートが生成されます。プログラムが kill コマンドによって強制終了されていなければ、可能性のあるリークがすべて報告されます。デフォルトでは、dbxenv 変数 rtc_mel_at_exit で指定してある簡易リークレポートが生成されます。

レポートは、リークのサイズによってソートされます。実際のメモリーリークが最初に報告され、次に可能性のあるリークが報告されます。詳細レポートには、スタックトレース情報の詳細が示されます。行番号とソースファイルが使用可能であれば、これらも必ず含まれます。

次のメモリーリークエラー情報が、2 種類の報告のどちらにも含まれます。

リークしたブロックが割り当てられた場所 

リークしたブロックのアドレス 

リークしたブロックのサイズ 

割り当て時の呼び出しスタック。check -frames によって制約される

簡易レポートはエラー情報をテーブルに出しますが、詳細レポートでは、各エラーについて個別のエラーメッセージが示されます。どちらもソースコード内のエラーの位置にハイパーテキストでリンクされます。

次に、対応する簡易メモリーリークレポートを示します。


実際のリークの報告       (実際のリーク:        1  合計サイズ:       1 バイト)

合計     ブロック  リーク      割り当て呼出しスタック
サイズ   数        ブロック
                   アドレス
======   ========  =========   =====================================
    1        1     0x20f18     mel < leaks < main 

起こり得るリークの報告  (起こり得るリーク:      1  合計サイズ      4 バイト)

合計   ブロック リーク      割り当て呼出しスタック
サイズ  数    ブロック
          アドレス
======   ========  =========   =====================================
    4       1      0x20ef8     aib < leaks < main 

次に、典型的な詳細リークレポートを示します。


実際のリークの報告       (実際のリーク:        1  合計サイズ:       1 バイト)

メモリーリーク (mel):
大きさ 1 バイト のリークのあるブロックをアドレス 0x20f18 に発見
割り当て時のスタックの状態:
        [1] mel() 行番号 19 "leaks.c"
        [2] leaks() 行番号 6 "leaks.c"
        [3] main() 行番号 7 "main.c"


起こり得るリークの報告  (起こり得るリーク:      1  合計サイズ      4 バイト)

メモリーリークの可能性 -- ブロック中のアドレス (aib):
大きさ 4 バイト のリークのあるブロックをアドレス 0x20ef8 に発見
割り当て時のスタックの状態:
        [1] aib() 行番号 11 "leaks.c"
        [2] leaks() 行番号 5 "leaks.c"
        [3] main() 行番号 7 "main.c"

リークレポートの生成

showleaks コマンドを使用すると、いつでもリークレポートを要求することができます。このコマンドは、前回の showleaks コマンド以降の新しいメモリーリークを報告するものです。

リークレポート

リークレポートの数が多くなるのを避けるため、RTC は同じ場所で割り当てられたリークを自動的に 1 つにまとめて報告します。1 つにまとめるか、それぞれ各リークごとに報告するかは、一致フレーム数引数によって決まります。この引数は、check -leaks コマンドを実行する際は -match m オプション、showleaks コマンドを実行する際は -m オプションで指定します。呼び出しスタックが 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 を繰り返し実行することにより、ブロックがリークを起こした可能性のある範囲が狭まります。

メモリー使用状況検査の使用

RTC を使用すると、使用中のヒープメモリーすべてを確認することができます。この情報によって、プログラムのどこでメモリーが割り当てられたか、またはどのプログラムセクションが大半の動的メモリーを使用しているかを知ることができます。この情報は、プログラムの動的メモリー消費を削減するためにも有効であり、パフォーマンスの向上に役立ちます。

RTC は、パフォーマンス向上または仮想メモリーの使用制御に役立ちます。プログラムが終了したら、メモリー使用状況レポートを生成できます。メモリー使用情報は、メモリーの使用状況を表示させるコマンドを使用して、プログラムの実行中に随時入手することもできます。

メモリー使用状況検査をオンにすると、リーク検査もオンになります。プログラム終了時のリークレポートに加えて、使用中ブロック (biu) レポートも得ることができます。デフォルトでは、使用中ブロックの簡易レポートがプログラムの終了時に生成されます。これは、dbxenv 変数 rtc_biu_at_exit によって指定できます。

次に、典型的な簡易メモリー使用状況レポートを示します。


ブロック使用量の報告   (ブロック使用量: 1   合計サイズ:   32 バイト)

 合計    割合 ブロック  平均     割り当て呼び出しスタック
 サイズ    %    数   サイズ
======= ==== ====== ======  =====================================
     16  40%     2      8  nonleak < main 
      8  20%     1      8  nonleak < main 
      8  20%     1      8  cyclic_leaks < main 
      8  20%     1      8  cyclic_leaks < main 

次に、対応する詳細メモリー使用状況レポートを示します。


ブロック使用量の報告   (ブロック使用量: 4   合計サイズ:   32 バイト)

ブロック使用状況 (biu):
サイズ 8 バイト のブロックをアドレス 0x25498 で見つけました (合計 25.00%)
割り当て時のスタックの状態:
     [1] nonleaks() 行番号 8 "memuse.c"
     [2] nonleaks() 行番号 11 "memuse.c"
     [3] main() 行番号 4 "main.c"

ブロック使用状況(biu):
サイズ 8 バイト のブロックをアドレス 0x25478 で見つけました (合計 25.00%)
割り当て時のスタックの状態:
     [1] nonleaks() 行番号 8 "memuse.c"
     [2] main() 行番号 4 "main.c"

ブロック使用状況 (biu):
サイズ 8 バイト のブロックをアドレス 0x254b8 で見つけました (合計 25.00%)
割り当て時のスタックの状態:
     [1] cycle_leaks() 行番号 17 "memuse.c"
     [2] main() 行番号 5 "main.c"

ブロック使用状況(biu):
サイズ 8 バイト のブロックをアドレス 0x254d8 で見つけました (合計 25.00%)
割り当て時のスタックの状態:
     [1] cycle_leaks() 行番号 18 "memuse.c"
     [2] main() 行番号 5 "main.c"

showmemuse コマンドを使用すると、メモリー使用状況レポートをいつでも要求できます。

エラーの抑止

RTC はエラーレポートの数や種類を限定するよう、エラーの抑制機能を備えています。エラーが発生してもそれが抑制されている場合は、エラーは無視され、報告されずにプログラムは継続します。

エラー抑止は、suppress コマンドを使用して実行されます。抑止は、unsuppress コマンドによって取り消すことができます。抑止は、同じデバッグセッション内の run コマンドの実行を通して持続しますが、debug コマンドでは持続されません。

次の抑制機能があります。

どのエラーを抑止するかを指定する必要があります。以下のように、プログラムのどの部分に抑制を適用するかを指定できます。

大域 

スコープが指定されていないと全体のスコープが対象になり、 すべてのプログラムに適用されます。 

ロードオブジェクト 

共有ライブラリなど、すべてのロードオブジェクトが対象になります。 

ファイル 

特定のファイルのすべての関数が対象になります。 

関数 

特定の関数が対象になります。 

行 

特定のソース行が対象になります。 

アドレス 

特定のアドレスが対象になります。 

次の例では、main.cc はファイル名、foobar は関数を示し、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

デフォルトの抑止

RTC では、-g オプション (記号) を指定してコンパイルを行わなくてもすべてのエラーを検出できます。しかし、非初期化メモリーからの読み取りなど、正確さを保証するのに 記号 (-g) 情報が必要な特定のエラーもあります。このため、a.outrui や 共有ライブラリの rui, aib, air など特定のエラーは、記号情報が取得できない場合は、デフォルトで抑制されます。この動作は、suppressunsuppress コマンドの -d オプションを使用することで変更できます。

たとえば、


unsuppress -d rui

を実行すると、RTC は記号情報が存在しない (-g オプションを指定しないでコンパイルした) コードについて「非初期化メモリーからの読み取り (rui)」を抑制しません。

抑止によるエラーの制御

プログラムが大きい場合、エラーの数もそれに従って多くなることが予想されます。このような場合は、suppress コマンドを使用することにより、エラーレポートの数を管理しやすい大きさまで抑制し、一度で修正するエラーを制限します。抑制するエラーの数を徐々に減らしながら、この動作を繰り返してください。

たとえば、一度で検出するエラーをタイプによって制限できます。一般的によくあるエラーのタイプは rui、rua、wua に関連したもので、この順序で検出されます。rui エラーはそれほど致命的なエラーではなく、このエラーが検出されてもたいていの場合プログラムは問題なく実行終了します。それに比べて ruawua エラーは不正なメモリーアドレスにアクセスし、ある種のコーディングエラーを引き起こすため、問題は深刻です。

まず ruirua エラーを抑制し、wua エラーをすべて修正した後、もう一度プログラムを実行します。次に rui エラーだけを抑制し、rua エラーをすべて修正した後、もう一度プログラムを実行します。さらにエラーの抑制をせずに、すべての rui エラーを修正します。最後にプログラムを実行し、エラーがすべて修正されたことを確認してください。

最後に報告されたエラーを抑制したい場合は、suppress -last を使用します。また報告されるエラーの数を限定したい場合は、suppress コマンドの代わりに dbxenv rtc_error_limit n を使用します。

子プロセスでの RTC の使用

dbx は、RTC が親プロセスで有効であり、dbxenv 変数 follow_fork_modechild に設定されている場合、子プロセスの実行時検査をサポートします。フォークが起こると、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
program1 のシンボル情報を読んでいます
rtld /usr/lib/ld.so.1 のシンボル情報を読んでます
librtc.so のシンボル情報を読んでいます
libc.so.1 のシンボル情報を読んでいます
libdl.so.1 のシンボル情報を読んでいます
(dbx) check -all
アクセス検査 - ON
メモリー使用状況検査 - ON
(dbx) dbxenv follow_fork_mode child
(dbx) run
実行中: program1 
(プロセス id 10710)
実行時検査を有効にしています...終了 (注 1)
非初期化領域からの読み取り (rui):
4 バイト読み取り を アドレス 0xeffff110 で しようとしました
    それは 104 バイト 現スタックポインタより上 です
変数は 'parent_j' です。
現関数 :main
   11     parent_i = parent_j;

注 1: RTC は親プロセス、program1 の最初のエラーを報告します。


(dbx) cont
dbx: 警告: フォークしました。親プロセス内での実行時検査機能は休止します (注 2)
プロセス 10710 から切り離し中<
parent: child's pid = 10711
プロセス 10711 に接続しました。
_libc_fork で停止しました アドレス 0xef6b6f78
0xef6b6f78: _libc_fork+0x0008:    bgeu    _libc_fork+0x30
現関数 :main<
   13                             child_pid = fork();
(dbx) cont
child: In child (注 3)
非初期化領域からの読み取り (rui):
4 バイト読み取り を アドレス 0xeffff7c0 で しようとしました
    それは 96 バイト 現スタックポインタより上 です
変数は 'child_j' です。
現関数 :main
   22                                             child_i = child_j;
(dbx) cont
dbx: プロセス 10711 は exec("./program2") をするところです (注 4:)
dbx: プログラム "./program2" が今 exec されました
dbx: オリジナルプログラムに戻るには "debug $oprog" を使用します
program2 のシンボル情報を読んでいます
すでに読んでいるので、ld.so.1 を飛ばします
すでに読んでいるので、librtc.so を飛ばします
すでに読んでいるので、libc.so.1 を飛ばします
すでに読んでいるので、libdl.so.1 を飛ばします
実行時検査を有効にしています...終了
main で停止しました 行番号 8  ファイル "program2.c"
    8                             printf("program2: pid = %d¥n", getpid());
(dbx) cont
program2: pid = 10711

注 2: follow_fork_modechild に設定されているため、フォークが起こると、エラー検査が親プロセスから子プロセスに切り替えられます。

注 3: RTC は子プロセスのエラーを報告します。

注 4: program2 の実行が起こると、RTC 設定値は program2 から継承されるため、アクセスおよびメモリー使用状況の検査がそのプロセスに対して有効になります。


非初期化領域からの読み取り (rui):
4 バイト読み取り を アドレス 0xeffff7fc で しようとしました
    それは 100 バイト 現スタックポインタより上 です
変数は 'program2_j' です。
現関数 :main
    9                           program2_i = program2_j;
(dbx) cont
メモリーリーク検査中...
実際のリークの報告  (実際のリーク: 1  合計サイズ:    8 バイト)

合計  ブロック リーク   割り当て呼出しスタック
サイズ 数    ブロック
         アドレス
======  ======== ==========  ============================
  8    1   0x20c50  main 

起こり得るリークの報告 (起こり得るリーク: 0 合計サイズ: 0 バイト)

実行完了。終了コードは、0 です

注 5: RTC は、実行されたプログラム、program2 のアクセスエラーを報告します。

注 6: RTC は、RTC 制御下にある間に終了したプロセス、program2 に関するメモリー使用状況レポートとメモリーリークレポートを出力します。

接続されたプロセスへの RTC の使用

RTC は、接続されたプロセスでも動作します。ただし、接続したプロセスで RTC を使用するには、そのプロセスに librtc.so を事前に読み込んでおく必要があります。librtc.so は製品の lib ディレクトリにあります (dbx のパスの ../lib。製品が /opt にインストールされている場合は、/opt/SUNWspro/lib/librtc.so)。

librtc.so を事前に読み込むには、次のように入力します。


% setenv LD_PRELOAD path-to-librtc/librtc.so

必要な場合にのみ librtc を事前に読み込むように設定し (attach の場合と同様)、常時読むことがないようにしてください。次に例を示します。


% setenv LD_PRELOAD...
% start-your-application
% unsetenv LD_PRELOAD

プロセスに接続したら、RTC を有効にすることができます。

接続したいプログラムがフォークされるか、または別のプログラムによって実行された場合は、LD_PRELOAD をフォークを行うメインプログラムに設定する必要があります。LD_PRELOAD の設定値は fork/exec を通して継承されます。

RTC での修正継続機能の使用

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  }
    15   
% cat -n bug-fixed.c
     1  #include stdio.h
     2  cahr *s = NULL;
     3   
     4  void
     5  problem()
     6  {
     7   
     8          s = (cahr *)malloc(1);
     9          *s = 'c';
    10  }
    11   
    12  main()
    13  {
    14          problem();
    15          return 0;
    16  }

% cc -g bug.c
% dbx -C a.out
a.out のシンボル情報を読んでいます
rtld /usr/lib/ld.so.1 のシンボル情報を読んでます
librtc.so のシンボル情報を読んでいます
libc.so.1 のシンボル情報を読んでいます
libdl.so.1 のシンボル情報を読んでいます
(dbx) check -access
アクセス検査 - ON
(dbx) run
実行中: a.out 
(プロセス id 10804)
実行時検査を有効にしています...終了
非割り当て領域への書き込み (wua):
1 バイト書き込み を NULL ポインタを通して しようとしました。
現関数 :problem
    7   	*s = 'c';
(dbx) pop
main で停止しました 行番号 12  ファイル "bug.c"
   12   	problem();
(dbx) cp bug-fixed.c bug.c
(dbx) fix
修正中 "bug.c" ......
 pc は "bug.c":14  に移動しました
main で停止しました 行番号 14  ファイル "bug.c"
   14   	problem();
(dbx) cont

実行完了。終了コードは、0 です
(dbx) quit
'a.out' の下記のモジュールは変更されました (修正済み):
bug.c
プログラムを再構築することをお忘れなく。

実行時検査アプリケーションプログラミングインタフェース

リーク検出およびアクセスの両方の検査では、共有ライブラリ 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 [-access | -all | -leaks | -memuse] [-o logfile] [-q] [-s script] program [args]

-o logfile オプションを使用すると、ログファイルに別の名前を指定することができます。-s script オプションはプログラムの実行前に script を実行します。ファイル script に含まれる dbx コマンドを読み取ることができます。script ファイルには通常、suppressdbxenv などのコマンドが含まれていて、bcheck によるエラー出力を調整します。

-g オプションは、bcheck を完全な静止状態にして、プログラムと同じ状況になります。これは、スクリプトまたはメークファイルで bcheck を使用したい場合に便利です。

hello に対してリーク検査だけを実行します。


bcheck hello

mach に引数 5 を付けてアクセス検査だけを実行します。


bcheck -access mach 5

cc に対してメモリー使用状況検査だけを静止状態で実行し、通常の終了状況で終了します。


bcheck -memuse -q cc -c prog.c

プログラムは、実行時エラーがバッチモードで検出されても停止しません。すべてのエラー出力がエラーログファイル logfile にリダイレクトされます。しかしプログラムは、ブレークポイントを検出するか、またはプログラムが割り込みを受けると停止します。

バッチモードでは、完全なスタックバックトレースが生成されて、エラーログファイルにリダイレクトされます。スタックフレームの数は、dbxenv 変数 stack_max_size によって指定できます。

ファイル logfile がすでに存在する場合、bcheck はそのファイルの内容を消去してから、そこに出力をリダイレクトします。

次の dbxenv 変数を設定すると、dbx から直接バッチ式モードを有効にすることもできます。


(dbx) dbxenv rtc_auto_continue on
(dbx) dbxenv rtc_error_log_file_name logfile

この設定では、実行時エラーの検出時にプログラムは停止せず、すべてのエラー出力が各自のエラーログファイルにリダイレクトされます。

トラブルシューティングのヒント

プログラム中でエラー検査がオンになっていて、プログラムが実行中の場合、次のエラーが検出されることがあります。


librtc.so と dbx とのバージョンが合いません。; エラー検査を休止状態にしました

これは、RTC を接続されたプロセスに使用していて、LD_PRELOAD を、各自の Sun WorkShop dbx に添付されたもの以外の librtc.so バージョンに設定した場合に起こる可能性があります。これを修正するには、LD_PRELOAD の設定値を変更してください。


パッチエリアが遠すぎます (8MB の制限); アクセス検査を休止状態にしました

RTC は、アクセス検査を有効にするためにロードオブジェクトに十分に近いパッチスペースを検出できませんでした。この章に後述される「rtc_patch_area」の項を参照してください。

RTC の 8M バイト制限

アクセス検査を実行するために、dbx の RTC 機能は各ロードおよびストア命令を、パッチ領域への分岐命令と置き換えます。この分岐命令の有効範囲は 8Mバイトです。これは、デバッグされたプログラムが、置き換えられた特定のロード/ストア命令の 8Mバイトのアドレス空間をすべて使いきってしまった場合、パッチ領域を保存する場所がなくなることを意味します。

RTC がメモリーへのすべてのロードおよびストアにまったく割り込めない場合、RTC は正確な情報を提供できないので完全に無効になります。リークのチェックは影響を受けません。

この制約にぶつかった場合、dbx は何らかの処置を施します。その結果、問題が修正できれば続行しますが、問題が修正できない場合は、エラーメッセージを表示しアクセス検査を終了します。

8Mバイトの制限にぶつかった場合

dbx には、8Mバイトの限界に達したユーザーのために、いくつかの回避策が用意されています。これらの回避策では、SUN WorkShop に含まれる rtc_patch_area と呼ばれるユーティリティを使用する必要があります。

このユーティリティは、ユーザーのプログラムにリンク可能なオブジェクトファイルまたは共有オブジェクトファイルを作成し、RTC が使用するパッチ領域を作成します。

実行可能イメージ内のすべてのロードおよびストア命令から 8Mバイト以内で、dbx がパッチ領域を見つけられない場合、考えられる原因は次の 2 つです。

ケース 1: 静的にリンクされた a.out ファイルが大きすぎる。

ケース 2: 動的にリンクされた共有ライブラリ (1 つ以上) が大きすぎる。

dbx はこの限界に達すると、どのくらいのパッチ領域が必要かを示すメッセージを出力して、該当するケース (ケース 1 または 2) を指示します。

ケース 1

ケース 1 では、rtc_patch_area を使用して、パッチ領域として機能する 1 つまたは複数のオブジェクトファイルを作成し、それらを a.out にリンクすることができます。

次のようなメッセージが表示されたら、以下の手順に従ってください。


実行時検査を有効にしています... dbx: warning: rtc: cannot find patch space within 8Mb (need 6490432 bytes for ./a.out)
dbx: patch area too far (8Mb limitation); Access checking disabled
         (See `help rtc8M', case 1)


rtc_patch_area -o patch.o -size 6490432 

-size フラグは任意指定です。デフォルト値は 8000000 です。

  1. エラーメッセージからのサイズ要求を満たしたら、次の手順に進みます。それ以外の場合は、手順 1 を繰り返して、さらに多くの .o ファイルを必要に応じて作成します。

  2. a.out を再リンクして、patch.o ファイルをリンク行に追加します。

  3. RTC を新しいバイナリで再試行します。

    それでも RTC が失敗する場合は、リンク行上の patch.o ファイルを再配置することができます。

代替手段として、a.out をより小さい a.out と共有ライブラリに分割する方法があります。

ケース 2

RTC を接続されたプロセスで実行する場合は、ケース 2a を参照してください。それ以外の場合、唯一の対策は、余分なパッチスペースを持つ共有ライブラリの再作成です。

次のようなメッセージが表示されたら、以下の手順に従ってください。


実行時検査を有効にしています... dbx: warning: rtc: cannot find patch space within 8Mb (need 563332 bytes for ./sh1.so)
dbx: patch area too far (8Mb limitation); Access checking disabled
         (See `help rtc8M', case 2)


rtc_patch_area -o patch.o -size 563332

-size フラグは任意指定です。デフォルト値は 8000000 です。

  1. エラーメッセージからのサイズ要求を満たしたら、次の手順に進みます。それ以外の場合は、手順 1 を繰り返して、さらに多くの .o ファイルを必要に応じて作成します。

  2. sh1.so を再リンクして、patch.o ファイルをリンク行に追加します。

  3. RTC を新しいバイナリで再試行します。dbx が、別の共有ライブラリに対してパッチスペースを要求する場合は、そのライブラリについて、手順 1 〜 手順 2 を繰り返します。

ケース 2a

ケース 2a では、dbx によって要求される共有ライブラリのパッチスペースが 8Mバイト以下の場合、rtc_patch_area を使用して、パッチ領域として機能する共有ライブラリを作成し、それを a.out にリンクすることができます。

次のようなメッセージが表示されたら、以下の手順に従ってください。


実行時検査を有効にしています... dbx: warning: rtc: cannot find patch space within 8Mb (need 563332 bytes for ./sh1.so)
dbx: patch area too far (8Mb limitation); Access checking disabled
         (See `help rtc8M', case 2)


rtc_patch_area -so patch1.so -size 563332

-size フラグは任意指定です。デフォルト値は 8000000 です。

  1. リンク行で sh1.so の隣に patch1.so を置いてプログラムを再リンクします。dbx が同じ sh1.so に対してさらにパッチスペースを要求する場合は、patch1.so を sh1.so の直前に入れてみてください。

    必要な共有ライブラリ順序を得るには、ld -l オプションではなくフルパス名を使用する必要があります。

  2. 新しいバイナリで RTC を再試行します。dbx が、別の共有ライブラリに対してパッチスペースを要求する場合は、そのライブラリについて手順 1〜2 を繰り返します。

    dbx によって要求されるパッチスペースが、指定の共有ライブラリごとに 8Mバイトを超える場合は、上記のケース 2 の手順に従ってください。

rtc_patch_area

rtc_patch_area は、オブジェクトファイルや共有ライブラリファイルを作成するシェルスクリプトです。これらのファイルを使用してユーザーのプログラムにリンクし、大きなテキスト、データ、または bss イメージを使用するプログラムにパッチ領域を追加することができます。

作成されるオブジェクトファイル (共有ライブラリ) には、指定サイズ、または指定しなかった場合は 8000000 の RTC パッチ領域が含まれます。

結果のオブジェクトファイル (または共有ライブラリ) の名前は、標準出力に書き込まれます。-o または -so オプションのいずれかを使用する必要があります。

作成する共有ライブラリの名前を指定します。この名前は標準出力に書き込まれます。


-so sharedlibname

作成するオブジェクトファイルの名前を指定します。この名前は標準出力に書き込まれます。


-o objectname

size バイトのパッチ領域を作成します (デフォルト値で、かつ適切な最大値は 8000000 です)。


-size size

cc または acc の代わりに compiler を使用してオブジェクトファイルを構築します。


-cc compiler

例:

標準の 8Mバイトのパッチ領域のオブジェクトファイルを作成します。


rtc_patch_area -o patch.o

100,000 バイトのパッチを含むオブジェクトファイルを作成します。


rtc_patch_area -size 100000 -o patch.o

1Mバイトのパッチ領域の共有ライブラリを作成します。


rtc_patch_area -so rtc1M.so -size 1000000

コマンド参照

check|uncheck

以下に、checkuncheck の形式を示します。

RTC の現在の状態を出力します。


check

アクセス検査を行います。


check -access

リーク検査を行います。


check -leaks [-frames n] [-match m]

-frames n

リークしたブロックの割り当て追跡を表示するときのスタックフレームの最大数 n を指定します。

-match m

リークをまとめるのに使用します。呼び出しスタックが 2 つ以上のリークを割り当てる際に m 個のフレームと一致した場合は、リークは 1 つにまとめて報告されます。

n のデフォルト値は 8 か m の値 (どちらか大きい方) になり、最大値は 16 です。m のデフォルト値は 2 です。

check -memuse には check -leaks の意味も含まれています。プログラム終了時には、リークレポートに加えて、メモリー使用状況レポートも出力されます。プログラム実行中に、プログラムのメモリーがどこに割り当てられているかをいつでも見ることができます。

メモリー使用状況検査をオンにするには、次のように入力します。


check -memuse [-frames n] [-match m]

-frames n

使用中ブロックの割り当てトレースを示すときに、最大 n 個の明確なスタックフレームが一覧表示されます。

-match m

プログラム内に多数の使用中ブロックが存在する可能性があるため、RTC は、同じ実行トレースから割り当てられたブロックを自動的に 1 つのレポートに結合します。レポートを結合するための決定は、m の値によって指定されます。複数ブロックの割り当て時の呼び出しスタックが m フレームと一致する場合、これらのブロックは、結合された使用中ブロックレポートで報告されます。ブロックの結合方法は、リークレポートで使用される方式と似ています。

n のデフォルト値は 8 または m の値のどちらか大きい方です。n の最大値は 16 です。m のデフォルト値は 2 です。

次の 2 つのコマンドは同じ結果になります。


check -all [-frames n] [-match m]
check -access ; check -memuse [-frames n] [-match m]

アクセス検査をオフにします。


uncheck -access

リーク検査をオフにします。


uncheck -leaks

メモリー使用状況検査をオフにします (リーク検査もオフになります)。


uncheck -memuse

次の 2 つのコマンドは同じ結果になります。


uncheck -all
uncheck -access; uncheck -memuse

次の 2 つのコマンドは同じ結果になります。


uncheck [funcs] [files] [loadobjects]
suppress all in funcs files loadobjects

check コマンドの次の形式を使用すると、特定の関数の集合 function*、モジュール file*、ロードオブジェクト loadobject* 内だけで検査をオンにし、プログラムの残りの部分では検査をオフにすることができます。


check function* file* loadobject*

このコマンドは、次と同じ働きをします。


suppress all
unsuppress all in function* file* loadobject*

check コマンドは累計的に機能します。たとえば、次の初めの 3 つのコマンドは、その後の 4 つのコマンドと同じ結果になります。


check main
check foo
check f.c

suppress all
unsuppress all in main
unsuppress all in foo
unsuppress all in f.c

suppress all コマンドは一度だけ適用され、mainfoof.c では検査がオンのままになります。

次の 2 つのコマンドは同じ結果になります。


uncheck function* file* loadobject
suppress all in function* file* loadobject*

showblock


showblock -a addr

メモリー使用状況検査またはメモリーリーク検査がオンになっている場合、showblock は、アドレス addr にあるヒープブロックに関する詳細を示します。この詳細には、ブロックの割り当て位置とそのサイズが含まれます。

showleaks

最後の showleaks コマンド以降の新しいメモリーリークを報告するには、次のように入力します。


showleaks [-a] [-m m] [-n num] [-v]

デフォルトの簡易レポートでは、リークレコードごとに 1 行のレポートが出力されます。実際のリークに続けて可能性のあるリークが報告されます。レポートは、リークの結合サイズに従ってソートされます。

-a

これまでに発生したリークがすべて表示されます (前回の showleaks コマンドを実行した後のものだけではありません)。

-m m

リークをまとめるのに使用します。呼び出しスタックが 2 つ以上のリークを割り当てる際に m 個のフレームと一致した場合は、リークは 1 つにまとめて報告されます。-m オプションを指定すると、check コマンドで指定した m の大域値が無効になります。m のデフォルト値は 2 または check コマンドで大域値として指定した最新の値のどちらかです。

-n num

最大 num のレコードをレポートに示します。デフォルトでは、すべてのレコードが表示されます。

-v

詳細出力を生成します。デフォルトでは、簡易出力が示されます。 

showmemuse

このコマンドを実行すると、使用中のメモリーブロックがすべて表示されます。


showmemuse [-a] [-m m] [-n num] [-v]

使用中のブロックの上位 num 個をサイズでソートして報告します。前回 showmemuse コマンドを実行した後のブロックのみ表示されます。

-a

現時点までの使用中ブロックすべてを示します。 

-m m

使用中ブロックレポートの結合に使用します。複数ブロックの割り当て時の呼び出しスタックが m フレームに一致する場合、それらのブロックは単一の結合レポートで報告されます。-m オプションは、check コマンドに指定された m の大域値を無効にします。m のデフォルト値は 2 か、または check で最後に指定された大域値になります。

-n num

最大 num 個のレコードをレポートに示します。デフォルトでは 20 個のレコードが示されます。

-v

詳細出力を生成します。デフォルトでは、簡易出力が示されます。 

メモリー使用のチェックをオンにすると、プログラムの終了時 showmemuse -a -n 20 が暗黙的に実行されます。rtc_biu_at_exit dbxenv 変数を使用することにより、プログラムの終了時に詳細報告を出力できます。

suppress|unsuppress

ロードオブジェクト loadobject 中のファイルは、-g 付きでコンパイルされないことがあります。これは、そのファイル中の関数にデバッグ情報がないことを表します。RTC はこのような場合にデフォルトの抑止を使用します。location_specifier は位置指定子です。

デフォルトの抑止の一覧を得るには、


{suppress | unsuppress} -d

1 つのロードオブジェクトについてデフォルトの抑止を変更するには、


{suppress | unsuppress} -d [error type] [in loadobject]

すべてのロードオブジェクトについてデフォルトの抑止を変更するには、


{suppress | unsuppress} -d [error type]

設定をデフォルトに戻すには、


suppress -reset

次のコマンドは、最新エラーを抑止または抑止解除するために使用します。このコマンドは、アクセスエラーにのみ適用され、リークエラーには適用されません。


{suppress | unsuppress} -last

-d および -reset コマンドを含まない抑止コマンドの履歴を表示するには、次のように入力します。


{suppress | unsuppress}

指定位置の指定エラータイプについてのエラーレポートをオンまたはオフにします。


{suppress | unsuppress} [ error type... [ location_specifier ]]

id によって指定された suppress または unsuppress イベントを削除するには、次のように入力します。


suppress -r id...

suppress によって指定された抑止イベントと抑止解除イベントをすべて削除するには、次のように入力します。


suppress -r [0 | all | -all]

エラータイプの位置指定子

次の表にエラータイプの位置指定子を示します。

in loadobject

指定したプログラムやライブラリに含まれるすべての関数 (*) 

in file

指定のファイル内のすべての関数 

in function

指定の関数 

at line specifier

ソース内の特定の行 

addr address

指定の 16 進アドレス 

ロードオブジェクトの一覧を参照したい場合は、dbx のプロンプトで loadobjects と入力します。位置指定子が指定されない場合は、プログラムは大域的に検索されます。位置指定子はコマンドごとに使用できます。

RTC エラー

ブロック中のアドレス (aib)

意味 : メモリーリークの可能性がある。割り当てブロックの先頭への参照はないが、そのブロック内のアドレスへの参照が少なくとも 1 つ存在する。 

考えられる原因 : そのブロックの先頭を指す唯一のポインタが増分された。 

例 :


char *ptr;
main()
{
       ptr = (char *)malloc(4);
       ptr++;           /* ブロック中のアドレス (aib) */
}

レジスタ中のアドレス (air)

意味 : メモリーリークの可能性がある。割り当てられたブロックが解放されておらず、そのブロックへの参照がプログラム内のどこにも存在しない。 

考えられる原因 : 割り当てられているブロックへの唯一の参照は、レジスタ内に含まれています。このようなことは、コンパイラがプログラム変数をメモリー内ではなくレジスタ内だけに保持している場合に発生し、その動作は正しいこともあります。最適化がオンになっている場合、コンパイラは局所変数や関数パラメータに対してこのような操作をよく行います。最適化がオンになっていないときにこのエラーが発生した場合は、実際のメモリーリークが考えられます。たとえば、割り当てられているブロックへの唯一のポインタが、そのブロックの解放前にスコープ外に出た場合にこのエラーが発生します。 

例 :


      if (i == 0) {
            char *ptr = (char *)malloc(4);
            /* ポインタがスコープ外に出た */
      }
    /* メモリーリークまたはレジスタ中のアドレス (air) */

不正解放 (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) */

メモリーリーク (mel)

意味 : 割り当てられたブロックが解放されておらず、そのブロックへの参照がプログラム内のどこにも存在しない。 

考えられる原因 : プログラムが使用されなくなったブロックを解放しなかった。 

例 :


char *ptr;
   ptr = (char *)malloc(1);
   ptr = 0;
/* メモリーリーク (mel) */

メモリー不足 (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) */

dbxenv 変数

RTC 環境の設定には、次の dbxenv 変数を使用してください。これらの変数のいずれかをそのデフォルト値から永続的に変更したい場合は、dbxenv コマンドを $HOME/.dbxrc ファイルに入れます。すると、RTC を使用するときに必ず、各自の指定した値が使用されます。

dbxenv rtc_auto_continue {on | off}

rtc_auto_continue on にすると、RTC はエラーを見つけたところで停止せず、実行を継続します。また、すべてのエラーは $errorlogfile で指定されたファイルにリダイレクトされます。デフォルトは off です。

dbxenv rtc_auto_suppress {on | off}

rtc_auto_suppresson にすると、特定のエラーがある場所で最初見つかったときに報告され、その後同じエラーについては抑止されます。この機能は、同じエラーに対して重複して報告されないようにするのに便利です。デフォルトは on です。

dbxenv rtc_biu_at_exit {on | off | verbose}

この変数は check -memuse が有効 (暗黙的または check -all を使ってオンにしている) の場合に使用されます。この変数の値が on の場合、プログラム終了時に簡単なメモリー使用状況 (ブロック使用状況) レポートが生成されます。デフォルトは on です。

値が verbose の場合、プログラム終了時に詳細なメモリー使用状況レポートが生成されます。値を off にすると、何も出力されません。この変数は、対話形式の showmemuse コマンドに影響を及ぼしません。

dbxenv rtc_error_log_file_name {filename}

rtc_error_log_file_name は、RTC エラーメッセージを dbx の標準出力の代わりに指定されたファイル filename にリダイレクトします。デフォルト は /tmp/dbx.errlog.uniqueid です。

実行時エラーがバッチモードで検出された場合は、プログラムは自動的に停止しません。すべてのエラー出力は filename に格納されます。プログラムは、ブレークポイントを検出するか割り込みが行われると停止します。

バッチモードでは、完全なスタックバックトレースが生成され、filename にリダイレクトされます。すべてのエラーを端末にリダイレクトするには、rtc_error_log_file_name /dev/tty に設定してください。

dbxenv rtc_error_limit n

ここで、n は RTC が報告するエラーの最大数です。このエラーの制限値は、アクセスエラーとリークエラーとで別々に使用されます。たとえば、エラーの制限値を 5 に設定すると、プログラムの実行の最後に行われるリークレポートと実行する各 showleaks コマンドの両方で、最大 5 つのアクセスエラーと最大 5 つのメモリーリークが示されます。デフォルトは 1000 です。

dbxenv rtc_mel_at_exit {on|off|verbose}

この変数は、リーク検査が有効な場合に使用されます。変数の値が on の場合、簡易メモリーリークレポートがプログラムの終了時に作成されます。変数の値が verbose だと、詳細メモリーリークレポートがプログラムの終了時に作成されます。値が off の場合、出力は生成されません。この変数は、showleaks コマンドには影響を及ぼしません。デフォルト値は on です。