您的位置:首页 > 编程语言 > Go语言

Google Breakpad: 实战crash .

2014-04-30 15:55 756 查看
C/C++程序最棘手的时候就是一个字“挂”,总是经常和不经常的挂掉,尤其是那些线上的不经常挂的情况,光看日志定位问题真的很难。

为解决C挂挂的问题,有必要提供一个跨平台的crash处理系统,目前已知的支持平台有windows 、Linux、 OS X 、android、等待。

下面就来看看Google自己用的系统:Breakpad

最好的介绍莫过于谷歌自己的:

谷歌的介绍:http://code.google.com/p/google-breakpad/wiki/GettingStartedWithBreakpad

谷歌翻译-》本人修改版:(不一定准确)minidump-》小型转储

介绍

Breakpad是一个库和工具套件可以让你发布的应用程序(把编译器提供的调试信息剥离掉的)给用户,记录了崩溃紧凑的“dump”文件,发送回您的服务器,并从这些minidump产生C和C++堆栈踪迹。Breakpad可以根据请求使没有崩溃的程序也可以写出minidump。

目前使用Breakpad的有谷歌浏览器,火狐,谷歌的Picasa,卡米诺,谷歌地球,和其他项目。



Breakpad有三个主要组件:

客户端是一个库,包含在您的应用程序中。 它可以获取当前线程的状态和当前加载的可执行文件和共享库的ID写转储文件。您可以配置客户端发生了崩溃时写入一个minidump时,或明确要求时。
符号卸载器是一个程序,读取由编译器产生的调试信息,并生成一个使用Breakpad格式符号文件

处理器(minidump processor)是一个程序,读取一个minidump文件,找到相应的版本的符号文件的(可执行文件和共享库的转储提到的),并产生了一个人可读的C / C + +堆栈跟踪。

小型转储文件格式(即minidump)

转储文件的格式是由微软开发的类似存储的文件,崩溃便利上传。一个minidump文件包含:

在创建dump的进程中加载​​的可执行文件和共享库列表。此列表中包含的特定版本加载的那些文件的文件名和标识符。

在这个过程中存在的线程列表。对于每个线程转储包括处理器寄存器的状态,线程的堆栈存储器的内容。一般Breakpad客户端没有可用于产生函数名或行号,甚至确定堆栈帧的边界的调试信息,所以这些数据是不可解释的字节流。

其他收集的有关系统转储信息比如:处理器和操作系统版本,转储的原因,等等。

Breakpad在所有平台上使用Windows dump文件,而不是传统的core文件,有以下几个原因:

core文件可能会非常大,不适合在网络上发送给收集器处理。minidump较小,因此它们被设计为使用这种方式。
core文件格式缺乏文档信息。例如,Linux标准库不描述寄存器如何存储在PT_NOTE段的。
一个Windows机器上生成一个core dump文件,比起其他机器上生成一个minidump文件,哪个难很难说。
简化了Breakpad处理器,只支持一种文件格式。

概述/一个小型转储的生命周期

通过调用到Breakpad库生成一个minidump。默认初始化Breakpad时,安装了一个异常/信号handler,崩溃时可写一个minidump到磁盘。在Windows上,这是通过SetUnhandledExceptionFilter()实现;在 OS X上,这是通过创建一个线程,等待的Mach exception端口,在Linux上,这是通过安装一个信号handler来应对程序的各种异常情况,如SIGILL,SIGSEGV等。

一旦生成minidump,每个平台都有一个略有不同的方式上传的崩溃dump。在Windows和Linux上,提供一个单独的函数库,可以被称为做upload。在OS X上,一个单独的进程产生,提示用户是否允许,如果同意这样做则发送文件。

术语

进程内与进程外的异常处理——进程内写minidump通常被认为是不安全的,关键处理数据结构可能已经被破坏,异常处理程序运行所处的堆栈或可能已经被覆盖,等等。所有3个平台都支持“进程外”异常处理。

集成概述

Breakpad代码概述

所有的客户端代码访问谷歌计划在http://code.google.com/p/google-breakpad
。 是在src目录下的以下目录结构:

