xref: /aosp_15_r20/system/extras/boottime_tools/bootanalyze/bootanalyze.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/python3
2*288bf522SAndroid Build Coastguard Worker
3*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2016 The Android Open Source Project
4*288bf522SAndroid Build Coastguard Worker#
5*288bf522SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*288bf522SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*288bf522SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*288bf522SAndroid Build Coastguard Worker#
9*288bf522SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*288bf522SAndroid Build Coastguard Worker#
11*288bf522SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*288bf522SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*288bf522SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*288bf522SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*288bf522SAndroid Build Coastguard Worker# limitations under the License.
16*288bf522SAndroid Build Coastguard Worker#
17*288bf522SAndroid Build Coastguard Worker"""Tool to analyze logcat and dmesg logs.
18*288bf522SAndroid Build Coastguard Worker
19*288bf522SAndroid Build Coastguard Workerbootanalyze read logcat and dmesg logs and determines key points for boot.
20*288bf522SAndroid Build Coastguard Worker"""
21*288bf522SAndroid Build Coastguard Worker
22*288bf522SAndroid Build Coastguard Workerimport argparse
23*288bf522SAndroid Build Coastguard Workerimport collections
24*288bf522SAndroid Build Coastguard Workerimport datetime
25*288bf522SAndroid Build Coastguard Workerimport math
26*288bf522SAndroid Build Coastguard Workerimport operator
27*288bf522SAndroid Build Coastguard Workerimport os
28*288bf522SAndroid Build Coastguard Workerimport re
29*288bf522SAndroid Build Coastguard Workerimport select
30*288bf522SAndroid Build Coastguard Workerimport subprocess
31*288bf522SAndroid Build Coastguard Workerimport time
32*288bf522SAndroid Build Coastguard Workerimport threading
33*288bf522SAndroid Build Coastguard Workerimport yaml
34*288bf522SAndroid Build Coastguard Worker
35*288bf522SAndroid Build Coastguard Workerfrom datetime import datetime
36*288bf522SAndroid Build Coastguard Worker
37*288bf522SAndroid Build Coastguard WorkerTIME_DMESG = r"\[\s*(\d+\.\d+)\]"
38*288bf522SAndroid Build Coastguard WorkerTIME_LOGCAT = r"[0-9]+\.?[0-9]*"
39*288bf522SAndroid Build Coastguard WorkerKERNEL_TIME_KEY = "kernel"
40*288bf522SAndroid Build Coastguard WorkerBOOT_ANIM_END_TIME_KEY = "BootAnimEnd"
41*288bf522SAndroid Build Coastguard WorkerKERNEL_BOOT_COMPLETE = "BootComplete_kernel"
42*288bf522SAndroid Build Coastguard WorkerLOGCAT_BOOT_COMPLETE = "BootComplete"
43*288bf522SAndroid Build Coastguard WorkerANDROID_INIT_SECOND_STAGE = "android_init_2st_stage"
44*288bf522SAndroid Build Coastguard WorkerCARWATCHDOG_BOOT_COMPLETE = "CarWatchdogBootupProfilingComplete"
45*288bf522SAndroid Build Coastguard WorkerLAUNCHER_START = "LauncherStart"
46*288bf522SAndroid Build Coastguard WorkerCARWATCHDOG_DUMP_COMMAND = "adb shell dumpsys android.automotive.watchdog.ICarWatchdog/default"
47*288bf522SAndroid Build Coastguard WorkerBOOT_TIME_TOO_BIG = 200.0
48*288bf522SAndroid Build Coastguard WorkerMAX_RETRIES = 5
49*288bf522SAndroid Build Coastguard WorkerDEBUG = False
50*288bf522SAndroid Build Coastguard WorkerDEBUG_PATTERN = False
51*288bf522SAndroid Build Coastguard WorkerADB_CMD = "adb"
52*288bf522SAndroid Build Coastguard WorkerTIMING_THRESHOLD = 5.0
53*288bf522SAndroid Build Coastguard WorkerBOOT_PROP = r"\[ro\.boottime\.([^\]]+)\]:\s+\[(\d+)\]"
54*288bf522SAndroid Build Coastguard WorkerBOOTLOADER_TIME_PROP = r"\[ro\.boot\.boottime\]:\s+\[([^\]]+)\]"
55*288bf522SAndroid Build Coastguard WorkerCARWATCHDOG_PARSER_CMD = 'perf_stats_parser'
56*288bf522SAndroid Build Coastguard Worker
57*288bf522SAndroid Build Coastguard Workermax_wait_time = BOOT_TIME_TOO_BIG
58*288bf522SAndroid Build Coastguard Worker
59*288bf522SAndroid Build Coastguard Workerdef main():
60*288bf522SAndroid Build Coastguard Worker  global ADB_CMD
61*288bf522SAndroid Build Coastguard Worker
62*288bf522SAndroid Build Coastguard Worker  args = init_arguments()
63*288bf522SAndroid Build Coastguard Worker
64*288bf522SAndroid Build Coastguard Worker  if args.iterate < 1:
65*288bf522SAndroid Build Coastguard Worker    raise Exception('Number of iteration must be >=1')
66*288bf522SAndroid Build Coastguard Worker
67*288bf522SAndroid Build Coastguard Worker  if args.iterate > 1 and not args.reboot:
68*288bf522SAndroid Build Coastguard Worker    print("Forcing reboot flag")
69*288bf522SAndroid Build Coastguard Worker    args.reboot = True
70*288bf522SAndroid Build Coastguard Worker
71*288bf522SAndroid Build Coastguard Worker  if args.serial:
72*288bf522SAndroid Build Coastguard Worker    ADB_CMD = "%s %s" % ("adb -s", args.serial)
73*288bf522SAndroid Build Coastguard Worker
74*288bf522SAndroid Build Coastguard Worker  error_time = BOOT_TIME_TOO_BIG * 10
75*288bf522SAndroid Build Coastguard Worker  if args.errortime:
76*288bf522SAndroid Build Coastguard Worker    error_time = float(args.errortime)
77*288bf522SAndroid Build Coastguard Worker  if args.maxwaittime:
78*288bf522SAndroid Build Coastguard Worker    global max_wait_time
79*288bf522SAndroid Build Coastguard Worker    max_wait_time = float(args.maxwaittime)
80*288bf522SAndroid Build Coastguard Worker
81*288bf522SAndroid Build Coastguard Worker  components_to_monitor = {}
82*288bf522SAndroid Build Coastguard Worker  if args.componentmonitor:
83*288bf522SAndroid Build Coastguard Worker    items = args.componentmonitor.split(",")
84*288bf522SAndroid Build Coastguard Worker    for item in items:
85*288bf522SAndroid Build Coastguard Worker      kv = item.split("=")
86*288bf522SAndroid Build Coastguard Worker      key = kv[0]
87*288bf522SAndroid Build Coastguard Worker      value = float(kv[1])
88*288bf522SAndroid Build Coastguard Worker      components_to_monitor[key] = value
89*288bf522SAndroid Build Coastguard Worker
90*288bf522SAndroid Build Coastguard Worker  cfg = yaml.load(args.config, Loader=yaml.SafeLoader)
91*288bf522SAndroid Build Coastguard Worker
92*288bf522SAndroid Build Coastguard Worker  if args.stressfs:
93*288bf522SAndroid Build Coastguard Worker    if run_adb_cmd('install -r -g ' + args.stressfs) != 0:
94*288bf522SAndroid Build Coastguard Worker      raise Exception('StressFS APK not installed')
95*288bf522SAndroid Build Coastguard Worker
96*288bf522SAndroid Build Coastguard Worker  if args.iterate > 1 and args.bootchart:
97*288bf522SAndroid Build Coastguard Worker    run_adb_shell_cmd_as_root('touch /data/bootchart/enabled')
98*288bf522SAndroid Build Coastguard Worker
99*288bf522SAndroid Build Coastguard Worker  search_events_pattern = {key: re.compile(pattern)
100*288bf522SAndroid Build Coastguard Worker                   for key, pattern in cfg['events'].items()}
101*288bf522SAndroid Build Coastguard Worker  timing_events_pattern = {key: re.compile(pattern)
102*288bf522SAndroid Build Coastguard Worker                   for key, pattern in cfg['timings'].items()}
103*288bf522SAndroid Build Coastguard Worker  shutdown_events_pattern = {key: re.compile(pattern)
104*288bf522SAndroid Build Coastguard Worker                   for key, pattern in cfg['shutdown_events'].items()}
105*288bf522SAndroid Build Coastguard Worker  if DEBUG_PATTERN:
106*288bf522SAndroid Build Coastguard Worker    print("search event:{} timing event:{}".format(search_events_pattern, timing_events_pattern))
107*288bf522SAndroid Build Coastguard Worker
108*288bf522SAndroid Build Coastguard Worker  now = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
109*288bf522SAndroid Build Coastguard Worker  boot_chart_file_path_prefix = "bootchart-" + now
110*288bf522SAndroid Build Coastguard Worker  systrace_file_path_prefix = "systrace-" + now
111*288bf522SAndroid Build Coastguard Worker
112*288bf522SAndroid Build Coastguard Worker  if args.output:
113*288bf522SAndroid Build Coastguard Worker    boot_chart_file_path_prefix = args.output + '/' + boot_chart_file_path_prefix
114*288bf522SAndroid Build Coastguard Worker    systrace_file_path_prefix = args.output + '/' + systrace_file_path_prefix
115*288bf522SAndroid Build Coastguard Worker
116*288bf522SAndroid Build Coastguard Worker  data_points = {}
117*288bf522SAndroid Build Coastguard Worker  kernel_timing_points = collections.OrderedDict()
118*288bf522SAndroid Build Coastguard Worker  logcat_timing_points = collections.OrderedDict()
119*288bf522SAndroid Build Coastguard Worker  boottime_points = collections.OrderedDict()
120*288bf522SAndroid Build Coastguard Worker  shutdown_event_all = collections.OrderedDict()
121*288bf522SAndroid Build Coastguard Worker  shutdown_timing_event_all = collections.OrderedDict()
122*288bf522SAndroid Build Coastguard Worker  for it in range(0, args.iterate):
123*288bf522SAndroid Build Coastguard Worker    if args.iterate > 1:
124*288bf522SAndroid Build Coastguard Worker      print("Run: {0}".format(it))
125*288bf522SAndroid Build Coastguard Worker    attempt = 1
126*288bf522SAndroid Build Coastguard Worker    processing_data = None
127*288bf522SAndroid Build Coastguard Worker    timings = None
128*288bf522SAndroid Build Coastguard Worker    boottime_events = None
129*288bf522SAndroid Build Coastguard Worker    while attempt <= MAX_RETRIES and processing_data is None:
130*288bf522SAndroid Build Coastguard Worker      attempt += 1
131*288bf522SAndroid Build Coastguard Worker      processing_data, kernel_timings, logcat_timings, boottime_events, shutdown_events,\
132*288bf522SAndroid Build Coastguard Worker          shutdown_timing_events = iterate(\
133*288bf522SAndroid Build Coastguard Worker        args, search_events_pattern, timing_events_pattern, shutdown_events_pattern, cfg,\
134*288bf522SAndroid Build Coastguard Worker        error_time, components_to_monitor)
135*288bf522SAndroid Build Coastguard Worker    if shutdown_events:
136*288bf522SAndroid Build Coastguard Worker      for k, v in shutdown_events.items():
137*288bf522SAndroid Build Coastguard Worker        events = shutdown_event_all.get(k)
138*288bf522SAndroid Build Coastguard Worker        if not events:
139*288bf522SAndroid Build Coastguard Worker          events = []
140*288bf522SAndroid Build Coastguard Worker          shutdown_event_all[k] = events
141*288bf522SAndroid Build Coastguard Worker        events.append(v)
142*288bf522SAndroid Build Coastguard Worker    if shutdown_timing_events:
143*288bf522SAndroid Build Coastguard Worker      for k, v in shutdown_timing_events.items():
144*288bf522SAndroid Build Coastguard Worker        events = shutdown_timing_event_all.get(k)
145*288bf522SAndroid Build Coastguard Worker        if not events:
146*288bf522SAndroid Build Coastguard Worker          events = []
147*288bf522SAndroid Build Coastguard Worker          shutdown_timing_event_all[k] = events
148*288bf522SAndroid Build Coastguard Worker        events.append(v)
149*288bf522SAndroid Build Coastguard Worker    if not processing_data or not boottime_events:
150*288bf522SAndroid Build Coastguard Worker      # Processing error
151*288bf522SAndroid Build Coastguard Worker      print("Failed to collect valid samples for run {0}".format(it))
152*288bf522SAndroid Build Coastguard Worker      continue
153*288bf522SAndroid Build Coastguard Worker
154*288bf522SAndroid Build Coastguard Worker    if args.bootchart:
155*288bf522SAndroid Build Coastguard Worker      grab_bootchart(boot_chart_file_path_prefix + "_run_" + str(it))
156*288bf522SAndroid Build Coastguard Worker
157*288bf522SAndroid Build Coastguard Worker    if args.systrace:
158*288bf522SAndroid Build Coastguard Worker      grab_systrace(systrace_file_path_prefix + "_run_" + str(it))
159*288bf522SAndroid Build Coastguard Worker
160*288bf522SAndroid Build Coastguard Worker    if args.carwatchdog:
161*288bf522SAndroid Build Coastguard Worker      grab_carwatchdog_bootstats(args.output)
162*288bf522SAndroid Build Coastguard Worker
163*288bf522SAndroid Build Coastguard Worker    for k, v in processing_data.items():
164*288bf522SAndroid Build Coastguard Worker      if k not in data_points:
165*288bf522SAndroid Build Coastguard Worker        data_points[k] = []
166*288bf522SAndroid Build Coastguard Worker      data_points[k].append(v['value'])
167*288bf522SAndroid Build Coastguard Worker
168*288bf522SAndroid Build Coastguard Worker    if kernel_timings is not None:
169*288bf522SAndroid Build Coastguard Worker      for k, v in kernel_timings.items():
170*288bf522SAndroid Build Coastguard Worker        if k not in kernel_timing_points:
171*288bf522SAndroid Build Coastguard Worker          kernel_timing_points[k] = []
172*288bf522SAndroid Build Coastguard Worker        kernel_timing_points[k].append(v)
173*288bf522SAndroid Build Coastguard Worker    if logcat_timings is not None:
174*288bf522SAndroid Build Coastguard Worker      for k, v in logcat_timings.items():
175*288bf522SAndroid Build Coastguard Worker        if k not in logcat_timing_points:
176*288bf522SAndroid Build Coastguard Worker          logcat_timing_points[k] = []
177*288bf522SAndroid Build Coastguard Worker        logcat_timing_points[k].append(v)
178*288bf522SAndroid Build Coastguard Worker
179*288bf522SAndroid Build Coastguard Worker    for k, v in boottime_events.items():
180*288bf522SAndroid Build Coastguard Worker      if k not in boottime_points:
181*288bf522SAndroid Build Coastguard Worker        boottime_points[k] = []
182*288bf522SAndroid Build Coastguard Worker      boottime_points[k].append(v)
183*288bf522SAndroid Build Coastguard Worker
184*288bf522SAndroid Build Coastguard Worker  if args.stressfs:
185*288bf522SAndroid Build Coastguard Worker    run_adb_cmd('uninstall com.android.car.test.stressfs')
186*288bf522SAndroid Build Coastguard Worker    run_adb_shell_cmd('"rm -rf /storage/emulated/0/stressfs_data*"')
187*288bf522SAndroid Build Coastguard Worker
188*288bf522SAndroid Build Coastguard Worker  if args.iterate > 1:
189*288bf522SAndroid Build Coastguard Worker    print("-----------------")
190*288bf522SAndroid Build Coastguard Worker    print("\nshutdown events after {0} runs".format(args.iterate))
191*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs"))
192*288bf522SAndroid Build Coastguard Worker    for item in list(shutdown_event_all.items()):
193*288bf522SAndroid Build Coastguard Worker      num_runs = len(item[1])
194*288bf522SAndroid Build Coastguard Worker      print('{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format(
195*288bf522SAndroid Build Coastguard Worker          item[0], sum(item[1])/num_runs, stddev(item[1]),\
196*288bf522SAndroid Build Coastguard Worker          "*time taken" if item[0].startswith("init.") else "",\
197*288bf522SAndroid Build Coastguard Worker          num_runs if num_runs != args.iterate else ""))
198*288bf522SAndroid Build Coastguard Worker    print("\nshutdown timing events after {0} runs".format(args.iterate))
199*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs"))
200*288bf522SAndroid Build Coastguard Worker    for item in list(shutdown_timing_event_all.items()):
201*288bf522SAndroid Build Coastguard Worker      num_runs = len(item[1])
202*288bf522SAndroid Build Coastguard Worker      print('{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format(
203*288bf522SAndroid Build Coastguard Worker          item[0], sum(item[1])/num_runs, stddev(item[1]),\
204*288bf522SAndroid Build Coastguard Worker          "*time taken" if item[0].startswith("init.") else "",\
205*288bf522SAndroid Build Coastguard Worker          num_runs if num_runs != args.iterate else ""))
206*288bf522SAndroid Build Coastguard Worker
207*288bf522SAndroid Build Coastguard Worker    print("-----------------")
208*288bf522SAndroid Build Coastguard Worker    print("ro.boottime.* after {0} runs".format(args.iterate))
209*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs"))
210*288bf522SAndroid Build Coastguard Worker    for item in list(boottime_points.items()):
211*288bf522SAndroid Build Coastguard Worker        num_runs = len(item[1])
212*288bf522SAndroid Build Coastguard Worker        print('{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format(
213*288bf522SAndroid Build Coastguard Worker          item[0], sum(item[1])/num_runs, stddev(item[1]),\
214*288bf522SAndroid Build Coastguard Worker          "*time taken" if item[0].startswith("init.") else "",\
215*288bf522SAndroid Build Coastguard Worker          num_runs if num_runs != args.iterate else ""))
216*288bf522SAndroid Build Coastguard Worker
217*288bf522SAndroid Build Coastguard Worker    if args.timings:
218*288bf522SAndroid Build Coastguard Worker      dump_timings_points_summary("Kernel", kernel_timing_points, args)
219*288bf522SAndroid Build Coastguard Worker      dump_timings_points_summary("Logcat", logcat_timing_points, args)
220*288bf522SAndroid Build Coastguard Worker
221*288bf522SAndroid Build Coastguard Worker
222*288bf522SAndroid Build Coastguard Worker    print("-----------------")
223*288bf522SAndroid Build Coastguard Worker    print("Avg values after {0} runs".format(args.iterate))
224*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs"))
225*288bf522SAndroid Build Coastguard Worker
226*288bf522SAndroid Build Coastguard Worker    average_with_stddev = []
227*288bf522SAndroid Build Coastguard Worker    for item in list(data_points.items()):
228*288bf522SAndroid Build Coastguard Worker      average_with_stddev.append((item[0], sum(item[1])/len(item[1]), stddev(item[1]),\
229*288bf522SAndroid Build Coastguard Worker                                  len(item[1])))
230*288bf522SAndroid Build Coastguard Worker    for item in sorted(average_with_stddev, key=lambda entry: entry[1]):
231*288bf522SAndroid Build Coastguard Worker      print('{0:30}: {1:<7.5} {2:<7.5} {3}'.format(
232*288bf522SAndroid Build Coastguard Worker        item[0], item[1], item[2], item[3] if item[3] != args.iterate else ""))
233*288bf522SAndroid Build Coastguard Worker
234*288bf522SAndroid Build Coastguard Worker    run_adb_shell_cmd_as_root('rm /data/bootchart/enabled')
235*288bf522SAndroid Build Coastguard Worker
236*288bf522SAndroid Build Coastguard Worker
237*288bf522SAndroid Build Coastguard Workerdef dump_timings_points_summary(msg_header, timing_points, args):
238*288bf522SAndroid Build Coastguard Worker      averaged_timing_points = []
239*288bf522SAndroid Build Coastguard Worker      for item in list(timing_points.items()):
240*288bf522SAndroid Build Coastguard Worker        average = sum(item[1])/len(item[1])
241*288bf522SAndroid Build Coastguard Worker        std_dev = stddev(item[1])
242*288bf522SAndroid Build Coastguard Worker        averaged_timing_points.append((item[0], average, std_dev, len(item[1])))
243*288bf522SAndroid Build Coastguard Worker
244*288bf522SAndroid Build Coastguard Worker      print("-----------------")
245*288bf522SAndroid Build Coastguard Worker      print(msg_header + " timing in order, Avg time values after {0} runs".format(args.iterate))
246*288bf522SAndroid Build Coastguard Worker      print('{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs"))
247*288bf522SAndroid Build Coastguard Worker      for item in averaged_timing_points:
248*288bf522SAndroid Build Coastguard Worker        print('{0:30}: {1:<7.5} {2:<7.5} {3}'.format(
249*288bf522SAndroid Build Coastguard Worker          item[0], item[1], item[2], item[3] if item[3] != args.iterate else ""))
250*288bf522SAndroid Build Coastguard Worker
251*288bf522SAndroid Build Coastguard Worker      print("-----------------")
252*288bf522SAndroid Build Coastguard Worker      print(msg_header + " timing top items, Avg time values after {0} runs".format(args.iterate))
253*288bf522SAndroid Build Coastguard Worker      print('{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs"))
254*288bf522SAndroid Build Coastguard Worker      for item in sorted(averaged_timing_points, key=lambda entry: entry[1], reverse=True):
255*288bf522SAndroid Build Coastguard Worker        if item[1] < TIMING_THRESHOLD:
256*288bf522SAndroid Build Coastguard Worker          break
257*288bf522SAndroid Build Coastguard Worker        print('{0:30}: {1:<7.5} {2:<7.5} {3}'.format(
258*288bf522SAndroid Build Coastguard Worker          item[0], item[1], item[2], item[3] if item[3] != args.iterate else ""))
259*288bf522SAndroid Build Coastguard Worker
260*288bf522SAndroid Build Coastguard Workerdef capture_bugreport(bugreport_hint, boot_complete_time):
261*288bf522SAndroid Build Coastguard Worker    now = datetime.now()
262*288bf522SAndroid Build Coastguard Worker    bugreport_file = ("bugreport-%s-" + bugreport_hint + "-%s.zip") \
263*288bf522SAndroid Build Coastguard Worker        % (now.strftime("%Y-%m-%d-%H-%M-%S"), str(boot_complete_time))
264*288bf522SAndroid Build Coastguard Worker    print("Boot up time too big, will capture bugreport %s" % (bugreport_file))
265*288bf522SAndroid Build Coastguard Worker    os.system(ADB_CMD + " bugreport " + bugreport_file)
266*288bf522SAndroid Build Coastguard Worker
267*288bf522SAndroid Build Coastguard Workerdef generate_timing_points(timing_events, timings):
268*288bf522SAndroid Build Coastguard Worker  timing_points = collections.OrderedDict()
269*288bf522SAndroid Build Coastguard Worker  monitor_contention_points = collections.OrderedDict()
270*288bf522SAndroid Build Coastguard Worker  for k, l in timing_events.items():
271*288bf522SAndroid Build Coastguard Worker      for v in l:
272*288bf522SAndroid Build Coastguard Worker        name, time_v = extract_timing(v, timings)
273*288bf522SAndroid Build Coastguard Worker        if name and time_v:
274*288bf522SAndroid Build Coastguard Worker          if v.find("SystemServerTimingAsync") > 0:
275*288bf522SAndroid Build Coastguard Worker            name = "(" + name + ")"
276*288bf522SAndroid Build Coastguard Worker          if k.endswith("_secs"):
277*288bf522SAndroid Build Coastguard Worker            time_v = time_v * 1000.0
278*288bf522SAndroid Build Coastguard Worker          if k.startswith("long_monitor_contention"):
279*288bf522SAndroid Build Coastguard Worker            monitor_contention_points[v] = time_v
280*288bf522SAndroid Build Coastguard Worker            continue
281*288bf522SAndroid Build Coastguard Worker          new_name = name
282*288bf522SAndroid Build Coastguard Worker          name_index = 0
283*288bf522SAndroid Build Coastguard Worker          while timing_points.get(new_name): # if the name is already taken, append #digit
284*288bf522SAndroid Build Coastguard Worker            name_index += 1
285*288bf522SAndroid Build Coastguard Worker            new_name = name + "#" + str(name_index)
286*288bf522SAndroid Build Coastguard Worker          timing_points[new_name] = time_v
287*288bf522SAndroid Build Coastguard Worker  return timing_points, monitor_contention_points
288*288bf522SAndroid Build Coastguard Worker
289*288bf522SAndroid Build Coastguard Workerdef dump_timing_points(msg_header, timing_points):
290*288bf522SAndroid Build Coastguard Worker    print(msg_header + " event timing in time order, key: time")
291*288bf522SAndroid Build Coastguard Worker    for item in list(timing_points.items()):
292*288bf522SAndroid Build Coastguard Worker      print('{0:30}: {1:<7.5}'.format(item[0], item[1]))
293*288bf522SAndroid Build Coastguard Worker    print("-----------------")
294*288bf522SAndroid Build Coastguard Worker    print(msg_header + " event timing top items")
295*288bf522SAndroid Build Coastguard Worker    for item in sorted(list(timing_points.items()), key=operator.itemgetter(1), reverse=True):
296*288bf522SAndroid Build Coastguard Worker      if item[1] < TIMING_THRESHOLD:
297*288bf522SAndroid Build Coastguard Worker        break
298*288bf522SAndroid Build Coastguard Worker      print('{0:30}: {1:<7.5}'.format(
299*288bf522SAndroid Build Coastguard Worker        item[0], item[1]))
300*288bf522SAndroid Build Coastguard Worker    print("-----------------")
301*288bf522SAndroid Build Coastguard Worker
302*288bf522SAndroid Build Coastguard Workerdef dump_monitor_contentions(logcat_monitor_contentions):
303*288bf522SAndroid Build Coastguard Worker  print("Monitor contentions over 100ms:")
304*288bf522SAndroid Build Coastguard Worker  for item in list(logcat_monitor_contentions.items()):
305*288bf522SAndroid Build Coastguard Worker      if item[1] > 100:
306*288bf522SAndroid Build Coastguard Worker        print('{0:<7.5}ms: {1}'.format(item[1], item[0]))
307*288bf522SAndroid Build Coastguard Worker  print("-----------------")
308*288bf522SAndroid Build Coastguard Worker
309*288bf522SAndroid Build Coastguard Workerdef handle_reboot_log(capture_log_on_error, shutdown_events_pattern, components_to_monitor):
310*288bf522SAndroid Build Coastguard Worker  shutdown_events, shutdown_timing_events = collect_logcat_for_shutdown(capture_log_on_error,\
311*288bf522SAndroid Build Coastguard Worker		shutdown_events_pattern, components_to_monitor)
312*288bf522SAndroid Build Coastguard Worker  print("\nshutdown events: time")
313*288bf522SAndroid Build Coastguard Worker  for item in list(shutdown_events.items()):
314*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7.5}'.format(item[0], item[1]))
315*288bf522SAndroid Build Coastguard Worker  print("\nshutdown timing events: time")
316*288bf522SAndroid Build Coastguard Worker  for item in list(shutdown_timing_events.items()):
317*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7.5}'.format(item[0], item[1]))
318*288bf522SAndroid Build Coastguard Worker  return shutdown_events, shutdown_timing_events
319*288bf522SAndroid Build Coastguard Worker
320*288bf522SAndroid Build Coastguard Workerdef collect_dmesg_events(search_events_pattern, timings_pattern, results):
321*288bf522SAndroid Build Coastguard Worker  dmesg_events, kernel_timing_events = collect_events(search_events_pattern, ADB_CMD +\
322*288bf522SAndroid Build Coastguard Worker                                                      ' shell su root dmesg -w', timings_pattern,
323*288bf522SAndroid Build Coastguard Worker                                                      [
324*288bf522SAndroid Build Coastguard Worker                                                        KERNEL_BOOT_COMPLETE,
325*288bf522SAndroid Build Coastguard Worker                                                        ANDROID_INIT_SECOND_STAGE
326*288bf522SAndroid Build Coastguard Worker                                                      ],
327*288bf522SAndroid Build Coastguard Worker                                                      False, True)
328*288bf522SAndroid Build Coastguard Worker  results.append(dmesg_events)
329*288bf522SAndroid Build Coastguard Worker  results.append(kernel_timing_events)
330*288bf522SAndroid Build Coastguard Worker
331*288bf522SAndroid Build Coastguard Workerdef iterate(args, search_events_pattern, timings_pattern, shutdown_events_pattern, cfg, error_time,\
332*288bf522SAndroid Build Coastguard Worker    components_to_monitor):
333*288bf522SAndroid Build Coastguard Worker  shutdown_events = None
334*288bf522SAndroid Build Coastguard Worker  shutdown_timing_events = None
335*288bf522SAndroid Build Coastguard Worker  if args.reboot:
336*288bf522SAndroid Build Coastguard Worker    # sleep to make sure that logcat reader is reading before adb is gone by reboot. ugly but make
337*288bf522SAndroid Build Coastguard Worker    # impl simple.
338*288bf522SAndroid Build Coastguard Worker    t = threading.Thread(target=lambda: (time.sleep(2), reboot(args.serial, args.stressfs != '',\
339*288bf522SAndroid Build Coastguard Worker        args.permissive, args.adb_reboot, args.buffersize)))
340*288bf522SAndroid Build Coastguard Worker    t.start()
341*288bf522SAndroid Build Coastguard Worker    shutdown_events, shutdown_timing_events = handle_reboot_log(True, shutdown_events_pattern,\
342*288bf522SAndroid Build Coastguard Worker        components_to_monitor)
343*288bf522SAndroid Build Coastguard Worker    t.join()
344*288bf522SAndroid Build Coastguard Worker
345*288bf522SAndroid Build Coastguard Worker  results = []
346*288bf522SAndroid Build Coastguard Worker  t = threading.Thread(target=collect_dmesg_events, args=(search_events_pattern,\
347*288bf522SAndroid Build Coastguard Worker    timings_pattern, results))
348*288bf522SAndroid Build Coastguard Worker  t.start()
349*288bf522SAndroid Build Coastguard Worker
350*288bf522SAndroid Build Coastguard Worker  logcat_stop_events = [LOGCAT_BOOT_COMPLETE, LAUNCHER_START]
351*288bf522SAndroid Build Coastguard Worker  if args.fs_check:
352*288bf522SAndroid Build Coastguard Worker    logcat_stop_events.append("FsStat")
353*288bf522SAndroid Build Coastguard Worker  if args.carwatchdog:
354*288bf522SAndroid Build Coastguard Worker    logcat_stop_events.append(CARWATCHDOG_BOOT_COMPLETE)
355*288bf522SAndroid Build Coastguard Worker  logcat_events, logcat_timing_events = collect_events(
356*288bf522SAndroid Build Coastguard Worker    search_events_pattern, ADB_CMD + ' logcat -b all -v epoch', timings_pattern,\
357*288bf522SAndroid Build Coastguard Worker    logcat_stop_events, True, False)
358*288bf522SAndroid Build Coastguard Worker
359*288bf522SAndroid Build Coastguard Worker  t.join()
360*288bf522SAndroid Build Coastguard Worker  dmesg_events = results[0]
361*288bf522SAndroid Build Coastguard Worker  kernel_timing_events = results[1]
362*288bf522SAndroid Build Coastguard Worker
363*288bf522SAndroid Build Coastguard Worker  logcat_event_time = extract_time(logcat_events, TIME_LOGCAT, float)
364*288bf522SAndroid Build Coastguard Worker  logcat_original_time = extract_time(logcat_events, TIME_LOGCAT, str);
365*288bf522SAndroid Build Coastguard Worker  dmesg_event_time = extract_time(dmesg_events, TIME_DMESG, float);
366*288bf522SAndroid Build Coastguard Worker  boottime_events = fetch_boottime_property()
367*288bf522SAndroid Build Coastguard Worker  events = {}
368*288bf522SAndroid Build Coastguard Worker  events_to_correct = []
369*288bf522SAndroid Build Coastguard Worker  replaced_from_dmesg = set()
370*288bf522SAndroid Build Coastguard Worker
371*288bf522SAndroid Build Coastguard Worker  time_correction_delta = 0
372*288bf522SAndroid Build Coastguard Worker  time_correction_time = 0
373*288bf522SAndroid Build Coastguard Worker  if ('time_correction_key' in cfg
374*288bf522SAndroid Build Coastguard Worker      and cfg['time_correction_key'] in logcat_events):
375*288bf522SAndroid Build Coastguard Worker    match = search_events_pattern[cfg['time_correction_key']].search(
376*288bf522SAndroid Build Coastguard Worker      logcat_events[cfg['time_correction_key']])
377*288bf522SAndroid Build Coastguard Worker    if match and logcat_event_time[cfg['time_correction_key']]:
378*288bf522SAndroid Build Coastguard Worker      time_correction_delta = float(match.group(1))
379*288bf522SAndroid Build Coastguard Worker      time_correction_time = logcat_event_time[cfg['time_correction_key']]
380*288bf522SAndroid Build Coastguard Worker
381*288bf522SAndroid Build Coastguard Worker  debug("time_correction_delta = {0}, time_correction_time = {1}".format(
382*288bf522SAndroid Build Coastguard Worker    time_correction_delta, time_correction_time))
383*288bf522SAndroid Build Coastguard Worker
384*288bf522SAndroid Build Coastguard Worker  for k, v in logcat_event_time.items():
385*288bf522SAndroid Build Coastguard Worker    if v <= time_correction_time:
386*288bf522SAndroid Build Coastguard Worker      logcat_event_time[k] += time_correction_delta
387*288bf522SAndroid Build Coastguard Worker      v = v + time_correction_delta
388*288bf522SAndroid Build Coastguard Worker      debug("correcting event to event[{0}, {1}]".format(k, v))
389*288bf522SAndroid Build Coastguard Worker
390*288bf522SAndroid Build Coastguard Worker  diffs = []
391*288bf522SAndroid Build Coastguard Worker  if logcat_event_time.get(KERNEL_TIME_KEY) is None:
392*288bf522SAndroid Build Coastguard Worker    print("kernel time not captured in logcat")
393*288bf522SAndroid Build Coastguard Worker  else:
394*288bf522SAndroid Build Coastguard Worker    diffs.append((logcat_event_time[KERNEL_TIME_KEY], logcat_event_time[KERNEL_TIME_KEY]))
395*288bf522SAndroid Build Coastguard Worker
396*288bf522SAndroid Build Coastguard Worker  if logcat_event_time.get(BOOT_ANIM_END_TIME_KEY) and dmesg_event_time.get(BOOT_ANIM_END_TIME_KEY):
397*288bf522SAndroid Build Coastguard Worker    diffs.append((logcat_event_time[BOOT_ANIM_END_TIME_KEY],\
398*288bf522SAndroid Build Coastguard Worker                    logcat_event_time[BOOT_ANIM_END_TIME_KEY] -\
399*288bf522SAndroid Build Coastguard Worker                      dmesg_event_time[BOOT_ANIM_END_TIME_KEY]))
400*288bf522SAndroid Build Coastguard Worker  if logcat_event_time.get(LOGCAT_BOOT_COMPLETE) and dmesg_event_time.get(KERNEL_BOOT_COMPLETE):
401*288bf522SAndroid Build Coastguard Worker    diffs.append((
402*288bf522SAndroid Build Coastguard Worker        logcat_event_time[LOGCAT_BOOT_COMPLETE],
403*288bf522SAndroid Build Coastguard Worker        logcat_event_time[LOGCAT_BOOT_COMPLETE] - dmesg_event_time[KERNEL_BOOT_COMPLETE],
404*288bf522SAndroid Build Coastguard Worker    ))
405*288bf522SAndroid Build Coastguard Worker  elif logcat_event_time.get(ANDROID_INIT_SECOND_STAGE) and \
406*288bf522SAndroid Build Coastguard Worker      dmesg_event_time.get(ANDROID_INIT_SECOND_STAGE):
407*288bf522SAndroid Build Coastguard Worker    print("BootAnimEnd time or BootComplete-kernel not captured in both log" +\
408*288bf522SAndroid Build Coastguard Worker      ", use Android init 2nd stage get time diff")
409*288bf522SAndroid Build Coastguard Worker    diffs.append((
410*288bf522SAndroid Build Coastguard Worker      logcat_event_time[ANDROID_INIT_SECOND_STAGE],
411*288bf522SAndroid Build Coastguard Worker      logcat_event_time[ANDROID_INIT_SECOND_STAGE] - dmesg_event_time[ANDROID_INIT_SECOND_STAGE],
412*288bf522SAndroid Build Coastguard Worker    ))
413*288bf522SAndroid Build Coastguard Worker  else:
414*288bf522SAndroid Build Coastguard Worker    print("BootComplete and Android init 2nd stage not captured in both log" +\
415*288bf522SAndroid Build Coastguard Worker          ", cannot get time diff")
416*288bf522SAndroid Build Coastguard Worker    print('dmesg {} logcat {}'.format(dmesg_event_time, logcat_event_time))
417*288bf522SAndroid Build Coastguard Worker    return None, None, None, None, None, None
418*288bf522SAndroid Build Coastguard Worker
419*288bf522SAndroid Build Coastguard Worker  for k, v in logcat_event_time.items():
420*288bf522SAndroid Build Coastguard Worker    debug("event[{0}, {1}]".format(k, v))
421*288bf522SAndroid Build Coastguard Worker    events[k] = v
422*288bf522SAndroid Build Coastguard Worker    if k in dmesg_event_time:
423*288bf522SAndroid Build Coastguard Worker      debug("{0} is in dmesg".format(k))
424*288bf522SAndroid Build Coastguard Worker      events[k] = dmesg_event_time[k]
425*288bf522SAndroid Build Coastguard Worker      replaced_from_dmesg.add(k)
426*288bf522SAndroid Build Coastguard Worker    else:
427*288bf522SAndroid Build Coastguard Worker      events_to_correct.append(k)
428*288bf522SAndroid Build Coastguard Worker
429*288bf522SAndroid Build Coastguard Worker  diff_prev = diffs[0]
430*288bf522SAndroid Build Coastguard Worker  for k in events_to_correct:
431*288bf522SAndroid Build Coastguard Worker    diff = diffs[0]
432*288bf522SAndroid Build Coastguard Worker    while diff[0] < events[k] and len(diffs) > 1:
433*288bf522SAndroid Build Coastguard Worker      diffs.pop(0)
434*288bf522SAndroid Build Coastguard Worker      diff_prev = diff
435*288bf522SAndroid Build Coastguard Worker      diff = diffs[0]
436*288bf522SAndroid Build Coastguard Worker    events[k] = events[k] - diff[1]
437*288bf522SAndroid Build Coastguard Worker    if events[k] < 0.0:
438*288bf522SAndroid Build Coastguard Worker      if events[k] < -0.1: # maybe previous one is better fit
439*288bf522SAndroid Build Coastguard Worker        events[k] = events[k] + diff[1] - diff_prev[1]
440*288bf522SAndroid Build Coastguard Worker      else:
441*288bf522SAndroid Build Coastguard Worker        events[k] = 0.0
442*288bf522SAndroid Build Coastguard Worker
443*288bf522SAndroid Build Coastguard Worker  data_points = collections.OrderedDict()
444*288bf522SAndroid Build Coastguard Worker
445*288bf522SAndroid Build Coastguard Worker  print("-----------------")
446*288bf522SAndroid Build Coastguard Worker  print("ro.boottime.*: time")
447*288bf522SAndroid Build Coastguard Worker  for item in list(boottime_events.items()):
448*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7.5} {2}'.format(item[0], item[1],\
449*288bf522SAndroid Build Coastguard Worker      "*time taken" if item[0].startswith("init.") else ""))
450*288bf522SAndroid Build Coastguard Worker  print("-----------------")
451*288bf522SAndroid Build Coastguard Worker
452*288bf522SAndroid Build Coastguard Worker  if args.timings:
453*288bf522SAndroid Build Coastguard Worker    kernel_timing_points, _ = generate_timing_points(kernel_timing_events, timings_pattern)
454*288bf522SAndroid Build Coastguard Worker    logcat_timing_points, logcat_monitor_contentions =\
455*288bf522SAndroid Build Coastguard Worker      generate_timing_points(logcat_timing_events, timings_pattern)
456*288bf522SAndroid Build Coastguard Worker    dump_timing_points("Kernel", kernel_timing_points)
457*288bf522SAndroid Build Coastguard Worker    dump_timing_points("Logcat", logcat_timing_points)
458*288bf522SAndroid Build Coastguard Worker    dump_monitor_contentions(logcat_monitor_contentions)
459*288bf522SAndroid Build Coastguard Worker
460*288bf522SAndroid Build Coastguard Worker  for item in sorted(list(events.items()), key=operator.itemgetter(1)):
461*288bf522SAndroid Build Coastguard Worker    data_points[item[0]] = {
462*288bf522SAndroid Build Coastguard Worker      'value': item[1],
463*288bf522SAndroid Build Coastguard Worker      'from_dmesg': item[0] in replaced_from_dmesg,
464*288bf522SAndroid Build Coastguard Worker      'logcat_value': logcat_original_time[item[0]]
465*288bf522SAndroid Build Coastguard Worker    }
466*288bf522SAndroid Build Coastguard Worker  # add times with bootloader
467*288bf522SAndroid Build Coastguard Worker  if events.get("BootComplete") and boottime_events.get("bootloader"):
468*288bf522SAndroid Build Coastguard Worker    total = events["BootComplete"] + boottime_events["bootloader"]
469*288bf522SAndroid Build Coastguard Worker    data_points["*BootComplete+Bootloader"] = {
470*288bf522SAndroid Build Coastguard Worker      'value': total,
471*288bf522SAndroid Build Coastguard Worker      'from_dmesg': False,
472*288bf522SAndroid Build Coastguard Worker      'logcat_value': 0.0
473*288bf522SAndroid Build Coastguard Worker    }
474*288bf522SAndroid Build Coastguard Worker  if events.get("LauncherStart") and boottime_events.get("bootloader"):
475*288bf522SAndroid Build Coastguard Worker    total = events["LauncherStart"] + boottime_events["bootloader"]
476*288bf522SAndroid Build Coastguard Worker    data_points["*LauncherStart+Bootloader"] = {
477*288bf522SAndroid Build Coastguard Worker      'value': total,
478*288bf522SAndroid Build Coastguard Worker      'from_dmesg': False,
479*288bf522SAndroid Build Coastguard Worker      'logcat_value': 0.0
480*288bf522SAndroid Build Coastguard Worker    }
481*288bf522SAndroid Build Coastguard Worker  for k, v in data_points.items():
482*288bf522SAndroid Build Coastguard Worker    print('{0:30}: {1:<7.5} {2:1} ({3})'.format(
483*288bf522SAndroid Build Coastguard Worker      k, v['value'], '*' if v['from_dmesg'] else '', v['logcat_value']))
484*288bf522SAndroid Build Coastguard Worker
485*288bf522SAndroid Build Coastguard Worker  print('\n* - event time was obtained from dmesg log\n')
486*288bf522SAndroid Build Coastguard Worker
487*288bf522SAndroid Build Coastguard Worker  if events[LOGCAT_BOOT_COMPLETE] > error_time and not args.ignore:
488*288bf522SAndroid Build Coastguard Worker    capture_bugreport("bootuptoolong", events[LOGCAT_BOOT_COMPLETE])
489*288bf522SAndroid Build Coastguard Worker
490*288bf522SAndroid Build Coastguard Worker  for k, v in components_to_monitor.items():
491*288bf522SAndroid Build Coastguard Worker    logcat_value_measured = logcat_timing_points.get(k)
492*288bf522SAndroid Build Coastguard Worker    kernel_value_measured = kernel_timing_points.get(k)
493*288bf522SAndroid Build Coastguard Worker    data_from_data_points = data_points.get(k)
494*288bf522SAndroid Build Coastguard Worker    if logcat_value_measured and logcat_value_measured > v:
495*288bf522SAndroid Build Coastguard Worker      capture_bugreport(k + "-" + str(logcat_value_measured), events[LOGCAT_BOOT_COMPLETE])
496*288bf522SAndroid Build Coastguard Worker      break
497*288bf522SAndroid Build Coastguard Worker    elif kernel_value_measured and kernel_value_measured > v:
498*288bf522SAndroid Build Coastguard Worker      capture_bugreport(k + "-" + str(kernel_value_measured), events[LOGCAT_BOOT_COMPLETE])
499*288bf522SAndroid Build Coastguard Worker      break
500*288bf522SAndroid Build Coastguard Worker    elif data_from_data_points and data_from_data_points['value'] * 1000.0 > v:
501*288bf522SAndroid Build Coastguard Worker      capture_bugreport(k + "-" + str(data_from_data_points['value']), events[LOGCAT_BOOT_COMPLETE])
502*288bf522SAndroid Build Coastguard Worker      break
503*288bf522SAndroid Build Coastguard Worker
504*288bf522SAndroid Build Coastguard Worker  if args.fs_check:
505*288bf522SAndroid Build Coastguard Worker    fs_stat = None
506*288bf522SAndroid Build Coastguard Worker    if logcat_events.get("FsStat"):
507*288bf522SAndroid Build Coastguard Worker      fs_stat_pattern = cfg["events"]["FsStat"]
508*288bf522SAndroid Build Coastguard Worker      m = re.search(fs_stat_pattern, logcat_events.get("FsStat"))
509*288bf522SAndroid Build Coastguard Worker      if m:
510*288bf522SAndroid Build Coastguard Worker        fs_stat = m.group(1)
511*288bf522SAndroid Build Coastguard Worker    print('fs_stat:', fs_stat)
512*288bf522SAndroid Build Coastguard Worker
513*288bf522SAndroid Build Coastguard Worker    if fs_stat:
514*288bf522SAndroid Build Coastguard Worker      fs_stat_val = int(fs_stat, 0)
515*288bf522SAndroid Build Coastguard Worker      if (fs_stat_val & ~0x17) != 0:
516*288bf522SAndroid Build Coastguard Worker        capture_bugreport("fs_stat_" + fs_stat, events[LOGCAT_BOOT_COMPLETE])
517*288bf522SAndroid Build Coastguard Worker
518*288bf522SAndroid Build Coastguard Worker  return data_points, kernel_timing_points, logcat_timing_points, boottime_events, shutdown_events,\
519*288bf522SAndroid Build Coastguard Worker      shutdown_timing_events
520*288bf522SAndroid Build Coastguard Worker
521*288bf522SAndroid Build Coastguard Workerdef debug(string):
522*288bf522SAndroid Build Coastguard Worker  if DEBUG:
523*288bf522SAndroid Build Coastguard Worker    print(string)
524*288bf522SAndroid Build Coastguard Worker
525*288bf522SAndroid Build Coastguard Workerdef extract_timing(s, patterns):
526*288bf522SAndroid Build Coastguard Worker  for _, p in patterns.items():
527*288bf522SAndroid Build Coastguard Worker    m = p.search(s)
528*288bf522SAndroid Build Coastguard Worker    if m:
529*288bf522SAndroid Build Coastguard Worker      timing_dict = m.groupdict()
530*288bf522SAndroid Build Coastguard Worker      return timing_dict['name'], float(timing_dict['time'])
531*288bf522SAndroid Build Coastguard Worker  return None, None
532*288bf522SAndroid Build Coastguard Worker
533*288bf522SAndroid Build Coastguard Workerdef init_arguments():
534*288bf522SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description='Measures boot time.')
535*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-r', '--reboot', dest='reboot',
536*288bf522SAndroid Build Coastguard Worker                      action='store_true',
537*288bf522SAndroid Build Coastguard Worker                      help='reboot device for measurement', )
538*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-o', '--output', dest='output', type=str,
539*288bf522SAndroid Build Coastguard Worker                      help='Output directory where results are stored')
540*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-c', '--config', dest='config',
541*288bf522SAndroid Build Coastguard Worker                      default='config.yaml', type=argparse.FileType('r'),
542*288bf522SAndroid Build Coastguard Worker                      help='config file for the tool', )
543*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-s', '--stressfs', dest='stressfs',
544*288bf522SAndroid Build Coastguard Worker                      default='', type=str,
545*288bf522SAndroid Build Coastguard Worker                      help='APK file for the stressfs tool used to write to the data partition ' +\
546*288bf522SAndroid Build Coastguard Worker                           'during shutdown')
547*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-n', '--iterate', dest='iterate', type=int, default=1,
548*288bf522SAndroid Build Coastguard Worker                      help='number of time to repeat the measurement', )
549*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-g', '--ignore', dest='ignore', action='store_true',
550*288bf522SAndroid Build Coastguard Worker                      help='ignore too big values error', )
551*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-t', '--timings', dest='timings', action='store_true',
552*288bf522SAndroid Build Coastguard Worker                      help='print individual component times', default=True, )
553*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-p', '--serial', dest='serial', action='store',
554*288bf522SAndroid Build Coastguard Worker                      help='android device serial number')
555*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-e', '--errortime', dest='errortime', action='store',
556*288bf522SAndroid Build Coastguard Worker                      help='handle bootup time bigger than this as error')
557*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-w', '--maxwaittime', dest='maxwaittime', action='store',
558*288bf522SAndroid Build Coastguard Worker                      help='wait for up to this time to collect logs. Retry after this time.' +\
559*288bf522SAndroid Build Coastguard Worker                           ' Default is 200 sec.')
560*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-f', '--fs_check', dest='fs_check',
561*288bf522SAndroid Build Coastguard Worker                      action='store_true',
562*288bf522SAndroid Build Coastguard Worker                      help='check fs_stat after reboot', )
563*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-a', '--adb_reboot', dest='adb_reboot',
564*288bf522SAndroid Build Coastguard Worker                      action='store_true',
565*288bf522SAndroid Build Coastguard Worker                      help='reboot with adb reboot', )
566*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-v', '--permissive', dest='permissive',
567*288bf522SAndroid Build Coastguard Worker                      action='store_true',
568*288bf522SAndroid Build Coastguard Worker                      help='set selinux into permissive before reboot', )
569*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-m', '--componentmonitor', dest='componentmonitor', action='store',
570*288bf522SAndroid Build Coastguard Worker                      help='capture bugreport if specified timing component is taking more than ' +\
571*288bf522SAndroid Build Coastguard Worker                           'certain time. Unlike errortime, the result will not be rejected in' +\
572*288bf522SAndroid Build Coastguard Worker                           'averaging. Format is key1=time1,key2=time2...')
573*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-b', '--bootchart', dest='bootchart',
574*288bf522SAndroid Build Coastguard Worker                      action='store_true',
575*288bf522SAndroid Build Coastguard Worker                      help='collect bootchart from the device.', )
576*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-y', '--systrace', dest='systrace',
577*288bf522SAndroid Build Coastguard Worker                      action='store_true',
578*288bf522SAndroid Build Coastguard Worker                      help='collect systrace from the device. kernel trace should be already enabled', )
579*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-W', '--carwatchdog', dest='carwatchdog', action='store_true',
580*288bf522SAndroid Build Coastguard Worker                      help='collect carwatchdog boot stats')
581*288bf522SAndroid Build Coastguard Worker  parser.add_argument('-G', '--buffersize', dest='buffersize', action='store', type=str,
582*288bf522SAndroid Build Coastguard Worker                      default=None,
583*288bf522SAndroid Build Coastguard Worker                      help='set logcat buffersize')
584*288bf522SAndroid Build Coastguard Worker  return parser.parse_args()
585*288bf522SAndroid Build Coastguard Worker
586*288bf522SAndroid Build Coastguard Workerdef handle_zygote_event(zygote_pids, events, event, line):
587*288bf522SAndroid Build Coastguard Worker  words = line.split()
588*288bf522SAndroid Build Coastguard Worker  if len(words) > 1:
589*288bf522SAndroid Build Coastguard Worker    pid = int(words[1])
590*288bf522SAndroid Build Coastguard Worker    if len(zygote_pids) == 2:
591*288bf522SAndroid Build Coastguard Worker      if pid == zygote_pids[1]: # secondary
592*288bf522SAndroid Build Coastguard Worker        event = event + "-secondary"
593*288bf522SAndroid Build Coastguard Worker    elif len(zygote_pids) == 1:
594*288bf522SAndroid Build Coastguard Worker      if zygote_pids[0] != pid: # new pid, need to decide if old ones were secondary
595*288bf522SAndroid Build Coastguard Worker        primary_pid = min(pid, zygote_pids[0])
596*288bf522SAndroid Build Coastguard Worker        secondary_pid = max(pid, zygote_pids[0])
597*288bf522SAndroid Build Coastguard Worker        zygote_pids.pop()
598*288bf522SAndroid Build Coastguard Worker        zygote_pids.append(primary_pid)
599*288bf522SAndroid Build Coastguard Worker        zygote_pids.append(secondary_pid)
600*288bf522SAndroid Build Coastguard Worker        if pid == primary_pid: # old one was secondary:
601*288bf522SAndroid Build Coastguard Worker          move_to_secondary = []
602*288bf522SAndroid Build Coastguard Worker          for k, l in events.items():
603*288bf522SAndroid Build Coastguard Worker            if k.startswith("zygote"):
604*288bf522SAndroid Build Coastguard Worker              move_to_secondary.append((k, l))
605*288bf522SAndroid Build Coastguard Worker          for item in move_to_secondary:
606*288bf522SAndroid Build Coastguard Worker            del events[item[0]]
607*288bf522SAndroid Build Coastguard Worker            if item[0].endswith("-secondary"):
608*288bf522SAndroid Build Coastguard Worker              print("Secondary already exists for event %s  while found new pid %d, primary %d "\
609*288bf522SAndroid Build Coastguard Worker                % (item[0], secondary_pid, primary_pid))
610*288bf522SAndroid Build Coastguard Worker            else:
611*288bf522SAndroid Build Coastguard Worker              events[item[0] + "-secondary"] = item[1]
612*288bf522SAndroid Build Coastguard Worker        else:
613*288bf522SAndroid Build Coastguard Worker          event = event + "-secondary"
614*288bf522SAndroid Build Coastguard Worker    else:
615*288bf522SAndroid Build Coastguard Worker      zygote_pids.append(pid)
616*288bf522SAndroid Build Coastguard Worker  events[event] = line
617*288bf522SAndroid Build Coastguard Worker
618*288bf522SAndroid Build Coastguard Workerdef update_name_if_already_exist(events, name):
619*288bf522SAndroid Build Coastguard Worker  existing_event = events.get(name)
620*288bf522SAndroid Build Coastguard Worker  i = 0
621*288bf522SAndroid Build Coastguard Worker  new_name = name
622*288bf522SAndroid Build Coastguard Worker  while existing_event:
623*288bf522SAndroid Build Coastguard Worker    i += 1
624*288bf522SAndroid Build Coastguard Worker    new_name = name + "_" + str(i)
625*288bf522SAndroid Build Coastguard Worker    existing_event = events.get(new_name)
626*288bf522SAndroid Build Coastguard Worker  return new_name
627*288bf522SAndroid Build Coastguard Worker
628*288bf522SAndroid Build Coastguard Workerdef collect_logcat_for_shutdown(capture_log_on_error, shutdown_events_pattern,\
629*288bf522SAndroid Build Coastguard Worker    log_capture_conditions):
630*288bf522SAndroid Build Coastguard Worker  events = collections.OrderedDict()
631*288bf522SAndroid Build Coastguard Worker  # shutdown does not have timing_events but calculated from checking Xyz - XyzDone / XyzTimeout
632*288bf522SAndroid Build Coastguard Worker  timing_events = collections.OrderedDict()
633*288bf522SAndroid Build Coastguard Worker  process = subprocess.Popen(ADB_CMD + ' logcat -b all -v epoch', shell=True,
634*288bf522SAndroid Build Coastguard Worker                             stdout=subprocess.PIPE)
635*288bf522SAndroid Build Coastguard Worker  lines = []
636*288bf522SAndroid Build Coastguard Worker  capture_log = False
637*288bf522SAndroid Build Coastguard Worker  shutdown_start_time = 0
638*288bf522SAndroid Build Coastguard Worker  while True:
639*288bf522SAndroid Build Coastguard Worker    line = process.stdout.readline()
640*288bf522SAndroid Build Coastguard Worker    if not line:
641*288bf522SAndroid Build Coastguard Worker      break
642*288bf522SAndroid Build Coastguard Worker    line = line.decode('utf-8', 'ignore').lstrip().rstrip()
643*288bf522SAndroid Build Coastguard Worker    lines.append(line)
644*288bf522SAndroid Build Coastguard Worker    event = get_boot_event(line, shutdown_events_pattern)
645*288bf522SAndroid Build Coastguard Worker    if not event:
646*288bf522SAndroid Build Coastguard Worker      continue
647*288bf522SAndroid Build Coastguard Worker    time = extract_a_time(line, TIME_LOGCAT, float)
648*288bf522SAndroid Build Coastguard Worker    if time is None:
649*288bf522SAndroid Build Coastguard Worker      print("cannot get time from: " + line)
650*288bf522SAndroid Build Coastguard Worker      continue
651*288bf522SAndroid Build Coastguard Worker    if shutdown_start_time == 0:
652*288bf522SAndroid Build Coastguard Worker      shutdown_start_time = time
653*288bf522SAndroid Build Coastguard Worker    time = time - shutdown_start_time
654*288bf522SAndroid Build Coastguard Worker    events[event] = time
655*288bf522SAndroid Build Coastguard Worker    time_limit1 = log_capture_conditions.get(event)
656*288bf522SAndroid Build Coastguard Worker    if time_limit1 and time_limit1 <= time:
657*288bf522SAndroid Build Coastguard Worker      capture_log = True
658*288bf522SAndroid Build Coastguard Worker    pair_event = None
659*288bf522SAndroid Build Coastguard Worker    if event.endswith('Done'):
660*288bf522SAndroid Build Coastguard Worker      pair_event = event[:-4]
661*288bf522SAndroid Build Coastguard Worker    elif event.endswith('Timeout'):
662*288bf522SAndroid Build Coastguard Worker      pair_event = event[:-7]
663*288bf522SAndroid Build Coastguard Worker      if capture_log_on_error:
664*288bf522SAndroid Build Coastguard Worker        capture_log = True
665*288bf522SAndroid Build Coastguard Worker    if not pair_event:
666*288bf522SAndroid Build Coastguard Worker      continue
667*288bf522SAndroid Build Coastguard Worker    start_time = events.get(pair_event)
668*288bf522SAndroid Build Coastguard Worker    if not start_time:
669*288bf522SAndroid Build Coastguard Worker      print("No start event for " + event)
670*288bf522SAndroid Build Coastguard Worker      continue
671*288bf522SAndroid Build Coastguard Worker    time_spent = time - start_time
672*288bf522SAndroid Build Coastguard Worker    timing_event_name = pair_event + "Duration"
673*288bf522SAndroid Build Coastguard Worker    timing_events[timing_event_name] = time_spent
674*288bf522SAndroid Build Coastguard Worker    time_limit2 = log_capture_conditions.get(timing_event_name)
675*288bf522SAndroid Build Coastguard Worker    if time_limit2 and time_limit2 <= time_spent:
676*288bf522SAndroid Build Coastguard Worker      capture_log = True
677*288bf522SAndroid Build Coastguard Worker
678*288bf522SAndroid Build Coastguard Worker  if capture_log:
679*288bf522SAndroid Build Coastguard Worker    now = datetime.now()
680*288bf522SAndroid Build Coastguard Worker    log_file = ("shutdownlog-error-%s.txt") % (now.strftime("%Y-%m-%d-%H-%M-%S"))
681*288bf522SAndroid Build Coastguard Worker    print("Shutdown error, capture log to %s" % (log_file))
682*288bf522SAndroid Build Coastguard Worker    with open(log_file, 'w') as f:
683*288bf522SAndroid Build Coastguard Worker      f.write('\n'.join(lines))
684*288bf522SAndroid Build Coastguard Worker  return events, timing_events
685*288bf522SAndroid Build Coastguard Worker
686*288bf522SAndroid Build Coastguard Workerdef log_timeout(time_left, stop_events, events, timing_events):
687*288bf522SAndroid Build Coastguard Worker  print("timeout waiting for event, continue", time_left)
688*288bf522SAndroid Build Coastguard Worker  print(" remaininig events {}, event {} timing events {}".\
689*288bf522SAndroid Build Coastguard Worker    format(stop_events, events, timing_events))
690*288bf522SAndroid Build Coastguard Worker
691*288bf522SAndroid Build Coastguard Workerdef collect_events(search_events, command, timings, stop_events,
692*288bf522SAndroid Build Coastguard Worker                   collects_all_events, disable_timing_after_zygote):
693*288bf522SAndroid Build Coastguard Worker  events = collections.OrderedDict()
694*288bf522SAndroid Build Coastguard Worker  timing_events = {}
695*288bf522SAndroid Build Coastguard Worker
696*288bf522SAndroid Build Coastguard Worker  data_available = stop_events is None
697*288bf522SAndroid Build Coastguard Worker  zygote_pids = []
698*288bf522SAndroid Build Coastguard Worker  start_time = time.time()
699*288bf522SAndroid Build Coastguard Worker  zygote_found = False
700*288bf522SAndroid Build Coastguard Worker  line = None
701*288bf522SAndroid Build Coastguard Worker  if collects_all_events:
702*288bf522SAndroid Build Coastguard Worker    print("remaining stop_events:", stop_events)
703*288bf522SAndroid Build Coastguard Worker  else:
704*288bf522SAndroid Build Coastguard Worker    print("waiting for any of stop_events:", stop_events)
705*288bf522SAndroid Build Coastguard Worker  init = True
706*288bf522SAndroid Build Coastguard Worker  while True:
707*288bf522SAndroid Build Coastguard Worker    if init:
708*288bf522SAndroid Build Coastguard Worker      process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
709*288bf522SAndroid Build Coastguard Worker      read_poll = select.poll()
710*288bf522SAndroid Build Coastguard Worker      read_poll.register(process.stdout, select.POLLIN)
711*288bf522SAndroid Build Coastguard Worker      init = False
712*288bf522SAndroid Build Coastguard Worker    if len(stop_events) == 0:
713*288bf522SAndroid Build Coastguard Worker      break
714*288bf522SAndroid Build Coastguard Worker    time_left = start_time + max_wait_time - time.time()
715*288bf522SAndroid Build Coastguard Worker    if time_left <= 0:
716*288bf522SAndroid Build Coastguard Worker      log_timeout(time_left, stop_events, events, timing_events)
717*288bf522SAndroid Build Coastguard Worker      break
718*288bf522SAndroid Build Coastguard Worker    polled_events = read_poll.poll(time_left * 1000.0)
719*288bf522SAndroid Build Coastguard Worker    # adb logcat subprocess is auto-terminated when the adb connection is lost.
720*288bf522SAndroid Build Coastguard Worker    # Thus, check for the subprocess return code and reconnect to the device if
721*288bf522SAndroid Build Coastguard Worker    # needed. Otherwise, the logcat events cannot be polled completely.
722*288bf522SAndroid Build Coastguard Worker    if process.poll() is not None:
723*288bf522SAndroid Build Coastguard Worker      print("adb might be disconnected?\nRetrying to connect.")
724*288bf522SAndroid Build Coastguard Worker      run_adb_cmd('wait-for-device')
725*288bf522SAndroid Build Coastguard Worker      print(" reconnected")
726*288bf522SAndroid Build Coastguard Worker      init = True
727*288bf522SAndroid Build Coastguard Worker      continue
728*288bf522SAndroid Build Coastguard Worker    if len(polled_events) == 0:
729*288bf522SAndroid Build Coastguard Worker      log_timeout(time_left, stop_events, events, timing_events)
730*288bf522SAndroid Build Coastguard Worker      break
731*288bf522SAndroid Build Coastguard Worker    for polled_event in polled_events:
732*288bf522SAndroid Build Coastguard Worker      if polled_event[1] == select.POLLIN:
733*288bf522SAndroid Build Coastguard Worker        line = process.stdout.readline().decode('utf-8', 'ignore')
734*288bf522SAndroid Build Coastguard Worker      else:
735*288bf522SAndroid Build Coastguard Worker        if polled_event[1] == select.POLLHUP:
736*288bf522SAndroid Build Coastguard Worker          if len(stop_events) == 0:
737*288bf522SAndroid Build Coastguard Worker            break;
738*288bf522SAndroid Build Coastguard Worker        # adb connection lost
739*288bf522SAndroid Build Coastguard Worker        print("poll error waiting for event, adb lost?")
740*288bf522SAndroid Build Coastguard Worker        if time_left > 0:
741*288bf522SAndroid Build Coastguard Worker          print("retry adb")
742*288bf522SAndroid Build Coastguard Worker          run_adb_cmd('wait-for-device')
743*288bf522SAndroid Build Coastguard Worker          print(" reconnected")
744*288bf522SAndroid Build Coastguard Worker          init = True
745*288bf522SAndroid Build Coastguard Worker          continue
746*288bf522SAndroid Build Coastguard Worker        else:
747*288bf522SAndroid Build Coastguard Worker          break
748*288bf522SAndroid Build Coastguard Worker      if not data_available:
749*288bf522SAndroid Build Coastguard Worker        print("Collecting data samples from '%s'. Please wait...\n" % command)
750*288bf522SAndroid Build Coastguard Worker        data_available = True
751*288bf522SAndroid Build Coastguard Worker      event = get_boot_event(line, search_events)
752*288bf522SAndroid Build Coastguard Worker      if event:
753*288bf522SAndroid Build Coastguard Worker        debug("event[{0}] captured: {1}".format(event, line))
754*288bf522SAndroid Build Coastguard Worker        if event == "starting_zygote":
755*288bf522SAndroid Build Coastguard Worker          events[event] = line
756*288bf522SAndroid Build Coastguard Worker          zygote_found = True
757*288bf522SAndroid Build Coastguard Worker        elif event.startswith("zygote"):
758*288bf522SAndroid Build Coastguard Worker          handle_zygote_event(zygote_pids, events, event, line)
759*288bf522SAndroid Build Coastguard Worker        else:
760*288bf522SAndroid Build Coastguard Worker          new_event = update_name_if_already_exist(events, event)
761*288bf522SAndroid Build Coastguard Worker          events[new_event] = line
762*288bf522SAndroid Build Coastguard Worker        if event in stop_events:
763*288bf522SAndroid Build Coastguard Worker          if collects_all_events:
764*288bf522SAndroid Build Coastguard Worker            stop_events.remove(event)
765*288bf522SAndroid Build Coastguard Worker            print("remaining stop_events:", stop_events)
766*288bf522SAndroid Build Coastguard Worker          else:
767*288bf522SAndroid Build Coastguard Worker            # no need to wait for others
768*288bf522SAndroid Build Coastguard Worker            stop_events = []
769*288bf522SAndroid Build Coastguard Worker
770*288bf522SAndroid Build Coastguard Worker      timing_event = get_boot_event(line, timings)
771*288bf522SAndroid Build Coastguard Worker      if timing_event and (not disable_timing_after_zygote or not zygote_found):
772*288bf522SAndroid Build Coastguard Worker        if timing_event not in timing_events:
773*288bf522SAndroid Build Coastguard Worker          timing_events[timing_event] = []
774*288bf522SAndroid Build Coastguard Worker        timing_events[timing_event].append(line)
775*288bf522SAndroid Build Coastguard Worker        debug("timing_event[{0}] captured: {1}".format(timing_event, line))
776*288bf522SAndroid Build Coastguard Worker
777*288bf522SAndroid Build Coastguard Worker  process.terminate()
778*288bf522SAndroid Build Coastguard Worker  return events, timing_events
779*288bf522SAndroid Build Coastguard Worker
780*288bf522SAndroid Build Coastguard Workerdef fetch_boottime_property():
781*288bf522SAndroid Build Coastguard Worker  cmd = ADB_CMD + ' shell su root getprop'
782*288bf522SAndroid Build Coastguard Worker  events = {}
783*288bf522SAndroid Build Coastguard Worker  process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
784*288bf522SAndroid Build Coastguard Worker  out = process.stdout
785*288bf522SAndroid Build Coastguard Worker  pattern = re.compile(BOOT_PROP)
786*288bf522SAndroid Build Coastguard Worker  pattern_bootloader = re.compile(BOOTLOADER_TIME_PROP)
787*288bf522SAndroid Build Coastguard Worker  bootloader_time = 0.0
788*288bf522SAndroid Build Coastguard Worker  for line in out:
789*288bf522SAndroid Build Coastguard Worker    line = line.decode('utf-8', 'ignore')
790*288bf522SAndroid Build Coastguard Worker    match = pattern.match(line)
791*288bf522SAndroid Build Coastguard Worker    if match:
792*288bf522SAndroid Build Coastguard Worker      if match.group(1).startswith("init."):
793*288bf522SAndroid Build Coastguard Worker        events[match.group(1)] = float(match.group(2)) / 1000.0 #ms to s
794*288bf522SAndroid Build Coastguard Worker      else:
795*288bf522SAndroid Build Coastguard Worker        events[match.group(1)] = float(match.group(2)) / 1000000000.0 #ns to s
796*288bf522SAndroid Build Coastguard Worker    match = pattern_bootloader.match(line)
797*288bf522SAndroid Build Coastguard Worker    if match:
798*288bf522SAndroid Build Coastguard Worker      items = match.group(1).split(",")
799*288bf522SAndroid Build Coastguard Worker      for item in items:
800*288bf522SAndroid Build Coastguard Worker        entry_pair = item.split(":")
801*288bf522SAndroid Build Coastguard Worker        entry_name = entry_pair[0]
802*288bf522SAndroid Build Coastguard Worker        time_spent = float(entry_pair[1]) / 1000 #ms to s
803*288bf522SAndroid Build Coastguard Worker        if entry_name != "SW":
804*288bf522SAndroid Build Coastguard Worker          bootloader_time = bootloader_time + time_spent
805*288bf522SAndroid Build Coastguard Worker  ordered_event = collections.OrderedDict()
806*288bf522SAndroid Build Coastguard Worker  if bootloader_time != 0.0:
807*288bf522SAndroid Build Coastguard Worker    ordered_event["bootloader"] = bootloader_time
808*288bf522SAndroid Build Coastguard Worker  for item in sorted(list(events.items()), key=operator.itemgetter(1)):
809*288bf522SAndroid Build Coastguard Worker    ordered_event[item[0]] = item[1]
810*288bf522SAndroid Build Coastguard Worker  return ordered_event
811*288bf522SAndroid Build Coastguard Worker
812*288bf522SAndroid Build Coastguard Worker
813*288bf522SAndroid Build Coastguard Workerdef get_boot_event(line, events):
814*288bf522SAndroid Build Coastguard Worker  for event_key, event_pattern in events.items():
815*288bf522SAndroid Build Coastguard Worker    if event_pattern.search(line):
816*288bf522SAndroid Build Coastguard Worker      return event_key
817*288bf522SAndroid Build Coastguard Worker  return None
818*288bf522SAndroid Build Coastguard Worker
819*288bf522SAndroid Build Coastguard Workerdef extract_a_time(line, pattern, date_transform_function):
820*288bf522SAndroid Build Coastguard Worker    found = re.findall(pattern, line)
821*288bf522SAndroid Build Coastguard Worker    if len(found) > 0:
822*288bf522SAndroid Build Coastguard Worker      return date_transform_function(found[0])
823*288bf522SAndroid Build Coastguard Worker    else:
824*288bf522SAndroid Build Coastguard Worker      return None
825*288bf522SAndroid Build Coastguard Worker
826*288bf522SAndroid Build Coastguard Workerdef extract_time(events, pattern, date_transform_function):
827*288bf522SAndroid Build Coastguard Worker  result = collections.OrderedDict()
828*288bf522SAndroid Build Coastguard Worker  for event, data in events.items():
829*288bf522SAndroid Build Coastguard Worker    time = extract_a_time(data, pattern, date_transform_function)
830*288bf522SAndroid Build Coastguard Worker    if time is not None:
831*288bf522SAndroid Build Coastguard Worker      result[event] = time
832*288bf522SAndroid Build Coastguard Worker    else:
833*288bf522SAndroid Build Coastguard Worker      print("Failed to find time for event: ", event, data)
834*288bf522SAndroid Build Coastguard Worker  return result
835*288bf522SAndroid Build Coastguard Worker
836*288bf522SAndroid Build Coastguard Worker
837*288bf522SAndroid Build Coastguard Workerdef do_reboot(serial, use_adb_reboot):
838*288bf522SAndroid Build Coastguard Worker  # do not update time
839*288bf522SAndroid Build Coastguard Worker  run_adb_cmd('shell settings put global auto_time 0')
840*288bf522SAndroid Build Coastguard Worker  run_adb_cmd('shell settings put global auto_time_zone 0')
841*288bf522SAndroid Build Coastguard Worker  original_devices = subprocess.check_output("adb devices", shell=True).decode('utf-8', 'ignore')
842*288bf522SAndroid Build Coastguard Worker  if use_adb_reboot:
843*288bf522SAndroid Build Coastguard Worker    print('Rebooting the device using adb reboot')
844*288bf522SAndroid Build Coastguard Worker    run_adb_cmd('reboot')
845*288bf522SAndroid Build Coastguard Worker  else:
846*288bf522SAndroid Build Coastguard Worker    print('Rebooting the device using svc power reboot')
847*288bf522SAndroid Build Coastguard Worker    run_adb_shell_cmd_as_root('svc power reboot')
848*288bf522SAndroid Build Coastguard Worker  # Wait for the device to go away
849*288bf522SAndroid Build Coastguard Worker  retry = 0
850*288bf522SAndroid Build Coastguard Worker  while retry < 20:
851*288bf522SAndroid Build Coastguard Worker    current_devices = subprocess.check_output("adb devices", shell=True).decode('utf-8', 'ignore')
852*288bf522SAndroid Build Coastguard Worker    if original_devices != current_devices:
853*288bf522SAndroid Build Coastguard Worker      if not serial or (serial and re.findall(serial + ".*offline", current_devices, re.MULTILINE)):
854*288bf522SAndroid Build Coastguard Worker        return True
855*288bf522SAndroid Build Coastguard Worker    time.sleep(1)
856*288bf522SAndroid Build Coastguard Worker    retry += 1
857*288bf522SAndroid Build Coastguard Worker  return False
858*288bf522SAndroid Build Coastguard Worker
859*288bf522SAndroid Build Coastguard Workerdef reboot(serial, use_stressfs, permissive, use_adb_reboot, adb_buffersize=None):
860*288bf522SAndroid Build Coastguard Worker  if use_stressfs:
861*288bf522SAndroid Build Coastguard Worker    print('Starting write to data partition')
862*288bf522SAndroid Build Coastguard Worker    run_adb_shell_cmd('am start' +\
863*288bf522SAndroid Build Coastguard Worker                      ' -n com.android.car.test.stressfs/.WritingActivity' +\
864*288bf522SAndroid Build Coastguard Worker                      ' -a com.android.car.test.stressfs.START')
865*288bf522SAndroid Build Coastguard Worker    # Give this app some time to start.
866*288bf522SAndroid Build Coastguard Worker    time.sleep(1)
867*288bf522SAndroid Build Coastguard Worker  if permissive:
868*288bf522SAndroid Build Coastguard Worker    run_adb_shell_cmd_as_root('setenforce 0')
869*288bf522SAndroid Build Coastguard Worker
870*288bf522SAndroid Build Coastguard Worker  retry = 0
871*288bf522SAndroid Build Coastguard Worker  while retry < 5:
872*288bf522SAndroid Build Coastguard Worker    if do_reboot(serial, use_adb_reboot):
873*288bf522SAndroid Build Coastguard Worker      break
874*288bf522SAndroid Build Coastguard Worker    retry += 1
875*288bf522SAndroid Build Coastguard Worker
876*288bf522SAndroid Build Coastguard Worker  print('Waiting the device')
877*288bf522SAndroid Build Coastguard Worker  run_adb_cmd('wait-for-device')
878*288bf522SAndroid Build Coastguard Worker  print(' found a device')
879*288bf522SAndroid Build Coastguard Worker
880*288bf522SAndroid Build Coastguard Worker  if adb_buffersize is not None:
881*288bf522SAndroid Build Coastguard Worker    # increase the buffer size
882*288bf522SAndroid Build Coastguard Worker    if run_adb_cmd('logcat -G {}'.format(adb_buffersize)) != 0:
883*288bf522SAndroid Build Coastguard Worker      debug('Fail to set logcat buffer size as {}'.format(adb_buffersize))
884*288bf522SAndroid Build Coastguard Worker
885*288bf522SAndroid Build Coastguard Worker'''
886*288bf522SAndroid Build Coastguard WorkerRuns adb command. If do_return_result is true then output of command is
887*288bf522SAndroid Build Coastguard Workerreturned otherwise an empty string is returned.
888*288bf522SAndroid Build Coastguard Worker'''
889*288bf522SAndroid Build Coastguard Workerdef run_adb_cmd(cmd, do_return_result=False):
890*288bf522SAndroid Build Coastguard Worker  if do_return_result:
891*288bf522SAndroid Build Coastguard Worker    return subprocess.check_output(ADB_CMD + ' ' + cmd, shell=True).decode('utf-8', 'ignore').strip()
892*288bf522SAndroid Build Coastguard Worker  subprocess.call(ADB_CMD + ' ' + cmd, shell=True)
893*288bf522SAndroid Build Coastguard Worker  return ""
894*288bf522SAndroid Build Coastguard Worker
895*288bf522SAndroid Build Coastguard Workerdef run_adb_shell_cmd(cmd, do_return_result=False):
896*288bf522SAndroid Build Coastguard Worker  return run_adb_cmd('shell ' + cmd, do_return_result)
897*288bf522SAndroid Build Coastguard Worker
898*288bf522SAndroid Build Coastguard Workerdef run_adb_shell_cmd_as_root(cmd, do_return_result=False):
899*288bf522SAndroid Build Coastguard Worker  return run_adb_shell_cmd('su root ' + cmd, do_return_result)
900*288bf522SAndroid Build Coastguard Worker
901*288bf522SAndroid Build Coastguard Workerdef logcat_time_func(offset_year):
902*288bf522SAndroid Build Coastguard Worker  def f(date_str):
903*288bf522SAndroid Build Coastguard Worker    ndate = datetime.datetime.strptime(str(offset_year) + '-' +
904*288bf522SAndroid Build Coastguard Worker                                 date_str, '%Y-%m-%d %H:%M:%S.%f')
905*288bf522SAndroid Build Coastguard Worker    return datetime_to_unix_time(ndate)
906*288bf522SAndroid Build Coastguard Worker  return f
907*288bf522SAndroid Build Coastguard Worker
908*288bf522SAndroid Build Coastguard Workerdef datetime_to_unix_time(ndate):
909*288bf522SAndroid Build Coastguard Worker  return time.mktime(ndate.timetuple()) + ndate.microsecond/1000000.0
910*288bf522SAndroid Build Coastguard Worker
911*288bf522SAndroid Build Coastguard Workerdef stddev(data):
912*288bf522SAndroid Build Coastguard Worker  items_count = len(data)
913*288bf522SAndroid Build Coastguard Worker  avg = sum(data) / items_count
914*288bf522SAndroid Build Coastguard Worker  sq_diffs_sum = sum([(v - avg) ** 2 for v in data])
915*288bf522SAndroid Build Coastguard Worker  variance = sq_diffs_sum / items_count
916*288bf522SAndroid Build Coastguard Worker  return math.sqrt(variance)
917*288bf522SAndroid Build Coastguard Worker
918*288bf522SAndroid Build Coastguard Workerdef grab_bootchart(boot_chart_file_path):
919*288bf522SAndroid Build Coastguard Worker  subprocess.run("$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh", shell=True,
920*288bf522SAndroid Build Coastguard Worker                 stdout=subprocess.DEVNULL)
921*288bf522SAndroid Build Coastguard Worker  print("Saving boot chart as " + boot_chart_file_path + ".tgz")
922*288bf522SAndroid Build Coastguard Worker  subprocess.call('cp /tmp/android-bootchart/bootchart.tgz ' + boot_chart_file_path + '.tgz', \
923*288bf522SAndroid Build Coastguard Worker                  shell=True)
924*288bf522SAndroid Build Coastguard Worker  subprocess.call('cp ./bootchart.png ' + boot_chart_file_path + '.png', shell=True)
925*288bf522SAndroid Build Coastguard Worker
926*288bf522SAndroid Build Coastguard Workerdef grab_systrace(systrace_file_path_prefix):
927*288bf522SAndroid Build Coastguard Worker  trace_file = systrace_file_path_prefix + "_trace.txt"
928*288bf522SAndroid Build Coastguard Worker  with open(trace_file, 'w') as f:
929*288bf522SAndroid Build Coastguard Worker    f.write("TRACE:\n")
930*288bf522SAndroid Build Coastguard Worker  run_adb_shell_cmd_as_root("cat /d/tracing/trace >> " + trace_file)
931*288bf522SAndroid Build Coastguard Worker  html_file = systrace_file_path_prefix + ".html"
932*288bf522SAndroid Build Coastguard Worker  subprocess.call("$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=" + trace_file + " -o " +\
933*288bf522SAndroid Build Coastguard Worker                  html_file, shell=True)
934*288bf522SAndroid Build Coastguard Worker
935*288bf522SAndroid Build Coastguard Workerdef capture_build_info(out_dir, build_info_file_name):
936*288bf522SAndroid Build Coastguard Worker  fingerprint = run_adb_shell_cmd('getprop ro.build.fingerprint', True)
937*288bf522SAndroid Build Coastguard Worker  brand = run_adb_shell_cmd('getprop ro.product.brand', True)
938*288bf522SAndroid Build Coastguard Worker  product = run_adb_shell_cmd('getprop ro.product.name', True)
939*288bf522SAndroid Build Coastguard Worker  device = run_adb_shell_cmd('getprop ro.product.device', True)
940*288bf522SAndroid Build Coastguard Worker  version_release = run_adb_shell_cmd('getprop ro.build.version.release', True)
941*288bf522SAndroid Build Coastguard Worker  id = run_adb_shell_cmd('getprop ro.build.id', True)
942*288bf522SAndroid Build Coastguard Worker  version_incremental = run_adb_shell_cmd('getprop ro.build.version.incremental', True)
943*288bf522SAndroid Build Coastguard Worker  type = run_adb_shell_cmd('getprop ro.build.type', True)
944*288bf522SAndroid Build Coastguard Worker  tags = run_adb_shell_cmd('getprop ro.build.tags', True)
945*288bf522SAndroid Build Coastguard Worker  sdk = run_adb_shell_cmd('getprop ro.build.version.sdk', True)
946*288bf522SAndroid Build Coastguard Worker  platform_minor = run_adb_shell_cmd('getprop ro.android.car.version.platform_minor', True)
947*288bf522SAndroid Build Coastguard Worker  codename = run_adb_shell_cmd('getprop ro.build.version.codename', True)
948*288bf522SAndroid Build Coastguard Worker  carwatchdog_collection_interval = run_adb_shell_cmd('getprop ro.carwatchdog.system_event_collection_interval', True)
949*288bf522SAndroid Build Coastguard Worker  carwatchdog_post_event_duration = run_adb_shell_cmd('getprop ro.carwatchdog.post_system_event_duration', True)
950*288bf522SAndroid Build Coastguard Worker  carwatchdog_top_n_category = run_adb_shell_cmd('getprop ro.carwatchdog.top_n_stats_per_category', True)
951*288bf522SAndroid Build Coastguard Worker  carwatchdog_top_n_subcategory = run_adb_shell_cmd('getprop ro.carwatchdog.top_n_stats_per_subcategory', True)
952*288bf522SAndroid Build Coastguard Worker
953*288bf522SAndroid Build Coastguard Worker  # TODO: Change file format to JSON to avoid custom parser
954*288bf522SAndroid Build Coastguard Worker  build_info = []
955*288bf522SAndroid Build Coastguard Worker  build_info.append('Build information: ')
956*288bf522SAndroid Build Coastguard Worker  build_info.append('-' * 20)
957*288bf522SAndroid Build Coastguard Worker  build_info.append('fingerprint: ' + fingerprint)
958*288bf522SAndroid Build Coastguard Worker  build_info.append('brand: ' + brand)
959*288bf522SAndroid Build Coastguard Worker  build_info.append('product: ' + product)
960*288bf522SAndroid Build Coastguard Worker  build_info.append('device: ' + device)
961*288bf522SAndroid Build Coastguard Worker  build_info.append('version.release: ' + version_release)
962*288bf522SAndroid Build Coastguard Worker  build_info.append('id: ' + id)
963*288bf522SAndroid Build Coastguard Worker  build_info.append('version.incremental: ' + version_incremental)
964*288bf522SAndroid Build Coastguard Worker  build_info.append('type: ' + type)
965*288bf522SAndroid Build Coastguard Worker  build_info.append('tags: ' + tags)
966*288bf522SAndroid Build Coastguard Worker  build_info.append('sdk: ' + sdk)
967*288bf522SAndroid Build Coastguard Worker  build_info.append('platform minor version: ' + platform_minor)
968*288bf522SAndroid Build Coastguard Worker  build_info.append('codename: ' + codename)
969*288bf522SAndroid Build Coastguard Worker  build_info.append('carwatchdog collection interval (s): ' + carwatchdog_collection_interval)
970*288bf522SAndroid Build Coastguard Worker  build_info.append('carwatchdog post event duration (s): ' + carwatchdog_post_event_duration)
971*288bf522SAndroid Build Coastguard Worker  build_info.append('carwatchdog top N packages: ' + carwatchdog_top_n_category)
972*288bf522SAndroid Build Coastguard Worker  build_info.append('carwatchdog top N processes: ' + carwatchdog_top_n_subcategory)
973*288bf522SAndroid Build Coastguard Worker
974*288bf522SAndroid Build Coastguard Worker  build_info_str = '\n'.join(build_info)
975*288bf522SAndroid Build Coastguard Worker
976*288bf522SAndroid Build Coastguard Worker  with open(out_dir + '/' + build_info_file_name, 'w') as f:
977*288bf522SAndroid Build Coastguard Worker    f.write(build_info_str)
978*288bf522SAndroid Build Coastguard Worker
979*288bf522SAndroid Build Coastguard Workerdef generate_proto(dump_file, build_info_file, out_proto_file):
980*288bf522SAndroid Build Coastguard Worker  subprocess.run("{} -f {} -b {} -d {}".format(CARWATCHDOG_PARSER_CMD,
981*288bf522SAndroid Build Coastguard Worker                                               dump_file,
982*288bf522SAndroid Build Coastguard Worker                                               build_info_file,
983*288bf522SAndroid Build Coastguard Worker                                               out_proto_file),
984*288bf522SAndroid Build Coastguard Worker                  shell=True, stdout=subprocess.DEVNULL)
985*288bf522SAndroid Build Coastguard Worker
986*288bf522SAndroid Build Coastguard Workerdef grab_carwatchdog_bootstats(result_dir):
987*288bf522SAndroid Build Coastguard Worker  carwatchdog_state = run_adb_shell_cmd_as_root('getprop init.svc.carwatchdogd', True)
988*288bf522SAndroid Build Coastguard Worker  if carwatchdog_state != "running":
989*288bf522SAndroid Build Coastguard Worker    print('carwatchdog (-d) flag set but CarWatchdog is not running on device')
990*288bf522SAndroid Build Coastguard Worker    return
991*288bf522SAndroid Build Coastguard Worker  elif not result_dir:
992*288bf522SAndroid Build Coastguard Worker    print('carwatchdog needs the output directory to be specified.')
993*288bf522SAndroid Build Coastguard Worker    return
994*288bf522SAndroid Build Coastguard Worker  print("Capturing carwatchdog stats")
995*288bf522SAndroid Build Coastguard Worker  build_info_file_name = "device_build_info.txt"
996*288bf522SAndroid Build Coastguard Worker  capture_build_info(result_dir, build_info_file_name)
997*288bf522SAndroid Build Coastguard Worker
998*288bf522SAndroid Build Coastguard Worker  # Capture CW dump and save dump to txt
999*288bf522SAndroid Build Coastguard Worker  dump_file_name = result_dir + '/carwatchdog_dump.txt'
1000*288bf522SAndroid Build Coastguard Worker  subprocess.call(CARWATCHDOG_DUMP_COMMAND + " > " + dump_file_name, shell=True)
1001*288bf522SAndroid Build Coastguard Worker
1002*288bf522SAndroid Build Coastguard Worker  # Generate proto from dump
1003*288bf522SAndroid Build Coastguard Worker  build_info_file_path = result_dir + '/' + build_info_file_name
1004*288bf522SAndroid Build Coastguard Worker  out_proto_file_path = result_dir + '/carwatchdog_perf_stats_out.pb'
1005*288bf522SAndroid Build Coastguard Worker  generate_proto(dump_file_name, build_info_file_path, out_proto_file_path)
1006*288bf522SAndroid Build Coastguard Worker
1007*288bf522SAndroid Build Coastguard Worker
1008*288bf522SAndroid Build Coastguard Workerif __name__ == '__main__':
1009*288bf522SAndroid Build Coastguard Worker  main()
1010