解决方案

Android bugreport工具分析和使用

seo靠我 2023-09-26 03:23:20

bugreport是什么,怎么用?

Android系统想要成为一个功能完备,生态繁荣的操作系统,那就必须提供完整的应用开发环境。而在应用开发中,app程序的调试分析是日常生产中进程会进行的工作。AndrSEO靠我oid为了方便开发人员分析整个系统平台和某个app在运行一段时间之内的所有信息,专门开发了bugreport工具。这个工具使用起来十分简单,只要在终端执行(linux或者win):

? 1 <code SEO靠我class = "hljs avrasm" >adb bugreport > bugreport.txt</code>

即可生成bugreport文件。但是有一个问题是,这个生成的文件有的时候异常庞大,SEO靠我能够达到15M+,想一想对于一个txt文本格式的文件内容长度达到了15M+是一个什么概念,如果使用文本工具打开查看将是一个噩梦。因此google针对android 5.0(api 21)以上的系统开发SEO靠我了一个叫做battery historian的分析工具,这个工具就是用来解析这个txt文本文件,然后使用web图形的形式展现出来,这样出来的效果更加人性化,更加可读。它的基本界面像下面这个样子:

目前gSEO靠我oogle已经将bettery histZ喎�"/kf/ware/vc/" target="_blank" class="keylink">vcmlhbr+q1LTBy6Osv6rUtM/uxL+1xSEO靠我LXY1rejujxiciAvPg0KPGEgaHJlZj0="https://github.com/google/battery-historian">https://github.com/googSEO靠我le/battery-historian

google写了一个比较详细的说明文档,大家可以自行查阅一下。这个工具可以查看以下信息:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1SEO靠我5 16 17 18 19 20 21 <code class = "hljs avrasm" ><code class = "hljs applescript" >Brightness CPU ruSEO靠我nning Charging on Charging status Health JobScheduler Kernel only uptime Level Package active PartiaSEO靠我l wakelock Phone scanning Phone state Plug Plugged Screen Temperature Top app Voltage Wifi on Wifi rSEO靠我unning Wifi supplicant</code></code>

数据还是比较详细的。

当然,同样的bugreport数据也可以有不同的解析和阅读方式,你如果不太喜欢google的battery SEO靠我historian的话,你还有别的选择,那就是选择Sony开源的ChkBugReport,这个工具提供了不同于battery historian的视角去解读bugreport文件,见面简单明了:

这个项SEO靠我目的文档:

http://developer.sonymobile.com/2012/01/25/new-bugreport-analysis-tool-released-as-open-source/SEO靠我

开源地址首页:

https://github.com/sonyxperiadev/ChkBugReport

这里说明一下,笔者使用过ChkBugReport这个工具,感觉很不错,最好结合google的baSEO靠我ttery historian;另外ChkBugReport这个工具还有一点bug,不过不影响使用。

bugreport的原理是什么?

下面我们简要分析一下adb bugreport运行的原理。我们知道,SEO靠我使用bugreport只要执行adb bugreport命令就可以了,因此我们的分析肯定是从adbd这个daemon进程开始,我们查看这个进程的代码的时候发现这里处理了bugreport选项:

adb_SEO靠我commandline@system/core/adb/commandline.cpp

我们可以清楚地看到,这里判断如果附带的参数是bugreport的话,那就直接调用send_shell_commanSEO靠我d函数处理,这个函数的代码比较简单,我们就不分析了,这个函数的功能就是使用shell执行参数中的命令,因此我们这里相当于执行了bugreport命令。

在android设备中,bugreport命令存在SEO靠我于system/bin/目录下,这是一个可执行文件,所以我们要查看这个可执行文件实现的地方,它的实现代码在/frameworks/native/cmds/bugreport/目录下:

我们看到,bugrSEO靠我eport的实现是比较简单的,只有一个Android.mk和一个cpp实现代码,我们先看一下Android.mk文件:

这里我们看到该目录下的代码会被编译成一个名字叫做bugreport的可执行文件,这SEO靠我就是我们想要找的。现在我们看一下bugreport.cpp文件的实现,这个文件中代码比较简单,只有一个main函数:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1SEO靠我7 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50SEO靠我 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 <code class = "hljs avrasm" ><code class = "hljs apSEO靠我plescript" ><code class = "hljs cpp" > // This program will trigger the dumpstate service to start aSEO靠我 call to // dumpstate, then connect to the dumpstate local client to read the // output. All of the SEO靠我dumpstate output is written to stdout, including // any errors encountered while reading/writing theSEO靠我 output. int main() {    // Start the dumpstate service.    property_set( "ctl.start" , "dumpstate" SEO靠我);    // Socket will not be available until service starts.    int s;    for ( int i = 0 ; i < 20 ; SEO靠我i++) {      s = socket_local_client( "dumpstate" , ANDROID_SOCKET_NAMESPACE_RESERVED,               SEO靠我               SOCK_STREAM);      if (s >= 0 )        break ;      // Try again in 1 second.      slSEO靠我eep( 1 );    }    if (s == - 1 ) {      printf( "Failed to connect to dumpstate service: %s\n" , strSEO靠我error(errno));      return 1 ;    }    // Set a timeout so that if nothing is read in 3 minutes, welSEO靠我l stop    // reading and quit. No timeout in dumpstate is longer than 60 seconds,    // so this giveSEO靠我s lots of leeway in case of unforeseen time outs.    struct timeval tv;    tv.tv_sec = 3 * 60 ;    tSEO靠我v.tv_usec = 0 ;    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == - 1 ) {      printSEO靠我f( "WARNING: Cannot set socket timeout: %s\n" , strerror(errno));    }    while ( 1 ) {      char buSEO靠我ffer[ 65536 ];      ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));      iSEO靠我f (bytes_read == 0 ) {        break ;      } else if (bytes_read == - 1 ) {        // EAGAIN really SEO靠我means time out, so change the errno.        if (errno == EAGAIN) {          errno = ETIMEDOUT;      SEO靠我  }        printf( "\nBugreport read terminated abnormally (%s).\n" , strerror(errno));        breakSEO靠我 ;      }      ssize_t bytes_to_send = bytes_read;      ssize_t bytes_written;      do {        byteSEO靠我s_written = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,                                                 SEO靠我buffer + bytes_read - bytes_to_send,                                                 bytes_to_send))SEO靠我;        if (bytes_written == - 1 ) {          printf( "Failed to write data to stdout: read %zd, trSEO靠我ying to send %zd (%s)\n" ,                 bytes_read, bytes_to_send, strerror(errno));          retSEO靠我urn 1 ;        }        bytes_to_send -= bytes_written;      } while (bytes_written != 0 && bytes_toSEO靠我_send > 0 );    }    close(s);    return 0 ; }</code></code></code>

这里的代码非常简单,主要的逻辑就是:

1.启动dumpstate sSEO靠我ervice

2. 和dumpstate service建立socket链接

3. 从socket中读取数据,并且答应到stdout中

4. 读取完成之后关闭socket,然后退出

因此,我们分析的重点需要转SEO靠我移到dumpstate中了。这里说明一下,前面启动dumpstate service的方法是使用系统属性来实现,这个属性的改变消息会被init进程收到,然后init进程会启动dumpstate这个服务SEO靠我

dumpstate其实也是一个可执行文件,也存在于system/bin目录下。现在我们明白了,其实bugreport就是dumpstate,只是bugreport将dumpstate包装了一下而已。SEO靠我

现在我们需要分析一下dumpstate的实现,它的实现代码在:frameworks/native/cmds/dumpstate目录下,我们看下这个目录下的代码结构:

这里的代码也是十分简单,只要少数的几SEO靠我个实现文件,其中main函数在dumpstate.c文件中,这个main函数我们这里不详细分析了,总结下它的主要工作:

1. 根据启动参数,初始化相关资源

2. 如果启动参数中带有-s的话(init启动会SEO靠我加上这个参数),就表示使用socket,那么就启动socket,并且在这个socket中等待链接。

3. 如果client端(也就是bugreport进程)链接成功,那就初始化所要用到的内存,并且设置优SEO靠我先级为较高优先级,防止被OOM干掉。

4. 然后使用vibrator震动一下(如果设备有这个硬件的话),提示用户开始截取log了

5. 调用dumpstate函数开始真正的dump工作

6. dump完成之SEO靠我后再次调用vibrator震动3次,提示用户dump完成。