处理器---包含转储处理的代码,它用于在服务器端,而不是在客户端上使用
客户端---包含适用于所有平台的客户端转储产生库
工具---包含源代码和项目,生成在每个平台上的各种工具。

(在其他目录)

Windows
Integration Guide
Windows集成指南
Mac
Integration Guide
Mac的集成指南
Linux
Integration Guide
Linux集成指南

生成过程的详细资料(符号生成)

适用于所有平台。src/tools/{platform}/dump_syms是一个工具,可以读取每个平台的调试信息(例如,OS X / Linux操作系统,DWARF和STABS,以及Windows,PDB文件),和产生一个Breakpad的符号文件。应在二进制程序剥离符号之前(比如OS X/Linux)运行此工具,需要存储dump在处理器可以找到的地方。还有另一种工具,symupload,可以上传符号文件。如果你写了一个服务器,可以接收它们。

还是来自Google:http://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide

由于有代码等原因,本人直接按自己理解,白话,E文好的直接点链接看。

怎样把Breakpad加入你的程序呢,E文是母语的直接点连接。

How To Add Breakpad To Your Linux Application

Building the Breakpad libraries
Integrating Breakpad into your Application
Sending the minidump file
Producing symbols for your application
Processing the minidump to produce a stack trace

This document is an overview of using the Breakpad client libraries on Linux.

先编译库
源码目录下运行

./configure && make

生成 src/client/linux/libbreakpad_client.a

把Breakpad整合进程序
首先,把libbreakpad_client.a链接进你的程序,把src目录include进去,诸如:(g++ -g test.cc -I. -L./client/linux/ -lbreakpad_client -lpthread -o test)。

#include
"client/linux/handler/exception_handler.h"
在程序刚开始的时候实例化google_breakpad::MinidumpDescriptor和google_breakpad::ExceptionHandler 这两个东东。dump目录可改,dump时可设回调来获取dump信息,示例代码如下:

static
bool dumpCallback(const google_breakpad::MinidumpDescriptor&
descriptor, void* context,
bool succeeded){ printf("Dump path: %s\n",
descriptor.path());
return succeeded;}void crash(){
volatile int* a
= (int*)(NULL);
*a =
1;}int main(int
argc, char* argv[]){ google_breakpad::MinidumpDescriptor
descriptor("/tmp"); google_breakpad::ExceptionHandler eh(descriptor,
NULL, dumpCallback, NULL,
true,
-1); crash();
return 0;}
编译运行会产生dmp在/tmp下,ExceptionHandler 结构的信息参考
in the exception_handler.h source file.

回调做的工作越少越好,因为crash的程序本身就不安全,分配内存或调用另一个库里的函数都是不安全的,使用fork另起个进程来做事才是最安全的. 如果必须在crash时使用回调参考some simple
reimplementations of libc functions,避免直接调用libc, 比如a header file for making Linux system calls (insrc/third_party/lss) 避免使用动态库.(其实就是提倡使用系统调用system
call而已,原理就是使用子进程来do something)

如何发送dump文件
参考 some HTTP upload sourcea
minidump upload tool.

处理符号文件
为了产生有用的堆栈跟踪, Breakpad需要你把二进制中的调试符号转成text-format symbol files. 首先,编译时必须用-g 包含调试符号。其次编译这个工具dump_syms ,这个在上边make的时候一起生成好了。

$ google-breakpad/src/tools/linux/dump_syms/dump_syms
./test > test.sym
像下面这样搞,就是搞出个特定的目录来,目录名是head命令 查看 test.sym第一行得出来的,然后把test.sym搞进去,最好复制。

$ head -n1 test.symMODULE
Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test$ mkdir
-p ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830$
mv test.sym ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
symbolstore.py 这个 Mozilla 的脚本能搞定上边几步。

Usage: symbolstore.py <params> <dump_syms path> <symbol store path> <debug info files or dirs>

例:./symbolstore.py ./tools/linux/dump_syms/dump_syms symbols test

处理minidump来生成堆栈信息
还是用工具minidump_stackwalk 来搞

google-breakpad/src/processor/minidump_stackwalk
minidump.dmp ./symbols
在 stderr输出了很长的输出信息,在stdout输出了堆栈信息,然后你就可以定位挂在哪里了。

