Android adb bugreport工具分析和使用

BugReport 概述 Android BugReport 中包含文本 (.txt) 格式的 dumpsys、dumpstate、logcat 数据,便于轻松搜索特定内容。以下各部分详细说明了BugReport 的组成部分及常见问题,并提供了关于查找与这些错误相关的日志的实用提示和 grep 命令。 一、如何阅读 BugReport 1、Logcat Logcat 日志是所有 Logcat 信息采用字符串形式的转储。其中,包括:System Log、Event Log。 (1)System Log 部分专门用于记录框架方面的信息, 阅读详情
AI摘要

本文介绍了Android的bugreport工具,用于收集系统和应用的运行信息。通过adb bugreport命令生成的文件可以用battery historian或ChkBugReport进行解析。bugreport的原理涉及启动dumpstate服务,通过binder调用服务的dump函数获取信息。文章还提到了一些配合dumpsys的选项,如重置电池统计和wakelock分析。

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选项:
adb_commandline@system/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
手机端手抓取Bug report PC端抓取Bug report和手机端手抓取Bug report的方法 阅读详情

相关推荐

Log抓取和分析-BugReport

参考文章: http://blog.youkuaiyun.com/createchance/article/details/51954142 Google为了方便Android开发人员分析整个系统平台或者某个APP运行一段时间后的所有信息,专门开发了adb bugreport工具。开发人员可以使用adb bugreport命令获取系统运行的所有log信息。命令如下:adb bugreport &gt; bug...

leizhiheng320的博客 3万+

Android应用调试-BugReport

文章目录一、BugReport是什么?二、如何获取bug reports1.从手机上获取bug reports2.使用 adb 获取bug reports三、如何对BugReport进行分析?总结 一、BugReport是什么? 在安卓应用开发中,app程序的调试分析是日常生产中进程会进行的工作。而BugReport中包含了设备日志、堆栈轨迹和其他诊断信息,可以帮助开发人员查找和修复应用中的错误。 二、如何获取bug reports 一般来说有三种方法可以获取Bug reports,分别是通过安卓手机直接

weixin_49858366的博客 8044

bugreport 分析之chkbugreport

由于目前的android安全意识越来越高,导致pull文件到电脑的时候比较麻烦,anr报告导出到本地就不太好用了 那有没有好点儿的办法呢?当然,这也是我写这个笔记的内容 bugreport这个工具是我最近了解的,用来导出android中app的异常日志 首先目的是为了导出android的日志,所以adb环境必不可少。所以前提是adb的环境准备好 1.adb devices,查看手机是否连接到电脑 2.执行以下adb命令 adb bugreport > bugreport.txt 3.

dayuzhuangzhi的博客 1236

adb bugreport :查看设备所有信息(获取错误报告)

1.命令: $ adb bugreport E:\Reports\MyBugReports //7.0以下,输出到文件当前路径的bugreport.txt文件 $ adb bugreport > bugreport.txt 2.生成文件: bugreport-BUILD_ID-DATE.zip 3.其中bugreport-BUILD_ID-DATE.txt,此文件就是错误报告: 包含系统服务 (dumpsys)、错误日志 (dumpstate) 和系统消息日志 (logcat) 的诊断输出。 系

shenyang的博客 1万+

手机功耗续航分析利器 ChkBugReport

和你一起终身学习,这里是程序员Android经典好文推荐,通过阅读本文,您将收获以下知识点:一、ChkBugReport 概述二、ChkBugReport 获取方法三、ChkBugReport 使用方法四、ChkBugReport 使用详细说明一、ChkBugReport 概述ChkBugReport 工具的目的是分析处理手机上生成的bugreport,并且为开发者提取尽可能多的有用数据。ChkB...

程序员Android 876

android bugreport 分析

帮助你快速掌握如何分析android bugreport

adb bugreport 原理

0.文章学习 https://www.2cto.com/kf/201607/528696.html 1.使用场景 使用 adb bugreport 可以导出功耗分析所需文件,然后通过 https://github.com/google/battery-historian 进行更加详细的分析 adb bugreport &gt; Bugreport.zip adb bugreport...

苏法迪的专栏 9467

Windows 11 部署 Docker Desktop:从 WSL 报错到服务配置的完整排障实录

