Android稳定性系列5 Native进程的Trace原理

一 概述

当发生 ANR,对于 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 命令后:

  • Client 进程调用 send_request() 方法向 debuggerd 服务端发出 DEBUGGER_ACTION_DUMP_BACKTRACE 命令
  • Debugggerd 进程收到该命令,fork 子进程中再执行 worker_process() 过程
  • 子进程通过 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);
  dump_thread(&log, map, pid, tid);

  for (pid_t sibling : siblings) {
    dump_thread(&log, map, pid, sibling);}

  dump_process_footer(&log, pid);
}

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(函数名+偏移量)
#01pc 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 而来的。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值