现在我们看下dumpstate函数的实现:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 SEO靠我20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 5SEO靠我3 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86SEO靠我 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 SEO靠我115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 SEO靠我140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 SEO靠我165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 SEO靠我190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 SEO靠我215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 SEO靠我240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 SEO靠我265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 SEO靠我290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 <code class = "hljs avrasm" ><code cSEO靠我lass = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" > /* dumps the SEO靠我current system state to stdout */ static void dumpstate() {      unsigned long timeout;      time_t SEO靠我now = time(NULL);      char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];      char raSEO靠我dio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];      char network[PROPERTY_VALUE_MAX], dateSEO靠我[ 80 ];      char build_type[PROPERTY_VALUE_MAX];      property_get( "ro.build.display.id" , build, SEO靠我"(unknown)" );      property_get( "ro.build.fingerprint" , fingerprint, "(unknown)" );      propertySEO靠我_get( "ro.build.type" , build_type, "(unknown)" );      property_get( "ro.baseband" , radio, "(unknoSEO靠我wn)" );      property_get( "ro.bootloader" , bootloader, "(unknown)" );      property_get( "gsm.operSEO靠我ator.alpha" , network, "(unknown)" );      strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S" , localtSEO靠我ime(&now));      printf( "========================================================\n" );      printfSEO靠我( "== dumpstate: %s\n" , date);      printf( "======================================================SEO靠我==\n" );      printf( "\n" );      printf( "Build: %s\n" , build);      printf( "Build fingerprint: SEO靠我%s\n" , fingerprint); /* format is important for other tools */      printf( "Bootloader: %s\n" , boSEO靠我otloader);      printf( "Radio: %s\n" , radio);      printf( "Network: %s\n" , network);      printfSEO靠我( "Kernel: " );      dump_file(NULL, "/proc/version" );      printf( "Command line: %s\n" , strtok(cSEO靠我mdline_buf, "\n" ));      printf( "\n" );      dump_dev_files( "TRUSTY VERSION" , "/sys/bus/platformSEO靠我/drivers/trusty" , "trusty_version" );      run_command( "UPTIME" , 10 , "uptime" , NULL);      dumpSEO靠我_files( "UPTIME MMC PERF" , mmcblk0, skip_not_stat, dump_stat_from_fd);      dump_file( "MEMORY INFOSEO靠我" , "/proc/meminfo" );      run_command( "CPU INFO" , 10 , "top" , "-n" , "1" , "-d" , "1" , "-m" , SEO靠我"30" , "-t" , NULL);      run_command( "PROCRANK" , 20 , "procrank" , NULL);      dump_file( "VIRTUASEO靠我L MEMORY STATS" , "/proc/vmstat" );      dump_file( "VMALLOC INFO" , "/proc/vmallocinfo" );      dumSEO靠我p_file( "SLAB INFO" , "/proc/slabinfo" );      dump_file( "ZONEINFO" , "/proc/zoneinfo" );      dumpSEO靠我_file( "PAGETYPEINFO" , "/proc/pagetypeinfo" );      dump_file( "BUDDYINFO" , "/proc/buddyinfo" );  SEO靠我    dump_file( "FRAGMENTATION INFO" , "/d/extfrag/unusable_index" );      dump_file( "KERNEL WAKELOCSEO靠我KS" , "/proc/wakelocks" );      dump_file( "KERNEL WAKE SOURCES" , "/d/wakeup_sources" );      dump_SEO靠我file( "KERNEL CPUFREQ" , "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state" );      dump_filSEO靠我e( "KERNEL SYNC" , "/d/sync" );      run_command( "PROCESSES" , 10 , "ps" , "-P" , NULL);      run_cSEO靠我ommand( "PROCESSES AND THREADS" , 10 , "ps" , "-t" , "-p" , "-P" , NULL);      run_command( "PROCESSSEO靠我ES (SELINUX LABELS)" , 10 , "ps" , "-Z" , NULL);      run_command( "LIBRANK" , 10 , "librank" , NULLSEO靠我);      do_dmesg();      run_command( "LIST OF OPEN FILES" , 10 , SU_PATH, "root" , "lsof" , NULL); SEO靠我     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES" );      for_each_tid(show_wchan, "BLOCKED PROSEO靠我CESS WAIT-CHANNELS" );      if (screenshot_path[ 0 ]) {          ALOGI( "taking screenshot\n" );    SEO靠我      run_command(NULL, 10 , "/system/bin/screencap" , "-p" , screenshot_path, NULL);          ALOGISEO靠我( "wrote screenshot: %s\n" , screenshot_path);      }      // dump_file("EVENT LOG TAGS", "/etc/evenSEO靠我t-log-tags");      // calculate timeout      timeout = logcat_timeout( "main" ) + logcat_timeout( "sSEO靠我ystem" ) + logcat_timeout( "crash" );      if (timeout < 20000 ) {          timeout = 20000 ;      }SEO靠我      run_command( "SYSTEM LOG" , timeout / 1000 , "logcat" , "-v" , "threadtime" , "-d" , "*:v" , NSEO靠我ULL);      timeout = logcat_timeout( "events" );      if (timeout < 20000 ) {          timeout = 200SEO靠我00 ;      }      run_command( "EVENT LOG" , timeout / 1000 , "logcat" , "-b" , "events" , "-v" , "thSEO靠我readtime" , "-d" , "*:v" , NULL);      timeout = logcat_timeout( "radio" );      if (timeout < 20000SEO靠我 ) {          timeout = 20000 ;      }      run_command( "RADIO LOG" , timeout / 1000 , "logcat" , "SEO靠我-b" , "radio" , "-v" , "threadtime" , "-d" , "*:v" , NULL);      run_command( "LOG STATISTICS" , 10 SEO靠我, "logcat" , "-b" , "all" , "-S" , NULL);      /* show the traces we collected in main(), if that waSEO靠我s done */      if (dump_traces_path != NULL) {          dump_file( "VM TRACES JUST NOW" , dump_traceSEO靠我s_path);      }      /* only show ANR traces if theyre less than 15 minutes old */      struct stat SEO靠我st;      char anr_traces_path[PATH_MAX];      property_get( "dalvik.vm.stack-trace-file" , anr_traceSEO靠我s_path, "" );      if (!anr_traces_path[ 0 ]) {          printf( "*** NO VM TRACES FILE DEFINED (dalSEO靠我vik.vm.stack-trace-file)\n\n" );      } else {        int fd = TEMP_FAILURE_RETRY(open(anr_traces_paSEO靠我th,                                         O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));       SEO靠我 if (fd < 0 ) {            printf( "*** NO ANR VM TRACES FILE (%s): %s\n\n" , anr_traces_path, strerSEO靠我ror(errno));        } else {            dump_file_from_fd( "VM TRACES AT LAST ANR" , anr_traces_pathSEO靠我, fd);        }      }      /* slow traces for slow operations */      if (anr_traces_path[ 0 ] != 0SEO靠我 ) {          int tail = strlen(anr_traces_path)- 1 ;          while (tail > 0 && anr_traces_path[taSEO靠我il] != / ) {              tail--;          }          int i = 0 ;          while ( 1 ) {            SEO靠我  sprintf(anr_traces_path+tail+ 1 , "slow%02d.txt" , i);              if (stat(anr_traces_path, &st)SEO靠我) {                  // No traces file at this index, done with the files.                  break ; SEO靠我             }              dump_file( "VM TRACES WHEN SLOW" , anr_traces_path);              i++;  SEO靠我        }      }      int dumped = 0 ;      for (size_t i = 0 ; i < NUM_TOMBSTONES; i++) {          SEO靠我if (tombstone_data[i].fd != - 1 ) {              dumped = 1 ;              dump_file_from_fd( "TOMBSSEO靠我TONE" , tombstone_data[i].name, tombstone_data[i].fd);              tombstone_data[i].fd = - 1 ;    SEO靠我      }      }      if (!dumped) {          printf( "*** NO TOMBSTONES to dump in %s\n\n" , TOMBSTONSEO靠我E_DIR);      }      dump_file( "NETWORK DEV INFO" , "/proc/net/dev" );      dump_file( "QTAGUID NETWSEO靠我ORK INTERFACES INFO" , "/proc/net/xt_qtaguid/iface_stat_all" );      dump_file( "QTAGUID NETWORK INTSEO靠我ERFACES INFO (xt)" , "/proc/net/xt_qtaguid/iface_stat_fmt" );      dump_file( "QTAGUID CTRL INFO" , SEO靠我"/proc/net/xt_qtaguid/ctrl" );      dump_file( "QTAGUID STATS INFO" , "/proc/net/xt_qtaguid/stats" )SEO靠我;      if (!stat(PSTORE_LAST_KMSG, &st)) {          /* Also TODO: Make console-ramoops CAP_SYSLOG prSEO靠我otected. */          dump_file( "LAST KMSG" , PSTORE_LAST_KMSG);      } else {          /* TODO: MakSEO靠我e last_kmsg CAP_SYSLOG protected. b/5555691 */          dump_file( "LAST KMSG" , "/proc/last_kmsg" )SEO靠我;      }      /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */      run_cSEO靠我ommand( "LAST LOGCAT" , 10 , "logcat" , "-L" , "-v" , "threadtime" ,                                SEO靠我               "-b" , "all" , "-d" , "*:v" , NULL);      /* The following have a tendency to get wedSEO靠我ged when wifi drivers/fw goes belly-up. */      run_command( "NETWORK INTERFACES" , 10 , "ip" , "linSEO靠我k" , NULL);      run_command( "IPv4 ADDRESSES" , 10 , "ip" , "-4" , "addr" , "show" , NULL);      ruSEO靠我n_command( "IPv6 ADDRESSES" , 10 , "ip" , "-6" , "addr" , "show" , NULL);      run_command( "IP RULESEO靠我S" , 10 , "ip" , "rule" , "show" , NULL);      run_command( "IP RULES v6" , 10 , "ip" , "-6" , "ruleSEO靠我" , "show" , NULL);      dump_route_tables();      run_command( "ARP CACHE" , 10 , "ip" , "-4" , "neSEO靠我igh" , "show" , NULL);      run_command( "IPv6 ND CACHE" , 10 , "ip" , "-6" , "neigh" , "show" , NULSEO靠我L);      run_command( "IPTABLES" , 10 , SU_PATH, "root" , "iptables" , "-L" , "-nvx" , NULL);      rSEO靠我un_command( "IP6TABLES" , 10 , SU_PATH, "root" , "ip6tables" , "-L" , "-nvx" , NULL);      run_commaSEO靠我nd( "IPTABLE NAT" , 10 , SU_PATH, "root" , "iptables" , "-t" , "nat" , "-L" , "-nvx" , NULL);      /SEO靠我* no ip6 nat */      run_command( "IPTABLE RAW" , 10 , SU_PATH, "root" , "iptables" , "-t" , "raw" ,SEO靠我 "-L" , "-nvx" , NULL);      run_command( "IP6TABLE RAW" , 10 , SU_PATH, "root" , "ip6tables" , "-t"SEO靠我 , "raw" , "-L" , "-nvx" , NULL);      run_command( "WIFI NETWORKS" , 20 ,              SU_PATH, "roSEO靠我ot" , "wpa_cli" , "IFNAME=wlan0" , "list_networks" , NULL); #ifdef FWDUMP_bcmdhd      run_command( "SEO靠我ND OFFLOAD TABLE" , 5 ,              SU_PATH, "root" , "wlutil" , "nd_hostip" , NULL);      run_commSEO靠我and( "DUMP WIFI INTERNAL COUNTERS (1)" , 20 ,              SU_PATH, "root" , "wlutil" , "counters" ,SEO靠我 NULL);      run_command( "ND OFFLOAD STATUS (1)" , 5 ,              SU_PATH, "root" , "wlutil" , "nSEO靠我d_status" , NULL); #endif      dump_file( "INTERRUPTS (1)" , "/proc/interrupts" );      run_command(SEO靠我 "NETWORK DIAGNOSTICS" , 10 , "dumpsys" , "connectivity" , "--diag" , NULL); #ifdef FWDUMP_bcmdhd   SEO靠我   run_command( "DUMP WIFI STATUS" , 20 ,              SU_PATH, "root" , "dhdutil" , "-i" , "wlan0" SEO靠我, "dump" , NULL);      run_command( "DUMP WIFI INTERNAL COUNTERS (2)" , 20 ,              SU_PATH, "SEO靠我root" , "wlutil" , "counters" , NULL);      run_command( "ND OFFLOAD STATUS (2)" , 5 ,              SEO靠我SU_PATH, "root" , "wlutil" , "nd_status" , NULL); #endif      dump_file( "INTERRUPTS (2)" , "/proc/iSEO靠我nterrupts" );      print_properties();      run_command( "VOLD DUMP" , 10 , "vdc" , "dump" , NULL); SEO靠我     run_command( "SECURE CONTAINERS" , 10 , "vdc" , "asec" , "list" , NULL);      run_command( "FILSEO靠我ESYSTEMS & FREE SPACE" , 10 , "df" , NULL);      run_command( "LAST RADIO LOG" , 10 , "parse_radio_lSEO靠我og" , "/proc/last_radio_log" , NULL);      printf( "------ BACKLIGHTS ------\n" );      printf( "LCDSEO靠我 brightness=" );      dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness" );      printf( "BuSEO靠我tton brightness=" );      dump_file(NULL, "/sys/class/leds/button-backlight/brightness" );      prinSEO靠我tf( "Keyboard brightness=" );      dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness" )SEO靠我;      printf( "ALS mode=" );      dump_file(NULL, "/sys/class/leds/lcd-backlight/als" );      printSEO靠我f( "LCD driver registers:\n" );      dump_file(NULL, "/sys/class/leds/lcd-backlight/registers" );   SEO靠我   printf( "\n" );      /* Binder state is expensive to look at as it uses a lot of memory. */      SEO靠我dump_file( "BINDER FAILED TRANSACTION LOG" , "/sys/kernel/debug/binder/failed_transaction_log" );   SEO靠我   dump_file( "BINDER TRANSACTION LOG" , "/sys/kernel/debug/binder/transaction_log" );      dump_filSEO靠我e( "BINDER TRANSACTIONS" , "/sys/kernel/debug/binder/transactions" );      dump_file( "BINDER STATS"SEO靠我 , "/sys/kernel/debug/binder/stats" );      dump_file( "BINDER STATE" , "/sys/kernel/debug/binder/stSEO靠我ate" );      printf( "========================================================\n" );      printf( "=SEO靠我= Board\n" );      printf( "========================================================\n" );      dumpSEO靠我state_board();      printf( "\n" );      /* Migrate the ril_dumpstate to a dumpstate_board()? */    SEO靠我  char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = { 0 };      property_get( "ril.dumpstate.timeout"SEO靠我 , ril_dumpstate_timeout, "30" );      if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1 ) >SEO靠我 0 ) {          if ( 0 == strncmp(build_type, "user" , PROPERTY_VALUE_MAX - 1 )) {              // sSEO靠我u does not exist on user builds, so try running without it.              // This way any implementatSEO靠我ions of vril-dump that do not require              // root can run on user builds.              run_SEO靠我command( "DUMP VENDOR RIL LOGS" , atoi(ril_dumpstate_timeout),                      "vril-dump" , NUSEO靠我LL);          } else {              run_command( "DUMP VENDOR RIL LOGS" , atoi(ril_dumpstate_timeoutSEO靠我),                      SU_PATH, "root" , "vril-dump" , NULL);          }      }      printf( "=====SEO靠我===================================================\n" );      printf( "== Android Framework ServiceSEO靠我s\n" );      printf( "========================================================\n" );      /* the fulSEO靠我l dumpsys is starting to take a long time, so we need         to increase its timeout.  we really neSEO靠我ed to do the timeouts in         dumpsys itself... */      run_command( "DUMPSYS" , 60 , "dumpsys" ,SEO靠我 NULL);      printf( "========================================================\n" );      printf( "=SEO靠我= Checkins\n" );      printf( "========================================================\n" );      rSEO靠我un_command( "CHECKIN BATTERYSTATS" , 30 , "dumpsys" , "batterystats" , "-c" , NULL);      run_commanSEO靠我d( "CHECKIN MEMINFO" , 30 , "dumpsys" , "meminfo" , "--checkin" , NULL);      run_command( "CHECKIN SEO靠我NETSTATS" , 30 , "dumpsys" , "netstats" , "--checkin" , NULL);      run_command( "CHECKIN PROCSTATS"SEO靠我 , 30 , "dumpsys" , "procstats" , "-c" , NULL);      run_command( "CHECKIN USAGESTATS" , 30 , "dumpsSEO靠我ys" , "usagestats" , "-c" , NULL);      run_command( "CHECKIN PACKAGE" , 30 , "dumpsys" , "package" SEO靠我, "--checkin" , NULL);      printf( "========================================================\n" ); SEO靠我     printf( "== Running Application Activities\n" );      printf( "================================SEO靠我========================\n" );      run_command( "APP ACTIVITIES" , 30 , "dumpsys" , "activity" , "aSEO靠我ll" , NULL);      printf( "========================================================\n" );      printSEO靠我f( "== Running Application Services\n" );      printf( "============================================SEO靠我============\n" );      run_command( "APP SERVICES" , 30 , "dumpsys" , "activity" , "service" , "allSEO靠我" , NULL);      printf( "========================================================\n" );      printf(SEO靠我 "== Running Application Providers\n" );      printf( "=============================================SEO靠我===========\n" );      run_command( "APP SERVICES" , 30 , "dumpsys" , "activity" , "provider" , "allSEO靠我" , NULL);      printf( "========================================================\n" );      printf(SEO靠我 "== dumpstate: done\n" );      printf( "========================================================\n"SEO靠我 ); }</code></code></code></code>