示例:
[root@CentOS src]# ./processor/minidump_stackwalk 5ee28168-f798-caba-749b962b-312eaf19.dmp ./symbols/

2012-12-13 17:15:29: minidump_processor.cc:281: INFO: Processing minidump in file 5ee28168-f798-caba-749b962b-312eaf19.dmp

2012-12-13 17:15:29: minidump.cc:3832: INFO: Minidump opened minidump 5ee28168-f798-caba-749b962b-312eaf19.dmp

2012-12-13 17:15:29: minidump.cc:3943: INFO: Minidump not byte-swapping minidump

2012-12-13 17:15:29: minidump.cc:4309: INFO: GetStream: type 1197932545 not present

2012-12-13 17:15:29: minidump.cc:4309: INFO: GetStream: type 1197932546 not present

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for /workspace/Breakpad/trunk/src/test

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for /lib64/ld-2.12.so

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for /lib64/libc-2.12.so

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for /lib64/libpthread-2.12.so

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for /lib64/libm-2.12.so

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for /lib64/libgcc_s-4.4.6-20120305.so.1

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for /usr/lib64/libstdc++.so.6.0.13

2012-12-13 17:15:29: minidump.cc:2011: INFO: MinidumpModule could not determine version for linux-gate.so

2012-12-13 17:15:29: minidump_processor.cc:128: INFO: Minidump 5ee28168-f798-caba-749b962b-312eaf19.dmp has CPU info, OS info, no Breakpad info, exception, module list, thread list, no dump thread, and requesting thread

2012-12-13 17:15:29: minidump_processor.cc:166: INFO: Looking at thread 5ee28168-f798-caba-749b962b-312eaf19.dmp:0/1 id 0x38f5

2012-12-13 17:15:29: minidump.cc:308: INFO: MinidumpContext: looks like AMD64 context

2012-12-13 17:15:29: minidump.cc:308: INFO: MinidumpContext: looks like AMD64 context

2012-12-13 17:15:29: source_line_resolver_base.cc:220: INFO: Loading symbols for module /workspace/Breakpad/trunk/src/test from memory buffer

2012-12-13 17:15:29: simple_symbol_supplier.cc:193: INFO: No symbol file at ./symbols//libc-2.12.so/77DDBBF2BF8AFBECA0C59BBCBA94D1150/libc-2.12.so.sym

2012-12-13 17:15:29: basic_code_modules.cc:88: INFO: No module at 0x0

2012-12-13 17:15:29: basic_code_modules.cc:88: INFO: No module at 0x7fff6c4b8bc8

2012-12-13 17:15:29: basic_code_modules.cc:88: INFO: No module at 0x100000000

2012-12-13 17:15:29: minidump_processor.cc:275: INFO: Processed 5ee28168-f798-caba-749b962b-312eaf19.dmp

2012-12-13 17:15:29: minidump.cc:3804: INFO: Minidump closing minidump

Operating system: Linux

0.0.0 Linux 2.6.32-279.11.1.el6.x86_64 #1 SMP Tue Oct 16 15:57:10 UTC 2012 x86_64

CPU: amd64

family 6 model 42 stepping 7

1 CPU
Crash reason: SIGSEGV//挂掉类型

Crash address: 0x0
Thread 0 (crashed)//挂掉线程id

0 test!crash() [test.cc : 10 + 0x4]//代码: *a = 1; //a是一个int*的指针,值为NULL,给NULL赋值1,必须挂,源码的第10行,从左到右第四个字符的位置挂了,即a。

rbx = 0x00007fff6c4b8a90 r12 = 0x0000000000401a00

r13 = 0x00007fff6c4b8bc0 r14 = 0x0000000000000000

r15 = 0x0000000000000000 rip = 0x0000000000401b2d

rsp = 0x00007fff6c4b89e0 rbp = 0x00007fff6c4b89e0

Found by: given as instruction pointer in context

1 test!main [test.cc : 16 + 0x4]

rbx = 0x00007fff6c4b8a90 r12 = 0x0000000000401a00

r13 = 0x00007fff6c4b8bc0 r14 = 0x0000000000000000

r15 = 0x0000000000000000 rip = 0x0000000000401c29

