Oracle Solaris Studio 12.2 Discover および Uncover ユーザーズガイド

Discover エラーメッセージの解釈

場合によっては、Discover は実際にはエラーでないエラーを報告することがあります。そのようなケースは、擬陽性と呼ばれます。Discover は計測時にコードを分析し、同様なツールと比較して、擬陽性の発生を削減しますが、依然として発生する場合があります。次のセクションでは、Discover レポートでの擬陽性を特定し、可能であれば回避できるようにするヒントを提供します。

部分的に初期化されたメモリー

C および C++ のビットフィールドでは、コンパクトなデータ型を作成できます。次に例を示します。


struct my_struct {
  unsigned int valid : 1;
  char         c;
};

この例では、構造メンバー my_struct.valid はメモリー内の 1 ビットのみ取得します。ただし、SPARC プラットフォーム上では、CPU はバイト単位でのみメモリーを変更できるため、struct.valid を含むバイト全体が構造メンバーにアクセスまたは変更するためにロードされる必要があります。また、コンパイラは 1 度に数バイト (たとえば、4 バイトの機械語) をロードする方がより効率的であることがわかる場合があります。Discover がこのようなロードを検出する場合、追加情報なしに、すべて 4 バイトが使用されると仮定します。また、たとえば、フィールド my_struct.valid は初期化されたが、フィールド my_struct.c は初期化されず、両方のフィールドを含む機械語がロードされた場合、Discover は部分的に初期化されたメモリーからの読み取り (PIR) にフラグを立てます。

擬陽性の別のソースはビットフィールドの初期化です。1 バイト部分を書き込むには、コンパイラは最初にバイトをロードするコードを生成する必要があります。バイトが読み取るより前に書き込まれていない場合、結果は非初期化メモリーからの読み取りエラー (UMR) となります。

ビットフィールドの擬陽性を回避するには、コンパイル時に -g オプションまたは -g0 オプションを使用します。これらのオプションは、Discover に追加のデバッグ情報を提供し、ビットフィールドのロードと初期化を特定するのに役立ち、多くの擬陽性を削除します。何らかの理由で -g オプションを使用してコンパイルできない場合は、memset() などの関数を使用して構造を初期化します。次に例を示します。


...
struct my_struct s;
/* Initialize structure prio to use */
memset(&sm 0, sizeof(struct my_struct));
...

スペキュレイティブロード

コンパイラは、ロードの結果がすべてのプログラムパスで有効ではない条件下の不明なメモリーアドレスからロードを生成する場合があります。このようなロード命令は分岐命令の遅延スロットに配置できるため、この状況は SPARK プラットフォーム上で発生する場合がよくあります。たとえば、ここに C コードフラグメントがあります。


int i'
if (foo(&i) != 0) { /* foo returns nonzero if it has initialized i */
  printf("5d\n", i);
}

このコードから、コンパイラは次のコードに等しいコードを生成できます。


int i;
int t1, t2'
t1 = foo(&i);
t2 = i; /* value in i is loaded */
if (t1 != 0) {
  printf("%d\n", t2);
}

この例では、関数 foo()0 を返し、i を初期化しないと仮定します。i からのロードは、依然として生成されますが、使用されません。しかし、ロードは Discover によって確認され、非初期化変数のロード (UMR) を報告します。

Discover はデータフロー分析を使用して、可能な場合には常にそのようなケースを特定しますが、検出できない場合もあります。

最適化レベルを低くしてコンパイルすることによって、これらのタイプの擬陽性の発生を削減できます。

未計測コード

Discover はプログラムの 100% を計測できない場合があります。おそらく、コードの一部がアセンブリ言語のソースファイルまたは再コンパイルできないサードパーティのライブラリから来ているため、計測できない場合があります。Discover には、未計測コードがアクセスまたは変更しているメモリーブロックの知識がありません。たとえば、サードパーティの共有ライブラリから得られる関数がのちにメイン (計測済み) プログラムによって読み取られるメモリーブロックを初期化すると仮定します。Discover はメモリーがライブラリで初期化されていることを認識していないため、後続の読み取りにより、非初期化メモリーエラー (UMR) が生成されます。

このような場合の解決策を提供するため、Discover API には次の関数が含まれています:


void __ped_memory_write(unsigned long addr, long size, unsigned long pc);
void __ped_memory_read(unsigned long addr, long size, unsigned long pc);
void __ped_memory_copy(unsigned long src, unsigned lond dst, long size, unsigned long pc);

プログラムから API 関数を呼び出し、Discover にメモリー領域への書き込み (__ped_memory_write ()) やメモリー領域からの読み取り (__ped_memory read()) などの特定のイベントを知らせることができます。どちらの場合も、メモリー領域の開始アドレスは、addr パラメータで渡され、そのサイズは size パラメータで渡されます。pc パラメータを 0 に設定します。

__ped_memory_copy 関数を使用して、Discover にメモリーがある場所から別の場所にコピーされていることを知らせます。ソースメモリーの開始アドレスは src パラメータで渡され、宛先領域の開始アドレスは dst パラメータで渡され、サイズは size パラメータで渡されます。pc パラメータを 0 に設定します。

API を使用するには、プログラムでこれらの関数を weak と宣言します。たとえば、ソースコードに次のコードフラグメントを含めます。


#ifdef __cplusplus
extern "C" {
#endif

extern void __ped_memory_write(unsigned long addr, long size, unsigned long pc);
extern void __ped_memory_read(unsigned long addr, long size, unsigned long pc);
extern void __ped_memory_copy(unsigned long src, unsigned long dst, long size, unsigned long pc);

#prgama weak __ped_memory_write
#pragma weak __ped_memory_read
#pragma weak __ped_memory_copy

#ifdef __cplusplus
}
#endif

API 関数は内部 Discover ライブラリで定義され、計測時にプログラムとリンクされます。ただし、プログラムが計測されない場合、このライブラリはリンクされず、API 関数へのすべての呼び出しによってアプリケーションがハングアップします。そのため、Discover 下でプログラムを実行していない場合は、これらの関数を無効にする必要があります。または、API 関数の空の定義を使用して動的ライブラリを作成し、それをプログラムとリンクすることができます。この場合、Discover を使用しないでプログラムを実行する場合は、ライブラリが使用されますが、Discover 下で実行する場合は、真の API 関数が自動的に呼び出されます。