JavaScript is required to for searching.
跳过导航链接
退出打印视图
Oracle Solaris Studio 12.3:Discover 和 Uncover 用户指南     Oracle Solaris Studio 12.3 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  简介

2.  内存错误搜索工具 (Discover)

Discover 的使用要求

必须正确准备二进制文件

不能使用使用预装或审计的二进制文件

可以使用重新定义标准内存分配函数的二进制文件

快速入门

检测准备好的二进制文件

缓存共享库

检测共享库

忽略库

命令行选项

输出选项

检测选项

缓存选项

其他选项

bit.rc 初始化文件

SUNW_DISCOVER_OPTIONS 环境变量

SUNW_DISCOVER_FOLLOW_FORK_MODE 环境变量

运行检测过的二进制文件

分析 Discover 报告

分析 HTML 报告

使用 `Errors`(错误)选项卡

使用 `Warnings`(警告)选项卡

使用 `Memory Leaks`(内存泄漏)选项卡

使用控制面板

分析 ASCII 报告

内存访问错误和警告

内存访问错误

ABR

ABW

BFM

BRP

DFM

FMR

FMW

FRP

IMR

IMW

OLP

PIR

SBR

SBW

UAR

UAW

UMR

内存访问警告

AZS

解释 Discover 错误消息

部分初始化内存

可疑装入

未检测的代码

使用 Discover 时的限制

仅检测有注释的代码

计算机指令可能不同于源代码

编译器选项影响生成的代码

系统库可能会影响报告的错误

定制内存管理可能会影响数据的准确性

无法检测到静态和自动数组的超出边界错误

3.  代码覆盖工具 (Uncover)

索引

解释 Discover 错误消息

在某些情况下,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 函数。