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

智能推荐

874计算机科学基础综合,2018年四川大学874计算机科学专业基础综合之计算机操作系统考研仿真模拟五套题...-程序员宅基地

文章浏览阅读1.1k次。一、选择题1. 串行接口是指( )。A. 接口与系统总线之间串行传送,接口与I/0设备之间串行传送B. 接口与系统总线之间串行传送,接口与1/0设备之间并行传送C. 接口与系统总线之间并行传送,接口与I/0设备之间串行传送D. 接口与系统总线之间并行传送,接口与I/0设备之间并行传送【答案】C2. 最容易造成很多小碎片的可变分区分配算法是( )。A. 首次适应算法B. 最佳适应算法..._874 计算机科学专业基础综合题型

XShell连接失败:Could not connect to '192.168.191.128' (port 22): Connection failed._could not connect to '192.168.17.128' (port 22): c-程序员宅基地

文章浏览阅读9.7k次,点赞5次,收藏15次。连接xshell失败,报错如下图,怎么解决呢。1、通过ps -e|grep ssh命令判断是否安装ssh服务2、如果只有客户端安装了,服务器没有安装,则需要安装ssh服务器,命令:apt-get install openssh-server3、安装成功之后,启动ssh服务,命令:/etc/init.d/ssh start4、通过ps -e|grep ssh命令再次判断是否正确启动..._could not connect to '192.168.17.128' (port 22): connection failed.

杰理之KeyPage【篇】_杰理 空白芯片 烧入key文件-程序员宅基地

文章浏览阅读209次。00000000_杰理 空白芯片 烧入key文件

一文读懂ChatGPT,满足你对chatGPT的好奇心_引发对chatgpt兴趣的表述-程序员宅基地

文章浏览阅读475次。2023年初,“ChatGPT”一词在社交媒体上引起了热议,人们纷纷探讨它的本质和对社会的影响。就连央视新闻也对此进行了报道。作为新传专业的前沿人士,我们当然不能忽视这一热点。本文将全面解析ChatGPT,打开“技术黑箱”,探讨它对新闻与传播领域的影响。_引发对chatgpt兴趣的表述

中文字符频率统计python_用Python数据分析方法进行汉字声调频率统计分析-程序员宅基地

文章浏览阅读259次。用Python数据分析方法进行汉字声调频率统计分析木合塔尔·沙地克;布合力齐姑丽·瓦斯力【期刊名称】《电脑知识与技术》【年(卷),期】2017(013)035【摘要】该文首先用Python程序,自动获取基本汉字字符集中的所有汉字,然后用汉字拼音转换工具pypinyin把所有汉字转换成拼音,最后根据所有汉字的拼音声调,统计并可视化拼音声调的占比.【总页数】2页(13-14)【关键词】数据分析;数据可..._汉字声调频率统计

linux输出信息调试信息重定向-程序员宅基地

文章浏览阅读64次。最近在做一个android系统移植的项目,所使用的开发板com1是调试串口,就是说会有uboot和kernel的调试信息打印在com1上(ttySAC0)。因为后期要使用ttySAC0作为上层应用通信串口,所以要把所有的调试信息都给去掉。参考网上的几篇文章,自己做了如下修改,终于把调试信息重定向到ttySAC1上了,在这做下记录。参考文章有:http://blog.csdn.net/longt..._嵌入式rootfs 输出重定向到/dev/console

随便推点

uniapp 引入iconfont图标库彩色symbol教程_uniapp symbol图标-程序员宅基地

文章浏览阅读1.2k次,点赞4次,收藏12次。1,先去iconfont登录,然后选择图标加入购物车 2,点击又上角车车添加进入项目我的项目中就会出现选择的图标 3,点击下载至本地,然后解压文件夹,然后切换到uniapp打开终端运行注:要保证自己电脑有安装node(没有安装node可以去官网下载Node.js 中文网)npm i -g iconfont-tools(mac用户失败的话在前面加个sudo,password就是自己的开机密码吧)4,终端切换到上面解压的文件夹里面,运行iconfont-tools 这些可以默认也可以自己命名(我是自己命名的_uniapp symbol图标

C、C++ 对于char*和char[]的理解_c++ char*-程序员宅基地

文章浏览阅读1.2w次,点赞25次,收藏192次。char*和char[]都是指针,指向第一个字符所在的地址,但char*是常量的指针,char[]是指针的常量_c++ char*

Sublime Text2 使用教程-程序员宅基地

文章浏览阅读930次。代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大、灵活的编辑器,相信你和我一样,都不会例外。我用过的编辑器不少,真不少~ 但却没有哪款让我特别心仪的,直到我遇到了 Sublime Text 2 !如果说“神器”是我能给予一款软件最高的评价,那么我很乐意为它封上这么一个称号。它小巧绿色且速度非

对10个整数进行按照从小到大的顺序排序用选择法和冒泡排序_对十个数进行大小排序java-程序员宅基地

文章浏览阅读4.1k次。一、选择法这是每一个数出来跟后面所有的进行比较。2.冒泡排序法,是两个相邻的进行对比。_对十个数进行大小排序java

物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)_网络调试助手连接阿里云连不上-程序员宅基地

文章浏览阅读2.9k次。物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)其实作者本意是使用4G模块来实现与阿里云物联网平台的连接过程,但是由于自己用的4G模块自身的限制,使得阿里云连接总是无法建立,已经联系客服返厂检修了,于是我在此使用网络调试助手来演示如何与阿里云物联网平台建立连接。一.准备工作1.MQTT协议说明文档(3.1.1版本)2.网络调试助手(可使用域名与服务器建立连接)PS:与阿里云建立连解释,最好使用域名来完成连接过程,而不是使用IP号。这里我跟阿里云的售后工程师咨询过,表示对应_网络调试助手连接阿里云连不上

<<<零基础C++速成>>>_无c语言基础c++期末速成-程序员宅基地

文章浏览阅读544次,点赞5次,收藏6次。运算符与表达式任何高级程序设计语言中,表达式都是最基本的组成部分,可以说C++中的大部分语句都是由表达式构成的。_无c语言基础c++期末速成