本文详细记录了在Windows 11上部署Docker Desktop时遇到的WSL报错问题及完整解决方案。从Hyper-V配置、BIOS设置到系统服务检查,提供了一系列实用命令和排查步骤,帮助开发者快速解决'Unexpecter WSL error'等常见问题,实现Docker与WSL2的稳定运行。

weixin_30687051的博客 120

Android adb bugreport 工具分析和使用

执行 adb bugreport 会自动下载一个 zip 的压缩包 至于有什么用,我也不晓得。开发让我提供,我就提供。 bugreport是什么,怎么用? Android系统想要成为一个功能完备,生态繁荣的操作系统,那就必须提供完整的应用开发环境。而在应用开发中,app程序的调试分析是日常生产中进程会进行的工作。Android为了方便开发人员分析整个系统平台和某个app在运行一段时间之内的所有信息,专门开发了bugreport工具。这个工具使用起来十分简单,只要在终端执行(linux或者win): 了解更多

weixin_45598506的博客 1454

基于Plecs的LLC谐振变换器闭环设计:从PI参数整定到动态响应优化

本文详细介绍了在Plecs仿真环境中,如何为半桥LLC谐振变换器设计并优化闭环控制系统。从开环模型搭建与特性分析入手,重点阐述了基于频域法的PI控制器参数整定流程,并通过负载阶跃、输入电压突变等测试优化动态响应。文章还分享了从仿真模型过渡到实际硬件时的关键注意事项,帮助工程师实现稳定、高效的电源设计。

e4f5g6h的博客 624

利用Ghidra与SVD插件实现STM32固件深度逆向分析

本文详细介绍了如何利用Ghidra逆向工程框架结合SVD插件,实现对STM32固件(.bin文件)的深度逆向分析。通过加载描述芯片外设寄存器的SVD文件,Ghidra能将晦涩的硬件地址操作转换为可读的寄存器符号,极大提升了对嵌入式固件中通信协议、安全校验等关键逻辑的分析效率。文章涵盖了从Java环境搭建、工具配置到实战逆向解析的完整流程。

peach的博客 942

Android bugreport资料汇总

参考链接: Log抓取和分析-BugReporthttps://blog.youkuaiyun.com/leizhiheng320/article/details/79410088 (工具介绍,log分析等) Android adb bugreport工具分析和使用https://blog.youkuaiyun.com/createchance/article/details/51954142 ...

逐渐成长的coder 381

Halcon dyn_threshold详解

dyn_threshold 原型 dyn_threshold(OrigImage, ThresholdImage : RegionDynThresh : Offset, LightDark : ) 功能 利用局部阈值分割图像 参数列表 OrigImage (input_object):原始图像 ThresholdImage (input_object):处理后图像(一般采用滤波处理) RegionDynThresh (output_object):分割后区域 Offset (input_control):灰

ZiLiangQin的博客 1万+

【Android12】Bugreport实现原理

Android Bugreport是一个用于记录和收集 Android设备上系统信息、日志和调试信息的工具。系统发生某些问题时,可以通过bugreport把系统当前时刻点(运行BugRepot的时刻)的系统相关的状态和信息都抓到一个zip中。通过bugreport可以帮忙开发人员分析和解决问题。Bugreport其实就是一系列的信息的汇总,包括日志、内存状态、进程信息、崩溃信息、服务状态等等。用一个大而近乎全的现场,来帮忙更好的分析问题。

林多 4525

无法加载文件:C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本编程

当您遇到类似于"无法加载文件:C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本编程"的错误消息时,请按照上述步骤来解决问题。当您尝试加载文件C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1时,可能会遇到类似于"无法加载文件:C:\Users\Lenovo\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本编程"的错误消息。请注意,更改执行策略可能会带来一些安全风险。

PixelInk的博客 1921

第14.8章 高翔博士最新开源框架Lightning-LM源码解读(八):位姿平滑器PoseSmoother深度剖析,从原理到代码实现

本文深度解析了高翔博士开源框架Lightning-LM中的位姿平滑器PoseSmoother模块。该模块通过设置平滑因子,在保证位姿准确性的同时减少突变,使位姿变化更平稳。文章详细讲解了模块功能、数据流程和核心代码实现,包括构造函数、拷贝构造函数以及关键的PushDRPose和PushPose函数。重点分析了位姿平滑算法原理,涉及运动有效性校验、位姿队列维护和平滑因子调节等核心机制。此外还介绍了相关C++语法(如显式构造函数、默认拷贝构造)和优化方向。本解析为SLAM学习者提供了位姿平滑处理的实用实现范例。