上面的代码比较长,是因为所要dump的模块太多,但是基本逻辑还是比较清楚的,我们看到基本的数据来源就是:

1.系统属性

2./proc和/SEO靠我sys节点文件

3.执行shell命令获得相关输出

4.logcat输出

5.Android Framework Services信息基本使用dumpsys命令通过binder调用服务中的dump函数获得信SEO靠我

这里我们需要看一下dumpsys命令的实现,这个命令也是比较简单,实现全部在main函数中:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 SEO靠我21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 5SEO靠我4 55 56 57 <code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" >SEO靠我<code class = "hljs objectivec" ><code class = "hljs objectivec" > int main( int argc, char * const SEO靠我argv[]) {      signal(SIGPIPE, SIG_IGN);      sp<iservicemanager> sm = defaultServiceManager();     SEO靠我 fflush(stdout);      if (sm == NULL) {          ALOGE( "Unable to get default service manager!" ); SEO靠我         aerr << "dumpsys: Unable to get default service manager!" << endl;          return 20 ;    SEO靠我  }      Vector<string16> services;      Vector<string16> args;      bool showListOnly = false ;    SEO靠我  if ((argc == 2 ) && (strcmp(argv[ 1 ], "-l" ) == 0 )) {          showListOnly = true ;      }     SEO靠我 if ((argc == 1 ) || showListOnly) {          services = sm->listServices();          services.sort(SEO靠我sort_func);          args.add(String16( "-a" ));      } else {          services.add(String16(argv[ SEO靠我1 ]));          for ( int i= 2 ; i 1 ) {          // first print a list of the current services     SEO靠我     aout << "Currently running services:" << endl;          for (size_t i= 0 ; i<n; ibinder= "" > sSEO靠我ervice = sm->checkService(services[i]);              if (service != NULL) {                  aout <<SEO靠我 "  " << services[i] << endl;              }          }      }      if (showListOnly) {          retSEO靠我urn 0 ;      }      for (size_t i= 0 ; i<n; ibinder= "" > service = sm->checkService(services[i]);  SEO靠我        if (service != NULL) {              if (N > 1 ) {                  aout << "----------------SEO靠我--------------------------------------------"                          "-------------------" << endlSEO靠我;                  aout << "DUMP OF SERVICE " << services[i] << ":" << endl;              }         SEO靠我     int err = service->dump(STDOUT_FILENO, args);              if (err != 0 ) {                  aeSEO靠我rr << "Error dumping service info: (" << strerror(err)                          << ") " << services[SEO靠我i] << endl;              }          } else {              aerr << "Cant find service: " << services[SEO靠我i] << endl;          }      }      return 0 ; }</n;></n;></argc;></string16></string16></iservicemanSEO靠我ager></code></code></code></code></code>

我们看到它的代码逻辑就是,通过Binder的SM查找参数中的service,然后通过:

? 1 <code class =SEO靠我 "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objeSEO靠我ctivec" ><code class = "hljs objectivec" ><code class = "hljs perl" > int err = service->dump(STDOUTSEO靠我_FILENO, args);</code></code></code></code></code></code>

这句来调用service的dump函数。

dumpstate会调用到所有binder中的SEO靠我service的dump函数,因为dumpstate函数执行了这一句:? 1 2 3 4 <code class = "hljs avrasm" ><code class = "hljs applesSEO靠我cript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" >SEO靠我<code class = "hljs perl" ><code class = "hljs applescript" > /* the full dumpsys is starting to takSEO靠我e a long time, so we need     to increase its timeout.  we really need to do the timeouts in     dumSEO靠我psys itself... */ run_command( "DUMPSYS" , 60 , "dumpsys" , NULL);</code></code></code></code></codeSEO靠我></code></code>

直接执行dumpsys,没有参数,并且注释中也说的很清楚,就是采集所有的信息。这会执行以下service的dump函数(执行dumpsys | grep “DUMP OFSEO靠我 SERVICE”可以看到):

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31SEO靠我 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 SEO靠我65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 9SEO靠我8 99 100 101 102 103 104 105 <code class = "hljs avrasm" ><code class = "hljs applescript" ><code clSEO靠我ass = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hSEO靠我ljs perl" ><code class = "hljs applescript" ><code class = "hljs vhdl" >DUMP OF SERVICE DockObserverSEO靠我: DUMP OF SERVICE SurfaceFlinger: DUMP OF SERVICE accessibility: DUMP OF SERVICE account: DUMP OF SESEO靠我RVICE activity: DUMP OF SERVICE alarm: DUMP OF SERVICE android.security.keystore: DUMP OF SERVICE anSEO靠我droid.service.gatekeeper.IGateKeeperService: DUMP OF SERVICE appops: DUMP OF SERVICE appwidget: DUMPSEO靠我 OF SERVICE assetatlas: DUMP OF SERVICE audio: DUMP OF SERVICE backup: DUMP OF SERVICE battery: DUMPSEO靠我 OF SERVICE batteryproperties: DUMP OF SERVICE batterystats: DUMP OF SERVICE bluetooth_manager: DUMPSEO靠我 OF SERVICE carrier_config: DUMP OF SERVICE clipboard: DUMP OF SERVICE commontime_management: DUMP OSEO靠我F SERVICE connectivity: DUMP OF SERVICE consumer_ir: DUMP OF SERVICE content: DUMP OF SERVICE countrSEO靠我y_detector: DUMP OF SERVICE cpuinfo: DUMP OF SERVICE dbinfo: DUMP OF SERVICE device_policy: DUMP OF SEO靠我SERVICE deviceidle: DUMP OF SERVICE devicestoragemonitor: DUMP OF SERVICE diskstats: DUMP OF SERVICESEO靠我 display: DUMP OF SERVICE display.qservice: DUMP OF SERVICE dreams: DUMP OF SERVICE drm.drmManager: SEO靠我DUMP OF SERVICE dropbox: DUMP OF SERVICE ethernet: DUMP OF SERVICE fingerprint: DUMP OF SERVICE gfxiSEO靠我nfo: DUMP OF SERVICE graphicsstats: DUMP OF SERVICE imms: DUMP OF SERVICE input: DUMP OF SERVICE inpSEO靠我ut_method: DUMP OF SERVICE iphonesubinfo: DUMP OF SERVICE isms: DUMP OF SERVICE isub: DUMP OF SERVICSEO靠我E jobscheduler: DUMP OF SERVICE launcherapps: DUMP OF SERVICE location: DUMP OF SERVICE lock_settingSEO靠我s: DUMP OF SERVICE media.audio_flinger: DUMP OF SERVICE media.audio_policy: DUMP OF SERVICE media.caSEO靠我mera: DUMP OF SERVICE media.camera.proxy: DUMP OF SERVICE media.player: DUMP OF SERVICE media.radio:SEO靠我 DUMP OF SERVICE media.resource_manager: DUMP OF SERVICE media.sound_trigger_hw: DUMP OF SERVICE medSEO靠我ia_projection: DUMP OF SERVICE media_router: DUMP OF SERVICE media_session: DUMP OF SERVICE meminfo:SEO靠我 DUMP OF SERVICE midi: DUMP OF SERVICE mount: DUMP OF SERVICE netpolicy: DUMP OF SERVICE netstats: DSEO靠我UMP OF SERVICE network_management: DUMP OF SERVICE network_score: DUMP OF SERVICE nfc: DUMP OF SERVISEO靠我CE notification: DUMP OF SERVICE package : DUMP OF SERVICE permission: DUMP OF SERVICE persistent_daSEO靠我ta_block: DUMP OF SERVICE phone: DUMP OF SERVICE power: DUMP OF SERVICE print: DUMP OF SERVICE proceSEO靠我ssinfo: DUMP OF SERVICE procstats: DUMP OF SERVICE restrictions: DUMP OF SERVICE rttmanager: DUMP OFSEO靠我 SERVICE samplingprofiler: DUMP OF SERVICE scheduling_policy: DUMP OF SERVICE search: DUMP OF SERVICSEO靠我E sensorservice: DUMP OF SERVICE serial: DUMP OF SERVICE servicediscovery: DUMP OF SERVICE simphonebSEO靠我ook: DUMP OF SERVICE sip: DUMP OF SERVICE statusbar: DUMP OF SERVICE telecom: DUMP OF SERVICE telephSEO靠我ony.registry: DUMP OF SERVICE textservices: DUMP OF SERVICE trust: DUMP OF SERVICE uimode: DUMP OF SSEO靠我ERVICE updatelock: DUMP OF SERVICE usagestats: DUMP OF SERVICE usb: DUMP OF SERVICE user: DUMP OF SESEO靠我RVICE vibrator: DUMP OF SERVICE voiceinteraction: DUMP OF SERVICE wallpaper: DUMP OF SERVICE webviewSEO靠我update: DUMP OF SERVICE wifi: DUMP OF SERVICE wifip2p: DUMP OF SERVICE wifiscanner: DUMP OF SERVICE SEO靠我window:</code></code></code></code></code></code></code></code>

