跳过导航链接 | |
退出打印视图 | |
![]() |
Oracle Solaris Studio 12.3:Discover 和 Uncover 用户指南 Oracle Solaris Studio 12.3 Information Library (简体中文) |
在某些情况下,Discover 可能会报告一个实际上不算错误的错误。这种情况称为“误报”。与同类工具相比,Discover 在检测时会分析代码以减少误报的次数,不过难免也会出现误报。以下几部分提供了一些提示,可能对识别 Discover 报告中的误报并尽可能地避免出现误报有所帮助。
以 C/C++ 语言编写的位字段可让您创建紧凑的数据类型。例如:
struct my_struct { unsigned int valid : 1; char c; };
在本例中,结构成员 my_struct.valid 在内存中仅占用一位。但是,在 SPARC 平台上,CPU 只能以字节为单位修改内存,因此,只有装入含有 struct.valid 的整个字节才能访问或修改该结构成员。此外,编译器有时会认为一次装入多个字节(例如,由四个字节构成的计算机字)会更高效。当 Discover 检测到此类装入操作时,如果没有更多信息,会假定使用了所有四个字节。例如,字段 my_struct.valid 已初始化,但字段 my_struct.c 未初始化,如果装入了包含这两个字段的计算机字,则 Discover 会标记部分初始化内存读取 (PIR) 错误。
误报的另一个原因是位字段初始化。要写入某个字节的一部分,编译器必须首先生成用于装入该字节的代码。如果该字节不是在读取之前写入的,将生成未初始化内存读取 (UMR) 错误。
要避免位字段误报,请在编译时使用 -g 选项或 -g0 选项。这些选项为 Discover 提供了额外的调试信息,可帮助 Discover 识别位字段装入和初始化,从而可以排除大多数误报。如果出于某种原因无法使用 -g 选项编译,请使用 memset() 等函数初始化结构。例如:
... struct my_struct s; /* Initialize structure prio to use */ memset(&sm 0, sizeof(struct my_struct)); ...
有时,当装入的结果对某些程序路径无效时,编译器会从已知的内存地址生成装入。这种情况经常发生在 SPARC 平台上,因为此类装入指令可以放置在分支指令的延迟槽中。下面提供了一个示例代码片段:
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 无法完整地检测您的程序。某些代码可能来自无法重新编译的汇编语言源文件或第三方库,因此无法检测。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,请在程序中将这些函数声明为弱函数。例如,在源代码中包含以下代码片段。
#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 函数。