Android bugreport工具分析和使用-程序员宅基地

技术标签: android  

bugreport是什么,怎么用?

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

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

即可生成bugreport文件。但是有一个问题是,这个生成的文件有的时候异常庞大,能够达到15M+,想一想对于一个txt文本格式的文件内容长度达到了15M+是一个什么概念,如果使用文本工具打开查看将是一个噩梦。因此google针对android 5.0(api 21)以上的系统开发了一个叫做battery historian的分析工具,这个工具就是用来解析这个txt文本文件,然后使用web图形的形式展现出来,这样出来的效果更加人性化,更加可读。它的基本界面像下面这个样子:
这里写图片描述
目前google已经将bettery histZ喎�"/kf/ware/vc/" target="_blank" class="keylink">vcmlhbr+q1LTBy6Osv6rUtM/uxL+1xLXY1rejujxiciAvPg0KPGEgaHJlZj0="https://github.com/google/battery-historian">https://github.com/google/battery-historian
google写了一个比较详细的说明文档,大家可以自行查阅一下。这个工具可以查看以下信息:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<code class = "hljs avrasm" ><code class = "hljs applescript" >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</code></code>

数据还是比较详细的。
当然,同样的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函数:

?
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
31
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
65
66
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" > // 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 ;
}</code></code></code>

这里的代码非常简单,主要的逻辑就是:
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函数的实现:

?
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
31
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
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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
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
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
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
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
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
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" > /* 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" );
}</code></code></code></code>

上面的代码比较长,是因为所要dump的模块太多,但是基本逻辑还是比较清楚的,我们看到基本的数据来源就是:
1.系统属性
2./proc和/sys节点文件
3.执行shell命令获得相关输出
4.logcat输出
5.Android Framework Services信息基本使用dumpsys命令通过binder调用服务中的dump函数获得信息
这里我们需要看一下dumpsys命令的实现,这个命令也是比较简单,实现全部在main函数中:

?
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
31
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
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" > 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 1 ) {
         // first print a list of the current services
         aout << "Currently running services:" << endl;
 
         for (size_t i= 0 ; i<n; ibinder= "" > service = sm->checkService(services[i]);
             if (service != NULL) {
                 aout << "  " << services[i] << endl;
             }
         }
     }
 
     if (showListOnly) {
         return 0 ;
     }
 
     for (size_t i= 0 ; i<n; 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 ;
}</n;></n;></argc;></string16></string16></iservicemanager></code></code></code></code></code>

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

?
1
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hljs perl" > int err = service->dump(STDOUT_FILENO, args);</code></code></code></code></code></code>

这句来调用service的dump函数。
dumpstate会调用到所有binder中的service的dump函数,因为dumpstate函数执行了这一句:

?
1
2
3
4
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" > /* 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);</code></code></code></code></code></code></code>

直接执行dumpsys,没有参数,并且注释中也说的很清楚,就是采集所有的信息。这会执行以下service的dump函数(执行dumpsys | grep “DUMP OF 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
31
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
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
98
99
100
101
102
103
104
105
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" ><code class = "hljs vhdl" >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:</code></code></code></code></code></code></code></code>

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

adb bugreport的其他选项

bugreport本身并没有什么选项,主要是通过dumpsys等命令配合完成,详见battery historian项目主页:https://github.com/google/battery-historian,以下是个总结:
1). 重置电池统计信息:

?
1
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" ><code class = "hljs vhdl" ><code class = "hljs livecodeserver" >adb shell dumpsys batterystats --reset</code></code></code></code></code></code></code></code></code>

2). Wakelock analysis全部wakelock信息:

?
1
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" ><code class = "hljs vhdl" ><code class = "hljs livecodeserver" ><code class = "hljs lasso" >adb shell dumpsys batterystats --enable full-wake-history</code></code></code></code></code></code></code></code></code></code>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" ><code class = "hljs vhdl" ><code class = "hljs livecodeserver" ><code class = "hljs lasso" ><code class = "hljs sql" >$ 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</code></code></code></code></code></code></code></code></code></code></code>

然后获得log:

?
1
2
3
4
5
<code class = "hljs avrasm" ><code class = "hljs applescript" ><code class = "hljs cpp" ><code class = "hljs objectivec" ><code class = "hljs objectivec" ><code class = "hljs perl" ><code class = "hljs applescript" ><code class = "hljs vhdl" ><code class = "hljs livecodeserver" ><code class = "hljs lasso" ><code class = "hljs sql" ><code class = "hljs smalltalk" >$ echo 0 > /d/tracing/tracing_on
$ adb pull /d/tracing/trace <some path= "" >
 
# Take a bug report at this time.
$ adb bugreport > bugreport.txt</some></code></code></code></code></code></code></code></code></code></code></code></code>
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Fybon/article/details/68926757

智能推荐

简单对比Flat, Gouraud & Phong shading_phone的光照模型比gouraud-程序员宅基地

文章浏览阅读3.4k次,点赞6次,收藏7次。1. Flat shading:根据表面法向量计算光照,并应用到整个面片上;速度最快,效果最差,容易暴露物体的多边形本质;2. Phong shading:注意与Phong反射模型(Phong reflection model)区分开;根据顶点的法向量插值计算出表面法向量,再逐像素计算光照;速度最慢,但效果最好;3. Gouraud shading:根据顶点_phone的光照模型比gouraud

创建型模式之单例模式SINGLETON-程序员宅基地

文章浏览阅读283次。一、简析 单例模式原则就是该类自己创建自己的对象,且只能有一个对象被创建。一般情况对于打印机,数据库连接等减少内存开销,避免多线程同时访问问题会使用该模式。 根据原则: 只能有一个实例:必须私有构造函数不给其他函数创建机会,且每次调用需要判断是否已经存在实例,为了让该实例始终存在需static; 必须自己创建实例:由于构造函数私有,只能在类自身内部实例化对象,所以只能通过静态方法调用实例化函