rsp = 0x00007fff6c4b89f0 rbp = 0x00007fff6c4b8ae0

Found by: call frame info

2 libc-2.12.so + 0x1ecdc

rbx = 0x0000000000000000 r12 = 0x0000000000401a00

r13 = 0x00007fff6c4b8bc0 r14 = 0x0000000000000000

r15 = 0x0000000000000000 rip = 0x0000003d64e1ecdd

rsp = 0x00007fff6c4b8af0 rbp = 0x0000000000000000

Found by: call frame info

3 test!crash() [test.cc : 11 + 0x1]

rip = 0x0000000000401b35 rsp = 0x00007fff6c4b8b10

Found by: stack scanning
Loaded modules:

0x00400000 - 0x00412fff test ??? (main)

0x3d64a00000 - 0x3d64a1ffff ld-2.12.so ???

0x3d64e00000 - 0x3d6518dfff libc-2.12.so ???

0x3d65600000 - 0x3d65818fff libpthread-2.12.so ???

0x3d65a00000 - 0x3d65c83fff libm-2.12.so ???

0x3d6a600000 - 0x3d6a815fff libgcc_s-4.4.6-20120305.so.1 ???

0x3d6c600000 - 0x3d6c8f0fff libstdc++.so.6.0.13 ???

0x7fff6c4f9000 - 0x7fff6c4f9fff linux-gate.so ???

首先产生工程文件,下载python2.7,windows二进制版本安装好,设好Path环境变量。

然后Dos进入Breakpad目录,设置好使用vs2010,使用gyp脚本生成sln工程文件。

set GYP_MSVS_VERSION=2010

src\tools\gyp\gyp.bat --no-circular-check src\client\windows\breakpad_client.gyp

src\client\windows下边有生成好的sln文件,打开sln,Debug编译,在Debug下边有生成好的exception_handler.lib,和exception_handler.h这两个加入你的项目中。

#include "exception_handler.h"//使用时include头文件

handler = new ExceptionHandler(const wstring& dump_path,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
int handler_types,
MINIDUMP_TYPE dump_type,
const wchar_t* pipe_name,
const CustomClientInfo* custom_info);

The parameters, in order, are:

pathname for minidumps to be written to - this is ignored if OOP dump generation is used //dump文件路径,如果使用OOP方式产生dump则忽略,个人理解是描述符、管道、等等吧。
A callback that is called when the exception is first handled - you can return true/false here to continue/stop exception processing //crash时调用回调函数,返回ture/false来继续/停止 异常处理。
A callback that is called after minidumps have been written //minidump写入后调用的回调函数
Context for the callbacks //设备上下文,回调使用的
Which exceptions to handle - see HandlerType enumeration in exception_handler.h //HandlerType异常类型,可在exception_handler.h查看
The type of minidump to generate, using the MINIDUMP_TYPE definitions in
DbgHelp.h //minidump的类型,使用DbgHelp.h中MINIDUMP_TYPE类型
A pipe name that can be used to communicate with a crash generation server //接收crash的server端的管道名
A pointer to a CustomClientInfo class that can be used to send custom data along with the minidump when using OOP generation //使用OOP产生minidump时,使用这个自定义客户信息类指针来发送自定义数据

src/client/windows/tests/crash_generation_app/* 有OOP方式的示例。

说白了,和linux一样,在程序开始时,先new一个ExceptionHandler,同时也决定了minidump的产生方式。

C盘下建立Dumps目录,打开src\client\windows\tests\crash_generation_app目录下面的sln,菜单栏选择client,里边有多种 crash 方式,你可选择一种。

然后Dumps里可以找到挂掉的dmp文件,复制到src\client\windows\tests\crash_generation_app\Debug下面,里面有:crash_generation_app.exe crash_generation_app.pdb

双击dmp运行调试,点击右上角的“使用 仅限本机 进行调试”,OK,运行了,挂了,选择“中断”,然后可以在“调用堆栈”窗口里找到挂的位置。可能挂在系统库里,你需要在

“调用堆栈”窗口里找到你的代码行,从上往下找到第一个你的代码行,双击即可定位,可以看到printf(NULL)。



Origin:/article/7601873.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: