diff options
author | Wei Wang | 2018-03-23 13:32:21 -0500 |
---|---|---|
committer | Wei Wang | 2018-03-23 13:33:16 -0500 |
commit | 5e129f1497bade6618be989bf73050108b30c010 (patch) | |
tree | d49d569a47716af8832883ab2e2a919d57d679f2 /tools | |
parent | b966052d2c360bb5b7157ca8ef1015a569848e74 (diff) | |
download | platform-packages-services-car-5e129f1497bade6618be989bf73050108b30c010.tar.gz platform-packages-services-car-5e129f1497bade6618be989bf73050108b30c010.tar.xz platform-packages-services-car-5e129f1497bade6618be989bf73050108b30c010.zip |
Move boottime tools to system/extra
Bug: 65481007
Test: Build
Change-Id: I250ef87a6b580c30b15ea78fec32bbdb1bc78de7
Diffstat (limited to 'tools')
21 files changed, 0 insertions, 2402 deletions
diff --git a/tools/bootanalyze/Android.mk b/tools/bootanalyze/Android.mk deleted file mode 100644 index 5df0dd83..00000000 --- a/tools/bootanalyze/Android.mk +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | # Copyright (C) 2017 The Android Open Source Project | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | # | ||
15 | # | ||
16 | |||
17 | LOCAL_PATH := $(call my-dir) | ||
18 | |||
19 | # Include the sub-makefiles | ||
20 | include $(call all-makefiles-under,$(LOCAL_PATH)) | ||
diff --git a/tools/bootanalyze/README.md b/tools/bootanalyze/README.md deleted file mode 100644 index d6e0412f..00000000 --- a/tools/bootanalyze/README.md +++ /dev/null | |||
@@ -1,15 +0,0 @@ | |||
1 | # bootanalyze # | ||
2 | |||
3 | The bootanalyze tool helps to profile boot timing. | ||
4 | |||
5 | Per specific product modify config.yaml file to include | ||
6 | events you are looking for. Config should look like: | ||
7 | |||
8 | stop_event: <logcat log message which will terminate log collection after reboot> | ||
9 | events: | ||
10 | event1_name: <pattern that matches log message> | ||
11 | event2_..... | ||
12 | |||
13 | On some devise clock is showing incorrect time for first couple of seconds after boot. | ||
14 | To ensure correct adjustment of time, one has to include event in config that will | ||
15 | be present in dmesg log after clock correction. | ||
diff --git a/tools/bootanalyze/bootanalyze.py b/tools/bootanalyze/bootanalyze.py deleted file mode 100755 index b8783585..00000000 --- a/tools/bootanalyze/bootanalyze.py +++ /dev/null | |||
@@ -1,818 +0,0 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | # Copyright (C) 2016 The Android Open Source Project | ||
4 | # | ||
5 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | # you may not use this file except in compliance with the License. | ||
7 | # You may obtain a copy of the License at | ||
8 | # | ||
9 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | # | ||
11 | # Unless required by applicable law or agreed to in writing, software | ||
12 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | # See the License for the specific language governing permissions and | ||
15 | # limitations under the License. | ||
16 | # | ||
17 | """Tool to analyze logcat and dmesg logs. | ||
18 | |||
19 | bootanalyze read logcat and dmesg loga and determines key points for boot. | ||
20 | """ | ||
21 | |||
22 | import argparse | ||
23 | import collections | ||
24 | import datetime | ||
25 | import math | ||
26 | import operator | ||
27 | import os | ||
28 | import re | ||
29 | import select | ||
30 | import subprocess | ||
31 | import sys | ||
32 | import time | ||
33 | import threading | ||
34 | import yaml | ||
35 | |||
36 | from datetime import datetime, date | ||
37 | |||
38 | |||
39 | TIME_DMESG = "\[\s*(\d+\.\d+)\]" | ||
40 | TIME_LOGCAT = "[0-9]+\.?[0-9]*" | ||
41 | KERNEL_TIME_KEY = "kernel" | ||
42 | BOOT_ANIM_END_TIME_KEY = "BootAnimEnd" | ||
43 | KERNEL_BOOT_COMPLETE = "BootComplete_kernel" | ||
44 | LOGCAT_BOOT_COMPLETE = "BootComplete" | ||
45 | LAUNCHER_START = "LauncherStart" | ||
46 | BOOT_TIME_TOO_BIG = 200.0 | ||
47 | MAX_RETRIES = 5 | ||
48 | DEBUG = False | ||
49 | ADB_CMD = "adb" | ||
50 | TIMING_THRESHOLD = 5.0 | ||
51 | BOOT_PROP = "\[ro\.boottime\.([^\]]+)\]:\s+\[(\d+)\]" | ||
52 | BOOTLOADER_TIME_PROP = "\[ro\.boot\.boottime\]:\s+\[([^\]]+)\]" | ||
53 | |||
54 | max_wait_time = BOOT_TIME_TOO_BIG | ||
55 | |||
56 | def main(): | ||
57 | global ADB_CMD | ||
58 | |||
59 | args = init_arguments() | ||
60 | |||
61 | if args.iterate < 1: | ||
62 | raise Exception('Number of iteration must be >=1'); | ||
63 | |||
64 | if args.iterate > 1 and not args.reboot: | ||
65 | print "Forcing reboot flag" | ||
66 | args.reboot = True | ||
67 | |||
68 | if args.serial: | ||
69 | ADB_CMD = "%s %s" % ("adb -s", args.serial) | ||
70 | |||
71 | error_time = BOOT_TIME_TOO_BIG * 10 | ||
72 | if args.errortime: | ||
73 | error_time = float(args.errortime) | ||
74 | if args.maxwaittime: | ||
75 | global max_wait_time | ||
76 | max_wait_time = float(args.maxwaittime) | ||
77 | |||
78 | components_to_monitor = {} | ||
79 | if args.componentmonitor: | ||
80 | items = args.componentmonitor.split(",") | ||
81 | for item in items: | ||
82 | kv = item.split("=") | ||
83 | key = kv[0] | ||
84 | value = float(kv[1]) | ||
85 | components_to_monitor[key] = value | ||
86 | |||
87 | cfg = yaml.load(args.config) | ||
88 | |||
89 | if args.stressfs: | ||
90 | if run_adb_cmd('install -r -g ' + args.stressfs) != 0: | ||
91 | raise Exception('StressFS APK not installed'); | ||
92 | |||
93 | if args.iterate > 1 and args.bootchart: | ||
94 | run_adb_shell_cmd_as_root('touch /data/bootchart/enabled') | ||
95 | |||
96 | search_events_pattern = {key: re.compile(pattern) | ||
97 | for key, pattern in cfg['events'].iteritems()} | ||
98 | timing_events_pattern = {key: re.compile(pattern) | ||
99 | for key, pattern in cfg['timings'].iteritems()} | ||
100 | shutdown_events_pattern = {key: re.compile(pattern) | ||
101 | for key, pattern in cfg['shutdown_events'].iteritems()} | ||
102 | |||
103 | data_points = {} | ||
104 | kernel_timing_points = collections.OrderedDict() | ||
105 | logcat_timing_points = collections.OrderedDict() | ||
106 | boottime_points = collections.OrderedDict() | ||
107 | boot_chart_file_name_prefix = "bootchart-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S") | ||
108 | systrace_file_name_prefix = "systrace-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S") | ||
109 | shutdown_event_all = collections.OrderedDict() | ||
110 | shutdown_timing_event_all = collections.OrderedDict() | ||
111 | for it in range(0, args.iterate): | ||
112 | if args.iterate > 1: | ||
113 | print "Run: {0}".format(it) | ||
114 | attempt = 1 | ||
115 | processing_data = None | ||
116 | timings = None | ||
117 | boottime_events = None | ||
118 | while attempt <= MAX_RETRIES and processing_data is None: | ||
119 | attempt += 1 | ||
120 | processing_data, kernel_timings, logcat_timings, boottime_events, shutdown_events,\ | ||
121 | shutdown_timing_events = iterate(\ | ||
122 | args, search_events_pattern, timing_events_pattern, shutdown_events_pattern, cfg,\ | ||
123 | error_time, components_to_monitor) | ||
124 | if shutdown_events: | ||
125 | for k, v in shutdown_events.iteritems(): | ||
126 | events = shutdown_event_all.get(k) | ||
127 | if not events: | ||
128 | events = [] | ||
129 | shutdown_event_all[k] = events | ||
130 | events.append(v) | ||
131 | if shutdown_timing_events: | ||
132 | for k, v in shutdown_timing_events.iteritems(): | ||
133 | events = shutdown_timing_event_all.get(k) | ||
134 | if not events: | ||
135 | events = [] | ||
136 | shutdown_timing_event_all[k] = events | ||
137 | events.append(v) | ||
138 | if not processing_data or not boottime_events: | ||
139 | # Processing error | ||
140 | print "Failed to collect valid samples for run {0}".format(it) | ||
141 | continue | ||
142 | if args.bootchart: | ||
143 | grab_bootchart(boot_chart_file_name_prefix + "_run_" + str(it)) | ||
144 | |||
145 | if args.systrace: | ||
146 | grab_systrace(systrace_file_name_prefix + "_run_" + str(it)) | ||
147 | for k, v in processing_data.iteritems(): | ||
148 | if k not in data_points: | ||
149 | data_points[k] = [] | ||
150 | data_points[k].append(v['value']) | ||
151 | |||
152 | if kernel_timings is not None: | ||
153 | for k, v in kernel_timings.iteritems(): | ||
154 | if k not in kernel_timing_points: | ||
155 | kernel_timing_points[k] = [] | ||
156 | kernel_timing_points[k].append(v) | ||
157 | if logcat_timings is not None: | ||
158 | for k, v in logcat_timings.iteritems(): | ||
159 | if k not in logcat_timing_points: | ||
160 | logcat_timing_points[k] = [] | ||
161 | logcat_timing_points[k].append(v) | ||
162 | |||
163 | for k, v in boottime_events.iteritems(): | ||
164 | if not k in boottime_points: | ||
165 | boottime_points[k] = [] | ||
166 | boottime_points[k].append(v) | ||
167 | |||
168 | if args.stressfs: | ||
169 | run_adb_cmd('uninstall com.android.car.test.stressfs') | ||
170 | run_adb_shell_cmd('"rm -rf /storage/emulated/0/stressfs_data*"') | ||
171 | |||
172 | if args.iterate > 1: | ||
173 | print "-----------------" | ||
174 | print "\nshutdown events after {0} runs".format(args.iterate) | ||
175 | print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") | ||
176 | for item in shutdown_event_all.items(): | ||
177 | num_runs = len(item[1]) | ||
178 | print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format( | ||
179 | item[0], sum(item[1])/num_runs, stddev(item[1]),\ | ||
180 | "*time taken" if item[0].startswith("init.") else "",\ | ||
181 | num_runs if num_runs != args.iterate else "") | ||
182 | print "\nshutdown timing events after {0} runs".format(args.iterate) | ||
183 | print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") | ||
184 | for item in shutdown_timing_event_all.items(): | ||
185 | num_runs = len(item[1]) | ||
186 | print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format( | ||
187 | item[0], sum(item[1])/num_runs, stddev(item[1]),\ | ||
188 | "*time taken" if item[0].startswith("init.") else "",\ | ||
189 | num_runs if num_runs != args.iterate else "") | ||
190 | |||
191 | print "-----------------" | ||
192 | print "ro.boottime.* after {0} runs".format(args.iterate) | ||
193 | print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") | ||
194 | for item in boottime_points.items(): | ||
195 | num_runs = len(item[1]) | ||
196 | print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format( | ||
197 | item[0], sum(item[1])/num_runs, stddev(item[1]),\ | ||
198 | "*time taken" if item[0].startswith("init.") else "",\ | ||
199 | num_runs if num_runs != args.iterate else "") | ||
200 | |||
201 | if args.timings: | ||
202 | dump_timings_points_summary("Kernel", kernel_timing_points, args) | ||
203 | dump_timings_points_summary("Logcat", logcat_timing_points, args) | ||
204 | |||
205 | |||
206 | print "-----------------" | ||
207 | print "Avg values after {0} runs".format(args.iterate) | ||
208 | print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") | ||
209 | |||
210 | average_with_stddev = [] | ||
211 | for item in data_points.items(): | ||
212 | average_with_stddev.append((item[0], sum(item[1])/len(item[1]), stddev(item[1]),\ | ||
213 | len(item[1]))) | ||
214 | for item in sorted(average_with_stddev, key=lambda entry: entry[1]): | ||
215 | print '{0:30}: {1:<7.5} {2:<7.5} {3}'.format( | ||
216 | item[0], item[1], item[2], item[3] if item[3] != args.iterate else "") | ||
217 | |||
218 | run_adb_shell_cmd_as_root('rm /data/bootchart/enabled') | ||
219 | |||
220 | |||
221 | def dump_timings_points_summary(msg_header, timing_points, args): | ||
222 | averaged_timing_points = [] | ||
223 | for item in timing_points.items(): | ||
224 | average = sum(item[1])/len(item[1]) | ||
225 | std_dev = stddev(item[1]) | ||
226 | averaged_timing_points.append((item[0], average, std_dev, len(item[1]))) | ||
227 | |||
228 | print "-----------------" | ||
229 | print msg_header + " timing in order, Avg time values after {0} runs".format(args.iterate) | ||
230 | print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") | ||
231 | for item in averaged_timing_points: | ||
232 | print '{0:30}: {1:<7.5} {2:<7.5} {3}'.format( | ||
233 | item[0], item[1], item[2], item[3] if item[3] != args.iterate else "") | ||
234 | |||
235 | print "-----------------" | ||
236 | print msg_header + " timing top items, Avg time values after {0} runs".format(args.iterate) | ||
237 | print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs") | ||
238 | for item in sorted(averaged_timing_points, key=lambda entry: entry[1], reverse=True): | ||
239 | if item[1] < TIMING_THRESHOLD: | ||
240 | break | ||
241 | print '{0:30}: {1:<7.5} {2:<7.5} {3}'.format( | ||
242 | item[0], item[1], item[2], item[3] if item[3] != args.iterate else "") | ||
243 | |||
244 | def capture_bugreport(bugreport_hint, boot_complete_time): | ||
245 | now = datetime.now() | ||
246 | bugreport_file = ("bugreport-%s-" + bugreport_hint + "-%s.zip") \ | ||
247 | % (now.strftime("%Y-%m-%d-%H-%M-%S"), str(boot_complete_time)) | ||
248 | print "Boot up time too big, will capture bugreport %s" % (bugreport_file) | ||
249 | os.system(ADB_CMD + " bugreport " + bugreport_file) | ||
250 | |||
251 | def generate_timing_points(timing_events, timings): | ||
252 | timing_points = collections.OrderedDict() | ||
253 | for k, l in timing_events.iteritems(): | ||
254 | for v in l: | ||
255 | name, time_v = extract_timing(v, timings) | ||
256 | if name and time_v: | ||
257 | if v.find("SystemServerTimingAsync") > 0: | ||
258 | name = "(" + name + ")" | ||
259 | new_name = name | ||
260 | name_index = 0 | ||
261 | while timing_points.get(new_name): # if the name is already taken, append #digit | ||
262 | name_index += 1 | ||
263 | new_name = name + "#" + str(name_index) | ||
264 | name = new_name | ||
265 | if k.endswith("_secs"): | ||
266 | timing_points[name] = time_v * 1000.0 | ||
267 | else: | ||
268 | timing_points[name] = time_v | ||
269 | return timing_points | ||
270 | |||
271 | def dump_timing_points(msg_header, timing_points): | ||
272 | print msg_header + " event timing in time order, key: time" | ||
273 | for item in timing_points.items(): | ||
274 | print '{0:30}: {1:<7.5}'.format(item[0], item[1]) | ||
275 | print "-----------------" | ||
276 | print msg_header + " event timing top items" | ||
277 | for item in sorted(timing_points.items(), key=operator.itemgetter(1), reverse = True): | ||
278 | if item[1] < TIMING_THRESHOLD: | ||
279 | break | ||
280 | print '{0:30}: {1:<7.5}'.format( | ||
281 | item[0], item[1]) | ||
282 | print "-----------------" | ||
283 | |||
284 | def handle_reboot_log(capture_log_on_error, shutdown_events_pattern, components_to_monitor): | ||
285 | shutdown_events, shutdown_timing_events = collect_logcat_for_shutdown(capture_log_on_error,\ | ||
286 | shutdown_events_pattern, components_to_monitor) | ||
287 | print "\nshutdown events: time" | ||
288 | for item in shutdown_events.items(): | ||
289 | print '{0:30}: {1:<7.5}'.format(item[0], item[1]) | ||
290 | print "\nshutdown timing events: time" | ||
291 | for item in shutdown_timing_events.items(): | ||
292 | print '{0:30}: {1:<7.5}'.format(item[0], item[1]) | ||
293 | return shutdown_events, shutdown_timing_events | ||
294 | |||
295 | def iterate(args, search_events_pattern, timings_pattern, shutdown_events_pattern, cfg, error_time,\ | ||
296 | components_to_monitor): | ||
297 | shutdown_events = None | ||
298 | shutdown_timing_events = None | ||
299 | if args.reboot: | ||
300 | # sleep to make sure that logcat reader is reading before adb is gone by reboot. ugly but make | ||
301 | # impl simple. | ||
302 | t = threading.Thread(target = lambda : (time.sleep(2), reboot(args.serial, args.stressfs != '',\ | ||
303 | args.permissive, args.adb_reboot))) | ||
304 | t.start() | ||
305 | shutdown_events, shutdown_timing_events = handle_reboot_log(True, shutdown_events_pattern,\ | ||
306 | components_to_monitor) | ||
307 | t.join() | ||
308 | |||
309 | dmesg_events, kernel_timing_events = collect_events(search_events_pattern, ADB_CMD +\ | ||
310 | ' shell su root dmesg -w', timings_pattern,\ | ||
311 | [ KERNEL_BOOT_COMPLETE ], True) | ||
312 | |||
313 | logcat_stop_events = [ LOGCAT_BOOT_COMPLETE, KERNEL_BOOT_COMPLETE, LAUNCHER_START] | ||
314 | if args.fs_check: | ||
315 | logcat_stop_events.append("FsStat") | ||
316 | logcat_events, logcat_timing_events = collect_events( | ||
317 | search_events_pattern, ADB_CMD + ' logcat -b all -v epoch', timings_pattern,\ | ||
318 | logcat_stop_events, False) | ||
319 | logcat_event_time = extract_time( | ||
320 | logcat_events, TIME_LOGCAT, float); | ||
321 | logcat_original_time = extract_time( | ||
322 | logcat_events, TIME_LOGCAT, str); | ||
323 | dmesg_event_time = extract_time( | ||
324 | dmesg_events, TIME_DMESG, float); | ||
325 | boottime_events = fetch_boottime_property() | ||
326 | events = {} | ||
327 | diff_time = 0 | ||
328 | max_time = 0 | ||
329 | events_to_correct = [] | ||
330 | replaced_from_dmesg = set() | ||
331 | |||
332 | time_correction_delta = 0 | ||
333 | time_correction_time = 0 | ||
334 | if ('time_correction_key' in cfg | ||
335 | and cfg['time_correction_key'] in logcat_events): | ||
336 | match = search_events[cfg['time_correction_key']].search( | ||
337 | logcat_events[cfg['time_correction_key']]) | ||
338 | if match and logcat_event_time[cfg['time_correction_key']]: | ||
339 | time_correction_delta = float(match.group(1)) | ||
340 | time_correction_time = logcat_event_time[cfg['time_correction_key']] | ||
341 | |||
342 | debug("time_correction_delta = {0}, time_correction_time = {1}".format( | ||
343 | time_correction_delta, time_correction_time)) | ||
344 | |||
345 | for k, v in logcat_event_time.iteritems(): | ||
346 | if v <= time_correction_time: | ||
347 | logcat_event_time[k] += time_correction_delta | ||
348 | v = v + time_correction_delta | ||
349 | debug("correcting event to event[{0}, {1}]".format(k, v)) | ||
350 | |||
351 | if not logcat_event_time.get(KERNEL_TIME_KEY): | ||
352 | print "kernel time not captured in logcat, cannot get time diff" | ||
353 | return None, None, None, None | ||
354 | diffs = [] | ||
355 | diffs.append((logcat_event_time[KERNEL_TIME_KEY], logcat_event_time[KERNEL_TIME_KEY])) | ||
356 | if logcat_event_time.get(BOOT_ANIM_END_TIME_KEY) and dmesg_event_time.get(BOOT_ANIM_END_TIME_KEY): | ||
357 | diffs.append((logcat_event_time[BOOT_ANIM_END_TIME_KEY],\ | ||
358 | logcat_event_time[BOOT_ANIM_END_TIME_KEY] -\ | ||
359 | dmesg_event_time[BOOT_ANIM_END_TIME_KEY])) | ||
360 | if not dmesg_event_time.get(KERNEL_BOOT_COMPLETE): | ||
361 | print "BootAnimEnd time or BootComplete-kernel not captured in both log" +\ | ||
362 | ", cannot get time diff" | ||
363 | return None, None, None, None | ||
364 | diffs.append((logcat_event_time[KERNEL_BOOT_COMPLETE],\ | ||
365 | logcat_event_time[KERNEL_BOOT_COMPLETE] - dmesg_event_time[KERNEL_BOOT_COMPLETE])) | ||
366 | |||
367 | for k, v in logcat_event_time.iteritems(): | ||
368 | debug("event[{0}, {1}]".format(k, v)) | ||
369 | events[k] = v | ||
370 | if k in dmesg_event_time: | ||
371 | debug("{0} is in dmesg".format(k)) | ||
372 | events[k] = dmesg_event_time[k] | ||
373 | replaced_from_dmesg.add(k) | ||
374 | else: | ||
375 | events_to_correct.append(k) | ||
376 | |||
377 | diff_prev = diffs[0] | ||
378 | for k in events_to_correct: | ||
379 | diff = diffs[0] | ||
380 | while diff[0] < events[k] and len(diffs) > 1: | ||
381 | diffs.pop(0) | ||
382 | diff_prev = diff | ||
383 | diff = diffs[0] | ||
384 | events[k] = events[k] - diff[1] | ||
385 | if events[k] < 0.0: | ||
386 | if events[k] < -0.1: # maybe previous one is better fit | ||
387 | events[k] = events[k] + diff[1] - diff_prev[1] | ||
388 | else: | ||
389 | events[k] = 0.0 | ||
390 | |||
391 | data_points = collections.OrderedDict() | ||
392 | |||
393 | print "-----------------" | ||
394 | print "ro.boottime.*: time" | ||
395 | for item in boottime_events.items(): | ||
396 | print '{0:30}: {1:<7.5} {2}'.format(item[0], item[1],\ | ||
397 | "*time taken" if item[0].startswith("init.") else "") | ||
398 | print "-----------------" | ||
399 | |||
400 | if args.timings: | ||
401 | kernel_timing_points = generate_timing_points(kernel_timing_events, timings_pattern) | ||
402 | logcat_timing_points = generate_timing_points(logcat_timing_events, timings_pattern) | ||
403 | dump_timing_points("Kernel", kernel_timing_points) | ||
404 | dump_timing_points("Logcat", logcat_timing_points) | ||
405 | |||
406 | for item in sorted(events.items(), key=operator.itemgetter(1)): | ||
407 | data_points[item[0]] = { | ||
408 | 'value': item[1], | ||
409 | 'from_dmesg': item[0] in replaced_from_dmesg, | ||
410 | 'logcat_value': logcat_original_time[item[0]] | ||
411 | } | ||
412 | # add times with bootloader | ||
413 | if events.get("BootComplete") and boottime_events.get("bootloader"): | ||
414 | total = events["BootComplete"] + boottime_events["bootloader"] | ||
415 | data_points["*BootComplete+Bootloader"] = { | ||
416 | 'value': total, | ||
417 | 'from_dmesg': False, | ||
418 | 'logcat_value': 0.0 | ||
419 | } | ||
420 | if events.get("LauncherStart") and boottime_events.get("bootloader"): | ||
421 | total = events["LauncherStart"] + boottime_events["bootloader"] | ||
422 | data_points["*LauncherStart+Bootloader"] = { | ||
423 | 'value': total, | ||
424 | 'from_dmesg': False, | ||
425 | 'logcat_value': 0.0 | ||
426 | } | ||
427 | for k, v in data_points.iteritems(): | ||
428 | print '{0:30}: {1:<7.5} {2:1} ({3})'.format( | ||
429 | k, v['value'], '*' if v['from_dmesg'] else '', v['logcat_value']) | ||
430 | |||
431 | print '\n* - event time was obtained from dmesg log\n' | ||
432 | |||
433 | if events[LOGCAT_BOOT_COMPLETE] > error_time and not args.ignore: | ||
434 | capture_bugreport("bootuptoolong", events[LOGCAT_BOOT_COMPLETE]) | ||
435 | |||
436 | for k, v in components_to_monitor.iteritems(): | ||
437 | logcat_value_measured = logcat_timing_points.get(k) | ||
438 | kernel_value_measured = kernel_timing_points.get(k) | ||
439 | data_from_data_points = data_points.get(k) | ||
440 | if logcat_value_measured and logcat_value_measured > v: | ||
441 | capture_bugreport(k + "-" + str(logcat_value_measured), events[LOGCAT_BOOT_COMPLETE]) | ||
442 | break | ||
443 | elif kernel_value_measured and kernel_value_measured > v: | ||
444 | capture_bugreport(k + "-" + str(kernel_value_measured), events[LOGCAT_BOOT_COMPLETE]) | ||
445 | break | ||
446 | elif data_from_data_points and data_from_data_points['value'] * 1000.0 > v: | ||
447 | capture_bugreport(k + "-" + str(data_from_data_points['value']), events[LOGCAT_BOOT_COMPLETE]) | ||
448 | break | ||
449 | |||
450 | if args.fs_check: | ||
451 | fs_stat = None | ||
452 | if logcat_events.get("FsStat"): | ||
453 | fs_stat_pattern = cfg["events"]["FsStat"] | ||
454 | m = re.search(fs_stat_pattern, logcat_events.get("FsStat")) | ||
455 | if m: | ||
456 | fs_stat = m.group(1) | ||
457 | print 'fs_stat:', fs_stat | ||
458 | |||
459 | if fs_stat: | ||
460 | fs_stat_val = int(fs_stat, 0) | ||
461 | if (fs_stat_val & ~0x17) != 0: | ||
462 | capture_bugreport("fs_stat_" + fs_stat, events[LOGCAT_BOOT_COMPLETE]) | ||
463 | |||
464 | return data_points, kernel_timing_points, logcat_timing_points, boottime_events, shutdown_events,\ | ||
465 | shutdown_timing_events | ||
466 | |||
467 | def debug(string): | ||
468 | if DEBUG: | ||
469 | print string | ||
470 | |||
471 | def extract_timing(s, patterns): | ||
472 | for k, p in patterns.iteritems(): | ||
473 | m = p.search(s) | ||
474 | if m: | ||
475 | g_dict = m.groupdict() | ||
476 | return g_dict['name'], float(g_dict['time']) | ||
477 | return None, None | ||
478 | |||
479 | def init_arguments(): | ||
480 | parser = argparse.ArgumentParser(description='Measures boot time.') | ||
481 | parser.add_argument('-r', '--reboot', dest='reboot', | ||
482 | action='store_true', | ||
483 | help='reboot device for measurement', ) | ||
484 | parser.add_argument('-c', '--config', dest='config', | ||
485 | default='config.yaml', type=argparse.FileType('r'), | ||
486 | help='config file for the tool', ) | ||
487 | parser.add_argument('-s', '--stressfs', dest='stressfs', | ||
488 | default='', type=str, | ||
489 | help='APK file for the stressfs tool used to write to the data partition ' +\ | ||
490 | 'during shutdown') | ||
491 | parser.add_argument('-n', '--iterate', dest='iterate', type=int, default=1, | ||
492 | help='number of time to repeat the measurement', ) | ||
493 | parser.add_argument('-g', '--ignore', dest='ignore', action='store_true', | ||
494 | help='ignore too big values error', ) | ||
495 | parser.add_argument('-t', '--timings', dest='timings', action='store_true', | ||
496 | help='print individual component times', default=True, ) | ||
497 | parser.add_argument('-p', '--serial', dest='serial', action='store', | ||
498 | help='android device serial number') | ||
499 | parser.add_argument('-e', '--errortime', dest='errortime', action='store', | ||
500 | help='handle bootup time bigger than this as error') | ||
501 | parser.add_argument('-w', '--maxwaittime', dest='maxwaittime', action='store', | ||
502 | help='wait for up to this time to collect logs. Retry after this time.' +\ | ||
503 | ' Default is 200 sec.') | ||
504 | parser.add_argument('-f', '--fs_check', dest='fs_check', | ||
505 | action='store_true', | ||
506 | help='check fs_stat after reboot', ) | ||
507 | parser.add_argument('-a', '--adb_reboot', dest='adb_reboot', | ||
508 | action='store_true', | ||
509 | help='reboot with adb reboot', ) | ||
510 | parser.add_argument('-v', '--permissive', dest='permissive', | ||
511 | action='store_true', | ||
512 | help='set selinux into permissive before reboot', ) | ||
513 | parser.add_argument('-m', '--componentmonitor', dest='componentmonitor', action='store', | ||
514 | help='capture bugreport if specified timing component is taking more than ' +\ | ||
515 | 'certain time. Unlike errortime, the result will not be rejected in' +\ | ||
516 | 'averaging. Format is key1=time1,key2=time2...') | ||
517 | parser.add_argument('-b', '--bootchart', dest='bootchart', | ||
518 | action='store_true', | ||
519 | help='collect bootchart from the device.', ) | ||
520 | parser.add_argument('-y', '--systrace', dest='systrace', | ||
521 | action='store_true', | ||
522 | help='collect systrace from the device. kernel trace should be already enabled', ) | ||
523 | return parser.parse_args() | ||
524 | |||
525 | def handle_zygote_event(zygote_pids, events, event, line): | ||
526 | words = line.split() | ||
527 | if len(words) > 1: | ||
528 | pid = int(words[1]) | ||
529 | if len(zygote_pids) == 2: | ||
530 | if pid == zygote_pids[1]: # secondary | ||
531 | event = event + "-secondary" | ||
532 | elif len(zygote_pids) == 1: | ||
533 | if zygote_pids[0] != pid: # new pid, need to decide if old ones were secondary | ||
534 | primary_pid = min(pid, zygote_pids[0]) | ||
535 | secondary_pid = max(pid, zygote_pids[0]) | ||
536 | zygote_pids.pop() | ||
537 | zygote_pids.append(primary_pid) | ||
538 | zygote_pids.append(secondary_pid) | ||
539 | if pid == primary_pid: # old one was secondary: | ||
540 | move_to_secondary = [] | ||
541 | for k, l in events.iteritems(): | ||
542 | if k.startswith("zygote"): | ||
543 | move_to_secondary.append((k, l)) | ||
544 | for item in move_to_secondary: | ||
545 | del events[item[0]] | ||
546 | if item[0].endswith("-secondary"): | ||
547 | print "Secondary already exists for event %s while found new pid %d, primary %d "\ | ||
548 | % (item[0], secondary_pid, primary_pid) | ||
549 | else: | ||
550 | events[item[0] + "-secondary"] = item[1] | ||
551 | else: | ||
552 | event = event + "-secondary" | ||
553 | else: | ||
554 | zygote_pids.append(pid) | ||
555 | events[event] = line | ||
556 | |||
557 | def update_name_if_already_exist(events, name): | ||
558 | existing_event = events.get(name) | ||
559 | i = 0 | ||
560 | new_name = name | ||
561 | while existing_event: | ||
562 | i += 1 | ||
563 | new_name = name + "_" + str(i) | ||
564 | existing_event = events.get(new_name) | ||
565 | return new_name | ||
566 | |||
567 | def collect_logcat_for_shutdown(capture_log_on_error, shutdown_events_pattern,\ | ||
568 | log_capture_conditions): | ||
569 | events = collections.OrderedDict() | ||
570 | # shutdown does not have timing_events but calculated from checking Xyz - XyzDone / XyzTimeout | ||
571 | timing_events = collections.OrderedDict() | ||
572 | process = subprocess.Popen(ADB_CMD + ' logcat -b all -v epoch', shell=True, | ||
573 | stdout=subprocess.PIPE); | ||
574 | lines = [] | ||
575 | capture_log = False | ||
576 | shutdown_start_time = 0 | ||
577 | while (True): | ||
578 | line = process.stdout.readline().lstrip().rstrip() | ||
579 | if not line: | ||
580 | break | ||
581 | lines.append(line) | ||
582 | event = get_boot_event(line, shutdown_events_pattern); | ||
583 | if not event: | ||
584 | continue | ||
585 | time = extract_a_time(line, TIME_LOGCAT, float) | ||
586 | if not time: | ||
587 | print "cannot get time from: " + line | ||
588 | continue | ||
589 | if shutdown_start_time == 0: | ||
590 | shutdown_start_time = time | ||
591 | time = time - shutdown_start_time | ||
592 | events[event] = time | ||
593 | time_limit1 = log_capture_conditions.get(event) | ||
594 | if time_limit1 and time_limit1 <= time: | ||
595 | capture_log = True | ||
596 | pair_event = None | ||
597 | if event.endswith('Done'): | ||
598 | pair_event = event[:-4] | ||
599 | elif event.endswith('Timeout'): | ||
600 | pair_event = event[:-7] | ||
601 | if capture_log_on_error: | ||
602 | capture_log = True | ||
603 | if not pair_event: | ||
604 | continue | ||
605 | start_time = events.get(pair_event) | ||
606 | if not start_time: | ||
607 | print "No start event for " + event | ||
608 | continue | ||
609 | time_spent = time - start_time | ||
610 | timing_event_name = pair_event + "Duration" | ||
611 | timing_events[timing_event_name] = time_spent | ||
612 | time_limit2 = log_capture_conditions.get(timing_event_name) | ||
613 | if time_limit2 and time_limit2 <= time_spent: | ||
614 | capture_log = True | ||
615 | |||
616 | if capture_log: | ||
617 | now = datetime.now() | ||
618 | log_file = ("shutdownlog-error-%s.txt") % (now.strftime("%Y-%m-%d-%H-%M-%S")) | ||
619 | print "Shutdown error, capture log to %s" % (log_file) | ||
620 | with open(log_file, 'w') as f: | ||
621 | f.write('\n'.join(lines)) | ||
622 | return events, timing_events | ||
623 | |||
624 | |||
625 | def collect_events(search_events, command, timings, stop_events, disable_timing_after_zygote): | ||
626 | events = collections.OrderedDict() | ||
627 | timing_events = {} | ||
628 | process = subprocess.Popen(command, shell=True, | ||
629 | stdout=subprocess.PIPE); | ||
630 | data_available = stop_events is None | ||
631 | zygote_pids = [] | ||
632 | start_time = time.time() | ||
633 | zygote_found = False | ||
634 | |||
635 | line = None | ||
636 | read_poll = select.poll() | ||
637 | read_poll.register(process.stdout, select.POLLIN) | ||
638 | while True: | ||
639 | time_left = start_time + max_wait_time - time.time() | ||
640 | if time_left <= 0: | ||
641 | print "timeout waiting for event, continue", time_left | ||
642 | break | ||
643 | read_r = read_poll.poll(time_left * 1000.0) | ||
644 | if len(read_r) > 0 and read_r[0][1] == select.POLLIN: | ||
645 | line = process.stdout.readline() | ||
646 | else: | ||
647 | print "poll timeout waiting for event, continue", time_left | ||
648 | break | ||
649 | if not data_available: | ||
650 | print "Collecting data samples from '%s'. Please wait...\n" % command | ||
651 | data_available = True | ||
652 | event = get_boot_event(line, search_events); | ||
653 | if event: | ||
654 | debug("event[{0}] captured: {1}".format(event, line)) | ||
655 | if event == "starting_zygote": | ||
656 | events[event] = line | ||
657 | zygote_found = True | ||
658 | elif event.startswith("zygote"): | ||
659 | handle_zygote_event(zygote_pids, events, event, line) | ||
660 | else: | ||
661 | new_event = update_name_if_already_exist(events, event) | ||
662 | events[new_event] = line | ||
663 | if event in stop_events: | ||
664 | stop_events.remove(event) | ||
665 | print "remaining stop_events:", stop_events | ||
666 | if len(stop_events) == 0: | ||
667 | break; | ||
668 | |||
669 | timing_event = get_boot_event(line, timings); | ||
670 | if timing_event and (not disable_timing_after_zygote or not zygote_found): | ||
671 | if timing_event not in timing_events: | ||
672 | timing_events[timing_event] = [] | ||
673 | timing_events[timing_event].append(line) | ||
674 | debug("timing_event[{0}] captured: {1}".format(timing_event, line)) | ||
675 | |||
676 | process.terminate() | ||
677 | return events, timing_events | ||
678 | |||
679 | def fetch_boottime_property(): | ||
680 | cmd = ADB_CMD + ' shell su root getprop' | ||
681 | events = {} | ||
682 | process = subprocess.Popen(cmd, shell=True, | ||
683 | stdout=subprocess.PIPE); | ||
684 | out = process.stdout | ||
685 | pattern = re.compile(BOOT_PROP) | ||
686 | pattern_bootloader = re.compile(BOOTLOADER_TIME_PROP) | ||
687 | bootloader_time = 0.0 | ||
688 | for line in out: | ||
689 | match = pattern.match(line) | ||
690 | if match: | ||
691 | if match.group(1).startswith("init."): | ||
692 | events[match.group(1)] = float(match.group(2)) / 1000.0 #ms to s | ||
693 | else: | ||
694 | events[match.group(1)] = float(match.group(2)) / 1000000000.0 #ns to s | ||
695 | match = pattern_bootloader.match(line) | ||
696 | if match: | ||
697 | items = match.group(1).split(",") | ||
698 | for item in items: | ||
699 | entry_pair = item.split(":") | ||
700 | entry_name = entry_pair[0] | ||
701 | time_spent = float(entry_pair[1]) / 1000 #ms to s | ||
702 | if entry_name != "SW": | ||
703 | bootloader_time = bootloader_time + time_spent | ||
704 | ordered_event = collections.OrderedDict() | ||
705 | if bootloader_time != 0.0: | ||
706 | ordered_event["bootloader"] = bootloader_time; | ||
707 | for item in sorted(events.items(), key=operator.itemgetter(1)): | ||
708 | ordered_event[item[0]] = item[1] | ||
709 | return ordered_event | ||
710 | |||
711 | |||
712 | def get_boot_event(line, events): | ||
713 | for event_key, event_pattern in events.iteritems(): | ||
714 | if event_pattern.search(line): | ||
715 | return event_key | ||
716 | return None | ||
717 | |||
718 | def extract_a_time(line, pattern, date_transform_function): | ||
719 | found = re.findall(pattern, line) | ||
720 | if len(found) > 0: | ||
721 | return date_transform_function(found[0]) | ||
722 | else: | ||
723 | return None | ||
724 | |||
725 | def extract_time(events, pattern, date_transform_function): | ||
726 | result = collections.OrderedDict() | ||
727 | for event, data in events.iteritems(): | ||
728 | time = extract_a_time(data, pattern, date_transform_function) | ||
729 | if time: | ||
730 | result[event] = time | ||
731 | else: | ||
732 | print "Failed to find time for event: ", event, data | ||
733 | return result | ||
734 | |||
735 | |||
736 | def do_reboot(serial, use_adb_reboot): | ||
737 | original_devices = subprocess.check_output("adb devices", shell=True) | ||
738 | if use_adb_reboot: | ||
739 | print 'Rebooting the device using adb reboot' | ||
740 | run_adb_cmd('reboot') | ||
741 | else: | ||
742 | print 'Rebooting the device using svc power reboot' | ||
743 | run_adb_shell_cmd_as_root('svc power reboot') | ||
744 | # Wait for the device to go away | ||
745 | retry = 0 | ||
746 | while retry < 20: | ||
747 | current_devices = subprocess.check_output("adb devices", shell=True) | ||
748 | if original_devices != current_devices: | ||
749 | if not serial or (serial and current_devices.find(serial) < 0): | ||
750 | return True | ||
751 | time.sleep(1) | ||
752 | retry += 1 | ||
753 | return False | ||
754 | |||
755 | def reboot(serial, use_stressfs, permissive, use_adb_reboot): | ||
756 | if use_stressfs: | ||
757 | print 'Starting write to data partition' | ||
758 | run_adb_shell_cmd('am start' +\ | ||
759 | ' -n com.android.car.test.stressfs/.WritingActivity' +\ | ||
760 | ' -a com.android.car.test.stressfs.START') | ||
761 | # Give this app some time to start. | ||
762 | time.sleep(1) | ||
763 | if permissive: | ||
764 | run_adb_shell_cmd_as_root('setenforce 0') | ||
765 | |||
766 | retry = 0 | ||
767 | while retry < 5: | ||
768 | if do_reboot(serial, use_adb_reboot): | ||
769 | break | ||
770 | retry += 1 | ||
771 | |||
772 | print 'Waiting the device' | ||
773 | run_adb_cmd('wait-for-device') | ||
774 | |||
775 | def run_adb_cmd(cmd): | ||
776 | return subprocess.call(ADB_CMD + ' ' + cmd, shell=True) | ||
777 | |||
778 | def run_adb_shell_cmd(cmd): | ||
779 | return subprocess.call(ADB_CMD + ' shell ' + cmd, shell=True) | ||
780 | |||
781 | def run_adb_shell_cmd_as_root(cmd): | ||
782 | return subprocess.call(ADB_CMD + ' shell su root ' + cmd, shell=True) | ||
783 | |||
784 | def logcat_time_func(offset_year): | ||
785 | def f(date_str): | ||
786 | ndate = datetime.datetime.strptime(str(offset_year) + '-' + | ||
787 | date_str, '%Y-%m-%d %H:%M:%S.%f') | ||
788 | return datetime_to_unix_time(ndate) | ||
789 | return f | ||
790 | |||
791 | def datetime_to_unix_time(ndate): | ||
792 | return time.mktime(ndate.timetuple()) + ndate.microsecond/1000000.0 | ||
793 | |||
794 | def stddev(data): | ||
795 | items_count = len(data) | ||
796 | avg = sum(data) / items_count | ||
797 | sq_diffs_sum = sum([(v - avg) ** 2 for v in data]) | ||
798 | variance = sq_diffs_sum / items_count | ||
799 | return math.sqrt(variance) | ||
800 | |||
801 | def grab_bootchart(boot_chart_file_name): | ||
802 | subprocess.call("./system/core/init/grab-bootchart.sh", shell=True) | ||
803 | print "Saving boot chart as " + boot_chart_file_name + ".tgz" | ||
804 | subprocess.call('cp /tmp/android-bootchart/bootchart.tgz ./' + boot_chart_file_name + '.tgz',\ | ||
805 | shell=True) | ||
806 | subprocess.call('cp ./bootchart.png ./' + boot_chart_file_name + '.png', shell=True) | ||
807 | |||
808 | def grab_systrace(systrace_file_name): | ||
809 | trace_file = systrace_file_name + "_trace.txt" | ||
810 | with open(trace_file, 'w') as f: | ||
811 | f.write("TRACE:\n") | ||
812 | run_adb_shell_cmd_as_root("cat /d/tracing/trace >> " + trace_file) | ||
813 | html_file = systrace_file_name + ".html" | ||
814 | subprocess.call("./external/chromium-trace/systrace.py --from-file=" + trace_file + " -o " +\ | ||
815 | html_file, shell=True) | ||
816 | |||
817 | if __name__ == '__main__': | ||
818 | main() | ||
diff --git a/tools/bootanalyze/bugreport_anayze.py b/tools/bootanalyze/bugreport_anayze.py deleted file mode 100644 index 2575ebf6..00000000 --- a/tools/bootanalyze/bugreport_anayze.py +++ /dev/null | |||
@@ -1,386 +0,0 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | # Copyright (C) 2017 The Android Open Source Project | ||
4 | # | ||
5 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | # you may not use this file except in compliance with the License. | ||
7 | # You may obtain a copy of the License at | ||
8 | # | ||
9 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | # | ||
11 | # Unless required by applicable law or agreed to in writing, software | ||
12 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | # See the License for the specific language governing permissions and | ||
15 | # limitations under the License. | ||
16 | # | ||
17 | """Tool to analyze boot-up time from bugreport.""" | ||
18 | |||
19 | import argparse | ||
20 | import collections | ||
21 | import datetime | ||
22 | import math | ||
23 | import operator | ||
24 | import os | ||
25 | import re | ||
26 | import select | ||
27 | import subprocess | ||
28 | import sys | ||
29 | import time | ||
30 | import threading | ||
31 | import yaml | ||
32 | |||
33 | from datetime import datetime, date | ||
34 | |||
35 | DBG = True | ||
36 | |||
37 | LOG_START_PATTERN = r"""\-\-\-\-\-\-\s(.*)\s\-\-\-\-\-\-""" | ||
38 | LOG_END_PATTERN = r"""\-\-\-\-\-\-\s\S.*\s\-\-\-\-\-\-""" | ||
39 | |||
40 | KERNEL_LOG_TITLE = "KERNEL LOG" | ||
41 | SYSYEM_LOG_TITLE = "SYSTEM LOG" | ||
42 | LAST_KMSG_TITLE = "LAST KMSG" | ||
43 | LAST_LOGCAT_TITLE = "LAST LOGCAT" | ||
44 | |||
45 | SYSTEM_PROPS_TITLE = "SYSTEM PROPERTIES" | ||
46 | |||
47 | TIME_DMESG = "\[\s*(\d+\.\d+)\]" | ||
48 | TIME_LOGCAT = "(\d+)\-(\d+)\s(\d+):(\d+):(\d+\.\d+)" | ||
49 | |||
50 | NATIVE_CRASH_START_PATTERN = "I\sDEBUG\s+:\s\*\*\*\s\*\*\*" | ||
51 | NATIVE_CRASH_PATTERN = "I\sDEBUG\s+:" | ||
52 | JAVA_CRASH_START_PATTERN = "E\sAndroidRuntime:\sFATAL\sEXCEPTION" | ||
53 | JAVA_CRASH_PATTERN = "E\sAndroidRuntime:\s" | ||
54 | |||
55 | EPOCH = datetime.utcfromtimestamp(0) | ||
56 | |||
57 | def init_arguments(): | ||
58 | parser = argparse.ArgumentParser(description='Measures boot time from bugreport.') | ||
59 | parser.add_argument('-c', '--config', dest='config', | ||
60 | default='config.yaml', type=argparse.FileType('r'), | ||
61 | help='config file for the tool') | ||
62 | parser.add_argument('bugreport_file', nargs=1, help='bugreport txt file', | ||
63 | type=argparse.FileType('r')) | ||
64 | parser.add_argument('-n', '--iterate', dest='iterate', type=int, default=1, | ||
65 | help='number of time to repeat the measurement', ) | ||
66 | return parser.parse_args() | ||
67 | |||
68 | # Event per each reboot, for distinghishing current boot from last boot | ||
69 | class Events: | ||
70 | def __init__(self): | ||
71 | self.events = collections.OrderedDict() #K: keyword, V:time in ms | ||
72 | self.timings = collections.OrderedDict() | ||
73 | self.shutdown_events = collections.OrderedDict() | ||
74 | self.java_crash = collections.OrderedDict() #K:time, V:list of crash infos, each entry per line | ||
75 | self.native_crash = collections.OrderedDict() | ||
76 | |||
77 | def reset_events_time(self, delta): | ||
78 | new_events = collections.OrderedDict() | ||
79 | for entry in self.events.iteritems(): | ||
80 | new_events[entry[0]] = entry[1] - delta | ||
81 | self.events = new_events | ||
82 | if len(self.native_crash) > 0: | ||
83 | new_crash = collections.OrderedDict() | ||
84 | for entry in self.native_crash.iteritems(): | ||
85 | new_crash[entry[0] - delta] = entry[1] | ||
86 | self.native_crash = new_crash | ||
87 | if len(self.java_crash) > 0: | ||
88 | new_crash = collections.OrderedDict() | ||
89 | for entry in self.java_crash.iteritems(): | ||
90 | new_crash[entry[0] - delta] = entry[1] | ||
91 | self.java_crash = new_crash | ||
92 | |||
93 | def reset_shutdown_events_time(self): | ||
94 | if len(self.shutdown_events) == 0: | ||
95 | return | ||
96 | time_offset = 0 | ||
97 | new_events = collections.OrderedDict() | ||
98 | for entry in self.shutdown_events.iteritems(): | ||
99 | if time_offset == 0: | ||
100 | time_offset = entry[1] | ||
101 | new_events[entry[0]] = entry[1] - time_offset | ||
102 | self.shutdown_events = new_events | ||
103 | |||
104 | def dump_dict(self, d): | ||
105 | for entry in d.iteritems(): | ||
106 | print ' {0:30}: {1}'.format(entry[0], entry[1]) | ||
107 | |||
108 | def dump_crash(self, time, stack): | ||
109 | print " Crash time:", time, " stack:" | ||
110 | print ' '.join(stack) | ||
111 | |||
112 | def dump(self): | ||
113 | if len(self.events) > 0: | ||
114 | print "\n***Events:" | ||
115 | self.dump_dict(self.events) | ||
116 | if len(self.timings) > 0: | ||
117 | print "\n***Timings top 20" | ||
118 | timings_sorted = sorted(self.timings.items(), key = lambda item: item[1], reverse=True) | ||
119 | nums_to_dump = min(20, len(timings_sorted)) | ||
120 | for i in range(nums_to_dump): | ||
121 | print ' {0:30}: {1}'.format(timings_sorted[i][0], timings_sorted[i][1]) | ||
122 | print "\n***Timings:" | ||
123 | self.dump_dict(self.timings) | ||
124 | if len(self.shutdown_events) > 0: | ||
125 | print "\n***Shutdown Events (time relative to the begining of shutdown) :" | ||
126 | self.dump_dict(self.shutdown_events) | ||
127 | if len(self.native_crash) > 0: | ||
128 | print "\n***Native crash founds:", len(self.native_crash) | ||
129 | for entry in self.native_crash.iteritems(): | ||
130 | self.dump_crash(entry[0], entry[1]) | ||
131 | if len(self.java_crash) > 0: | ||
132 | print "\n***Java crash founds:", len(self.java_crash) | ||
133 | for entry in self.java_crash.iteritems(): | ||
134 | self.dump_crash(entry[0], entry[1]) | ||
135 | |||
136 | class Parser: | ||
137 | def __init__(self, config_file, bugreport_file): | ||
138 | self.re_log_start = re.compile(LOG_START_PATTERN) | ||
139 | self.re_log_end = re.compile(LOG_END_PATTERN) | ||
140 | self.f = bugreport_file | ||
141 | cfg = yaml.load(config_file) | ||
142 | self.event_patterns = {key: re.compile(pattern) | ||
143 | for key, pattern in cfg['events'].iteritems()} | ||
144 | self.timing_patterns = {key: re.compile(pattern) | ||
145 | for key, pattern in cfg['timings'].iteritems()} | ||
146 | self.shutdown_event_patterns = {key: re.compile(pattern) | ||
147 | for key, pattern in cfg['shutdown_events'].iteritems()} | ||
148 | self.current_boot_kernel = Events() | ||
149 | self.current_boot_logcat = Events() | ||
150 | self.last_boot_kernel = Events() | ||
151 | self.last_boot_logcat = Events() | ||
152 | self.boottime_props = collections.OrderedDict() # K:prop, V:boot time, added in boot time order | ||
153 | self.bootloader_time = 0 | ||
154 | self.re_time_dmesg = re.compile(TIME_DMESG) | ||
155 | self.re_time_logcat = re.compile(TIME_LOGCAT) | ||
156 | self.re_native_crash_start = re.compile(NATIVE_CRASH_START_PATTERN) | ||
157 | self.re_native_crash = re.compile(NATIVE_CRASH_PATTERN) | ||
158 | self.re_java_crash_start = re.compile(JAVA_CRASH_START_PATTERN) | ||
159 | self.re_java_crash = re.compile(JAVA_CRASH_PATTERN) | ||
160 | |||
161 | def match_an_event(self, event_patterns, line): | ||
162 | for event_key, event_pattern in event_patterns.iteritems(): | ||
163 | m = event_pattern.search(line) | ||
164 | if m: | ||
165 | return event_key, m | ||
166 | return None, None | ||
167 | |||
168 | def get_event_time(self, line, is_kernel): | ||
169 | if is_kernel: | ||
170 | m = self.re_time_dmesg.search(line) | ||
171 | if not m: | ||
172 | print "Cannot get time from log:", line | ||
173 | return -1 | ||
174 | return int(float(m.group(1)) * 1000) | ||
175 | else: | ||
176 | m = self.re_time_logcat.search(line) | ||
177 | if not m: | ||
178 | print "Cannot get time from log:", line | ||
179 | return -1 | ||
180 | mm = int(m.group(1)) | ||
181 | dd = int(m.group(2)) | ||
182 | hh = int(m.group(3)) | ||
183 | min = int(m.group(4)) | ||
184 | usecs = int(float(m.group(5)) * 1000000) | ||
185 | secs = usecs / 1000000 | ||
186 | usecs = usecs - 1000000 * secs | ||
187 | dt = datetime(2017, mm, dd, hh, min, secs, usecs) | ||
188 | return int((dt - EPOCH).total_seconds() * 1000) | ||
189 | |||
190 | def queue_crash(self, event, crash_time, crash_stacks, is_native_crash): | ||
191 | stacks = list(crash_stacks) | ||
192 | if is_native_crash: | ||
193 | event.native_crash[crash_time] = stacks | ||
194 | else: | ||
195 | event.java_crash[crash_time] = stacks | ||
196 | |||
197 | def check_crash(self, event, orig_line): | ||
198 | line = orig_line | ||
199 | crash_time = 0 | ||
200 | crash_stacks = [] | ||
201 | is_native_crash = True | ||
202 | while len(line) > 0: | ||
203 | m = self.re_native_crash_start.search(line) | ||
204 | if m: | ||
205 | if len(crash_stacks) > 0: | ||
206 | self.queue_crash(event, crash_time, crash_stacks, is_native_crash) | ||
207 | crash_stacks = [] | ||
208 | is_native_crash = True | ||
209 | crash_stacks.append(line) | ||
210 | crash_time = self.get_event_time(line, False) | ||
211 | line = self.f.readline() | ||
212 | continue | ||
213 | m = self.re_native_crash.search(line) | ||
214 | if m: | ||
215 | crash_stacks.append(line) | ||
216 | line = self.f.readline() | ||
217 | continue | ||
218 | m = self.re_java_crash_start.search(line) | ||
219 | if m: | ||
220 | if len(crash_stacks) > 0: | ||
221 | self.queue_crash(event, crash_time, crash_stacks, is_native_crash) | ||
222 | crash_stacks = [] | ||
223 | is_native_crash = False | ||
224 | crash_stacks.append(line) | ||
225 | crash_time = self.get_event_time(line, False) | ||
226 | line = self.f.readline() | ||
227 | continue | ||
228 | m = self.re_java_crash.search(line) | ||
229 | if m: | ||
230 | crash_stacks.append(line) | ||
231 | line = self.f.readline() | ||
232 | continue | ||
233 | # reaching here means not crash, so return new line | ||
234 | if line != orig_line: | ||
235 | return line | ||
236 | else: | ||
237 | return self.f.readline() | ||
238 | |||
239 | |||
240 | |||
241 | def handle_events(self, event, is_kernel): | ||
242 | line = self.f.readline() | ||
243 | while len(line) > 0 and not self.re_log_end.match(line): | ||
244 | key, m = self.match_an_event(self.event_patterns, line) | ||
245 | if m: | ||
246 | event.events[key] = self.get_event_time(line, is_kernel) | ||
247 | line = self.f.readline() | ||
248 | continue | ||
249 | key, m = self.match_an_event(self.timing_patterns, line) | ||
250 | if m: | ||
251 | name = m.group('name') | ||
252 | time = float(m.group('time')) | ||
253 | if key.endswith('_secs'): | ||
254 | time = time * 1000 | ||
255 | event.timings[name] = int(time) | ||
256 | line = self.f.readline() | ||
257 | continue | ||
258 | key, m = self.match_an_event(self.shutdown_event_patterns, line) | ||
259 | if m: | ||
260 | event.shutdown_events[key] = self.get_event_time(line, is_kernel) | ||
261 | line = self.f.readline() | ||
262 | continue | ||
263 | if not is_kernel: # collect crash | ||
264 | line = self.check_crash(event, line) | ||
265 | continue | ||
266 | line = self.f.readline() | ||
267 | |||
268 | def handle_kernel_log(self): | ||
269 | if DBG: | ||
270 | print "start " + KERNEL_LOG_TITLE | ||
271 | self.handle_events(self.current_boot_kernel, True) | ||
272 | |||
273 | def handle_system_log(self): | ||
274 | if DBG: | ||
275 | print "start " + SYSYEM_LOG_TITLE | ||
276 | self.handle_events(self.current_boot_logcat, False) | ||
277 | |||
278 | def handle_last_kernel_log(self): | ||
279 | if DBG: | ||
280 | print "start " + LAST_KMSG_TITLE | ||
281 | self.handle_events(self.last_boot_kernel, True) | ||
282 | |||
283 | def handle_last_system_log(self): | ||
284 | if DBG: | ||
285 | print "start " + LAST_LOGCAT_TITLE | ||
286 | self.handle_events(self.last_boot_logcat, False) | ||
287 | |||
288 | def handle_system_props(self): | ||
289 | if DBG: | ||
290 | print "start " + SYSTEM_PROPS_TITLE | ||
291 | re_prop = re.compile(r"""\[(.+)\].*\[(.*)\]""") | ||
292 | RO_BOOTTIME_PROP = "ro.boottime." | ||
293 | boottime_props = {} # K: prop name, V: boot time in ms | ||
294 | line = self.f.readline() | ||
295 | while len(line) > 0 and not self.re_log_end.match(line): | ||
296 | m = re_prop.match(line) | ||
297 | if not m: | ||
298 | print "Cannot parse prop:", line | ||
299 | line = self.f.readline() | ||
300 | continue | ||
301 | if m.group(1).startswith(RO_BOOTTIME_PROP): | ||
302 | name = m.group(1)[len(RO_BOOTTIME_PROP):] | ||
303 | time = int(m.group(2)) / 1000000 # ns to ms | ||
304 | boottime_props[name] = time | ||
305 | elif m.group(1) == "ro.boot.boottime": | ||
306 | print "Found bootloader boottime ", line | ||
307 | entries = m.group(2).split(",") | ||
308 | for entry in entries: | ||
309 | values = entry.split(":") | ||
310 | if values[0] != "SW": | ||
311 | self.bootloader_time += int(values[1]) | ||
312 | line = self.f.readline() | ||
313 | self.boottime_props = collections.OrderedDict(sorted( | ||
314 | boottime_props.items(), key = lambda item: item[1])) | ||
315 | |||
316 | def parse(self): | ||
317 | while (True): | ||
318 | l = self.f.readline() | ||
319 | if len(l) == 0: # EOF | ||
320 | return | ||
321 | m = self.re_log_start.match(l) | ||
322 | if not m: | ||
323 | continue | ||
324 | #print m.group(1) | ||
325 | if m.group(1).startswith(KERNEL_LOG_TITLE): | ||
326 | self.handle_kernel_log() | ||
327 | elif m.group(1).startswith(SYSYEM_LOG_TITLE): | ||
328 | self.handle_system_log() | ||
329 | elif m.group(1).startswith(SYSTEM_PROPS_TITLE): | ||
330 | self.handle_system_props() | ||
331 | elif m.group(1).startswith(LAST_KMSG_TITLE): | ||
332 | self.handle_last_kernel_log() | ||
333 | elif m.group(1).startswith(LAST_LOGCAT_TITLE): | ||
334 | self.handle_last_system_log() | ||
335 | |||
336 | def dump_props(self): | ||
337 | if self.bootloader_time != 0: | ||
338 | print "*bootloader time:", self.bootloader_time | ||
339 | if self.boottime_props: | ||
340 | print "*ro.boottime.*:" | ||
341 | for name, t in self.boottime_props.iteritems(): | ||
342 | print ' {0:30}: {1}'.format(name, t) | ||
343 | |||
344 | def reset_event_times(self, kernel_event, logcat_event): | ||
345 | has_boot_complete = True | ||
346 | kernel_bootcomplete_time = kernel_event.events.get("BootComplete_kernel") | ||
347 | if not kernel_bootcomplete_time: | ||
348 | has_boot_complete = False | ||
349 | logcat_bootcomplete_time = logcat_event.events.get("BootComplete") | ||
350 | if not logcat_bootcomplete_time: | ||
351 | has_boot_complete = False | ||
352 | time_delta = 0 | ||
353 | if has_boot_complete: | ||
354 | time_delta = logcat_bootcomplete_time - kernel_bootcomplete_time | ||
355 | else: | ||
356 | time_delta = logcat_event.events.values()[0] if len(logcat_event.events) > 0 else 0 | ||
357 | logcat_event.reset_events_time(time_delta) | ||
358 | logcat_event.reset_shutdown_events_time() | ||
359 | kernel_event.reset_shutdown_events_time() | ||
360 | return has_boot_complete | ||
361 | |||
362 | def dump(self): | ||
363 | self.dump_props() | ||
364 | boot_complete_found = self.reset_event_times(self.current_boot_kernel, self.current_boot_logcat) | ||
365 | print "* Kernel dmesg:" | ||
366 | self.current_boot_kernel.dump() | ||
367 | print "\n\n* Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found\ | ||
368 | else "(time set relative to the first event):") | ||
369 | self.current_boot_logcat.dump() | ||
370 | print "\n\n\n==== Data from last boot ===" | ||
371 | boot_complete_found = self.reset_event_times(self.last_boot_kernel, self.last_boot_logcat) | ||
372 | print "\n\n* Last Kernel dmesg:" | ||
373 | self.last_boot_kernel.dump() | ||
374 | print "\n\n* Last Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found \ | ||
375 | else "(time set relative to the first event):") | ||
376 | self.last_boot_logcat.dump() | ||
377 | |||
378 | def main(): | ||
379 | args = init_arguments() | ||
380 | |||
381 | parser = Parser(args.config, args.bugreport_file[0]) | ||
382 | parser.parse() | ||
383 | parser.dump() | ||
384 | |||
385 | if __name__ == '__main__': | ||
386 | main() | ||
diff --git a/tools/bootanalyze/config.yaml b/tools/bootanalyze/config.yaml deleted file mode 100644 index 37f2891b..00000000 --- a/tools/bootanalyze/config.yaml +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | #YAML | ||
2 | time_correction_key: correction | ||
3 | timings: | ||
4 | system_server: SystemServerTiming(Async)?\s*:\s*(?P<name>\S+) took to complete:\s(?P<time>[0-9]+)ms | ||
5 | fs_shutdown: (?P<name>boot_fs_shutdown),(?P<time>[0-9]+),([0-9]+) | ||
6 | ueventd_secs: ueventd:\s(?P<name>\S.+)\stook\s(?P<time>[.0-9]+)\sseconds | ||
7 | init_command_ms: init:\sCommand\s(?P<name>\S.+)\sreturned.*took\s(?P<time>[.0-9]+)ms | ||
8 | init_service_exec_secs: init:\sService\s.*exec\s\S+\s\((?P<name>\S.+)\).*pid.*\swaiting\stook\s(?P<time>[.0-9]+)\sseconds | ||
9 | zygote64_timing: (?P<name>Zygote64Timing\:\s\S+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms | ||
10 | zygote32_timing: (?P<name>Zygote32Timing\:\s\S+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms | ||
11 | events: | ||
12 | kernel: Linux version | ||
13 | android_init_1st_stage: init first stage started | ||
14 | android_init_2st_stage: init second stage started | ||
15 | late_init: processing action \(late-init\) | ||
16 | fs: processing action \(fs\) | ||
17 | post-fs: processing action \(post-fs\) | ||
18 | late-fs: processing action \(late-fs\) | ||
19 | post-fs-data: processing action \(post-fs-data\) | ||
20 | nonencrypted: processing action \(nonencrypted\) | ||
21 | vold: starting service 'vold' | ||
22 | starting_zygote: starting service 'zygote' | ||
23 | starting_zygote_secondary: starting service 'zygote_secondary' | ||
24 | load_persist_props_action: processing action \(load_persist_props_action\) | ||
25 | early-boot: processing action \(early-boot\) | ||
26 | boot: processing action \(boot\) | ||
27 | ueventd: Starting service 'ueventd' | ||
28 | system_mounted: target=/system | ||
29 | data_mounted: target=/data | ||
30 | correction: Updating system time diff=([0-9]+\.?[0-9]*), cuttime=([0-9]+) | ||
31 | servicemanager_start_by_init: starting service 'servicemanager' | ||
32 | zygoteInit: START com.android.internal.os.ZygoteInit | ||
33 | ZygoteMainSystemServer: app_process\smain\swith\sargv.*\-\-start\-system\-server | ||
34 | ZygoteMainOthers: app_process\smain\swith\sargv | ||
35 | zygote_preload_start: Zygote\s*:\s*begin preload | ||
36 | zygote_preload_classes_start: Zygote\s*:\s*Preloading classes... | ||
37 | zygote_preload_res_start: Zygote\s*:\s*Preloading resources... | ||
38 | zygote_preload_end: Zygote\s*:\s*end preload | ||
39 | zygote_create_system_server: Zygote\s*:\s*System server process [0-9]* has been created | ||
40 | SystemServer_start: Entered the Android system server! | ||
41 | system_server_ready: Enabled StrictMode for system server main | ||
42 | PackageManagerInit_start: SystemServer\s*:\s*StartPackageManagerService | ||
43 | BluetoothService_start: Starting com.android.server.BluetoothService | ||
44 | SystemUi_start: for service com.android.systemui/. | ||
45 | CarLauncherReady: Em.Overview:\s*onResume | ||
46 | CarService_start: for service com.android.car/.CarService | ||
47 | BootAnimStart: starting service 'bootanim' | ||
48 | BootAnimSfWait: BootAnimation:\sWaiting\sfor\sSurfaceFlinger\stook\s | ||
49 | BootAnimShowStart: BootAnimation:\sBootAnimationShownTiming\sstart\stime | ||
50 | BootAnimStopRequest: TELLING SURFACE FLINGER WE ARE BOOTED | ||
51 | BootAnimEnd: Service 'bootanim' | ||
52 | KeyguardStart: KeyguardServiceDelegate.*\*\*\* Keyguard started | ||
53 | KeyguardConnected: KeyguardServiceDelegate.*\*\*\* Keyguard connected | ||
54 | KeyguardShown: KeyguardServiceDelegate.*\*\*\*\* SHOWN CALLED \*\*\*\* | ||
55 | BootComplete: Starting phase 1000 | ||
56 | BootComplete_kernel: processing action \(sys\.boot_completed=1\) | ||
57 | LauncherStart: START.*HOME.*(NexusLauncherActivity|GEL|LensPickerTrampolineActivity|SetupWizardActivity) | ||
58 | FsStat: fs_stat, partition:userdata stat:(0x\S+) | ||
59 | shutdown_events: | ||
60 | ShutdownStart: ShutdownThread:\sNotifying thread to start shutdown | ||
61 | ShutdownBroadcast: ShutdownThread:\sSending shutdown broadcast | ||
62 | ShutdownActivityManagerService: ShutdownThread:\sShutting down activity manager | ||
63 | ShutdownPackageManagerService: ShutdownThread:\sShutting down package manager | ||
64 | ShutdownNfc: ShutdownThread:\sTurning off NFC | ||
65 | ShutdownBt: ShutdownThread:\sDisabling Bluetooth | ||
66 | ShutdownRadio: ShutdownThread:\sTurning off cellular radios | ||
67 | ShutdownRadiosWait: ShutdownThread:\sWaiting for NFC, Bluetooth and Radio | ||
68 | ShutdownBtDone: ShutdownThread:\sBluetooth turned off | ||
69 | ShutdownRadioDone: ShutdownThread:\sRadio turned off | ||
70 | ShutdownNfcDone: ShutdownThread:\sNFC turned off | ||
71 | ShutdownRadiosWaitDone: ShutdownThread:\sNFC, Radio and Bluetooth shutdown complete | ||
72 | ShutdownRadiosWaitTimeout: ShutdownThread:\sTimed out waiting for NFC, Radio and Bluetooth shutdown | ||
73 | ShutdownStorageManagerSerivce: ShutdownThread:\sShutting down StorageManagerService | ||
74 | ShutdownStorageManagerSerivceDone: ShutdownThread:\sResult code [\d]+ from StorageManagerService\.shutdown | ||
75 | ShutdownStorageManagerSerivceTimeout: ShutdownThread:\sShutdown wait timed out | ||
76 | ShutdownStartDone: ShutdownThread:\sPerforming low-level shutdown | ||
77 | ShutdownInitAction: init\s+:\sprocessing action \(sys\.shutdown\.requested | ||
78 | ShutdownInitFsShutdown: init\s+:\sShutdown timeout | ||
diff --git a/tools/bootanalyze/stressfs/Android.mk b/tools/bootanalyze/stressfs/Android.mk deleted file mode 100644 index 2e54e64b..00000000 --- a/tools/bootanalyze/stressfs/Android.mk +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | # Copyright (C) 2017 The Android Open Source Project | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | # | ||
15 | # | ||
16 | |||
17 | LOCAL_PATH := $(call my-dir) | ||
18 | |||
19 | include $(CLEAR_VARS) | ||
20 | |||
21 | LOCAL_PACKAGE_NAME := StressFS | ||
22 | LOCAL_SRC_FILES := $(call all-java-files-under, src) | ||
23 | |||
24 | LOCAL_MODULE_TAGS := tests | ||
25 | LOCAL_SDK_VERSION := current | ||
26 | |||
27 | LOCAL_PROGUARD_FLAG_FILES := proguard.flags | ||
28 | |||
29 | include $(BUILD_PACKAGE) | ||
diff --git a/tools/bootanalyze/stressfs/AndroidManifest.xml b/tools/bootanalyze/stressfs/AndroidManifest.xml deleted file mode 100644 index 8713c511..00000000 --- a/tools/bootanalyze/stressfs/AndroidManifest.xml +++ /dev/null | |||
@@ -1,38 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- Copyright (C) 2017 The Android Open Source Project | ||
3 | |||
4 | Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | you may not use this file except in compliance with the License. | ||
6 | You may obtain a copy of the License at | ||
7 | |||
8 | http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | |||
10 | Unless required by applicable law or agreed to in writing, software | ||
11 | distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | See the License for the specific language governing permissions and | ||
14 | limitations under the License. | ||
15 | --> | ||
16 | |||
17 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
18 | package="com.android.car.test.stressfs"> | ||
19 | |||
20 | <original-package android:name="com.android.car.test.stressfs" /> | ||
21 | |||
22 | <uses-sdk android:minSdkVersion="25" | ||
23 | android:targetSdkVersion="25" /> | ||
24 | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||
25 | |||
26 | <application android:label="Stress Filesystem" | ||
27 | android:directBootAware="true" | ||
28 | android:allowBackup="false"> | ||
29 | |||
30 | <activity android:name=".WritingActivity"> | ||
31 | <intent-filter> | ||
32 | <action android:name="com.android.car.test.stressfs.START" /> | ||
33 | </intent-filter> | ||
34 | </activity> | ||
35 | <service android:name=".WritingService"> | ||
36 | </service> | ||
37 | </application> | ||
38 | </manifest> | ||
diff --git a/tools/bootanalyze/stressfs/proguard.flags b/tools/bootanalyze/stressfs/proguard.flags deleted file mode 100644 index 1e5ff3dd..00000000 --- a/tools/bootanalyze/stressfs/proguard.flags +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | -verbose | ||
2 | -keep public class * extends android.app.Activity | ||
3 | -keep public class * extends android.app.Service | ||
diff --git a/tools/bootanalyze/stressfs/src/com/android/car/test/stressfs/WritingActivity.java b/tools/bootanalyze/stressfs/src/com/android/car/test/stressfs/WritingActivity.java deleted file mode 100644 index 6fe5cc61..00000000 --- a/tools/bootanalyze/stressfs/src/com/android/car/test/stressfs/WritingActivity.java +++ /dev/null | |||
@@ -1,61 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | package com.android.car.test.stressfs; | ||
17 | |||
18 | import android.app.Activity; | ||
19 | import android.content.ComponentName; | ||
20 | import android.content.Context; | ||
21 | import android.content.Intent; | ||
22 | import android.content.ServiceConnection; | ||
23 | import android.os.Bundle; | ||
24 | import android.os.IBinder; | ||
25 | |||
26 | /** | ||
27 | * Used to instantiate the WritingService service at a high priority. | ||
28 | */ | ||
29 | public class WritingActivity extends Activity { | ||
30 | |||
31 | private static final String TAG = "StressFS"; | ||
32 | |||
33 | /** | ||
34 | * Activity-wide connection to keep the service going. | ||
35 | * Not used for any specific interaction. | ||
36 | */ | ||
37 | private ServiceConnection mConnection = new ServiceConnection() { | ||
38 | /** No-op */ | ||
39 | public void onServiceConnected(ComponentName className, IBinder service) { | ||
40 | // Nothing to do. | ||
41 | } | ||
42 | |||
43 | /** No-op */ | ||
44 | public void onServiceDisconnected(ComponentName className) { | ||
45 | // Nothing to do. | ||
46 | } | ||
47 | }; | ||
48 | |||
49 | @Override | ||
50 | public void onCreate(Bundle savedInstanceState) { | ||
51 | super.onCreate(savedInstanceState); | ||
52 | bindService( | ||
53 | new Intent( | ||
54 | getIntent().getAction(), | ||
55 | getIntent().getData(), | ||
56 | this, | ||
57 | WritingService.class), | ||
58 | mConnection, | ||
59 | Context.BIND_AUTO_CREATE); | ||
60 | } | ||
61 | } | ||
diff --git a/tools/bootanalyze/stressfs/src/com/android/car/test/stressfs/WritingService.java b/tools/bootanalyze/stressfs/src/com/android/car/test/stressfs/WritingService.java deleted file mode 100644 index 0db75d45..00000000 --- a/tools/bootanalyze/stressfs/src/com/android/car/test/stressfs/WritingService.java +++ /dev/null | |||
@@ -1,186 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | package com.android.car.test.stressfs; | ||
17 | |||
18 | import android.app.Service; | ||
19 | import android.app.Notification; | ||
20 | import android.app.NotificationChannel; | ||
21 | import android.content.Context; | ||
22 | import android.content.Intent; | ||
23 | import android.net.Uri; | ||
24 | import android.os.Binder; | ||
25 | import android.os.Bundle; | ||
26 | import android.os.Environment; | ||
27 | import android.os.IBinder; | ||
28 | import android.util.Log; | ||
29 | import android.R.drawable; | ||
30 | |||
31 | import java.io.*; | ||
32 | import java.util.*; | ||
33 | |||
34 | /** | ||
35 | * Used to stress the file writing before and during shutdown to help ensure | ||
36 | * that the filesystem shuts down at the right time, in a consistent manner, | ||
37 | * and does not get corrupted. | ||
38 | * | ||
39 | * Writes to two files - one on the data partition, one on the external storage | ||
40 | * partition - simultaneous in two separate threads; starting over after a | ||
41 | * certain amount of data is written. | ||
42 | * | ||
43 | * This class is intended to be invoked from the shell. For a 64KB file | ||
44 | * written in 1KB chunks, invoke from the host workstation: | ||
45 | * adb install -g StressFS.apk | ||
46 | * adb shell am start \ | ||
47 | * -n com.android.car.test.stressfs/.WritingActivity \ | ||
48 | * -a com.android.car.test.stressfs.START | ||
49 | * -d "stressfs://start?block=1024\&file=65536" | ||
50 | * | ||
51 | * After reboot: | ||
52 | * adb uninstall com.android.car.test.stressfs | ||
53 | * adb shell "rm -rf /storage/emulated/0/stressfs_data*" | ||
54 | * | ||
55 | * On boot after running this while shutting down, fsck should flag any | ||
56 | * corruption that it sees resulting from this running. The goal is to set the | ||
57 | * shutdown sequence in a manner that does not corrupt so that this check can | ||
58 | * be avoided. | ||
59 | */ | ||
60 | public class WritingService extends Service { | ||
61 | |||
62 | private static final String TAG = "StressFS"; | ||
63 | |||
64 | private static final String ACTION_START = "com.android.car.test.stressfs.START"; | ||
65 | |||
66 | private static final int DEFAULT_BLOCK_SIZE = 4096; | ||
67 | private static final int DEFAULT_FILE_SIZE = 16 * 1024 * 1024; | ||
68 | |||
69 | private static final String FILENAME_PREFIX = "stressfs_data_"; | ||
70 | |||
71 | private static final int NOTIFICATION_ID = 100; | ||
72 | |||
73 | /** Continuously writes test data to a specified file. */ | ||
74 | private static class WriteToDisk extends Thread { | ||
75 | private final String mLogTag; | ||
76 | private final File mFile; | ||
77 | private final int mFileSize; | ||
78 | private final byte[] mTestData; | ||
79 | |||
80 | public WriteToDisk( | ||
81 | String logTag, | ||
82 | File file, | ||
83 | int fileSize, | ||
84 | byte[] testData) { | ||
85 | mLogTag = logTag; | ||
86 | mFile = file; | ||
87 | mFileSize = fileSize; | ||
88 | mTestData = testData; | ||
89 | } | ||
90 | |||
91 | /** Writes data to a file, restarting once the maximum amount of data is reached.*/ | ||
92 | @Override | ||
93 | public void run() { | ||
94 | Log.d(TAG, mLogTag + " thread started"); | ||
95 | while (true) { | ||
96 | try { | ||
97 | FileOutputStream fos = new FileOutputStream(mFile); | ||
98 | // Write in chunks until the amount of data requested | ||
99 | // is written. | ||
100 | for (int j = 0; j < mFileSize; j += mTestData.length) { | ||
101 | fos.write(mTestData); | ||
102 | } | ||
103 | fos.close(); | ||
104 | } catch (FileNotFoundException e) { | ||
105 | Log.e(TAG, "File not found: ", e); | ||
106 | } catch (IOException e) { | ||
107 | Log.e(TAG, "IO error: ", e); | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | /** Raises service priority and starts the writing threads. */ | ||
114 | @Override | ||
115 | public IBinder onBind(Intent intent) { | ||
116 | Notification notification = | ||
117 | new Notification.Builder(this, NotificationChannel.DEFAULT_CHANNEL_ID) | ||
118 | .setContentTitle("Stress Filesystem service running.") | ||
119 | .setSmallIcon(drawable.ic_menu_save) | ||
120 | .build(); | ||
121 | |||
122 | // Raise priority of this service. | ||
123 | startForeground(NOTIFICATION_ID, notification); | ||
124 | |||
125 | File dataPartitionFile = getFileStreamPath(FILENAME_PREFIX + UUID.randomUUID()); | ||
126 | File externalPartitionFile = new File( | ||
127 | Environment.getExternalStorageDirectory(), FILENAME_PREFIX + UUID.randomUUID()); | ||
128 | |||
129 | Log.i(TAG, "External storage state: " + | ||
130 | Environment.getExternalStorageState(externalPartitionFile)); | ||
131 | |||
132 | Uri data = intent.getData(); | ||
133 | if (data != null) { | ||
134 | Log.i(TAG, "Data: " + data.toString()); | ||
135 | } | ||
136 | int blockSize = getQueryParam(data, "block", DEFAULT_BLOCK_SIZE); | ||
137 | int fileSize = getQueryParam(data, "file", DEFAULT_FILE_SIZE); | ||
138 | Log.i(TAG, "Block Size: " + blockSize); | ||
139 | Log.i(TAG, "File Size: " + fileSize); | ||
140 | |||
141 | if (fileSize % blockSize != 0) { | ||
142 | Log.w(TAG, "File size should be a multiple of block size."); | ||
143 | } | ||
144 | |||
145 | // Populate some test data. | ||
146 | StringBuilder builder = new StringBuilder(blockSize); | ||
147 | for (int i = 0; i < builder.capacity(); i++) { | ||
148 | builder.append((char)(i % 26 + 'A')); | ||
149 | } | ||
150 | byte[] testData = new String(builder).getBytes(); | ||
151 | |||
152 | // Spawn two threads - one to write to the /data partition, one to | ||
153 | // write to the SD card. | ||
154 | new WriteToDisk("data", dataPartitionFile, fileSize, testData).start(); | ||
155 | new WriteToDisk("external", externalPartitionFile, fileSize, testData).start(); | ||
156 | |||
157 | // No need to return a binder interface, since there is no more | ||
158 | // interaction needed from the activity starting the service. | ||
159 | return null; | ||
160 | } | ||
161 | |||
162 | /** Keeps service alive once started. */ | ||
163 | @Override | ||
164 | public int onStartCommand(Intent intent, int flags, int startId) { | ||
165 | return START_STICKY; | ||
166 | } | ||
167 | |||
168 | /** Parses an integer query parameter from the input Uri. */ | ||
169 | private int getQueryParam(Uri data, String key, int defaultValue) { | ||
170 | if (data == null) { | ||
171 | return defaultValue; | ||
172 | } | ||
173 | String inValString = data.getQueryParameter(key); | ||
174 | if (inValString != null) { | ||
175 | try { | ||
176 | int inVal = Integer.parseInt(inValString); | ||
177 | if (inVal != 0) { | ||
178 | return inVal; | ||
179 | } | ||
180 | } catch (NumberFormatException e) { | ||
181 | return defaultValue; | ||
182 | } | ||
183 | } | ||
184 | return defaultValue; | ||
185 | } | ||
186 | } | ||
diff --git a/tools/bootio/Android.mk b/tools/bootio/Android.mk deleted file mode 100644 index e4db8355..00000000 --- a/tools/bootio/Android.mk +++ /dev/null | |||
@@ -1,76 +0,0 @@ | |||
1 | # | ||
2 | # Copyright (C) 2016 The Android Open Source Project | ||
3 | # | ||
4 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | # you may not use this file except in compliance with the License. | ||
6 | # You may obtain a copy of the License at | ||
7 | # | ||
8 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | # | ||
10 | # Unless required by applicable law or agreed to in writing, software | ||
11 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | # See the License for the specific language governing permissions and | ||
14 | # limitations under the License. | ||
15 | # | ||
16 | |||
17 | LOCAL_PATH := $(call my-dir) | ||
18 | |||
19 | bootio_lib_src_files := \ | ||
20 | protos.proto \ | ||
21 | bootio_collector.cpp \ | ||
22 | |||
23 | bootio_src_files := \ | ||
24 | bootio.cpp \ | ||
25 | |||
26 | bootio_shared_libs := \ | ||
27 | libbase \ | ||
28 | libcutils \ | ||
29 | liblog \ | ||
30 | libprotobuf-cpp-lite \ | ||
31 | |||
32 | bootio_cflags := \ | ||
33 | -Wall \ | ||
34 | -Werror \ | ||
35 | -Wextra \ | ||
36 | |||
37 | define bootio_proto_include | ||
38 | $(call local-generated-sources-dir)/proto/$(LOCAL_PATH) | ||
39 | endef | ||
40 | |||
41 | # bootio static library | ||
42 | # ----------------------------------------------------------------------------- | ||
43 | |||
44 | include $(CLEAR_VARS) | ||
45 | |||
46 | LOCAL_MODULE := libbootio | ||
47 | LOCAL_MODULE_CLASS := SHARED_LIBRARIES | ||
48 | |||
49 | LOCAL_C_INCLUDES := \ | ||
50 | $(LOCAL_PATH)/.. \ | ||
51 | $(call bootio_proto_include) \ | ||
52 | |||
53 | LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) | ||
54 | LOCAL_CFLAGS := $(bootio_cflags) | ||
55 | LOCAL_SHARED_LIBRARIES := $(bootio_shared_libs) | ||
56 | LOCAL_PROTOC_OPTIMIZE_TYPE := lite | ||
57 | LOCAL_SRC_FILES := $(bootio_lib_src_files) | ||
58 | |||
59 | include $(BUILD_SHARED_LIBRARY) | ||
60 | |||
61 | |||
62 | # bootio binary | ||
63 | # ----------------------------------------------------------------------------- | ||
64 | |||
65 | include $(CLEAR_VARS) | ||
66 | |||
67 | LOCAL_MODULE := bootio | ||
68 | LOCAL_CFLAGS := $(bootio_cflags) | ||
69 | LOCAL_SHARED_LIBRARIES := \ | ||
70 | $(bootio_shared_libs) \ | ||
71 | libbootio \ | ||
72 | |||
73 | LOCAL_INIT_RC := bootio.rc | ||
74 | LOCAL_SRC_FILES := $(bootio_src_files) | ||
75 | |||
76 | include $(BUILD_EXECUTABLE) | ||
diff --git a/tools/bootio/README.md b/tools/bootio/README.md deleted file mode 100644 index ca075ded..00000000 --- a/tools/bootio/README.md +++ /dev/null | |||
@@ -1,24 +0,0 @@ | |||
1 | # bootio # | ||
2 | |||
3 | The bootio tool records I/O for processes during boot. | ||
4 | To use bootio kernel must be compiled with this flags: | ||
5 | |||
6 | CONFIG_TASKSTATS=y | ||
7 | CONFIG_TASK_DELAY_ACCT=y | ||
8 | CONFIG_TASK_XACCT=y | ||
9 | CONFIG_TASK_IO_ACCOUNTING=y | ||
10 | |||
11 | To use bootio make sure it's included in product config for the board. | ||
12 | Create file /data/misc/bootio/start with a command like the following: | ||
13 | |||
14 | adb shell 'echo "$TIMEOUT $SAMPLES" > /data/misc/bootio/start' | ||
15 | |||
16 | Where the value of $TIMEOUT corresponds to the desired bootio period in | ||
17 | seconds and $SAMPLES corresponds to the desired number of samples. | ||
18 | |||
19 | Note: /data/misc/bootio/start is not deleted automatically, so don't | ||
20 | forget to delete it when you're done collecting data. | ||
21 | |||
22 | To see collected logs run: | ||
23 | |||
24 | adb shell bootio -p | ||
diff --git a/tools/bootio/bootio.cpp b/tools/bootio/bootio.cpp deleted file mode 100644 index 629d04a7..00000000 --- a/tools/bootio/bootio.cpp +++ /dev/null | |||
@@ -1,162 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | // The bootio tool provides options to collect I/O stats for processes during boot. | ||
18 | |||
19 | #include <vector> | ||
20 | #include <getopt.h> | ||
21 | #include <unistd.h> | ||
22 | #include <android-base/file.h> | ||
23 | #include <android-base/logging.h> | ||
24 | #include <android-base/strings.h> | ||
25 | #include <log/log.h> | ||
26 | |||
27 | #include "bootio_collector.h" | ||
28 | |||
29 | namespace android { | ||
30 | |||
31 | #define LOG_ROOT "/data/misc/bootio" | ||
32 | #define LOG_START_FILE LOG_ROOT"/start" | ||
33 | #define SELF_IO "/proc/self/io" | ||
34 | |||
35 | static const int LOG_TIMEOUT_INDEX = 0; | ||
36 | static const int LOG_SAMPLES_INDEX = 1; | ||
37 | static const int LOG_MAX_TIMEOUT = 120; | ||
38 | static const int LOG_MAX_SAMPLES = 30; | ||
39 | |||
40 | void ShowHelp(const char *cmd) { | ||
41 | fprintf(stderr, "Usage: %s [options]\n", cmd); | ||
42 | fprintf(stderr, | ||
43 | "options include:\n" | ||
44 | " -h, --help Show this help\n" | ||
45 | " -p, --print Dump the boot io data to the console\n" | ||
46 | "\nNo options will start data collection process.\n"); | ||
47 | } | ||
48 | |||
49 | void PrintBootIo() { | ||
50 | printf("Boot I/O:\n"); | ||
51 | printf("------------\n"); | ||
52 | std::unique_ptr <BootioCollector> collector(new BootioCollector(LOG_ROOT)); | ||
53 | if (collector.get() == NULL) { | ||
54 | LOG(ERROR) << "Failed to create data collector"; | ||
55 | return; | ||
56 | } | ||
57 | collector->Print(); | ||
58 | } | ||
59 | |||
60 | void StartDataCollection() { | ||
61 | if (access(SELF_IO, F_OK) == -1) { | ||
62 | LOG(ERROR) << "Kernel doesn't support I/O profiling."; | ||
63 | printf("Kernel doesn't support I/O profiling."); | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | int timeout = 0; | ||
68 | int samples = 0; | ||
69 | |||
70 | std::string start; | ||
71 | android::base::ReadFileToString(LOG_START_FILE, &start); | ||
72 | |||
73 | if (!start.empty()) { | ||
74 | std::vector <std::string> components = android::base::Split(start, " "); | ||
75 | if (components.size() != 2) { | ||
76 | LOG(ERROR) << "Invalid value in start file." << start; | ||
77 | return; | ||
78 | } | ||
79 | timeout = atoi(components.at(LOG_TIMEOUT_INDEX).c_str()); | ||
80 | samples = atoi(components.at(LOG_SAMPLES_INDEX).c_str()); | ||
81 | } else { | ||
82 | LOG(INFO) << "No profiling requested. Exiting"; | ||
83 | printf("Boot I/O: no profiling requested. Exiting.\n"); | ||
84 | return; | ||
85 | } | ||
86 | if (timeout <= 0 || samples <= 0) { | ||
87 | LOG(ERROR) << "Boot I/O: failed to parse string:" << start; | ||
88 | printf("Boot I/O: failed to parse string: %s\n", start.c_str()); | ||
89 | return; | ||
90 | } | ||
91 | if (samples > timeout || samples > LOG_MAX_SAMPLES || timeout > LOG_MAX_TIMEOUT) { | ||
92 | LOG(ERROR) << "Bad values for bootio. timeout=" << timeout << | ||
93 | " samples=" << samples << " Max timeout=" << LOG_MAX_TIMEOUT << | ||
94 | " Max samples=" << LOG_MAX_SAMPLES; | ||
95 | return; | ||
96 | } | ||
97 | LOG(INFO) << "Boot I/O: collecting data. samples=" << samples << "timeout=" << timeout; | ||
98 | printf("Boot I/O: collecting data\ntimeout=%d, samples=%d\n", | ||
99 | timeout, samples); | ||
100 | std::unique_ptr <BootioCollector> collector(new BootioCollector(LOG_ROOT)); | ||
101 | if (collector.get() == NULL) { | ||
102 | LOG(ERROR) << "Failed to create data collector"; | ||
103 | return; | ||
104 | } | ||
105 | collector->StartDataCollection(timeout, samples); | ||
106 | } | ||
107 | |||
108 | } | ||
109 | |||
110 | int main(int argc, char **argv) { | ||
111 | android::base::InitLogging(argv); | ||
112 | |||
113 | LOG(INFO) << "Bootio started"; | ||
114 | |||
115 | int optionIndex = 0; | ||
116 | static const struct option longOptions[] = { | ||
117 | {"help", no_argument, NULL, 'h'}, | ||
118 | {"print", no_argument, NULL, 'p'}, | ||
119 | {NULL, 0, NULL, 0} | ||
120 | }; | ||
121 | |||
122 | int opt = 0; | ||
123 | bool startCollection = true; | ||
124 | while ((opt = getopt_long(argc, argv, "hlpr:", longOptions, &optionIndex)) != -1) { | ||
125 | switch (opt) { | ||
126 | case 0: { | ||
127 | const std::string option_name = longOptions[optionIndex].name; | ||
128 | LOG(ERROR) << "Invalid option: " << option_name; | ||
129 | break; | ||
130 | } | ||
131 | |||
132 | case 'h': { | ||
133 | android::ShowHelp(argv[0]); | ||
134 | startCollection = false; | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | case 'p': { | ||
139 | android::PrintBootIo(); | ||
140 | startCollection = false; | ||
141 | break; | ||
142 | } | ||
143 | |||
144 | default: { | ||
145 | DCHECK_EQ(opt, '?'); | ||
146 | |||
147 | // |optopt| is an external variable set by getopt representing | ||
148 | // the value of the invalid option. | ||
149 | LOG(ERROR) << "Invalid option: " << optopt; | ||
150 | android::ShowHelp(argv[0]); | ||
151 | return EXIT_FAILURE; | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | if (startCollection) { | ||
157 | android::StartDataCollection(); | ||
158 | } | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
diff --git a/tools/bootio/bootio.rc b/tools/bootio/bootio.rc deleted file mode 100644 index f0074cb4..00000000 --- a/tools/bootio/bootio.rc +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | # This file is the LOCAL_INIT_RC file for the bootio command. | ||
2 | |||
3 | service bootio /system/bin/bootio | ||
4 | class main | ||
5 | user root | ||
6 | group root | ||
7 | oneshot | ||
8 | |||
9 | on post-fs-data | ||
10 | mkdir /data/misc/bootio 0755 root shell | ||
diff --git a/tools/bootio/bootio_collector.cpp b/tools/bootio/bootio_collector.cpp deleted file mode 100644 index 495a9aa4..00000000 --- a/tools/bootio/bootio_collector.cpp +++ /dev/null | |||
@@ -1,381 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include "bootio_collector.h" | ||
18 | #include <android-base/logging.h> | ||
19 | #include <android-base/file.h> | ||
20 | #include <log/log.h> | ||
21 | #include "protos.pb.h" | ||
22 | #include "time.h" | ||
23 | #include <unordered_map> | ||
24 | #include <inttypes.h> | ||
25 | #include <dirent.h> | ||
26 | |||
27 | namespace android { | ||
28 | |||
29 | #define CPU_STAT_FILE "/proc/stat" | ||
30 | #define SAMPLES_FILE "/samples" | ||
31 | #define PID_STAT_FILE "/proc/%d/stat" | ||
32 | #define PID_CMDLINE_FILE "/proc/%d/cmdline" | ||
33 | #define PID_IO_FILE "/proc/%d/io" | ||
34 | #define PROC_DIR "/proc" | ||
35 | |||
36 | static const int MAX_LINE = 256; | ||
37 | |||
38 | #define die(...) { LOG(ERROR) << (__VA_ARGS__); exit(EXIT_FAILURE); } | ||
39 | |||
40 | void PopulateCpu(CpuData& cpu) { | ||
41 | long unsigned utime, ntime, stime, itime; | ||
42 | long unsigned iowtime, irqtime, sirqtime; | ||
43 | FILE *file; | ||
44 | file = fopen(CPU_STAT_FILE, "r"); | ||
45 | if (!file) die("Could not open /proc/stat.\n"); | ||
46 | fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &utime, &ntime, &stime, | ||
47 | &itime, &iowtime, &irqtime, &sirqtime); | ||
48 | fclose(file); | ||
49 | cpu.set_utime(utime); | ||
50 | cpu.set_ntime(ntime); | ||
51 | cpu.set_stime(stime); | ||
52 | cpu.set_itime(itime); | ||
53 | cpu.set_iowtime(iowtime); | ||
54 | cpu.set_irqtime(irqtime); | ||
55 | cpu.set_sirqtime(sirqtime); | ||
56 | } | ||
57 | |||
58 | void ClearPreviousResults(std::string path) { | ||
59 | std::string err; | ||
60 | if (!android::base::RemoveFileIfExists(path, &err)) { | ||
61 | LOG(ERROR) << "failed to remove the file " << path << " " << err; | ||
62 | return; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | int ReadIo(char *filename, AppSample *sample) { | ||
67 | FILE *file; | ||
68 | char line[MAX_LINE]; | ||
69 | unsigned int rchar, wchar, syscr, syscw, readbytes, writebytes; | ||
70 | |||
71 | file = fopen(filename, "r"); | ||
72 | if (!file) return 1; | ||
73 | while (fgets(line, MAX_LINE, file)) { | ||
74 | sscanf(line, "rchar: %u", &rchar); | ||
75 | sscanf(line, "wchar: %u", &wchar); | ||
76 | sscanf(line, "syscr: %u", &syscr); | ||
77 | sscanf(line, "syscw: %u", &syscw); | ||
78 | sscanf(line, "read_bytes: %u", &readbytes); | ||
79 | sscanf(line, "write_bytes: %u", &writebytes); | ||
80 | } | ||
81 | fclose(file); | ||
82 | sample->set_rchar(rchar); | ||
83 | sample->set_wchar(wchar); | ||
84 | sample->set_syscr(syscr); | ||
85 | sample->set_syscw(syscw); | ||
86 | sample->set_readbytes(readbytes); | ||
87 | sample->set_writebytes(writebytes); | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | int ReadStatForName(char *filename, AppData *app) { | ||
92 | FILE *file; | ||
93 | char buf[MAX_LINE], *open_paren, *close_paren; | ||
94 | |||
95 | file = fopen(filename, "r"); | ||
96 | if (!file) return 1; | ||
97 | fgets(buf, MAX_LINE, file); | ||
98 | fclose(file); | ||
99 | |||
100 | /* Split at first '(' and last ')' to get process name. */ | ||
101 | open_paren = strchr(buf, '('); | ||
102 | close_paren = strrchr(buf, ')'); | ||
103 | if (!open_paren || !close_paren) return 1; | ||
104 | |||
105 | *open_paren = *close_paren = '\0'; | ||
106 | if (!app->has_tname()) { | ||
107 | app->set_tname(open_paren + 1, close_paren - open_paren - 1); | ||
108 | } | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | int ReadStat(char *filename, AppSample *sample) { | ||
113 | FILE *file; | ||
114 | char buf[MAX_LINE], *open_paren, *close_paren; | ||
115 | |||
116 | file = fopen(filename, "r"); | ||
117 | if (!file) return 1; | ||
118 | fgets(buf, MAX_LINE, file); | ||
119 | fclose(file); | ||
120 | |||
121 | /* Split at first '(' and last ')' to get process name. */ | ||
122 | open_paren = strchr(buf, '('); | ||
123 | close_paren = strrchr(buf, ')'); | ||
124 | if (!open_paren || !close_paren) return 1; | ||
125 | |||
126 | uint64_t utime; | ||
127 | uint64_t stime; | ||
128 | uint64_t rss; | ||
129 | |||
130 | /* Scan rest of string. */ | ||
131 | sscanf(close_paren + 1, | ||
132 | " %*c " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " | ||
133 | "%" PRIu64 /*SCNu64*/ | ||
134 | "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d " | ||
135 | "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d", | ||
136 | &utime, | ||
137 | &stime, | ||
138 | &rss); | ||
139 | sample->set_utime(utime); | ||
140 | sample->set_stime(stime); | ||
141 | sample->set_rss(rss); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | int ReadCmdline(char *filename, AppData *app) { | ||
147 | FILE *file; | ||
148 | char line[MAX_LINE]; | ||
149 | |||
150 | line[0] = '\0'; | ||
151 | file = fopen(filename, "r"); | ||
152 | if (!file) return 1; | ||
153 | fgets(line, MAX_LINE, file); | ||
154 | fclose(file); | ||
155 | if (strlen(line) > 0) { | ||
156 | app->set_name(line, strlen(line)); | ||
157 | } else { | ||
158 | app->set_name("N/A"); | ||
159 | } | ||
160 | return 0; | ||
161 | }; | ||
162 | |||
163 | void ReadProcData(std::unordered_map<int, AppData*>& pidDataMap, DataContainer& dataContainer, | ||
164 | time_t currentTimeUtc, time_t currentUptime) { | ||
165 | DIR *procDir; | ||
166 | struct dirent *pidDir; | ||
167 | pid_t pid; | ||
168 | char filename[64]; | ||
169 | procDir = opendir(PROC_DIR); | ||
170 | if (!procDir) die("Could not open /proc.\n"); | ||
171 | while ((pidDir = readdir(procDir))) { | ||
172 | if (!isdigit(pidDir->d_name[0])) { | ||
173 | continue; | ||
174 | } | ||
175 | pid = atoi(pidDir->d_name); | ||
176 | AppData *data; | ||
177 | |||
178 | // TODO: in theory same pid can be shared for multiple processes, | ||
179 | // might need add extra check. | ||
180 | if (pidDataMap.count(pid) == 0) { | ||
181 | data = dataContainer.add_app(); | ||
182 | data->set_pid(pid); | ||
183 | sprintf(filename, PID_STAT_FILE, pid); | ||
184 | ReadStatForName(filename, data); | ||
185 | sprintf(filename, PID_CMDLINE_FILE, pid); | ||
186 | ReadCmdline(filename, data); | ||
187 | pidDataMap[pid] = data; | ||
188 | } else { | ||
189 | data = pidDataMap[pid]; | ||
190 | } | ||
191 | AppSample *sample = data->add_samples(); | ||
192 | sample->set_timestamp(currentTimeUtc); | ||
193 | sample->set_uptime(currentUptime); | ||
194 | |||
195 | sprintf(filename, PID_STAT_FILE, pid); | ||
196 | ReadStat(filename, sample); | ||
197 | |||
198 | sprintf(filename, PID_IO_FILE, pid); | ||
199 | ReadIo(filename, sample); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | uint64_t SumCpuValues(CpuData& cpu) { | ||
204 | return cpu.utime() + cpu.ntime() + cpu.stime() + cpu.itime() + cpu.iowtime() + | ||
205 | cpu.irqtime() + cpu.sirqtime(); | ||
206 | } | ||
207 | |||
208 | time_t GetUptime() { | ||
209 | std::string uptime_str; | ||
210 | if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) { | ||
211 | LOG(ERROR) << "Failed to read /proc/uptime"; | ||
212 | return -1; | ||
213 | } | ||
214 | |||
215 | // Cast intentionally rounds down. | ||
216 | return static_cast<time_t>(strtod(uptime_str.c_str(), NULL)); | ||
217 | } | ||
218 | |||
219 | struct Stats { | ||
220 | int uptime; | ||
221 | float cpu; | ||
222 | uint64_t rbytes; | ||
223 | uint64_t wbytes; | ||
224 | }; | ||
225 | |||
226 | void PrintPids(DataContainer& data, std::unordered_map<int, uint64_t>& cpuDataMap) { | ||
227 | printf("rchar: number of bytes the process read, using any read-like system call " | ||
228 | "(from files, pipes, tty...).\n"); | ||
229 | printf("wchar: number of bytes the process wrote using any write-like system call.\n"); | ||
230 | printf("wchar: number of bytes the process wrote using any write-like system call.\n"); | ||
231 | printf("syscr: number of write-like system call invocations that the process performed.\n"); | ||
232 | printf("rbytes: number of bytes the process directly read from disk.\n"); | ||
233 | printf("wbytes: number of bytes the process originally dirtied in the page-cache " | ||
234 | "(assuming they will go to disk later).\n\n"); | ||
235 | |||
236 | std::unique_ptr<AppSample> bootZeroSample(new AppSample()); | ||
237 | std::map<int, Stats> statsMap; | ||
238 | // Init stats map | ||
239 | Stats emptyStat {0, 0., 0, 0}; | ||
240 | for (auto it = cpuDataMap.begin(); it != cpuDataMap.end(); it++) { | ||
241 | statsMap[it->first] = emptyStat; | ||
242 | } | ||
243 | for (int i = 0; i < data.app_size(); i++) { | ||
244 | const AppData appData = data.app(i); | ||
245 | printf("\n-----------------------------------------------------------------------------\n"); | ||
246 | printf("PID:\t%u\n", appData.pid()); | ||
247 | printf("Name:\t%s\n", appData.name().c_str()); | ||
248 | printf("ThName:\t%s\n", appData.tname().c_str()); | ||
249 | printf("%-15s%-13s%-13s%-13s%-13s%-13s%-13s%-13s\n", "Uptime inter.", "rchar", "wchar", | ||
250 | "syscr", "syscw", "rbytes", "wbytes", "cpu%"); | ||
251 | const AppSample *olderSample = NULL; | ||
252 | const AppSample *newerSample = NULL; | ||
253 | bool isFirstSample = true; | ||
254 | for (int j = 0; j < appData.samples_size(); j++) { | ||
255 | olderSample = newerSample; | ||
256 | newerSample = &(appData.samples(j)); | ||
257 | if (olderSample == NULL) { | ||
258 | olderSample = bootZeroSample.get(); | ||
259 | } | ||
260 | float cpuLoad = 0.; | ||
261 | uint64_t cpuDelta; | ||
262 | if (isFirstSample) { | ||
263 | cpuDelta = cpuDataMap[newerSample->timestamp()]; | ||
264 | } else { | ||
265 | cpuDelta = cpuDataMap[newerSample->timestamp()] - | ||
266 | cpuDataMap[olderSample->timestamp()]; | ||
267 | } | ||
268 | if (cpuDelta != 0) { | ||
269 | cpuLoad = (newerSample->utime() - olderSample->utime() + | ||
270 | newerSample->stime() - olderSample->stime()) * 100. / cpuDelta; | ||
271 | } | ||
272 | Stats& stats = statsMap[newerSample->timestamp()]; | ||
273 | stats.uptime = newerSample->uptime(); | ||
274 | stats.cpu += cpuLoad; | ||
275 | stats.rbytes += (newerSample->readbytes() - olderSample->readbytes()); | ||
276 | stats.wbytes += (newerSample->writebytes() - olderSample->writebytes()); | ||
277 | |||
278 | // Note that all of these are explicitly `long long`s, not int64_t, | ||
279 | // so we can't use PRId64 here. | ||
280 | #define NUMBER "%-13lld" | ||
281 | printf("%5lld - %-5lld " NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER "%-9.2f\n", | ||
282 | #undef NUMBER | ||
283 | olderSample->uptime(), | ||
284 | newerSample->uptime(), | ||
285 | newerSample->rchar() - olderSample->rchar(), | ||
286 | newerSample->wchar() - olderSample->wchar(), | ||
287 | newerSample->syscr() - olderSample->syscr(), | ||
288 | newerSample->syscw() - olderSample->syscw(), | ||
289 | newerSample->readbytes() - olderSample->readbytes(), | ||
290 | newerSample->writebytes() - olderSample->writebytes(), | ||
291 | cpuLoad); | ||
292 | isFirstSample = false; | ||
293 | } | ||
294 | printf("-----------------------------------------------------------------------------\n"); | ||
295 | #define NUMBER "%-13lld" | ||
296 | printf("%-15s" NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER "\n", | ||
297 | #undef NUMBER | ||
298 | "Total", | ||
299 | newerSample->rchar(), | ||
300 | newerSample->wchar(), | ||
301 | newerSample->syscr(), | ||
302 | newerSample->syscw(), | ||
303 | newerSample->readbytes(), | ||
304 | newerSample->writebytes()); | ||
305 | } | ||
306 | printf("\nAggregations\n%-10s%-13s%-13s%-13s\n", | ||
307 | "Total", | ||
308 | "rbytes", | ||
309 | "wbytes", | ||
310 | "cpu%"); | ||
311 | |||
312 | for (auto it = statsMap.begin(); it != statsMap.end(); it++) { | ||
313 | printf("%-10u%-13" PRIu64 "%-13" PRIu64 "%-9.2f\n", | ||
314 | it->second.uptime, | ||
315 | it->second.rbytes, | ||
316 | it->second.wbytes, | ||
317 | it->second.cpu); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | } | ||
322 | |||
323 | BootioCollector::BootioCollector(std::string path) { | ||
324 | DCHECK_EQ('/', path.back()); | ||
325 | path_ = path; | ||
326 | } | ||
327 | |||
328 | void BootioCollector::StartDataCollection(int timeout, int samples) { | ||
329 | android::ClearPreviousResults(getStoragePath()); | ||
330 | int remaining = samples + 1; | ||
331 | int delayS = timeout / samples; | ||
332 | |||
333 | std::unordered_map < int, AppData * > pidDataMap; | ||
334 | std::unique_ptr <DataContainer> data(new DataContainer()); | ||
335 | while (remaining > 0) { | ||
336 | time_t currentTimeUtc = time(nullptr); | ||
337 | time_t currentUptime = android::GetUptime(); | ||
338 | CpuData *cpu = data->add_cpu(); | ||
339 | cpu->set_timestamp(currentTimeUtc); | ||
340 | cpu->set_uptime(currentUptime); | ||
341 | android::PopulateCpu(*cpu); | ||
342 | android::ReadProcData(pidDataMap, *data.get(), currentTimeUtc, currentUptime); | ||
343 | remaining--; | ||
344 | if (remaining == 0) { | ||
345 | continue; | ||
346 | } | ||
347 | sleep(delayS); | ||
348 | } | ||
349 | std::string file_data; | ||
350 | if (!data->SerializeToString(&file_data)) { | ||
351 | LOG(ERROR) << "Failed to serialize"; | ||
352 | return; | ||
353 | } | ||
354 | if (!android::base::WriteStringToFile(file_data, getStoragePath())) { | ||
355 | LOG(ERROR) << "Failed to write samples"; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | void BootioCollector::Print() { | ||
360 | std::string file_data; | ||
361 | if (!android::base::ReadFileToString(getStoragePath(), &file_data)) { | ||
362 | printf("Failed to read data from file.\n"); | ||
363 | return; | ||
364 | } | ||
365 | std::unique_ptr <DataContainer> data(new DataContainer()); | ||
366 | if (!data->ParsePartialFromString(file_data)) { | ||
367 | printf("Failed to parse data.\n"); | ||
368 | return; | ||
369 | } | ||
370 | std::unordered_map<int, uint64_t> cpuDataMap; | ||
371 | for (int i = 0; i < data->cpu_size(); i++) { | ||
372 | CpuData cpu_data = data->cpu(i); | ||
373 | cpuDataMap[cpu_data.timestamp()] = android::SumCpuValues(cpu_data); | ||
374 | } | ||
375 | android::PrintPids(*data.get(), cpuDataMap); | ||
376 | } | ||
377 | |||
378 | |||
379 | std::string BootioCollector::getStoragePath() { | ||
380 | return path_ + SAMPLES_FILE; | ||
381 | } | ||
diff --git a/tools/bootio/bootio_collector.h b/tools/bootio/bootio_collector.h deleted file mode 100644 index 7296e6f9..00000000 --- a/tools/bootio/bootio_collector.h +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #ifndef BOOTIO_COLLECTOR_H_ | ||
18 | #define BOOTIO_COLLECTOR_H_ | ||
19 | |||
20 | #include <string> | ||
21 | #include <android-base/macros.h> | ||
22 | |||
23 | class BootioCollector { | ||
24 | public: | ||
25 | BootioCollector(std::string path); | ||
26 | |||
27 | void StartDataCollection(int timeout, int samples); | ||
28 | |||
29 | void Print(); | ||
30 | |||
31 | private: | ||
32 | std::string getStoragePath(); | ||
33 | |||
34 | std::string path_; | ||
35 | |||
36 | DISALLOW_COPY_AND_ASSIGN(BootioCollector); | ||
37 | }; | ||
38 | |||
39 | #endif // BOOTIO_COLLECTOR_H_ | ||
diff --git a/tools/bootio/protos.proto b/tools/bootio/protos.proto deleted file mode 100644 index d5674ece..00000000 --- a/tools/bootio/protos.proto +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | // Copyright (C) 2016 The Android Open Source Project | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | syntax = "proto2"; | ||
16 | |||
17 | option optimize_for = LITE_RUNTIME; | ||
18 | |||
19 | message DataContainer { | ||
20 | repeated AppData app = 1; | ||
21 | repeated CpuData cpu = 2; | ||
22 | }; | ||
23 | |||
24 | message CpuData { | ||
25 | required int64 timestamp = 1; | ||
26 | required int64 uptime = 2; | ||
27 | required int64 utime = 3; | ||
28 | required int64 ntime = 4; | ||
29 | required int64 stime = 5; | ||
30 | required int64 itime = 6; | ||
31 | required int64 iowtime = 7; | ||
32 | required int64 irqtime = 9; | ||
33 | required int64 sirqtime = 10; | ||
34 | } | ||
35 | |||
36 | message AppData { | ||
37 | required int32 pid = 1; | ||
38 | required string tname = 2; | ||
39 | required string name = 3; | ||
40 | repeated AppSample samples = 4; | ||
41 | } | ||
42 | |||
43 | message AppSample { | ||
44 | required int64 timestamp = 1; | ||
45 | required int64 uptime = 2; | ||
46 | required int64 rchar = 3; | ||
47 | required int64 wchar = 4; | ||
48 | required int64 syscr = 5; | ||
49 | required int64 syscw = 6; | ||
50 | required int64 readbytes = 7; | ||
51 | required int64 writebytes = 8; | ||
52 | required int64 utime = 9; | ||
53 | required int64 stime = 10; | ||
54 | required int64 rss = 11; | ||
55 | }; | ||
diff --git a/tools/bootio/sepolicy/bootio.te b/tools/bootio/sepolicy/bootio.te deleted file mode 100644 index cd0fb80d..00000000 --- a/tools/bootio/sepolicy/bootio.te +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | # bootio command | ||
2 | type bootio, domain, coredomain; | ||
3 | type bootio_exec, exec_type, file_type; | ||
4 | |||
5 | init_daemon_domain(bootio) | ||
6 | |||
7 | # Allow persistent storage in /data/misc/bootio. | ||
8 | #allow bootio bootio_data_file:dir rw_dir_perms; | ||
9 | #allow bootio bootio_data_file:file create_file_perms; | ||
10 | |||
11 | # Read access to pseudo filesystems (for /proc/stats, proc/io/io, etc). | ||
12 | #r_dir_file(bootio, proc) | ||
diff --git a/tools/bootio/sepolicy/domain.te b/tools/bootio/sepolicy/domain.te deleted file mode 100644 index af42fe7e..00000000 --- a/tools/bootio/sepolicy/domain.te +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | # dontaudit bootio kernel:system module_request; | ||
2 | allow bootio kernel:fd use; \ No newline at end of file | ||
diff --git a/tools/bootio/sepolicy/file.te b/tools/bootio/sepolicy/file.te deleted file mode 100644 index 0320bc83..00000000 --- a/tools/bootio/sepolicy/file.te +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | # /data/misc subdirectories | ||
2 | type bootio_data_file, file_type, data_file_type, core_data_file_type; | ||
diff --git a/tools/bootio/sepolicy/file_contexts b/tools/bootio/sepolicy/file_contexts deleted file mode 100644 index 071227c2..00000000 --- a/tools/bootio/sepolicy/file_contexts +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | # System files | ||
2 | /system/bin/bootio u:object_r:bootio_exec:s0 | ||
3 | |||
4 | # Misc data | ||
5 | /data/misc/bootio(/.*)? u:object_r:bootio_data_file:s0 | ||