blob: f6a1c430269b5d384996307ef7f2a76427b79f39 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (C) 2023 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
from datetime import datetime
import yaml
import os
import report_pb2
import sys
import traceback
# Usage: python3 progress_report.py --logcat logcat.txt --config config.yaml --output_dir report_dir
#
# logcat.txt should contain the "boot_progress_start" and "boot_progress_enable_screen"".
# config.yaml contains all the keywords to be extracted.
# report_dir will contain three generated files:
#
# timestamp_log.txt: contains the same content as logcat.txt, but the timestamp is replaced
# with relative time with boot_progress_start time.
#
# report_proto.txt: contains the report for the events related to the keywords.
#
# report.txt: contains logcat messages corresponding to the events captured in report_proto.txt
def init_arguments():
parser = argparse.ArgumentParser(
prog = 'progrocess_report.py',
description='Extract timing information and generate a report.')
parser.add_argument(
'--logcat', type=str, required=True,
help = 'logcat file name')
parser.add_argument(
'--config', type=str, required=True,
help = 'configuration file for keywords')
parser.add_argument(
'--output_dir', type= str, required=True,
help = 'directory name to store the generated files')
return parser.parse_args()
# Find boot_progress_start line and boot_progress_enable_screen find the time difference
# return the start time string
def find_boot_progress_start_end(fp):
start = ""
end = ""
for line in fp:
if "boot_progress_start" in line:
start = line
if "boot_progress_enable_screen" in line and len(start):
end = line
break
missing_error = ""
if start == "":
missing_error = "******logcat file missing boot_progress_start\n"
elif end == "":
missing_error += "******logcat file missing boot_progress_end "
if missing_error != "":
sys.exit("Missing required message in the logcat:\n" + missing_error)
return [start, end]
# TODO(b/262259622): passing a tuple of (startDate, endDate)
def replace_timestamp_abs(line, timestamp_str, date_time_obj0):
index = line.find(" ", 6)
if index <= 0:
return line
substr0 = line[:index]
substr1 = line[index:]
try:
date_time_obj = datetime.strptime(substr0, '%m-%d %H:%M:%S.%f')
except ValueError:
return line
date_time_delta = date_time_obj - date_time_obj0
date_time_delta_str = str(date_time_delta)
return date_time_delta_str + substr1
def in_time_range(start, end, line):
try:
current_time = datetime.strptime(line[:18], '%m-%d %H:%M:%S.%f')
except ValueError:
return False
if current_time >= start and current_time <= end:
return True
return False
# Here is an example of event we would like extract:
# 09-15 16:04:15.655 root 991 991 I boot_progress_preload_start: 5440
# for each event, it is a tuple of(timestamp, event_name, timing)
def extract_event(line, keywords):
words = line.split(" ")
for keyword in keywords:
if keyword in words[-2]:
return (words[0], words[-2], words[-1])
return ()
def write_to_new_file(timestamps, keywords, logcat_fp, timestamp_fixed_logcat_fp, report_fp,
report_proto_fp):
start_timestamp_obj = datetime.strptime(timestamps[0][:18], '%m-%d %H:%M:%S.%f')
end_timestamp_obj = datetime.strptime(timestamps[1][:18], '%m-%d %H:%M:%S.%f')
report = report_pb2.Report()
for line in logcat_fp:
ts_fixed_line = replace_timestamp_abs(line, timestamps[0][:18], start_timestamp_obj)
timestamp_fixed_logcat_fp.write(ts_fixed_line)
if in_time_range(start_timestamp_obj, end_timestamp_obj, line):
event = extract_event(ts_fixed_line, keywords)
if len(event) == 0:
continue
report_fp.write(ts_fixed_line)
record = report.record.add()
record.timestamp = event[0]
record.event = event[1]
record.timing = int(event[2])
report_proto_fp.write(str(report))
def main():
args = init_arguments()
keywords = []
with open(args.config, 'r') as file:
keywords = yaml.safe_load(file)
if not os.path.isdir(args.output_dir):
os.mkdir(args.output_dir)
timestamp_fixed_logcat_fp = open(os.path.join(args.output_dir, "timestamp_fixed_log.txt"), 'w')
report_fp = open(os.path.join(args.output_dir, "report.txt"), 'w')
report_proto_fp = open(os.path.join(args.output_dir, "report_proto.txt"), 'w')
try:
with open(args.logcat, 'r', errors = 'ignore') as logcat_fp:
timestamps = find_boot_progress_start_end(logcat_fp)
logcat_fp.seek(0)
write_to_new_file(timestamps, keywords, logcat_fp, timestamp_fixed_logcat_fp, report_fp, report_proto_fp)
except Exception as e:
traceresult = traceback.format_exc()
print("Caught an exception: {}".format(traceback.format_exc()))
timestamp_fixed_logcat_fp.close()
report_fp.close()
report_proto_fp.close()
if __name__ == '__main__':
main()