Androidadbbugreport⼯具分析和使⽤
bugreport是什么,怎么⽤?
Android系统想要成为⼀个功能完备,⽣态繁荣的操作系统,那就必须提供完整的应⽤开发环境。⽽在应⽤开发中,app程序的调试分析是⽇常⽣产中进程会进⾏的⼯作。Android为了⽅便开发⼈员分析整个系统平台和某个app在运⾏⼀段时间之内的所有信息,专门开发了bugreport⼯具。这个⼯具使⽤起来⼗分简单,只要在终端执⾏(linux或者win):
adb bugreport >
Brightness
CPU running
Charging on
Charging status
Health
JobScheduler
Kernel only uptime
Level
Package active
Partial wakelock
Phone scanning
Phone state
Plug
Plugged
Screen
Temperature
Top app
Voltage
Wifi on
Wifi running
Wifi supplicant
bugreport的原理是什么?
下⾯我们简要分析⼀下adb bugreport运⾏的原理。我们知道,使⽤bugreport只要执⾏adb bugreport命令就可以了,因此我们的分析肯定是从adbd这个daemon进程开始,我们查看这个进程的代码的时候发现这⾥处理了bugreport选项:
adb_commandline@system/core/adb/commandline.cpp
我们可以清楚地看到,这⾥判断如果附带的参数是bugreport的话,那就直接调⽤send_shell_comman
d函数处理,这个函数的代码⽐较简单,我们就不分析了,这个函数的功能就是使⽤shell执⾏参数中的命令,因此我们这⾥相当于执⾏了bugreport命令。
在android设备中,bugreport命令存在于system/bin/⽬录下,这是⼀个可执⾏⽂件,所以我们要查看这个可执⾏⽂件实现的地⽅,它的实现代码在/frameworks/native/cmds/bugreport/⽬录下:
我们看到,bugreport的实现是⽐较简单的,只有⼀个Android.mk和⼀个cpp实现代码,我们先看⼀下Android.mk⽂件:
这⾥我们看到该⽬录下的代码会被编译成⼀个名字叫做bugreport的可执⾏⽂件,这就是我们想要的。现在我们看⼀下bugreport.cpp⽂件的实现,这个⽂件中代码⽐较简单,只有⼀个main函数:
// This program will trigger the dumpstate service to start a call to
// dumpstate, then connect to the dumpstate local client to read the
// output. All of the dumpstate output is written to stdout, including
// any errors encountered while reading/writing the output.
int main() {
// Start the dumpstate service.
property_set("ctl.start", "dumpstate");
// Socket will not be available until service starts.
int s;
for (int i = 0; i < 20; i++) {
s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (s >= 0)
break;
// Try again in 1 second.
sleep(1);
}
if (s == -1) {
printf("Failed to connect to dumpstate service: %s\n", strerror(errno));
return1;
}
// Set a timeout so that if nothing is read in 3 minutes, we'll stop
/
/ reading and quit. No timeout in dumpstate is longer than 60 seconds,
// so this gives lots of leeway in case of unforeseen time outs.
struct timeval tv;
tv.tv_sec = 3 * 60;
tv.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
}
}
while (1) {
char buffer[65536];
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
// EAGAIN really means time out, so change the errno.
if (errno == EAGAIN) {
errno = ETIMEDOUT;
}
printf("\nBugreport read terminated abnormally (%s).\n", strerror(errno));
break;
}
ssize_t bytes_to_send = bytes_read;
ssize_t bytes_written;
do {
bytes_written = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
buffer + bytes_read - bytes_to_send,
bytes_to_send));
if (bytes_written == -1) {
printf("Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
bytes_read, bytes_to_send, strerror(errno));
return1;
}
bytes_to_send -= bytes_written;
} while (bytes_written != 0 && bytes_to_send > 0);
}
close(s);
return0;
}
这⾥的代码⾮常简单,主要的逻辑就是:
1.启动dumpstate service
2. 和dumpstate service建⽴socket链接
3. 从socket中读取数据,并且答应到stdout中
4. 读取完成之后关闭socket,然后退出
因此,我们分析的重点需要转移到dumpstate中了。这⾥说明⼀下,前⾯启动dumpstate service的⽅法是使⽤系统属性来实现,这个属性的改变消息会被init进程收到,然后init进程会启动dumpstate这个服务。
dumpstate其实也是⼀个可执⾏⽂件,也存在于system/bin⽬录下。现在我们明⽩了,其实bugreport就是dumpstate,只是bugreport 将dumpstate包装了⼀下⽽已。
现在我们需要分析⼀下dumpstate的实现,它的实现代码在:frameworks/native/cmds/dumpstate⽬录下,我们看下这个⽬录下的代码结构:
这⾥的代码也是⼗分简单,只要少数的⼏个实现⽂件,其中main函数在dumpstate.c⽂件中,这个main函数我们这⾥不详细分析了,总结下它的主要⼯作:
1. 根据启动参数,初始化相关资源
2. 如果启动参数中带有-s的话(init启动会加上这个参数),就表⽰使⽤socket,那么就启动socket,
并且在这个socket中等待链接。
3. 如果client端(也就是bugreport进程)链接成功,那就初始化所要⽤到的内存,并且设置优先级为较⾼优先级,防⽌被OOM⼲掉。
4. 然后使⽤vibrator震动⼀下(如果设备有这个硬件的话),提⽰⽤户开始截取log了
5. 调⽤dumpstate函数开始真正的dump⼯作
6. dump完成之后再次调⽤vibrator震动3次,提⽰⽤户dump完成。
现在我们看下dumpstate函数的实现:
/* dumps the current system state to stdout */
static void dumpstate() {
unsigned long timeout;
time_t now = time(NULL);
char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
char network[PROPERTY_VALUE_MAX], date[80];
char build_type[PROPERTY_VALUE_MAX];
property_get("ro.build.display.id", build, "(unknown)");
property_get("ro.build.fingerprint", fingerprint, "(unknown)");
property_get("pe", build_type, "(unknown)");
property_get("ro.baseband", radio, "(unknown)");
property_get("ro.bootloader", bootloader, "(unknown)");
property_get("gsm.operator.alpha", network, "(unknown)");
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
printf("========================================================\n");
printf("== dumpstate: %s\n", date);
printf("========================================================\n");
printf("\n");
printf("Build: %s\n", build);
printf("Build fingerprint: '%s'\n", fingerprint); /* format is important for other tools */
printf("Bootloader: %s\n", bootloader);
printf("Radio: %s\n", radio);
printf("Network: %s\n", network);
printf("Kernel: ");
dump_file(NULL, "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
printf("\n");unknown怎么处理
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
run_command("UPTIME", 10, "uptime", NULL);
dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
dump_file("MEMORY INFO", "/proc/meminfo");
run_command("CPU INFO", 10, "top", "-n", "1", "-d", "1", "-m", "30", "-t", NULL);
run_command("PROCRANK", 20, "procrank", NULL);
dump_file("VIRTUAL MEMORY STATS", "/proc/vmstat");
dump_file("VMALLOC INFO", "/proc/vmallocinfo");
dump_file("SLAB INFO", "/proc/slabinfo");
dump_file("ZONEINFO", "/proc/zoneinfo");
dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
dump_file("BUDDYINFO", "/proc/buddyinfo");
dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");    dump_file("KERNEL SYNC", "/d/sync");
run_command("PROCESSES", 10, "ps", "-P", NULL);
run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
run_command("PROCESSES (SELINUX LABELS)", 10, "ps", "-Z", NULL);
run_command("LIBRANK", 10, "librank", NULL);
do_dmesg();
run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
if (screenshot_path[0]) {
ALOGI("taking screenshot\n");
ALOGI("taking screenshot\n");
run_command(NULL, 10, "/system/bin/screencap", "-p", screenshot_path, NULL);
ALOGI("wrote screenshot: %s\n", screenshot_path);
}
// dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
// calculate timeout
timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
if (timeout < 20000) {
timeout = 20000;
}
run_command("SYSTEM LOG", timeout / 1000, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
timeout = logcat_timeout("events");
if (timeout < 20000) {
timeout = 20000;
}
run_command("EVENT LOG", timeout / 1000, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);    timeout = logcat_timeout("radio");
if (timeout < 20000) {
timeout = 20000;
}
run_command("RADIO LOG", timeout / 1000, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);    run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
/* show the traces we collected in main(), if that was done */
if (dump_traces_path != NULL) {
dump_file("VM TRACES JUST NOW", dump_traces_path);
}
/* only show ANR traces if they're less than 15 minutes old */
struct stat st;
char anr_traces_path[PATH_MAX];
property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
if (!anr_traces_path[0]) {
printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
} else {
int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
if (fd < 0) {
printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
} else {
dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
}
}
/
* slow traces for slow operations */
if (anr_traces_path[0] != 0) {
int tail = strlen(anr_traces_path)-1;
while (tail > 0 && anr_traces_path[tail] != '/') {
tail--;
}
int i = 0;
while (1) {
sprintf(anr_traces_path+tail+1, "", i);
if (stat(anr_traces_path, &st)) {
// No traces file at this index, done with the files.
break;
}
dump_file("VM TRACES WHEN SLOW", anr_traces_path);
i++;
}
}
int dumped = 0;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。