google breakpad for linux(2)

(1) overview.

  

上图简述了整个breakpad工作的基本流程:

  1)程序编译完后,先用工具事先把程序的相关debug信息dump下来(to symbol file)。

    这些dump下来的东西包含了一些符号与源码的相关信息,如各个符号在程序中对应的地址等。

    有了它,breadpad经过相关处理,使得用户通过一个地址就能找出这个地址对应的是变量,还是函数等。

  2)把符号dump出来后,程序就可以pstrip一下,把debug信息去除,然后发布程序。

  3)程序在运行的过程中,如果发生了崩溃,程序就会进入breakpad的异常处理,这个异常处理会把当前程序的上下文,栈的内容,线程的信息等,有选择性的dump下来,保存为一个mini dump 格式的文件。

  4)mini dump 与symbol file结合,就可以把程序崩溃那一刻的call stack 重建出来。


(2)exception handler.
  breakpad通过响应信号进行异常处理,Linux下,程序崩溃能被捕的信号有几种:
  SIGSEGV,SIGABRT,SIGFPE,SIGILL,SIGBUS.
  breadpad 在初始化的时候,就给程序设置了这些信号的回调。
  具体可以参看:src/client/linux/handler/exception_handler.cc
  这里代码比较少,结构也很清晰,欲深究的读者可以去读代码,作者在文件里画了一个这样流程图:

  

 

 由上可知,在处理signal的时候,breakpad 调用clone()创建了一个新的进程(clone一个新进程使得接下来的操作能够在一个相对稳定的环境里进行)。

 然后在这个新的进程里通过ptrace与父进程进行交互,把一些context,stack等,以mini dump 的形式保存下来。

 所以,整个breakpad在client端的运行流程是比较简单的。

 

3)mini dump file format.

  client 程序崩溃之后中,dump出来的内容主要包含以下几个内容:

    a)当前cput context.这里主要是一堆寄存器中的内容,cpu的状态字等。

      有了它们我们就可以知道当前是在哪线程上运行,运行到了哪里,堆栈的指针等。

      这里主要与当前的硬件体系相关。

    b)关于每个线程的信息。这里就与具体的操作系统相关了。

       Linux下的话,LinuxPtraceDumper这个类中有说明:

         关于线程的信息来自于/proc/$pid/status/,主要包括:tgid,ppid,pid.

         此外还有栈的内容,以及与一些与线程相关的寄存器等。

         具体细节可以参看:src/client/linux/minidump_writer.cpp.

    c)被各线程当作栈用的内存区域,这个里的东西是重建call stack的关键数据所在,

       这里的数据往往占据了最后dump 文件的大部分。

    d)加载进来的各个模块的列表。

      这里主要包括:文件的名字(.dll,.so,.exe etc),模块所占用的内存区域,以前可能存在的debug信息等。

      这里的内容,并不保证都能获取的了。

 

   以上这些程序运行时的细节内容,都是以后重建call stack时用来与symbol file进行匹配的关键数据。

 

4) symbol file

  符号文件是一个包含程序源码与可执行文件里的机器码相对应的东西。

  gcc编译器开了-g进行编译时,会生成一堆的用于debug的信息,这些信息能通常以某种格式(DWARF,STABS)组织起来,存放在可执行文件的某个段位里。

  这些debug信息主要是:machine code to source mapping data。

  breakpad所生成的symbol file就是来自这些debug信息。

  所不同的只是:

        1)它更简单,数据量少

        2)它不放在可执行程序中。

  对于很多怕泄密的产品来说,第二点尤为重要,它避免了产品的源码信息向外暴露。

  下面具体来说一下,这个symbol file中都保存了些什么:

    1)全部内容都是ascii文本。

    2)每一行就是一条记录,每条记录中有多个字段,每个字段以空格分开。

    3)每条记录的开头是一个串字符,这个字符标记这条记录是什么类型的记录。

       但Line record除外,这种类型的记录,默认省略掉标记符,也就是如果有一行没有标记类型,这一行就是一个Line record.

    4)记录中有些字段是10进制或16进制的字符串,16进制也没有以0x开头,要分清某个数字具体是哪种进制,就要看这些数字是在哪种记录里,

       属于哪个字段,这些都是规定死了的。

    具体都有哪些字段,请看下图:

     

  

  这里结合一些具体的例子:

    文件记录:

    

   函数类型:

    

 

   行记录,函数记录:

   

  

   具体其中每条记录的各个字段代表什么意思,可以参看这里:

   https://code.google.com/p/google-breakpad/wiki/SymbolFiles

  

  就我自己使用过程中的研究来看,breakpad最麻烦的工作在于write dump & dump symbol, 这两部分工作都很与具体的细节相关,

  如Dwarf的格式,mini dump 的格式,进程的cpu context,stack 等,一系列与程序运行很相关的底层知识。

  因此必须对Linux操作系统,程序编译等有一定功力才能看懂这些细节。

 

 

posted on 2013-03-20 22:00  twoon  阅读(1817)  评论(0编辑  收藏  举报