一. 概述

当发生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. 有n个整数,使其前面各数顺序向后移n-m个位置,最后m个数变成最前面的m个数

    题目:有n个整数,使其前面各数顺序向后移n-m个位置,最后m个数变成最前面的m个数 public class 第三十六题数组向后移m个位置 { public static void main(Stri ...

  2. PHP20 PHP面向对象辅助

    学习要点 常用函数 命名空间 类的自动加载 常用函数 class_exists 作用:检查类是否已定义 语法格式: bool class_exists ( string $class_name [, ...

  3. 896. Monotonic Array@python

    An array is monotonic if it is either monotone increasing or monotone decreasing. An array A is mono ...

  4. 洛谷——P3801 红色的幻想乡

    P3801 红色的幻想乡 推荐阅读 https://blog.csdn.net/qq_41252892/article/details/79035942 非常清楚 线段树单点修改 emmm没什么了 # ...

  5. 文艺平衡树(splay模板)

    题干:splay模板,要求维护区间反转. splay是一种码量小于treap,但支持排名,前驱后继等treap可求的东西,也支持区间反转的平衡树. 但是有两个坏处: 1.splay常数远远大于trea ...

  6. 最近公共祖先-三(RMQ-ST)

    描述 上上回说到,小Hi和小Ho使用了Tarjan算法来优化了他们的"最近公共祖先"网站,但是很快这样一个离线算法就出现了问题:如果只有一个人提出了询问,那么小Hi和小Ho很难决定 ...

  7. 爬虫app信息抓取之apk反编译抓取

    之前也抓过一些app,数据都比较好取,也没有研究的太深,毕竟还有android 模拟器+ appium 的方法.直到遇见了一款app ,具体名字就不说了,它安装在模拟器上竟然打不开 !!第一次遇见上网 ...

  8. [工具]Visual Studio

    1,Tab键的使用: 如不说有这样的代码:public Member member { get; set; } 当我们编辑完Member后,按一下Tab键,就能够将光标锁定到member上,等待键盘输 ...

  9. MyISAM和InnoDB索引实现对比

    MyISAM索引实现 MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址.如图:  这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引 ...

  10. oracle 9i/10g/11g(11.2.0.3)安装包和PATCH下载地址汇总

    今天上PUB看见一位热心人汇总了这么个地址列表,转发来空间: 把下面的地址复制到讯雷里就可以下载. -------------------------------------------------- ...