一. 概述

当发生ANR(Application Not Response,对于Java进程可通过kill -3向目标进程发送信号SIGNAL_QUIT, 输出相应的traces信息保存到目录/data/anr/traces.txt;而对于Native进程可通过 debuggerd输出traces信息。

可通过一条命令来获取指定Native进程的traces信息,例如输出pid=17529进程信息:

adb shell debuggerd -b 17529 //可指定进程pid

执行完该命令后直接输出traces信息到屏幕,如下:

//【见小节2.2】
----- pid 17529 at 2016-11-12 22:22:22 -----
Cmd line: /system/bin/mediaserver
ABI: 'arm' //【见小节2.4】
"mediaserver" sysTid=17529
#00 pc 00042dac /system/lib/libc.so (__ioctl+8)
#01 pc 000498ad /system/lib/libc.so (ioctl+14)
#02 pc 0001ea5b /system/lib/libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+174)
#03 pc 0001ef9f /system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+6)
#04 pc 0001f0d7 /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+78)
#05 pc 00001cf1 /system/bin/mediaserver
#06 pc 0001667d /system/lib/libc.so (__libc_init+44)
#07 pc 00001f48 /system/bin/mediaserver "Binder_1" sysTid=17931
#00 pc 00042dac /system/lib/libc.so (__ioctl+8)
#01 pc 000498ad /system/lib/libc.so (ioctl+14)
#02 pc 0001ea5b /system/lib/libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+174)
#03 pc 0001ef9f /system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+6)
#04 pc 0001f0d7 /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+78)
#05 pc 00023bd1 /system/lib/libbinder.so
#06 pc 00010115 /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+112)
#07 pc 00041843 /system/lib/libc.so (_ZL15__pthread_startPv+30)
#08 pc 000192a5 /system/lib/libc.so (__start_thread+6)
... //此处省略剩余的N个线程.
----- end 17529 -----

接下来说说debuggerd是如何输出Native进程的trace.

二. Debuggerd

文章debuggerd守护进程详细介绍了Debuggerd的工作原理,此处当执行debuggerd -b命令后:

  1. Client进程调用send_request()方法向debuggerd服务端发出DEBUGGER_ACTION_DUMP_BACKTRACE命令;
  2. Debugggerd进程收到该命令,fork子进程中再执行worker_process()过程;
  3. 子进程通过perform_dump()方法来根据命令DEBUGGER_ACTION_DUMP_BACKTRACE,会调用到dump_backtrace()方法输出backtrace.

接下来,从dump_backtrace()方法讲起:

2.1 dump_backtrace

[-> debuggerd/backtrace.cpp]

void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid,
const std::set<pid_t>& siblings, std::string* amfd_data) {
log_t log;
log.tfd = fd;
log.amfd_data = amfd_data; dump_process_header(&log, pid); //【见小节2.2】
dump_thread(&log, map, pid, tid);//【见小节2.3】 for (pid_t sibling : siblings) {
dump_thread(&log, map, pid, sibling);//【见小节2.3】
} dump_process_footer(&log, pid);//【见小节2.4】
}

2.2 dump_process_header

[-> debuggerd/backtrace.cpp]

static void dump_process_header(log_t* log, pid_t pid) {
char path[PATH_MAX];
char procnamebuf[1024];
char* procname = NULL;
FILE* fp; //获取/proc/<pid>/cmdline节点的进程名
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
if ((fp = fopen(path, "r"))) {
procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
fclose(fp);
} time_t t = time(NULL);
struct tm tm;
localtime_r(&t, &tm);
char timestr[64];
strftime(timestr, sizeof(timestr), "%F %T", &tm);
_LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr); if (procname) {
_LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", procname);
}
_LOG(log, logtype::BACKTRACE, "ABI: '%s'\n", ABI_STRING);
}

例如:

----- pid 17529 at 2016-11-12 22:22:22 -----
Cmd line: /system/bin/mediaserver
ABI: 'arm'

2.3 dump_thread

[-> debuggerd/backtrace.cpp]

static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid) {
char path[PATH_MAX];
char threadnamebuf[1024];
char* threadname = NULL;
FILE* fp; //获取/proc/<tid>/comm节点的线程名
snprintf(path, sizeof(path), "/proc/%d/comm", tid);
if ((fp = fopen(path, "r"))) {
threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
fclose(fp);
if (threadname) {
size_t len = strlen(threadname);
if (len && threadname[len - 1] == '\n') {
threadname[len - 1] = '\0';
}
}
} _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
if (backtrace->Unwind(0)) {
//【见小节2.4】
dump_backtrace_to_log(backtrace.get(), log, " ");
}
}

2.4 dump_backtrace_to_log

[-> debuggerd/Backtrace.cpp]

void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
//NumFrames是backtrace中的栈帧数
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
//【见小节2.5】
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
}
}

通过循环遍历输出整个backtrace中的每一栈帧FormatFrameData的信息.

2.5 FormatFrameData

[-> debuggerd/Backtrace.cpp]

