Oracle® Solaris Studio 12.4: Discover および Uncover ユーザーズガイド

印刷ビューの終了

更新: 2015 年 12 月
 
 

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

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

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

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

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

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

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

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

...
struct my_struct s;
/* Initialize structure prior 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 関数を呼び出して、メモリー領域への書き込み (__ped_memory_write()) やメモリー領域からの読み取り (__ped_memory read()) などの特定のイベントを discover に知らせることができます。どちらの場合も、メモリー領域の開始アドレスは 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

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