最全浏览器兼容性问题以及解决方案_execcommand important-程序员宅基地

文章浏览阅读3.1k次,点赞2次,收藏25次。常见浏览器兼容性问题与解决方案  所谓的浏览器兼容性问题,是指因为不同的浏览器对同一段代码有不同的解析,造成页面显示效果不统一的情况。在大多数情况下,我们的需求是,无论用户用什么浏览器来查看我们的网站或者登陆我们的系统,都应该是统一的显示效果。所以浏览器的兼容性问题是前端开发人员经常会碰到和必须要解决的问题。  在学习浏览器兼容性之前,我想把前端开发人员划分为两类:  第一类是精确按照..._execcommand important

outlook2007无法打开outlook窗口的解决办法-程序员宅基地

文章浏览阅读224次。非法关机导致Outlook2007无法正常启动,运行Outlook,界面显示“无法启动wicrosoft office outlook ,无法打开outlook窗口”的错误信息。0.首先备份你的邮件,文件名一般为outlook.pst。在你的c盘用户文件下你的用户下的appdata-mirosoft-outllok目录中,个别机器可能不一样,可以自己查找!1.到你..._outlook2007对话历史无法打开某个窗体区域

剑指offer-js 删除链表中重复的结点_js移除链表中的重复节点-程序员宅基地

文章浏览阅读200次。删除链表中重复的结点相似题目:leetcode-java 删除排序链表中的重复元素–保留一个元素leetcode-java 删除排序链表中的重复元素II – 不保留重复元素上面用的方法会多一点,可以从多个角度理解一下题目描述: 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5问题分析: 方法一: 直接修改 _js移除链表中的重复节点

net::ERR_BLOCKED_BY_CLIENT的解决办法-程序员宅基地

文章浏览阅读2.9k次。我碰到该问题是因为前端代码在我们服务器存放,用对方提供的域名访问,结果因为对方域名可能被拉黑,广告插件自动拦截了所有资源,导致资源无法加载出来.另外还有一种部分图片加载不出来的问题,很有趣,我的图片名字是tanchuang.jpg,结果也加载不出来,然后我改了个hehe,就好了.解决办法有两种:1.笨办法:可以找一个好的域名(没有被列入黑名单的域名)来链接资源,路径都写成绝对路径,这样..._net::err_blocked_by_client

随便推点

5.2 使用向导创建选择查询_查询向导中不带选择条件的选择查询是啥-程序员宅基地

文章浏览阅读3k次。目录 1.从单个表中查询所需的信息罗斯文数据库中,选择”采购订单“表,创建——查询向导——简单查询选择查询字段设置查询表名称,完成 2.从多个表中查询所需要的数据创建——查询向导——简单查询——选择数据表选择这个表字段选择第二表及字段设置查询表名称,完成在使用数据库时,有时可能希望查看表中的所有数据,但有时可能只希望查看某些字段列中的数据,或者只希望在某些字段列满足某些条件时查看数据,为此可使用选择查询。创建选择查询有两种方法:使用查询向导和在设计..._查询向导中不带选择条件的选择查询是啥

【转载】 -- 15条使人醒悟的生活中的定律_断言醒悟-程序员宅基地

文章浏览阅读2k次。 1. 墨菲定律  “墨菲定律”源自于一位名叫墨菲的美国上尉。因为他认为某位同事是个倒霉蛋,便不经意地说了句笑话:“如果一件事情有可能被弄糟,让他去做就一定会弄糟。”后来这句话也就被延伸拓展,出现了一些其它的表达形式,比方说“ 如果坏事有可能发生,不管这种可能性多么小,它总会发生,并引起最大可能的损失”,“会出错的,终将会出错”等等。    基本上而言,根据“墨菲定律”可以推出_断言醒悟

相邻垂直外边距的塌陷-程序员宅基地

文章浏览阅读267次。垂直外边距的塌陷首先需要明确塌陷只会发生在块级元素的相邻垂直外边距间在使用CSS的垂直外边距样式时,相邻元素和父子元素都有可能发生相邻垂直外边距的塌陷(相邻元素是上方元素的下外边距和下方元素的上外边距之间,父子元素是父元素的上外边距和子元素的上外边距之间),为了在编写静态页面时避免由于垂直外边距的塌陷导致的布局混乱,需要了解塌陷时垂直外边距的表现和产生的原因——知道原因才能更好的解决问题塌陷时外边距的计算方式在《CSS权威指南》一书中对这种塌陷发生的描述非常的生动,书中将包围元素的外边距比作透明的塑_垂直外边距的塌陷

linux6单用户模式进入,Centos6进入单用户模式的两种方法-程序员宅基地

文章浏览阅读2.2k次。单用户模式的作用在使用Linux系统中,维护人员经常会碰到一个问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况。遇到这种情况,一般情况下,维护人员就会通过最常用的方法进行root密码重置,那就是单用户模式。其次,单用户最突出且实用的一个好处就是:可以无密码登录,所以,在忘记密码时,可以进入单用户模式修改登录密码。方法一先重启系统在Linux系统启动 内核启动顺序选择..._linux6进入单用户

一些网站-程序员宅基地

文章浏览阅读297次。转自 http://blog.sina.com.cn/s/blog_4062094e01008tc0.html 一些大概有价值的网站,需要进一步的补充和甄别。 终于发现把有用的网站放到收藏夹里也不可靠了,总是丢,也疏于整理。现在在 www.g...

深度学习-自动求导-程序员宅基地

文章浏览阅读846次。跟沐神学AI-矩阵计算_自动求导