酸菜猪蹄的程序人生
木下编程屯屯烫烫

2010年11月16日

MiniDumpWriteDump是MS DbgHelp.dll 中一个API, 用于导出当前运行的程序的Dump. 这个dll程序系统中就有, 但是很多软件, 都在自己的安装目录下保存了这个.dll的最新的版本.

为了测试这个API, 参考网上一些资料, 写了一个简单的C++ 程序. 目的是当有异常发生的时候, 自动生成Dump文件供之后的分析. 有了Dump文件, 我们就可以使用WinDBG等调试器来分析异常发生时的情况. 其实这个功能很多软件都有, 比如QQ, 魔兽世界, 等等. 它们在出现了异常的时候会弹出一个对话框, 让用户输入异常发生时的情况, 然后把异常的dump文件用email发回, 供开发者们分析修改bug.

不过有一点, 这里需要程序的调试符号文件(pdb文件). 对于Debug版来说, 是生成的, 但是Release版来说默认是不生成的. 可以设置VC的编译器, 让它在Release版的时候也生成调试信息. 这带来一个新的问题, 因为.pdb里面是保存了源文件的信息的, 为了避免泄密, 可以采用VS中的CVPack工具, 从中去除敏感的信息.

程序需要使用Dbghelp.hDbghelp.lib . 它们可以从MSDN找到.

//最主要的函数, 生成Dump
static void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo)
{
if (excpInfo == NULL) //如果没有传入异常, 比如是在程序里面调用的, 生成一个异常
{
// Generate exception to get proper context in dump
__try
{
OutputDebugString(_T("raising exception\r\n"));
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
}
__except(DumpMiniDump(hFile, GetExceptionInformation()),
EXCEPTION_CONTINUE_EXECUTION)
{
}
}
else
{
OutputDebugString(_T("writing minidump\r\n"));
MINIDUMP_EXCEPTION_INFORMATION eInfo;
eInfo.ThreadId = GetCurrentThreadId(); //把需要的信息添进去
eInfo.ExceptionPointers = excpInfo;
eInfo.ClientPointers = FALSE;

    // 调用, 生成Dump. 98不支持
// Dump的类型是小型的, 节省空间. 可以参考MSDN生成更详细的Dump
.
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
excpInfo ? &eInfo : NULL,
NULL,
NULL);
}
}

下面的是程序部分:

