bugreport是什么,怎么用?
Android系统想要成为一个功能完备,生态繁荣的操作系统,那就必须提供完整的应用开发环境。而在应用开发中,app程序的调试分析是日常生产中进程会进行的工作。Android为了方便开发人员分析整个系统平台和某个app在运行一段时间之内的所有信息,专门开发了bugreport工具。这个工具使用起来十分简单,只要在终端执行(linux或者win):
adb bugreport > bugreport.txt
即可生成bugreport文件。但是有一个问题是,这个生成的文件有的时候异常庞大,能够达到15M+,想一想对于一个txt文本格式的文件内容长度达到了15M+是一个什么概念,如果使用文本工具打开查看将是一个噩梦。因此google针对android 5.0(api 21)以上的系统开发了一个叫做battery historian的分析工具,这个工具就是用来解析这个txt文本文件,然后使用web图形的形式展现出来,这样出来的效果更加人性化,更加可读。它的基本界面像下面这个样子:
目前google已经将bettery historian开源了,开源项目的地址:
https://github.com/google/battery-historian
google写了一个比较详细的说明文档,大家可以自行查阅一下。这个工具可以查看以下信息:
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数据也可以有不同的解析和阅读方式,你如果不太喜欢google的battery historian的话,你还有别的选择,那就是选择Sony开源的ChkBugReport,这个工具提供了不同于battery historian的视角去解读bugreport文件,见面简单明了:
这个项目的文档:
http://developer.sonymobile.com/2012/01/25/new-bugreport-analysis-tool-released-as-open-source/
开源地址首页:
https://github.com/sonyxperiadev/ChkBugReport
这里说明一下,笔者使用过ChkBugReport这个工具,感觉很不错,最好结合google的battery historian;另外ChkBugReport这个工具还有一点bug,不过不影响使用。
bugreport的原理是什么?
下面我们简要分析一下adb bugreport运行的原理。我们知道,使用bugreport只要执行adb bugreport命令就可以了,因此我们的分析肯定是从adbd这个daemon进程开始,我们查看这个进程的代码的时候发现这里处理了bugreport选项:
[email protected]/core/adb/commandline.cpp
我们可以清楚地看到,这里判断如果附带的参数是bugreport的话,那就直接调用send_shell_command函数处理,这个函数的代码比较简单,我们就不分析了,这个函数的功能就是使用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));
return 1;
}
// 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));
return 1;
}
bytes_to_send -= bytes_written;
} while (bytes_written != 0 && bytes_to_send > 0);
}
close(s);
return 0;
}
这里的代码非常简单,主要的逻辑就是:
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("ro.build.type", 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");
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");
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, "slow%02d.txt", 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++) {
if (tombstone_data[i].fd != -1) {
dumped = 1;
dump_file_from_fd("TOMBSTONE", tombstone_data[i].name, tombstone_data[i].fd);
tombstone_data[i].fd = -1;
}
}
if (!dumped) {
printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
}
dump_file("NETWORK DEV INFO", "/proc/net/dev");
dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
if (!stat(PSTORE_LAST_KMSG, &st)) {
/* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
dump_file("LAST KMSG", PSTORE_LAST_KMSG);
} else {
/* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
dump_file("LAST KMSG", "/proc/last_kmsg");
}
/* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
run_command("LAST LOGCAT", 10, "logcat", "-L", "-v", "threadtime",
"-b", "all", "-d", "*:v", NULL);
/* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
run_command("NETWORK INTERFACES", 10, "ip", "link", NULL);
run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL);
run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL);
run_command("IP RULES", 10, "ip", "rule", "show", NULL);
run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
dump_route_tables();
run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
run_command("IPTABLES", 10, SU_PATH, "root", "iptables", "-L", "-nvx", NULL);
run_command("IP6TABLES", 10, SU_PATH, "root", "ip6tables", "-L", "-nvx", NULL);
run_command("IPTABLE NAT", 10, SU_PATH, "root", "iptables", "-t", "nat", "-L", "-nvx", NULL);
/* no ip6 nat */
run_command("IPTABLE RAW", 10, SU_PATH, "root", "iptables", "-t", "raw", "-L", "-nvx", NULL);
run_command("IP6TABLE RAW", 10, SU_PATH, "root", "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
run_command("WIFI NETWORKS", 20,
SU_PATH, "root", "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
#ifdef FWDUMP_bcmdhd
run_command("ND OFFLOAD TABLE", 5,
SU_PATH, "root", "wlutil", "nd_hostip", NULL);
run_command("DUMP WIFI INTERNAL COUNTERS (1)", 20,
SU_PATH, "root", "wlutil", "counters", NULL);
run_command("ND OFFLOAD STATUS (1)", 5,
SU_PATH, "root", "wlutil", "nd_status", NULL);
#endif
dump_file("INTERRUPTS (1)", "/proc/interrupts");
run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "connectivity", "--diag", NULL);
#ifdef FWDUMP_bcmdhd
run_command("DUMP WIFI STATUS", 20,
SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
run_command("DUMP WIFI INTERNAL COUNTERS (2)", 20,
SU_PATH, "root", "wlutil", "counters", NULL);
run_command("ND OFFLOAD STATUS (2)", 5,
SU_PATH, "root", "wlutil", "nd_status", NULL);
#endif
dump_file("INTERRUPTS (2)", "/proc/interrupts");
print_properties();
run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
printf("------ BACKLIGHTS ------\n");
printf("LCD brightness=");
dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
printf("Button brightness=");
dump_file(NULL, "/sys/class/leds/button-backlight/brightness");
printf("Keyboard brightness=");
dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness");
printf("ALS mode=");
dump_file(NULL, "/sys/class/leds/lcd-backlight/als");
printf("LCD driver registers:\n");
dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
printf("\n");
/* Binder state is expensive to look at as it uses a lot of memory. */
dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
printf("========================================================\n");
printf("== Board\n");
printf("========================================================\n");
dumpstate_board();
printf("\n");
/* Migrate the ril_dumpstate to a dumpstate_board()? */
char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30");
if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) {
if (0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1)) {
// su does not exist on user builds, so try running without it.
// This way any implementations of vril-dump that do not require
// root can run on user builds.
run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
"vril-dump", NULL);
} else {
run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
SU_PATH, "root", "vril-dump", NULL);
}
}
printf("========================================================\n");
printf("== Android Framework Services\n");
printf("========================================================\n");
/* the full dumpsys is starting to take a long time, so we need
to increase its timeout. we really need to do the timeouts in
dumpsys itself... */
run_command("DUMPSYS", 60, "dumpsys", NULL);
printf("========================================================\n");
printf("== Checkins\n");
printf("========================================================\n");
run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "batterystats", "-c", NULL);
run_command("CHECKIN MEMINFO", 30, "dumpsys", "meminfo", "--checkin", NULL);
run_command("CHECKIN NETSTATS", 30, "dumpsys", "netstats", "--checkin", NULL);
run_command("CHECKIN PROCSTATS", 30, "dumpsys", "procstats", "-c", NULL);
run_command("CHECKIN USAGESTATS", 30, "dumpsys", "usagestats", "-c", NULL);
run_command("CHECKIN PACKAGE", 30, "dumpsys", "package", "--checkin", NULL);
printf("========================================================\n");
printf("== Running Application Activities\n");
printf("========================================================\n");
run_command("APP ACTIVITIES", 30, "dumpsys", "activity", "all", NULL);
printf("========================================================\n");
printf("== Running Application Services\n");
printf("========================================================\n");
run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL);
printf("========================================================\n");
printf("== Running Application Providers\n");
printf("========================================================\n");
run_command("APP SERVICES", 30, "dumpsys", "activity", "provider", "all", NULL);
printf("========================================================\n");
printf("== dumpstate: done\n");
printf("========================================================\n");
}
上面的代码比较长,是因为所要dump的模块太多,但是基本逻辑还是比较清楚的,我们看到基本的数据来源就是:
1.系统属性
2./proc和/sys节点文件
3.执行shell命令获得相关输出
4.logcat输出
5.Android Framework Services信息基本使用dumpsys命令通过binder调用服务中的dump函数获得信息
这里我们需要看一下dumpsys命令的实现,这个命令也是比较简单,实现全部在main函数中:
int main(int argc, char* const argv[])
{
signal(SIGPIPE, SIG_IGN);
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == NULL) {
ALOGE("Unable to get default service manager!");
aerr << "dumpsys: Unable to get default service manager!" << endl;
return 20;
}
Vector<String16> services;
Vector<String16> args;
bool showListOnly = false;
if ((argc == 2) && (strcmp(argv[1], "-l") == 0)) {
showListOnly = true;
}
if ((argc == 1) || showListOnly) {
services = sm->listServices();
services.sort(sort_func);
args.add(String16("-a"));
} else {
services.add(String16(argv[1]));
for (int i=2; i<argc; i++) {
args.add(String16(argv[i]));
}
}
const size_t N = services.size();
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << endl;
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
aout << " " << services[i] << endl;
}
}
}
if (showListOnly) {
return 0;
}
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
if (N > 1) {
aout << "------------------------------------------------------------"
"-------------------" << endl;
aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
}
int err = service->dump(STDOUT_FILENO, args);
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err)
<< ") " << services[i] << endl;
}
} else {
aerr << "Can‘t find service: " << services[i] << endl;
}
}
return 0;
}
我们看到它的代码逻辑就是,通过Binder的SM查找参数中的service,然后通过:
int err = service->dump(STDOUT_FILENO, args);
这句来调用service的dump函数。
dumpstate会调用到所有binder中的service的dump函数,因为dumpstate函数执行了这一句:
/* the full dumpsys is starting to take a long time, so we need
to increase its timeout. we really need to do the timeouts in
dumpsys itself... */
run_command("DUMPSYS", 60, "dumpsys", NULL);
直接执行dumpsys,没有参数,并且注释中也说的很清楚,就是采集所有的信息。这会执行以下service的dump函数(执行dumpsys | grep “DUMP OF SERVICE”可以看到):
DUMP OF SERVICE DockObserver:
DUMP OF SERVICE SurfaceFlinger:
DUMP OF SERVICE accessibility:
DUMP OF SERVICE account:
DUMP OF SERVICE activity:
DUMP OF SERVICE alarm:
DUMP OF SERVICE android.security.keystore:
DUMP OF SERVICE android.service.gatekeeper.IGateKeeperService:
DUMP OF SERVICE appops:
DUMP OF SERVICE appwidget:
DUMP OF SERVICE assetatlas:
DUMP OF SERVICE audio:
DUMP OF SERVICE backup:
DUMP OF SERVICE battery:
DUMP OF SERVICE batteryproperties:
DUMP OF SERVICE batterystats:
DUMP OF SERVICE bluetooth_manager:
DUMP OF SERVICE carrier_config:
DUMP OF SERVICE clipboard:
DUMP OF SERVICE commontime_management:
DUMP OF SERVICE connectivity:
DUMP OF SERVICE consumer_ir:
DUMP OF SERVICE content:
DUMP OF SERVICE country_detector:
DUMP OF SERVICE cpuinfo:
DUMP OF SERVICE dbinfo:
DUMP OF SERVICE device_policy:
DUMP OF SERVICE deviceidle:
DUMP OF SERVICE devicestoragemonitor:
DUMP OF SERVICE diskstats:
DUMP OF SERVICE display:
DUMP OF SERVICE display.qservice:
DUMP OF SERVICE dreams:
DUMP OF SERVICE drm.drmManager:
DUMP OF SERVICE dropbox:
DUMP OF SERVICE ethernet:
DUMP OF SERVICE fingerprint:
DUMP OF SERVICE gfxinfo:
DUMP OF SERVICE graphicsstats:
DUMP OF SERVICE imms:
DUMP OF SERVICE input:
DUMP OF SERVICE input_method:
DUMP OF SERVICE iphonesubinfo:
DUMP OF SERVICE isms:
DUMP OF SERVICE isub:
DUMP OF SERVICE jobscheduler:
DUMP OF SERVICE launcherapps:
DUMP OF SERVICE location:
DUMP OF SERVICE lock_settings:
DUMP OF SERVICE media.audio_flinger:
DUMP OF SERVICE media.audio_policy:
DUMP OF SERVICE media.camera:
DUMP OF SERVICE media.camera.proxy:
DUMP OF SERVICE media.player:
DUMP OF SERVICE media.radio:
DUMP OF SERVICE media.resource_manager:
DUMP OF SERVICE media.sound_trigger_hw:
DUMP OF SERVICE media_projection:
DUMP OF SERVICE media_router:
DUMP OF SERVICE media_session:
DUMP OF SERVICE meminfo:
DUMP OF SERVICE midi:
DUMP OF SERVICE mount:
DUMP OF SERVICE netpolicy:
DUMP OF SERVICE netstats:
DUMP OF SERVICE network_management:
DUMP OF SERVICE network_score:
DUMP OF SERVICE nfc:
DUMP OF SERVICE notification:
DUMP OF SERVICE package:
DUMP OF SERVICE permission:
DUMP OF SERVICE persistent_data_block:
DUMP OF SERVICE phone:
DUMP OF SERVICE power:
DUMP OF SERVICE print:
DUMP OF SERVICE processinfo:
DUMP OF SERVICE procstats:
DUMP OF SERVICE restrictions:
DUMP OF SERVICE rttmanager:
DUMP OF SERVICE samplingprofiler:
DUMP OF SERVICE scheduling_policy:
DUMP OF SERVICE search:
DUMP OF SERVICE sensorservice:
DUMP OF SERVICE serial:
DUMP OF SERVICE servicediscovery:
DUMP OF SERVICE simphonebook:
DUMP OF SERVICE sip:
DUMP OF SERVICE statusbar:
DUMP OF SERVICE telecom:
DUMP OF SERVICE telephony.registry:
DUMP OF SERVICE textservices:
DUMP OF SERVICE trust:
DUMP OF SERVICE uimode:
DUMP OF SERVICE updatelock:
DUMP OF SERVICE usagestats:
DUMP OF SERVICE usb:
DUMP OF SERVICE user:
DUMP OF SERVICE vibrator:
DUMP OF SERVICE voiceinteraction:
DUMP OF SERVICE wallpaper:
DUMP OF SERVICE webviewupdate:
DUMP OF SERVICE wifi:
DUMP OF SERVICE wifip2p:
DUMP OF SERVICE wifiscanner:
DUMP OF SERVICE window:
这里总结以下,上面的bugreport整体逻辑如下图描述(如果图片太小看不清,请下载图片并查看):
adb bugreport的其他选项
bugreport本身并没有什么选项,主要是通过dumpsys等命令配合完成,详见battery historian项目主页:https://github.com/google/battery-historian,以下是个总结:
1). 重置电池统计信息:
adb shell dumpsys batterystats --reset
2). Wakelock analysis全部wakelock信息:
adb shell dumpsys batterystats --enable full-wake-history
3). Kernel trace analysis分析内核,主要分析wakeup source和wakelock activities,首先使能kernel分析:
$ adb root
$ adb shell
# Set the events to trace.
$ echo "power:wakeup_source_activate" >> /d/tracing/set_event
$ echo "power:wakeup_source_deactivate" >> /d/tracing/set_event
# The default trace size for most devices is 1MB, which is relatively low and might cause the logs to overflow.
# 8MB to 10MB should be a decent size for 5-6 hours of logging.
$ echo 8192 > /d/tracing/buffer_size_kb
$ echo 1 > /d/tracing/tracing_on
然后获得log:
$ echo 0 > /d/tracing/tracing_on
$ adb pull /d/tracing/trace <some path>
# Take a bug report at this time.
$ adb bugreport > bugreport.txt