std::string Backtrace::FormatFrameData(size_t frame_num) {
if (frame_num >= frames_.size()) {
return "";
}
return FormatFrameData(&frames_[frame_num]);
} std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
const char* map_name;
if (BacktraceMap::IsValid(frame->map) && !frame->map.name.empty()) {
map_name = frame->map.name.c_str();
} else {
map_name = "<unknown>";
}
uintptr_t relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc); //这是backtrace每一行的信息
std::string line(StringPrintf("#%02zu pc %" PRIPTR " %s",
frame->num, relative_pc, map_name)); if (frame->map.offset != 0) {
line += " (offset " + StringPrintf("0x%" PRIxPTR, frame->map.offset) + ")";
}
if (!frame->func_name.empty()) {
//函数名,偏移量
line += " (" + frame->func_name;
if (frame->func_offset) {
line += StringPrintf("+%" PRIuPTR, frame->func_offset);
}
line += ')';
} return line;
}

例如:(这些map信息是由/proc/%d/maps解析出来的)

#01 pc 000000000001cca4 /system/lib64/libc.so (epoll_pwait+32)

帧号 pc指针 map_name (函数名+偏移量)
#01 pc 000000000001cca4 /system/lib64/libc.so (epoll_pwait+32)

2.6 dump_process_footer

[-> debuggerd/backtrace.cpp]

static void dump_process_footer(log_t* log, pid_t pid) {
_LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
}

例如:----- end 1789 -----

三. 总结

通过debuggerd -b [pid],可输出Native进程的调用栈,这些信息是通过解析/proc/[pid]/maps而来的。

转自:http://gityuan.com/2016/11/27/native-traces/

Native进程之Trace原理(转)——可直接输出某进程的栈帧——debuggerd的更多相关文章

  1. React Native 从入门到原理一

    React Native 从入门到原理一 React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却 ...

  2. 创建多进程之multiprocess包中的process模块

    创建多进程之multiprocess包中的process模块 1.process模块是一个创建进程的模块 Process([group [, target [, name [, args [, kwa ...

  3. 多进程之multiprocessing模块和进程池的实现

    转载:https://www.cnblogs.com/xiaobeibei26/p/6484849.html Python多进程之multiprocessing模块和进程池的实现 1.利用multip ...

  4. [原理] Android Native内存泄漏检测原理解析

    转载请注明出处:https://www.cnblogs.com/zzcperf/articles/11615655.html 上一篇文章列举了不同版本Android OS内存泄漏的检测操作(传送门), ...

  5. c函数调用过程原理及函数栈帧分析

    转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707       今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...

  6. React Native 从入门到原理

    React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却寥寥无几. 本文分为两个部分:上半部分用通 ...

  7. React-Native系列Android——Native与Javascript通信原理(一)

    React-Native最核心的是Native与Javascript之间的通信,并且是双向通信.Native层到Javascript层,Javascript层到Native层.虽说是两个方向,但实现上 ...

  8. 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6621566 上一篇文章Android进程间通信 ...

  9. java多线程之CAS原理

    前言 在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全.下 ...

随机推荐

  1. ionic小白的学习路之目录结构分析、创建组件、创建页面、页面跳转

    一. 目录结构分析 hooks:编译cordova 时自定义的脚本命令,方便整合到我们的编译系统和版本控制系统中. node_modules :node 各类依赖包. resources :andro ...

  2. jQuery闪烁提示,让新消息在网页标题显示

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head& ...

  3. JavaScript中通过原型添加方法,解决数据共享问题,节省内存空间

    涉及知识点:(1)原型的引入(2)构造函数.原型对象和实例对象之间的关系(3)__proto__和prototype的理解 直接举例:在自定义构造函数创建对象时,因为创建的对象使用的不是同一个方法,所 ...

  4. Ubuntu下压缩与解压各种文件的命令

    1.压缩与解压xz文件 (1)压缩 xz -z  filename (2)解压 xz -d  filename.xz 2.压缩与解压tar文件 (1)压缩 tar -cvf  filename(压缩到 ...

  5. Python旅途——文件操作

    Python--文件操作 1.理解文件操作 可能有的时候有人会在想为什么要对文件进行操作,无论对于文件还是网络之间的交互,我们都是建立在数据之上的,如果我们没有数据,那么很多的事情也就不能够成立,比如 ...

  6. Django框架基础知识11-会话状态保持及表单

    浏览器存储cookie的方式不太安全,那有没有更好些的来存储登入状态的方式呢??? 状态保持----cookie和session: 状态保持: 1.http协议是无状态的:每次请求都是一次新的请求,不 ...

  7. 分分钟钟学会Python - 函数(function)

    函数(function) 1 基本结构 本质:将多行代码拿到别处并起个名字,以后通过名字就可以找到这行代码并执行 应用场景: 代码重复执行 代码量很多超过一屏,可以选择通过函数进行代码的分割 写代码方 ...

  8. Feedback on Ch5 paper based on CFD-RANS

    It is encouraging that you took the initiative to write this journal manuscript, but it needs a lot ...

  9. LeetCode(87) Gray Code

    题目 The gray code is a binary numeral system where two successive values differ in only one bit. Give ...

  10. UVA674-Coin Change,用动归思想来递推!

    674 - Coin Change 题意:有1分,5分,10分,25分,50分共5种硬币,数量不限.给你一个n求有多少种方法凑齐n,注意:d[0]=1; 思路:推了前几组样例,可以发现直接用当前状态累 ...