这里总结以下,上面的bugreport整体逻辑如下图描述(如果图片太小看不SEO靠我清,请下载图片并查看):

adb bugreport的其他选项

bugreport本身并没有什么选项,主要是通过dumpsys等命令配合完成,详见battery historian项目主页:https:/SEO靠我/github.com/google/battery-historian,以下是个总结:

1). 重置电池统计信息:? 1 <code class = "hljs avrasm" ><code clasSEO靠我s = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hlSEO靠我js objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" ><code class = "hljs vhdSEO靠我l" ><code class = "hljs livecodeserver" >adb shell dumpsys batterystats --reset</code></code></code>SEO靠我</code></code></code></code></code></code>

2). Wakelock analysis全部wakelock信息:

? 1 <code class = "hljs SEO靠我avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec"SEO靠我 ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" ><codSEO靠我e class = "hljs vhdl" ><code class = "hljs livecodeserver" ><code class = "hljs lasso" >adb shell duSEO靠我mpsys batterystats --enable full-wake-history</code></code></code></code></code></code></code></codeSEO靠我></code></code>

3). Kernel trace analysis分析内核,主要分析wakeup source和wakelock activities,首先使能kernel分析:

? 1 SEO靠我2 3 4 5 6 7 8 9 10 11 12 13 <code class = "hljs avrasm" ><code class = "hljs applescript" ><code claSEO靠我ss = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hlSEO靠我js perl" ><code class = "hljs applescript" ><code class = "hljs vhdl" ><code class = "hljs livecodesSEO靠我erver" ><code class = "hljs lasso" ><code class = "hljs sql" >$ adb root $ adb shell # Set the eventSEO靠我s to trace. $ echo "power:wakeup_source_activate" >> /d/tracing/set_event $ echo "power:wakeup_sourcSEO靠我e_deactivate" >> /d/tracing/set_event # The default trace size for most devices is 1MB, which is relSEO靠我atively low and might cause the logs to overflow. # 8MB to 10MB should be a decent size for 5 - 6 hoSEO靠我urs of logging. $ echo 8192 > /d/tracing/buffer_size_kb $ echo 1 > /d/tracing/tracing_on</code></codSEO靠我e></code></code></code></code></code></code></code></code></code>

然后获得log:

? 1 2 3 4 5 <code class = "SEO靠我hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectSEO靠我ivec" ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" SEO靠我><code class = "hljs vhdl" ><code class = "hljs livecodeserver" ><code class = "hljs lasso" ><code cSEO靠我lass = "hljs sql" ><code class = "hljs smalltalk" >$ echo 0 > /d/tracing/tracing_on $ adb pull /d/trSEO靠我acing/trace <some path= "" > # Take a bug report at this time. $ adb bugreport > bugreport.txt</someSEO靠我></code></code></code></code></code></code></code></code></code></code></code></code>
“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2