android抓trace工具,Android trace文件抓取原理

Android系统每次发生ANR后,都会在/data/anr/目录下面输出一个traces.txt文件,这个文件记录了发生问题进程的虚拟机相关信息和线程的堆栈信息,通过这个文件我们就能分析出当前线程正在做什么操作,继而可以分析出ANR的原因,它的生成与Signal Catcher线程是息息相关的,每一个从zygote派生出来的子进程都会有一个Signal Catcher线程,可以在终端的Shell环境下执行”ps -t &pid” 命令得到对应pid进程所有的子线程列表,如下图所示:

USER PID PPID VSIZE RSS WCHAN PC NAME

system 2953 2646 2784184 223904 SyS_epoll_ 7f92d20520 S system_server

system 2958 2953 2784184 223904 do_sigtime 7f92d20700 S Signal Catcher

system 2960 2953 2784184 223904 futex_wait 7f92cd3f20 S ReferenceQueueD

system 2961 2953 2784184 223904 futex_wait 7f92cd3f20 S FinalizerDaemon

system 2962 2953 2784184 223904 futex_wait 7f92cd3f20 S FinalizerWatchd

system 2963 2953 2784184 223904 futex_wait 7f92cd3f20 S HeapTaskDaemon

system 2970 2953 2784184 223904 binder_thr 7f92d20610 S Binder_1

system 2972 2953 2784184 223904 binder_thr 7f92d20610 S Binder_2

system 2985 2953 2784184 223904 SyS_epoll_ 7f92d20520 S android.bg

system 2986 2953 2784184 223904 SyS_epoll_ 7f92d20520 S ActivityManager

system 2987 2953 2784184 223904 SyS_epoll_ 7f92d20520 S android.ui

system 2988 2953 2784184 223904 SyS_epoll_ 7f92d20520 S android.fg

system 2989 2953 2784184 223904 inotify_re 7f92d20fe8 S FileObserver

system 2990 2953 2784184 223904 SyS_epoll_ 7f92d20520 S android.io

system 2991 2953 2784184 223904 SyS_epoll_ 7f92d20520 S android.display

system 2992 2953 2784184 223904 futex_wait 7f92cd3f20 S CpuTracker

system 2993 2953 2784184 223904 SyS_epoll_ 7f92d20520 S PowerManagerSer

上面打印的是system_server的线程列表,其中2958这个线程便是"Signal Catcher"线程,Signal是指进程发生问题时候Kernel发给它的信号,Signal Catcher这个线程就是在用户空间来处理信号。

Linux软中断信号(信号)是系统用来通知进程发生了异步事件,是在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。除此之外,信号机制除了基本通知功能外,还可以传递附加信息,总之信号是一种Linux系统中进程间通信手段,Linux默认已经给进程的信号有处理,如果你不关心信号的话,默认系统行为就好了,但是如果你关心某些信号,例如段错误SIGSEGV(一般是空指针、内存访问越界的时候由系统发送给当事进程),那么你就得重新编写信号处理函数来覆盖系统默认的行为,这种机制对于程序调试来说是很重要的一种手段,因为像这种段错误是不可预知的,它可以发生在任何地方,也就是说在应用程序的代码里面是不能处理这种异常的,这个时候要定位问题的话,就只能依靠信号这种机制,虽然应用程序不知道什么时候发生了段错误,但是系统底层(Kernel)是知道的,Kernel发现应用程序访问了非法地址的时候,就会发送一个SIGSEGV信号给该进程,在该进程从内核空间返回到用户空间时会检测是否有信号等待处理,如果用户自定义了信号处理函数,那么这个时候就会调用用户编写的函数,这个时候就可以做很多事情了:例如dump当前进程的堆栈、获取系统的全局信息(内存、IO、CPU)等,而这些信息对分析问题是非常重要的。

回到主题,Signal Catcher这个线程是由Android Runtime去创建的,在新起一个应用进程的时候,system_server进程会通过socket和zygote取得通信,并由zygote负责去创建一个子进程,在Linux系统中,创建一个进程一般通过fork机制,Android也不例外,zygote的子进程起来后,默认都会有一个main线程,在该main线程中都会调用到[email protected]这个函数,在这个函数中又会调用[email protected]这个函数,这个函数里面会新建一个SignalCatcher对象,Signal Catcher线程的起源便是来源于此。

void Runtime::StartSignalCatcher() {

if (!is_zygote_) {

signal_catcher_ = new SignalCatcher(stack_trace_file_);

}

}

在SignalCatcher的构造函数中会调用 pthread_create来创建一个传统意义上的Linux线程,说到底Android是一个基于Linux的系统,ART的线程概念直接复用了Linux的,毕竟Linux发展了这么久,线程机制这一方面已经很成熟了,ART没必要重复造轮子,在User空间再实现一套自己的线程机制,pthread_create是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数,它的函数原型为:

int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);

tidp 返回线程标识符的指针,attr 设置线程属性,start_rtn 是线程运行函数的起始地址,arg 是传递给start_rtn的参数。在SignalCatcher的构造函数中调用该函数的语句为:

CHECK_PTHREAD_CALL(pthread_create, (&pthread_, nullptr,&Run, this), "signal catcher thread");

CHECK_PTHREAD_CALL是一个宏定义,最终会调用pthread_create来新起一个Linux线程,从pthread_create的参数来看,线程创建出来之后会执行[email protected]这个函数,并且把this指针也就是创建的SignalCatcher对象作为参数传递给了Run函数,看一下Run函数的实现:

void* SignalCatcher::Run(void* arg) {

......

Runtime* runtime = Runtime::Current();

CHECK(runtime->AttachCurrentThread("Signal Catcher", true, runtime->GetSystemThreadGroup(),//attach linux线程,使得该线程拥有调用JNI函数的能力

Thread* self = Thread::Current();

......

// Set up mask with signals we want to handle.

SignalSet signals;

signals.Add(SIGQUIT); //监听SIGQUIT信号

signals.Add(SIGUSR1);

while (true) {

int signal_number = signal_catcher->WaitForSignal(self, signals); //等待Kernel给进程发送信号

if (signal_catcher->ShouldHalt()) {

runtime->DetachCurrentThread();

return nullptr;

}

switch (signal_number) {

case SIGQUIT:

signal_catcher->HandleSigQuit(); //调用HandleSigQuit去处理SIGQUIT信号

break;

......

default:

LOG(ERROR) << "Unexpected signal %d" << signal_number;

break;

}

}

}

在这个函数里面,首先调用 runtime->AttachCurrentThread去attach当前线程,然后安装信号处理函数,最后就是一个无限循环,在循环里等待信号的到来,如果Kernel发送了信号给虚拟机进程,那么就会执行对应信号的处理过程,这篇文章只关注SIGQUIT信号的处理,下面一步一步来分析这四个过程。

A

你可能感兴趣的:(android抓trace工具)