xref: /aosp_15_r20/system/extras/perf_tools/progress_report.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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