|
Why Discover
比起Java语言,用C/C++语言编程在内存操作方面对程序员的要求更高。因为指针、数组等数据类型的使用既可以灵活的实现各种功能,又有可能稍不留神发生内存泄漏、内存越界访问,使系统发生严重错误甚至崩溃。下面的小程序总结了常见的几种内存错误,也是大家经常犯的,先来考考大家的眼力。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p;
int *a=(int *)malloc(sizeof(int[5]));
printf("a[5]=%d\n",a[5]);//ABR
a[5]=5;//ABW
p = (int * )malloc(0);//AZS
p = (int * )malloc(sizeof(int));
printf("*p=%d\n",*p);//UMR
*(p+1)=1;//UAW
free(p+1);//BFM
free(p);
*p=1;//FMW
free(p);//DFM
}
<mem_err.c>
包括例子里面有的,常见的内存错误包括下面的很多种:
|
ABR/ABW
|
Array Bounds Read/Write
|
|
AZS
|
Allocation Zero Size
|
|
BFM
|
Freeing wrong Memory Block
|
|
BRP
|
Bad Realloc Parameter
|
|
DFM
|
Double Freeing Memory
|
|
FMR/FMW
|
Free Memory Read/Write
|
|
FRP
|
Freed Realloc Pointer
|
|
IMR/IMW
|
Invalid Memory Read/Write
|
|
PIR
|
Patially Initialized Read
|
|
UAR/UAW
|
UnAllocated Read/Write
|
|
UMR
|
Uninitialized Memory Read
|
如果你能轻易的说出上面的的英文简称代表的错误含义,并且和实际的代码立刻对应起来,那么恭喜你,事实证明你在内存使用方面有着良好的编码习惯和丰富的开发经验。不过很遗憾,即使这些问题没有发生在你负责的模块里,它们还是有可能出现在你所在的项目中。而且,实际的产品代码往往要比上面的例子复杂的多。如果产品带着这些错误进入系统测试阶段,即使测试人员和你一样有一双火眼金睛,要一下子把这些小地雷全挖出来也不那么容易了。
既然内存使用有这么多容易踩进去的陷阱,跟踪定位又这么辛苦,要是有个小工具既能帮助程序员及时检测出这些错误,又能准确定位到代码行就好了。假如这个工具“时髦”一点,不要钱,那就更合大家的胃口了。不错,这里要介绍的就是一个免费的小工具Discover。
Discover简介
这个工具的全称是Sun Memory Error Discovery Tool(简称Discover),主要用于侦测可执行文件中存在的内存错误。它是Sun公司发布的开发工具Sun Studio 12的附加软件,也可以作为运行于SPARC平台的GNU编译器-GCC的附加软件。目前只有SPARC平台的版本,先try try吧。
下载和安装Sun Studio
如果您已经安装了Sun Studio 11或者更低的版本,建议您先卸载然后安装Sun Studio 12。比如Sun Studio 11的卸载:
首先su成root用户,然后执行:
# cd /var/sadm/prod/com.sun.studio_11
# ./batch_uninstall_all
Sun Studio12的安装包从这里下载:
http://developers.sun.com/sunstudio/downloads/index.jsp,可以选择直接下载tar包然后解压的安装方式,这样不是超户也能安装了。为了方便后面的安装,把Sun Studio安装程序的默认安装路径/opt作为installation_directory。详细步骤如下:
-
下载 Sun Studio 12 tarfile ,并将其保存在临时目录(例如 /tmp)中。
-
通过键入以下命令,将 Sun Studio 12 tarfile 解压缩至选定的目录,并安装 Sun Studio 12 软件:
-
bzcat download_directory/SunStudio12ml-solaris-sparc-200709-ii.tar.bz2 | /bin/tar -xf -
-
修改环境变量,将可执行文件的路径加入PATH中,manpage的路径加入MANPATH中。
-
如果使用 C shell,请键入:
-
如果使用 Bourne shell 或 Korn shell,请键入:
PATH=installation_directory/SUNWspro/bin:$PATH; export PATH
MANPATH=installation_directory/SUNWspro/man:$MANPATH; export MANPATH
下载和安装Discover
从http://cooltools.sunsource.net/cmtdt/install.html页面选择SPARC版本,然后下载安装包SPROcmt.SPARCV9.tar。需要提供Sun Online Account 的用户名和密码,没有的话注册就可以了。
下面是安装步骤:
1.下载SPROcmt.SPARCV9.tar包到临时路径(比如/tmp),然后解压。解压缩后得到SPROprfns 和 SPROcool两个文件夹,里面不仅包含了Discover,还包含CoolTools提供的其他一些工具。
cd <my_download_dir>
/bin/tar -xvf SPROcmt.SPARCV9.tar
2.安装需要root身份,所以请先转换为超户再执行后面的步骤。
3.用pkgadd进行安装。作为Sun Studio12的附加工具,这两个文件夹中包含的各种工具将被安装到Sun Studio的默认安装路径/opt/SUNWspro的子目录中。
pkgadd -d <my_download_dir> SPROprfns SPROcool
执行后Discover和其他工具都被安装在路径/opt/SUNWspro/extra/bin中,man pages 被放置在 /opt/SUNWspro/extra/man目录下。
4.修改环境变量。如果使用 C shell,请键入:
-
如果使用 Bourne shell 或 Korn shell,请键入:
PATH=/opt/SUNWspro/extra/bin:$PATH; export PATH
MANPATH=/opt/SUNWspro/extra/man:$MANPATH; export MANPATH
Discover的man page里还有三个实例可以参考。在线man page链接如下:
http://cooltools.sunsource.net/discover/discover.1.html
使用Discover
试试用Discover检测一下前面的例子程序。要使用Discover,需要指定编译选项-xbinopt=prepare 和优化级别-xO[n],如果想在输出结果中看到代码,需要指定debug选项-g。
cc -g -xbinopt=prepare -xO1 mem_err.c
discover a.out
./a.out
执行后得到后面的输出。进程中所有的内存访问和使用错误都会被明确的标识出来,包括代码行、错误的简称和总的统计结果。为了方便大家理解,我们对第一个错误(ABR)的部分用斜体字添加了注解。
----------------侦测结果----------------------
ERROR (ABR): reading memory beyond array bounds at address 0x5001c (4 bytes) at:
<-------运行中发现错误(ERROR),错误代码为ABR,即读内存的越界访问。被访问的内存地址为0x5001c(位于heap中)处的数据,越界4个字节(一个int型数组元素的长度)。紧接着是stack的tracing信息,请按注解编号1,2,3顺序阅读。
main() + 0x30000 [/home/lz195630/studio12/a.out:0x300c4]
<-------3)main函数地址为0x30000,越界访问发生在地址0x300c4(代码段)的地方,对应到代码行就是第7行printf系统函数调用。
<mem_err.c:7>:
4: {
5: int *p;
6: int *a=(int *)malloc(sizeof(int[5]));
7:=> printf("a[5]=%d\n",a[5]);//ABR
8: a[5]=5;//ABW
9:
10: p = (int * )malloc(0);//AZS
<------2)因为编译时指定了debug 选项-g,因此代码会被显示在输出中
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
<------1)a.out执行的入口为_start(),地址为0x10994。运行至地址0x10a9c处调用了主函数main()。
block at 0x50008 (20 bytes long) was allocated at:
malloc() + 0x188 [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0xf500]
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30020]
<------这部分是关于分配内存的信息。分配内存发生在栈中地址0x30020处,即代码第6行malloc系统调用。一共从内存地址0x50008处(位于heap)分配了20个字节(5个int型数组元素)。而且,由于a.out已经被instrument,这里main函数在运行时首先在先调用了libdiscover中的malloc函数(地址为0x188),然后在libdiscover.so中运行到地址0xf500时真正调用了系统malloc函数。
<mem_err.c:6>:
3: int main()
4: {
5: int *p;
6:=> int *a=(int *)malloc(sizeof(int[5]));
7: printf("a[5]=%d\n",a[5]);//ABR
8: a[5]=5;//ABW
9:
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
--------------------ABR错误信息结束-------------------
ERROR (ABW): writing to memory beyond array bounds at address 0x5001c (4 bytes) at:
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30148]
<mem_err.c:8>:
5: int *p;
6: int *a=(int *)malloc(sizeof(int[5]));
7: printf("a[5]=%d\n",a[5]);//ABR
8:=> a[5]=5;//ABW
9:
10: p = (int * )malloc(0);//AZS
11: p = (int * )malloc(sizeof(int));
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
block at 0x50008 (20 bytes long) was allocated at:
malloc() + 0x188 [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0xf500]
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30020]
<mem_err.c:6>:
3: int main()
4: {
5: int *p;
6:=> int *a=(int *)malloc(sizeof(int[5]));
7: printf("a[5]=%d\n",a[5]);//ABR
8: a[5]=5;//ABW
9:
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
WARNING (AZS): allocating zero size memory block at:
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30150]
<mem_err.c:10>:
7: printf("a[5]=%d\n",a[5]);//ABR
8: a[5]=5;//ABW
9:
10:=> p = (int * )malloc(0);//AZS
11: p = (int * )malloc(sizeof(int));
12:
13: printf("*p=%d\n",*p);//UMR
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
ERROR (UMR): accessing uninitialized data from address 0x520b0 (4 bytes) at:
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30238]
<mem_err.c:13>:
10: p = (int * )malloc(0);//AZS
11: p = (int * )malloc(sizeof(int));
12:
13:=> printf("*p=%d\n",*p);//UMR
14:
15: *(p+1)=1;//UAW
16: free(p+1);//BFM
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
block at 0x520b0 (4 bytes long) was allocated at:
malloc() + 0x188 [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0xf500]
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30194]
<mem_err.c:11>:
8: a[5]=5;//ABW
9:
10: p = (int * )malloc(0);//AZS
11:=> p = (int * )malloc(sizeof(int));
12:
13: printf("*p=%d\n",*p);//UMR
14:
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
ERROR (UAW): writing to unallocated memory at address 0x520b4 (4 bytes) at:
main() + 0x30000 [/home/lz195630/studio12/a.out:0x302bc]
<mem_err.c:15>:
12:
13: printf("*p=%d\n",*p);//UMR
14:
15:=> *(p+1)=1;//UAW
16: free(p+1);//BFM
17: free(p);
18: *p=1;//FMW
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
ERROR (BFM): freeing wrong memory block at:
free() + 0xe8 [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0xf7d4]
main() + 0x30000 [/home/lz195630/studio12/a.out:0x302f8]
<mem_err.c:16>:
13: printf("*p=%d\n",*p);//UMR
14:
15: *(p+1)=1;//UAW
16:=> free(p+1);//BFM
17: free(p);
18: *p=1;//FMW
19: free(p);//DFM
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
ERROR (FMW): writing to freed memory at address 0x520b0 (4 bytes) at:
main() + 0x30000 [/home/lz195630/studio12/a.out:0x303a4]
<mem_err.c:18>:
15: *(p+1)=1;//UAW
16: free(p+1);//BFM
17: free(p);
18:=> *p=1;//FMW
19: free(p);//DFM
20: }
21:
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
block at 0x520b0 (4 bytes long) was allocated at:
malloc() + 0x188 [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0xf500]
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30194]
<mem_err.c:11>:
8: a[5]=5;//ABW
9:
10: p = (int * )malloc(0);//AZS
11:=> p = (int * )malloc(sizeof(int));
12:
13: printf("*p=%d\n",*p);//UMR
14:
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
freed at:
__ped_free_handler() + 0x10b4c [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0x10d30]
free() + 0xe8 [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0xf7d4]
main() + 0x30000 [/home/lz195630/studio12/a.out:0x30334]
<mem_err.c:17>:
14:
15: *(p+1)=1;//UAW
16: free(p+1);//BFM
17:=> free(p);
18: *p=1;//FMW
19: free(p);//DFM
20: }
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
ERROR (DFM): double freeing memory at:
__ped_free_handler() + 0x10b4c [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0x10d0c]
free() + 0xe8 [/opt/SUNWspro/prod/lib/postopt/libdiscover.so:0xf7d4]
main() + 0x30000 [/home/lz195630/studio12/a.out:0x303dc]
<mem_err.c:19>:
16: free(p+1);//BFM
17: free(p);
18: *p=1;//FMW
19:=> free(p);//DFM
20: }
21:
22:
_start() + 0x10994 [/home/lz195630/studio12/a.out:0x10a9c]
DISCOVER SUMMARY:
unique errors : 7 (7 total)
unique warnings : 1 (1 total)
----------------程序运行结果---------------------
a[5]=0
*p=336056
可以看见例子程序中的各种错误类型,Discover工具都准确的定位在相应的代码行了!
总结
程序开发人员可以通过这个免费的小工具,在SPARC平台上快速的扫描代码,揪出内存访问和使用错误并准确定位到代码行。结合Sun 公司发布的集成开发工具Sun Studio,高效率的生产出更健壮的代码。
几点说明
话说回来,Discover也不是包治百病。这个工具用于发现动态运行的程序中的错误。如果一段没有被执行的代码中存在内存错误,那这个小工具也无能为力了。另外,如果你使用的不是malloc/calloc,opertor new,operator delete等常用的方法进行动态内存的分配和释放,比如,你用的是brk/brk2直接改变用户进程中已经分配的空间大小,或者用 mmap/shmget系统函数来访问内存。 那么对不起,这个小工具恐怕也帮不上多少忙了。要想解决这些问题,Solaris 10上提供的动态跟踪工具Dtrace(Dynamic Tracing)可以帮上您的忙。
有用的链接
Cool Tools主页
http://cooltools.sunsource.net/index.html
Discover使用手册
http://cooltools.sunsource.net/discover/DISCOVER_users_guide.pdf
|