int _tmain(int argc, _TCHAR* argv[])
{
// 创建一个Dump文件
HANDLE hFile = CreateFile( _T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE,
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
int code;
__try
{   
// 把自己实现的main函数包装一下, 放在try .. except 块中. 这样出现了异常可以自动生成dump
main_wrapper(argc, argv);
}
__except( code=GetExceptionCode(), DumpMiniDump(hFile, GetExceptionInformation() ),       EXCEPTION_EXECUTE_HANDLER ) //出现了异常, 记录异常的code, 生成dump!!
{
printf("%x\n", code);
wchar_t msg[512];
wsprintf(msg, L"Exception happened. Exception code is %x", code);
MessageBox(NULL, msg, L"Exception", MB_OK); //显示消息给用户
}
CloseHandle( hFile ); //关闭Dump文件
getchar();
return 0;
}

最下面是两个测试的函数, main_wrapper函数将调用test1, test1将会生成一个异常(非法内存写)

void test1() {
int *p;
p = (int*)0x100;
*p = 0; //写0x100地址, 这个是非法的
}

void main_wrapper(int argc, _TCHAR* argv[]) {
test1();
}

运行, 异常被捕获了:

同时, dump文件也生成了:


用WinDBG打开Dump文件, 可以清楚的看出异常出现的情况:




从中可以比较清楚的看到异常发生的情况(Exception code), 异常出现的地址(test1函数, 偏移0x28). 因为这次测试的是Debug版, 有保存了源代码的.pdb文件, 所以WinDbg把源代码也列出来了. 这样可以非常容易的发现问题.

============================================

参考:
DbgHelp中的DumpAPI例子: http://www.debuginfo.com/examples/src/effminidumps/MiniDump.cpp
CrashReport: 程序出现异常的时候显示发送错误的对话框, 并把Dump文件发送到指定的地址. http://code.google.com/p/crashrpt/
XCrashReport: 与上面的类似的一个开源项目. http://www.codeproject.com/KB/debug/XCrashReportPt1.aspx
posted @ 2010-11-16 10:41 cooelaf 阅读(2669) | 评论 (1)编辑 收藏
 


作者:<leohe.leohe@gmail.com>

    Linux系统中在应用程序运行过程中经常会遇到程序突然崩溃,提示:Segmentation fault,这是因为应用程序收到了SIGSEGV信号。这个信号提示当进程发生了无效的存储访问,当接收到这个信号时,缺省动作是:终止w/core。 终止w/core的含义是:在进程当前目录生成core文件,并将进程的内存映象复制到core文件中,core文件的默认名称就是“core”(这是 Unix类系统的一个由来已久的功能)。
    事实上,并不是只有SIGSEGV信号产生coredump,还有下面一些信号也产生coredump:SIGABRT(异常终止)、SIGBUS(硬件 故障)、SIGEMT(硬件故障)、SIGFPE(算术异常)、SIGILL(非法硬件指令)、SIGIOT(硬件故 障),SIGQUIT,SIGSYS(无效系统调用),SIGTRAP(硬件故障)等。
    在程序的开发调试阶段(尤其是大型软件开发),发生程序异常崩溃时常规的调试方法常常是无比的痛苦:无穷的log中也不见得有什么有意义的信息。好在GDB提供和利用core文件进行调试的途径,大大方便了这类问题的调试。

    下面我们通过一个简单的例子来看看怎么通过GDB来调试一个违规访问内存导致的程序崩溃。这里我们顺便讲讲动态库的调试。

/******** mylib.h **********/
#ifndef __MY_LIB_H__
#define __MY_LIB_H__

int add(int x, int y);

#endif // __MY_LIB_H__
/******** end **********/



/******** mylib.c **********/
#include <stdlib.h>
#include "mylib.h"

int add(int x, int y)
{
    char* pc = NULL;
    *pc = 10;

    return x + y;
}
/******** end **********/

/******** main.c **********/

#include <stdio.h>
#include <stdlib.h>

#include "mylib.h"

int main (void)
{
    int ret = -1;
    int a = 10, b = 20;
    ret = add(a, b);

    printf("The result is: %d\n", ret);

    return 0;
}
/******** end **********/

#####################################
# File Name: Makefile
#
#####################################

CC = gcc
LD = gcc

all:
        $(CC) mylib.c -g -I. -fPIC -shared -o libmylib.so
        $(CC) main.c -g -I. -L. -lmylib -o test

clean:
        rm *.so test
#############   END   ###############

    首先将上面的代码分别存储到相应的目录,名称为:mylib.h、mylib.c、main.c、Makefile。

1)编译测试代码。注)编译时的 -g 选项是必须的。
[xxx@yyy]$ make
gcc mylib.c -g -I. -fPIC -shared -o libmylib.so
gcc main.c -g -I. -L. -lmylib -o t

通过ls命令我们可以看到生成了测试程序test.
[xxx@yyy]$ ls
libmylib.so main.c Makefile mylib.c mylib.h test

2)执行测试程序
[xxx@yyy]$ ./test
./test: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory

这个错误表明程序在运行阶段不能找到相应的动态库文件,此时需要通过环境变量 LD_LIBRARY_PATH 来指定运行期动态库的搜索目录,我们的动态库就在当前目录,如下:

[xxx@yyy]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

3)再次执行测试程序
[leo@localhost debug]$ ./test
Segmentation fault
[leo@localhost debug]$ ls
libmylib.so main.c Makefile mylib.c mylib.h test

4)设置core文件大小
Segmentation fault如期而至,但是却没有我们更想见到的core文件!
原来系统在默认情况下core文件的大小设置为0,换句话讲也就是不产生core文件。我们可以通过 ulimit 命令来修改core文件的大小,unlimited表示不限制core文件的大小,如下(设置core文件的大小需要root权限):
[root@yyy]# ulimit -c unlimited
[root@yyy]# ./test
Segmentation fault (core dumped)
[root@yyy]# ls
core.2890 libmylib.so main.c Makefile mylib.c mylib.h test

5)设置core文件的格式,输出路径
通过下面命令我们还可以指定core文件的命名格式,路径等(需要root权限):
[root@yyy]# echo "core_%e_%s" >/proc/sys/kernel/core_pattern
[root@yyy]# ./test
Segmentation fault (core dumped)
[root@yyy]# ls
core.2890 core_test_11.2898 libmylib.so main.c Makefile mylib.c mylib.h test

6)调试
[root@yyy]# gdb test core.2890
GNU gdb Red Hat Linux (6.5-8.fc6rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
Error while mapping shared library sections:
libmylib.so: Success.
Reading symbols from /home/xxx/tst/libmylib.so...done.
Loaded symbols for libmylib.so
Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x00a8969c in ?? ()
(gdb)

键入GDB命令 where
(gdb) where
#0 0x001ec44c in ?? ()
#1 0x00000000 in ?? ()

?? ()并不是我们想看到的,之所以这样,是因为GDB不能正确加载我们编写的动态库libmylib.so,我们需要在这里设置GDB的动态库搜索路径,如下:

(gdb) set solib-search-path .
Reading symbols from /home/xxx/test/tst/libmylib.so...done.
Loaded symbols for /home/xxx/test/tst/libmylib.so
Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2

可以看到GDB已经加载了libmylib.so,再次键入where命令:
(gdb) where
#0 0x001ec44c in add (x=10, y=20) at mylib.c:8
#1 0x0804847c in main () at main.c:12
(gdb)

这次我们期待的结果出现了,GDB清楚的列出了错误出现的位置:mylib.c的第8行,好了,到那里去改code吧!
posted @ 2010-11-16 10:40 cooelaf 阅读(1430) | 评论 (0)编辑 收藏

2008年11月20日

用户要想确定在所使用的Linux系统上有没有加上中文环境,具体可分为以下两步来进行。
首先对于bash,需要在/etc/profile或$HOME/.profile文件里加上下面的语句:
stty cs8 -istrip 
stty pass8

执行下面的命令:
#export LANG =C 
#export LC_CTYPE=iso-8859-1

然后,在$HOME/.inputrc里加上以下语句:
set convert -meta off 
set output -meta on

修改之后,用户不必重新启动系统,只需新开一个虚拟终端,或者注销后再登录即可生效。

(转载自http://developer.51cto.com/art/200509/3819.htm)

posted @ 2008-11-20 08:49 cooelaf 阅读(1341) | 评论 (0)编辑 收藏

2008年8月22日

     摘要: 先简要介绍dd的参数,后边通过几个实例介绍dd的应用,参考了其他网友的资料,在此一并感谢 小菜水平有限文中有什么错误请大家指正,关于dd还有什么好的使用方法,可以贴出来,小菜会及时更新 dd 是 Linux/UNIX 下的一个非常有用的命令,作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。 1. 命令简介 dd&n...  阅读全文
posted @ 2008-08-22 09:47 cooelaf 阅读(418) | 评论 (0)编辑 收藏

2008年1月27日

     摘要: 本文列举了在windows下禁用输入法的API。  阅读全文
posted @ 2008-01-27 16:43 cooelaf 阅读(1802) | 评论 (0)编辑 收藏

2007年10月16日

Technorati 标签: , ,

 

修改/etc/inittab文件。将id:5:initdefault: 中的5修改为3,表示不进入X界面。

反之亦然。

posted @ 2007-10-16 18:41 cooelaf 阅读(409) | 评论 (0)编辑 收藏

2007年10月10日

     摘要: 本文介绍了Fedora下Samba服务器的配置。  阅读全文
posted @ 2007-10-10 00:43 cooelaf 阅读(1270) | 评论 (5)编辑 收藏

2007年9月27日

     摘要: 本文介绍了多播编程的要点,不涉及具体代码。  阅读全文
posted @ 2007-09-27 22:29 cooelaf 阅读(3264) | 评论 (0)编辑 收藏
 
     摘要: 本文介绍了如何使用Live Writer来写blog。  阅读全文
posted @ 2007-09-27 21:09 cooelaf 阅读(726) | 评论 (3)编辑 收藏

2006年5月19日

     摘要:   阅读全文
posted @ 2006-05-19 17:48 cooelaf 阅读(56112) | 评论 (14)编辑 收藏
仅列出标题  下一页