前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >breakpad概述

breakpad概述

作者头像
kinnylee
发布2020-10-15 10:16:55
1.6K0
发布2020-10-15 10:16:55
举报
文章被收录于专栏:kinnylee钻研技术kinnylee钻研技术

breakpad是什么

  • google开源的一套实现崩溃报告系统的客户端和服务端组件
  • C++语言实现
  • 现在已经被广泛运用在google的一系列产品及其它公司的桌面程序上,如chrome,piscal,firefox等。
  • github地址
  • 官方网站

功能特性

  • 崩溃转储
  • 崩溃分析
  • 跨平台:windows、mac、linux
  • 可以运行于一系列架构的cpu上

主要组件

  • client:集成到应用程序源码中,用于抓取崩溃信息,并生成minidmp文件
  • symbol dumper:读取编译器生成的调试信息(pdb),生成symbol文件
  • processor:读取minidump文件,并结合symbol文件,生成可读的堆栈调用信息

各组件关系示意图

各组件关系示意图
各组件关系示意图

工作原理图

工作原理图
工作原理图

崩溃转储文件

coredump文件

  • Coredump叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快照。
  • linux内核提供的功能
  • 操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个文件里
coredump生成的条件
  • 条件一:需要有信号产生:进程中止前肯定有信号产生,内核根据信号类型来决定是否产生core文件
  • 条件二:需要编译器支持:需要把当前进程镜像以某种格式dump到文件中,比如:gcc/g++的-g选型
  • 条件三:环境参数支持:
    • 通过 ulimit –a 查看 core file size 是否为 0 ,如果为 0 则不能产生 core 文件。
    • 通过 ulimit –c unlimited 可以系统能支持的产生足够大的 core 文件,也可以设置为具体值
coredump的局限性
  • 文件巨大,不利于保存和传输
  • 有些情况崩溃导致堆栈被破坏,堆栈信息不准确
  • 一些信号导致崩溃,不会产生core文件
  • 不能实时产生崩溃文件,必须进程终止时

minidump文件

  • minidump文件格式是由微软开发的用于崩溃上传

各个组件详解

client

  • client模块作为一个静态库将会与使用者的程序编译在一块
  • 它的主要作用是在程序崩溃后,接管程序的异常处理
  • 主要做了两方面的事情:
    • 响应程序崩溃时接收到的signal
    • 获取程序崩溃那一刻的运行时信息,保存为一个minidump格式的文件
  • 内部原理:
    • 崩溃时线程可以异常了,通过克隆子进程,并通过ptrace与父进程交互,读取相关信息
    • 有两种异常处理模式:进程内、进程外

symbole dumper

  • 从可执行程序中提取与符号相关的信息,并保存为一种特定格式的文件
  • symbol file中全部内容都是ascii文本。
  • symbol file的内容以行单位,每一行称作一条记录,每条记录中有多个字段,每个字段以空格分开
  • 每条记录的开头是一个串字符,这个字符标记这条记录是什么类型的记录。(Line record除外,这种类型的记录,默认省略掉标记符)
  • 记录中有些字段是10进制或16进制的字符串,16进制也没有以0x开头,要分清某个数字具体是哪种进制,就要看这些数字是在哪种记录里,属于哪个字段,这些都是规定死了的
symbole文件格式

官方文档

样例:

代码语言:javascript
复制
MODULE windows x86 3AA3B2229C144C24AEBEF3D971F32D711 GMTSJWorker.pdb
FILE 18430 d:\winmain.public.x86fre\sdk\inc\rpcdce.h
FUNC b7990 49 0 `dynamic initializer for 'std::_Error_objects::_Generic_object''
b7990 49 611 282
FUNC c2a70 327 0 boost::algorithm::detail::process_segment_helper<0>::operator() >,std::basic_string,std::allocator >,std::_String_iterator > > >(std::deque > &,std::basic_string,std::allocator > &,std::_String_iterator > >,std::_String_iterator > >,std::_String_iterator > >)
c2a70 53 63 6337
c2ac3 62 71 6337
STACK WIN 4 207410 f 3 0 0 0 0 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + =
  • MODULE:第一条记录,用来描述当前这个模块文件
  • FILE:记录源文件,包含有文件名及路径信息。会被分配一个整形符号来作标记,然后在别的记录中可能会引用它。
  • FUNC:这种记录用来描述一个函数,包含函数名,函数在可执行文件中的地址等信息
  • Line:这种记录没有类型,描述一个给定范围的机器指令对应哪个源文件的哪一行。行记录总是跟在FUNC记录后面,从而描述每个函数里的指令对应在源码里的位置。
  • PUBLIC: 这种记录用来描述每一个链接符号的地址,如汇编函数里的各个入口点
  • STACK WIN: 这种记录用来描述函数调用时,函数帧(stack frame)的布局。有了这个记录,给定一特定的函数帧F,就可以找到哪个函数帧调用了F
  • STACK CFI:CFI, 就是Call Frame Info,这种记录用来表述当执行到某条指令的时候,怎样去查看当前的函数调用栈。

关于符号信息,linux环境:编译非release版本的程序时(如,gcc 开了-g选项),编译器通常会将带有符号相关的信息以某种格式(DWARF,STABS)组织起来,存放在可执行文件的某个段位里。windows环境:脱离可执行文件,放在单独的pdb中

processor

  • 根据coredump及symbol file,构建出可读的call stack
  • 针对每一个线程进行分析,根据不同的cpu,构建出当前线程的top frame,也就是函数调用的最顶一层。然后从top frame开始,对整个调用栈的栈帧进行解析。
解析包含的内容
1. 查找模块

根据当前帧的eip(x86)来调用 CodeModules::GetModuleForAddress()返回当前frame所属的模块信息

2. 定位符号

前面找到模块后,找到只是二进制相关的信息。要找到这个模块相应的名字及模块里其它函数,变量的名字等,还需要用到之前symbole file.

3. 查找符号

根据某个地址,查找出对应的符号名字,如,输入一个函数地址,返回函数的名字。

4. 查找出当前帧的调用帧
  • 当前帧解析完后,需要继续去解析调用当前帧的父帧
  • symbol file中有二种记录类型:stack win,stack cfi。
  • 这两种类型的记录完整的描述了各类函数调用的栈帧布局,因此借助这些记录理论上就可以找回当前帧的调用帧

参考文档

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • breakpad是什么
    • 功能特性
      • 主要组件
        • 各组件关系示意图
          • 工作原理图
          • 崩溃转储文件
            • coredump文件
              • coredump生成的条件
              • coredump的局限性
            • minidump文件
            • 各个组件详解
              • client
                • symbole dumper
                  • symbole文件格式
                • processor
                  • 解析包含的内容
              • 参考文档
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档