crash log具体流程概述

crash log具体流程概述

当某服务或者native code程序crash产生调试信息后有两中去向:
1.写入到logcat:
这种信息可以通过adb shell中的logcat中察看到。
2.写入到系统的/data/tombstones/文件夹中:
创建tombstone_xx文件后写入信息,xx
从00开始,最大支持49个tombstone_xx文件,超出后会从00开始重新写入覆盖之前.

当某一进程crash以后会向系统发送信号,信号在某个地方会被拦截下来发送给android的处理函数
debugger_signal_handler,此函数通过socket
发送给守护进程debuggerd,由debuggerd来处理相关操作

一)拦截信号单元: debugger (common\bionic\linker\debugger.c)

debugger_init()函数如下:

 1   void debugger_init()
 2 {
 3     struct sigaction act;
 4     memset(&act, 0, sizeof(act));
 5     act.sa_sigaction = debugger_signal_handler;
 6     act.sa_flags = SA_RESTART | SA_SIGINFO;
 7     sigemptyset(&act.sa_mask);
 8
 9     sigaction(SIGILL, &act, NULL);
10     sigaction(SIGABRT, &act, NULL);
11     sigaction(SIGBUS, &act, NULL);
12     sigaction(SIGFPE, &act, NULL);
13     sigaction(SIGSEGV, &act, NULL);
14     sigaction(SIGSTKFLT, &act, NULL);
15     sigaction(SIGPIPE, &act, NULL);
16 }

从如上代码可以看出此函数的功能是收到信号后会执行debugger_signal_handler处理函数。下面要把这个初始化函数放到启动的 进程中。
当某进程创建后在main()之前首先调用__start
函数然后调用__linker_init()函数在__linker_init()中会调用debugger_init()
这样当进程出现问题后会发信号给系统,这样在系统处理之前会首先跳到debugger_signal_handler做处理

void debugger_signal_handler(int n, siginfo_t* info, void* unused)
{
    //do something

    tid = gettid();
    s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);

    if (s >= 0) {
        int  ret;
        debugger_msg_t msg;
        msg.action = DEBUGGER_ACTION_CRASH;
        msg.tid = tid;
        //向服务端发送数据 包括进程的tid
        RETRY_ON_EINTR(ret, write(s, &msg, sizeof(msg)));
    if (ret == sizeof(msg)) {
            RETRY_ON_EINTR(ret, read(s, &tid, 1));
            int savedErrno = errno;
            notify_gdb_of_libraries();
            errno = savedErrno;
        }
   //..
}

如上代码是将pid发送给服务端,服务端就是debuggerd进程,这时debuggerd会接收到客户端的数据,里面包含crash线程的 tidread(s, &tid, 1);这个函数是等待socket发来的数据,他会一直阻塞在这里等待结束。

二)守护进程 debuggerd (common\system\core\debuggerd\debuggerd.c)

此进程在init.rc中可以找到跟随其他服务一起启动.在debuggerd的main函数中会执行do_server()函数