行知SLAM 600

Android bugreport 分析方法

文章来源:https://www.jianshu.com/p/20e1bfdf5161作者:特立独行的佩奇Android bugreport 概述bugreport 是Android 系统下的一个工具,功能类似于系统的一个黑匣子;通过执行相应的命令可以获取到bugreport 包,其中包含设备日志,堆栈跟踪和其他诊断信息,可帮助您查找和修复系统错误;bugreport信息量非常之大,几乎涵盖整个系...

767

搭建一个简单的 Django + Vue 项目

在本文中,我将为您详细介绍如何搭建一个简单的 Django + Vue 项目。Django 是一个使用 Python 编写的高级 Web 框架,而 Vue 是一个流行的 JavaScript 框架,用于构建用户界面。通过结合 Django 和 Vue,我们可以创建强大的全栈 Web 应用程序。创建 Django 项目设置 Django 后端创建 Vue 前端连接 Django 和 Vue让我们开始吧!

JfpBlockchain的博客 351

电量优化 之 导出Bugreport日志

安卓手机为了调试方便有个叫bugreport的东西,可以记录很多日志。上一篇讲了看这个日志的工具如何安装:电量优化 之 Battery Historian安装 这一篇记录一下bugreport的生成与导出。测试手机:安卓手机7.0以上 抓取日志 电脑上装个ADB,只装ADB就行,配下path,这样在任何路径都能直接用adb。SDK很大很大 打开调试模式,在关于手机里多按几遍版本号,把开发者...

MR CUI的专栏 3820

SAP-ABAP:SAP HANA性能神器FDA:是极速查询的引擎,也是CPU过载的元凶

在SAP S/4HANA的世界里,有一个被ABAP开发者们既爱又怕的特性——Fast Data Access(FDA),它能将性能提升百倍,也能让CPU瞬间飙升。本文将带您全面解剖这一核心机制。

baidu_35680696的博客 849

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Android-bugreport分析流程

立即生成报告:如果应用崩溃,立即使用 adb bugreport 命令来生成报告。这样,生成的报告将主要反映从清理日志到应用崩溃这段时间的设备状态和日志信息。命令清除设备上现有的日志。这将帮助确保 bugreport 中包含的日志信息是从你开始记录的时刻开始的。3.分析,解压后的文件夹里扔入 ChkBugReport.jar ,运行如下命令,当然,txt名字可能不同。运行应用:启动你的应用,并执行到可能导致崩溃的操作。清理日志:在开始运行应用之前,使用。运行 会生成一个zip包,解压即可。

hgy413的专栏 1810

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

安卓开发日志采集和分析面面谈

明确系统提供了哪些日志,cpu、meminfo、app、log等,我们怎么看日志,看日志的三步骤,怎么用工具提高分析日志的效率,bugreport和chkbugreport。

chivalrous 1076

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

简单的安卓ANR与卡顿分析

移动应用性能问题分析指南摘要(142字) 本文介绍两种常见移动应用性能问题的分析方法:1)ANR分析:通过adb命令获取bugreport日志,从FS/data/anr路径查找主线程阻塞信息,或使用Android Studio的Profiler工具检测内存泄漏;2)卡顿分析:使用Profiler的性能分析模块,在短时间间隔内监测页面切换时的方法调用耗时情况。两种方法均需结合时间戳定位问题节点,通过分析线程状态和方法执行时间来诊断性能瓶颈。文末附图展示了分析工具界面。

u013377714的专栏 405

android专项-电量实现

参考博客:https://blog.youkuaiyun.com/leizhiheng320/article/details/79410088 https://blog.youkuaiyun.com/liangxy2014/article/details/78311938   一、bugreport、battery historian是什么 1.bugreport是什么,怎么用? Google为了...

bisong4665的博客 360

android 电量分析工具

  1、参考文章:https://developer.android.com/studio/profile/battery-historian.html这篇文章讲的是如果dump 电量日子文件batterystats.txt,然后使用historian.py将其转化为html文件,让开发者更直接多了解其中信息。但是historian已经升级到2.0了,不在使用python来做数据分析,而是直...

weixin_34250434的博客 239
上一篇: Android 6.0 Overview Screen实现原理
下一篇: Android 6.0指纹识别App开发demo
createchance
博客等级 码龄15年 156粉丝 90原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值