場合によっては、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 関数が自動的に呼び出されます。