1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*288bf522SAndroid Build Coastguard Worker# 3*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2023 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 18*288bf522SAndroid Build Coastguard Workerimport argparse 19*288bf522SAndroid Build Coastguard Workerfrom datetime import datetime 20*288bf522SAndroid Build Coastguard Workerimport yaml 21*288bf522SAndroid Build Coastguard Workerimport os 22*288bf522SAndroid Build Coastguard Workerimport report_pb2 23*288bf522SAndroid Build Coastguard Workerimport sys 24*288bf522SAndroid Build Coastguard Workerimport traceback 25*288bf522SAndroid Build Coastguard Worker 26*288bf522SAndroid Build Coastguard Worker# Usage: python3 progress_report.py --logcat logcat.txt --config config.yaml --output_dir report_dir 27*288bf522SAndroid Build Coastguard Worker# 28*288bf522SAndroid Build Coastguard Worker# logcat.txt should contain the "boot_progress_start" and "boot_progress_enable_screen"". 29*288bf522SAndroid Build Coastguard Worker# config.yaml contains all the keywords to be extracted. 30*288bf522SAndroid Build Coastguard Worker# report_dir will contain three generated files: 31*288bf522SAndroid Build Coastguard Worker# 32*288bf522SAndroid Build Coastguard Worker# timestamp_log.txt: contains the same content as logcat.txt, but the timestamp is replaced 33*288bf522SAndroid Build Coastguard Worker# with relative time with boot_progress_start time. 34*288bf522SAndroid Build Coastguard Worker# 35*288bf522SAndroid Build Coastguard Worker# report_proto.txt: contains the report for the events related to the keywords. 36*288bf522SAndroid Build Coastguard Worker# 37*288bf522SAndroid Build Coastguard Worker# report.txt: contains logcat messages corresponding to the events captured in report_proto.txt 38*288bf522SAndroid Build Coastguard Worker 39*288bf522SAndroid Build Coastguard Workerdef init_arguments(): 40*288bf522SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 41*288bf522SAndroid Build Coastguard Worker prog = 'progrocess_report.py', 42*288bf522SAndroid Build Coastguard Worker description='Extract timing information and generate a report.') 43*288bf522SAndroid Build Coastguard Worker parser.add_argument( 44*288bf522SAndroid Build Coastguard Worker '--logcat', type=str, required=True, 45*288bf522SAndroid Build Coastguard Worker help = 'logcat file name') 46*288bf522SAndroid Build Coastguard Worker parser.add_argument( 47*288bf522SAndroid Build Coastguard Worker '--config', type=str, required=True, 48*288bf522SAndroid Build Coastguard Worker help = 'configuration file for keywords') 49*288bf522SAndroid Build Coastguard Worker parser.add_argument( 50*288bf522SAndroid Build Coastguard Worker '--output_dir', type= str, required=True, 51*288bf522SAndroid Build Coastguard Worker help = 'directory name to store the generated files') 52*288bf522SAndroid Build Coastguard Worker return parser.parse_args() 53*288bf522SAndroid Build Coastguard Worker 54*288bf522SAndroid Build Coastguard Worker# Find boot_progress_start line and boot_progress_enable_screen find the time difference 55*288bf522SAndroid Build Coastguard Worker# return the start time string 56*288bf522SAndroid Build Coastguard Workerdef find_boot_progress_start_end(fp): 57*288bf522SAndroid Build Coastguard Worker start = "" 58*288bf522SAndroid Build Coastguard Worker end = "" 59*288bf522SAndroid Build Coastguard Worker for line in fp: 60*288bf522SAndroid Build Coastguard Worker if "boot_progress_start" in line: 61*288bf522SAndroid Build Coastguard Worker start = line 62*288bf522SAndroid Build Coastguard Worker if "boot_progress_enable_screen" in line and len(start): 63*288bf522SAndroid Build Coastguard Worker end = line 64*288bf522SAndroid Build Coastguard Worker break 65*288bf522SAndroid Build Coastguard Worker 66*288bf522SAndroid Build Coastguard Worker missing_error = "" 67*288bf522SAndroid Build Coastguard Worker if start == "": 68*288bf522SAndroid Build Coastguard Worker missing_error = "******logcat file missing boot_progress_start\n" 69*288bf522SAndroid Build Coastguard Worker elif end == "": 70*288bf522SAndroid Build Coastguard Worker missing_error += "******logcat file missing boot_progress_end " 71*288bf522SAndroid Build Coastguard Worker if missing_error != "": 72*288bf522SAndroid Build Coastguard Worker sys.exit("Missing required message in the logcat:\n" + missing_error) 73*288bf522SAndroid Build Coastguard Worker return [start, end] 74*288bf522SAndroid Build Coastguard Worker 75*288bf522SAndroid Build Coastguard Worker# TODO(b/262259622): passing a tuple of (startDate, endDate) 76*288bf522SAndroid Build Coastguard Workerdef replace_timestamp_abs(line, timestamp_str, date_time_obj0): 77*288bf522SAndroid Build Coastguard Worker index = line.find(" ", 6) 78*288bf522SAndroid Build Coastguard Worker if index <= 0: 79*288bf522SAndroid Build Coastguard Worker return line 80*288bf522SAndroid Build Coastguard Worker substr0 = line[:index] 81*288bf522SAndroid Build Coastguard Worker substr1 = line[index:] 82*288bf522SAndroid Build Coastguard Worker 83*288bf522SAndroid Build Coastguard Worker try: 84*288bf522SAndroid Build Coastguard Worker date_time_obj = datetime.strptime(substr0, '%m-%d %H:%M:%S.%f') 85*288bf522SAndroid Build Coastguard Worker except ValueError: 86*288bf522SAndroid Build Coastguard Worker return line 87*288bf522SAndroid Build Coastguard Worker 88*288bf522SAndroid Build Coastguard Worker date_time_delta = date_time_obj - date_time_obj0 89*288bf522SAndroid Build Coastguard Worker date_time_delta_str = str(date_time_delta) 90*288bf522SAndroid Build Coastguard Worker return date_time_delta_str + substr1 91*288bf522SAndroid Build Coastguard Worker 92*288bf522SAndroid Build Coastguard Workerdef in_time_range(start, end, line): 93*288bf522SAndroid Build Coastguard Worker try: 94*288bf522SAndroid Build Coastguard Worker current_time = datetime.strptime(line[:18], '%m-%d %H:%M:%S.%f') 95*288bf522SAndroid Build Coastguard Worker except ValueError: 96*288bf522SAndroid Build Coastguard Worker return False 97*288bf522SAndroid Build Coastguard Worker 98*288bf522SAndroid Build Coastguard Worker if current_time >= start and current_time <= end: 99*288bf522SAndroid Build Coastguard Worker return True 100*288bf522SAndroid Build Coastguard Worker 101*288bf522SAndroid Build Coastguard Worker return False 102*288bf522SAndroid Build Coastguard Worker 103*288bf522SAndroid Build Coastguard Worker# Here is an example of event we would like extract: 104*288bf522SAndroid Build Coastguard Worker# 09-15 16:04:15.655 root 991 991 I boot_progress_preload_start: 5440 105*288bf522SAndroid Build Coastguard Worker# for each event, it is a tuple of(timestamp, event_name, timing) 106*288bf522SAndroid Build Coastguard Workerdef extract_event(line, keywords): 107*288bf522SAndroid Build Coastguard Worker words = line.split(" ") 108*288bf522SAndroid Build Coastguard Worker for keyword in keywords: 109*288bf522SAndroid Build Coastguard Worker if keyword in words[-2]: 110*288bf522SAndroid Build Coastguard Worker return (words[0], words[-2], words[-1]) 111*288bf522SAndroid Build Coastguard Worker return () 112*288bf522SAndroid Build Coastguard Worker 113*288bf522SAndroid Build Coastguard Workerdef write_to_new_file(timestamps, keywords, logcat_fp, timestamp_fixed_logcat_fp, report_fp, 114*288bf522SAndroid Build Coastguard Worker report_proto_fp): 115*288bf522SAndroid Build Coastguard Worker start_timestamp_obj = datetime.strptime(timestamps[0][:18], '%m-%d %H:%M:%S.%f') 116*288bf522SAndroid Build Coastguard Worker end_timestamp_obj = datetime.strptime(timestamps[1][:18], '%m-%d %H:%M:%S.%f') 117*288bf522SAndroid Build Coastguard Worker report = report_pb2.Report() 118*288bf522SAndroid Build Coastguard Worker for line in logcat_fp: 119*288bf522SAndroid Build Coastguard Worker ts_fixed_line = replace_timestamp_abs(line, timestamps[0][:18], start_timestamp_obj) 120*288bf522SAndroid Build Coastguard Worker timestamp_fixed_logcat_fp.write(ts_fixed_line) 121*288bf522SAndroid Build Coastguard Worker if in_time_range(start_timestamp_obj, end_timestamp_obj, line): 122*288bf522SAndroid Build Coastguard Worker event = extract_event(ts_fixed_line, keywords) 123*288bf522SAndroid Build Coastguard Worker if len(event) == 0: 124*288bf522SAndroid Build Coastguard Worker continue 125*288bf522SAndroid Build Coastguard Worker 126*288bf522SAndroid Build Coastguard Worker report_fp.write(ts_fixed_line) 127*288bf522SAndroid Build Coastguard Worker record = report.record.add() 128*288bf522SAndroid Build Coastguard Worker record.timestamp = event[0] 129*288bf522SAndroid Build Coastguard Worker record.event = event[1] 130*288bf522SAndroid Build Coastguard Worker record.timing = int(event[2]) 131*288bf522SAndroid Build Coastguard Worker report_proto_fp.write(str(report)) 132*288bf522SAndroid Build Coastguard Worker 133*288bf522SAndroid Build Coastguard Workerdef main(): 134*288bf522SAndroid Build Coastguard Worker args = init_arguments() 135*288bf522SAndroid Build Coastguard Worker 136*288bf522SAndroid Build Coastguard Worker keywords = [] 137*288bf522SAndroid Build Coastguard Worker with open(args.config, 'r') as file: 138*288bf522SAndroid Build Coastguard Worker keywords = yaml.safe_load(file) 139*288bf522SAndroid Build Coastguard Worker 140*288bf522SAndroid Build Coastguard Worker if not os.path.isdir(args.output_dir): 141*288bf522SAndroid Build Coastguard Worker os.mkdir(args.output_dir) 142*288bf522SAndroid Build Coastguard Worker timestamp_fixed_logcat_fp = open(os.path.join(args.output_dir, "timestamp_fixed_log.txt"), 'w') 143*288bf522SAndroid Build Coastguard Worker report_fp = open(os.path.join(args.output_dir, "report.txt"), 'w') 144*288bf522SAndroid Build Coastguard Worker report_proto_fp = open(os.path.join(args.output_dir, "report_proto.txt"), 'w') 145*288bf522SAndroid Build Coastguard Worker try: 146*288bf522SAndroid Build Coastguard Worker with open(args.logcat, 'r', errors = 'ignore') as logcat_fp: 147*288bf522SAndroid Build Coastguard Worker timestamps = find_boot_progress_start_end(logcat_fp) 148*288bf522SAndroid Build Coastguard Worker logcat_fp.seek(0) 149*288bf522SAndroid Build Coastguard Worker write_to_new_file(timestamps, keywords, logcat_fp, timestamp_fixed_logcat_fp, report_fp, report_proto_fp) 150*288bf522SAndroid Build Coastguard Worker except Exception as e: 151*288bf522SAndroid Build Coastguard Worker traceresult = traceback.format_exc() 152*288bf522SAndroid Build Coastguard Worker print("Caught an exception: {}".format(traceback.format_exc())) 153*288bf522SAndroid Build Coastguard Worker 154*288bf522SAndroid Build Coastguard Worker timestamp_fixed_logcat_fp.close() 155*288bf522SAndroid Build Coastguard Worker report_fp.close() 156*288bf522SAndroid Build Coastguard Worker report_proto_fp.close() 157*288bf522SAndroid Build Coastguard Worker 158*288bf522SAndroid Build Coastguard Workerif __name__ == '__main__': 159*288bf522SAndroid Build Coastguard Worker main() 160