do_server()函数如下:

 static int do_server() {
     //do sth

     s = socket_local_server(DEBUGGER_SOCKET_NAME,
             ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
     if(s < 0) return 1;
     fcntl(s, F_SETFD, FD_CLOEXEC);

    LOG("debuggerd: " __DATE__ " " __TIME__ "\n");

    for(;;) {
        struct sockaddr addr;
        socklen_t alen;
        int fd;
        alen = sizeof(addr);
        XLOG("waiting for connection\n");
        fd = accept(s, &addr, &alen);
        if(fd < 0) {
            XLOG("accept failed: %s\n", strerror(errno));
            continue;
        }

        fcntl(fd, F_SETFD, FD_CLOEXEC);
        handle_request(fd);
    }
    return 0;

从如上代码可以看出debuggerd创建完服务端以后会一直在for(;;)中等待客户端的消息,当检测到有消息到达后会执行
handle_request函数

 static void handle_request(int fd) {

     int status = read_request(fd, &request);

     ptrace(PTRACE_ATTACH, request.tid, 0, 0);

     TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) 

     int total_sleep_time_usec = 0;
     for (;;) {
          int signal = wait_for_signal(request.tid, &total_sleep_time_usec);
          if (signal < 0) {
           break;
     }
//

     switch (signal) {
             case SIGSTOP:
                  if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {

                            XLOG("stopped -- dumping to tombstone\n");
                            tombstone_path = engrave_tombstone(request.pid, request.tid,
                                    signal, true, true, &detach_failed,
                                    &total_sleep_time_usec);
                        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {

                            XLOG("stopped -- dumping to fd\n");
                            dump_backtrace(fd, request.pid, request.tid, &detach_failed,
                                    &total_sleep_time_usec);
                        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE_TO_LOG) {

                            XLOG("stopped -- dumping to log\n");
                            dump_backtrace_for_thread(fd, request.pid, request.tid, &detach_failed,
                                    &total_sleep_time_usec);
                        } else {
                            XLOG("stopped -- continuing\n");
                            status = ptrace(PTRACE_CONT, request.tid, 0, 0);
                            if (status) {
                                LOG("ptrace continue failed: %s\n", strerror(errno));
                            }

                LOG("handle_request:loop again");
                            continue; /* loop again */
                        }
                        break;
                    case SIGILL:
                    case SIGABRT:
                    case SIGBUS:
                    case SIGFPE:
                    case SIGSEGV:
                    case SIGPIPE:
                    case SIGSTKFLT: {
                        XLOG("stopped -- fatal signal\n");
                        /*
                         * Send a SIGSTOP to the process to make all of
                         * the non-signaled threads stop moving.  Without
                         * this we get a lot of "ptrace detach failed:
                         * No such process".
                         */
                        kill(request.pid, SIGSTOP);
                        /* don‘t dump sibling threads when attaching to GDB because it
                         * makes the process less reliable, apparently... */
                        tombstone_path = engrave_tombstone(request.pid, request.tid,
                                signal, !attach_gdb, false, &detach_failed,
                                &total_sleep_time_usec);
                        break;
                    }

}
//..

    kill(request.pid, SIGCONT);

    if (detach_failed) {
         LOG("debuggerd committing suicide to free the zombie!\n");
         kill(getpid(), SIGKILL);
     }

}

通过read_request读到了进程的pid,uid等后通过ptrace(PTRACE_ATTACH, request.tid, 0,
0),将debuggerd挂
在了crash进程上这样就可以控制crash进程的信息了,PTRACE_ATTACH会向被调试进程发送SIGSTOP,crash进程停止
TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1
会向客户端发送数据,但是这时crash进程停止了所以停止接受数据
此时debuggerd进程等待crash进程发来的信号 首先进入 case SIGSTOP 后通过request可以判断出 执行如下代码

  XLOG("stopped -- continuing\n");
                             status = ptrace(PTRACE_CONT, request.tid, 0, 0);
                             if (status) {
                                 LOG("ptrace continue failed: %s\n", strerror(errno));
                             }

                 LOG("handle_request:loop again");
                             continue; /* loop again */

此时ptrace(PTRACE_CONT)会将crash激活继续执行,激活后crash进程接收到了数据继续执行后将发送
SIGSEGV(crash的进程发送
此信号),再次被debuggerd接受到后执行switch ,开始执行 engrave_tombstone函数

三)debuggerd的核心处理 (common\system\core\debuggerd\tombstone.c)

engrave_tombstone()这个函数的主要工作是在/data/tombstones/下创建tombstones_xx文
件,然后将寄存器信息
堆栈信息写入进去。通过dump_crash函数写入

 
 static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal,
         bool dump_sibling_threads, int* total_sleep_time_usec, int index)

 {

     char date[80];
     time_t now = time(NULL);
    strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
    _LOG(log, false, "debuggerd: %s\n", date);

    _LOG(log, false,
            "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
    dump_build_info(log);

    dump_thread_info(log, pid, tid, true, index, signal);

    dump_fault_addr(log, tid, signal);

    ptrace_context_t* context = load_ptrace_context(tid);
    dump_thread(context, log, tid, true, total_sleep_time_usec);

    dump_maps(log, pid);
    dump_smaps(log, pid);
    dump_status(log, pid);
    dump_logs(log, pid, true);

    if (dump_sibling_threads) {
        detach_failed = dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec);
    }
    free_ptrace_context(context);

    return detach_failed;
}
以上dump_xxx函数中会调用__LOG(log,xxxxx);函数,此函数中会通过打开log参数中的文件句柄来执行write操作将log写入文件 LOG函数如下:
void _LOG(log_t* log, bool in_tombstone_only, const char *fmt, ...) {
    char buf[512];

    va_list ap;
    va_start(ap, fmt);

    if (log && log->tfd >= 0) {
        int len;
        vsnprintf(buf, sizeof(buf), fmt, ap);
        len = strlen(buf);
        write(log->tfd, buf, len);
    }

    if (!in_tombstone_only && (!log || !log->quiet)) {
        __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
    }
    va_end(ap);
}

所以可以通过in_tombstone_only来决定是否执行_android_log_vprint,进而决定是否输出到 logcat

时间: 2024-08-05 23:52:08

crash log具体流程概述的相关文章

IOS Crash Log 分析

上架AppStroe 被打回来了,原因是: Your app crashed on iPad running iOS 11.3.1 connected to an IPv6 network when we tapped on profile image. We have attached detailed crash logs to help troubleshoot this issue. 崩溃日志 一.Crash文件结构 当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,

WebKit加载流程 - 概述

之前写了几篇加载流程的说明,是从下向上看,有点只见树木不见森林的感觉.经过最近一段时间的学习,有了能加以概括抽象的方法. WebKit加载流程和页面组成是直接相关的,页面就是WebKit要加载的对象.所以WebKit负责加载的类也与负责页面管理的类相对应.Apple关于WebView的说明里清楚表现了页面视图上的MVC结构: 一个页面从元素上也有其层次结构,并且和加载类对应,如下: 从页面元素上讲WebView代表了一个页面的呈现,对应一个Page. 一个Page包含一个或多个Frame,其中一

IOS crash log分析

此处不讨论具体的如何根据.dsym文件解析crash log的方式. 一.一般的崩溃 1.违反苹果的政策:启动.恢复.暂停或退出超时; 用户强制退出: 低内存退出:MemoryWarning; 2.程序中有bug 二.崩溃解析说明 1.MemoryWarning的崩溃比较特别,没有任何trace,标志性信息即某一条trace后面有“jettisoned”. 解决方法: 可用Allocations.Leaks Instruments 或VM Tracker Instrument来帮助检查.另外,解

IOS崩溃日志解析(crash log)

IOS的应用程序少不了crash,互联网统计分析工具友盟有一项目错误分析的功能,专门用于应用程序崩溃日志统计,最近研究友盟上统计到的崩溃日志,在此对崩溃日志做一个简单的总结. IOS崩溃日志分类: 一.低内存崩溃:IOS设备检测到低内存时,虚拟内存系统发出通知请求应用释放内存.这些通知发送到所有正在运行的应用和进程,试图收回一些内存.如果内存使用依然居高不下,系统将会终止后台线程以缓解内存压力.如果可用内存足够,应用将能够继续运行而不会产生崩溃报告.否则,应用将被iOS终止,并产生低内存崩溃报告

Android dump .so 文件crash log

众所周知,在android系统上,有时候我们遇到so文件的crash只能打log,但是很多时候并不知道crash在什么地方,幸运的是crash后,一般可以产生一个.dmp文件. 我们可以根据这个文件来得到更为详细的statck trace. 主要用的就是google提供的一些方法,命令太复杂,很容易出错,所以我写了一个python脚本,简化步骤. 详情可以参考 https://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide #!

Crash log符号化与调试信息

这篇文章主要整理了crash log的符号化解析和调试信息与配置相关的一些内容. 对于做移动App开发的来说,质量和体验都是很重要的.一个客户端应用如果经常“闪退”,是产品质量很差的一个体现,用户体验就更不用提了.所以开发一个优秀的App,首先是保证自身的技术质量,尽量杜绝“闪退”,也就是“Crash”.但客户端上线后,偶尔出现一个隐藏很深的bug也在所难免.我们所能做的就是尽可能的收集问题相关的信息,争取在将来的新版本中解决和改进. 0. Crash 一个App启动之后,用着用着就突然被iOS

Windows Server 2012 R2 WSUS-10:流程概述

本篇文章来大概说一说打补丁的流程,一般来说打补丁的流程分为测试环境测试和生产环境安装两个部分.如果企业规模比较小,没有完善的流程制度,也是有一些打补丁的原则可以遵循的,比如: 对于安全级别为Low以上的各种安全补丁应该分发: 对于操作系统的安全补丁应该分发: 对于各种IE版本安全补丁应该分发: 对于其他各种安全补丁(如Media Player.OutLook Express等)应该分发: 对于状态为Updates修订版本的安全补丁,无需手工批准,系统会自动发布: 那么对于规模稍微复杂的企业来说,

iOS crash log 解析

iOS开发中,经常遇到App在开发及测试时不会有问题,但是装在别人的设备中会出现各种不定时的莫名的 crash,因为iOS设备会保存应用的大部分的 crash Log,所以可以通过 crash Log 来定位 crash 原因. 一. 获取iOS设备上的 crash log 1. 将iOS设备连接到电脑上,打开 Xcode -> Organizer -> Devices,找到该台设备,在 Device logs 中找到 crash log(后缀为 .crash 的 log 文件),将其导出即可

Android Native/Tombstone Crash Log 详细分析(转)

转自:http://weibo.com/p/230418702c2db50102vc2h Android 虽然已经有好几年了,但是NDK的开放速度却非常缓慢,所以目前网络上针对对Android NativeCrash的分析说明还比较少,尤其是非常详细的分析方式更难以查询.因此大部分程序员在遇到难以进行addr2line的crashlog时,会一筹莫展.事实上这份log中的其他部分同样提供了非常丰富的信息可供解读,所以在这里总结一下对在这方面的一些经验,在这里以Androidsamples中的he