summaryrefslogtreecommitdiff
path: root/perf_tools/progress_report.py
blob: f6a1c430269b5d384996307ef7f2a76427b79f39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/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()