1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (C) 2021 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import codecs
17import sys
18import os
19import re
20import time
21
22files = []
23trace_regex = "\s*(.*?)-(\d+?)\s+\(\s*(\d+?)\)\s+\[\d+\]\s+(.*?)\s+(.*?):\s+"
24all_traces_dict = {} # {"deviceName": [(timestamp, line) ...]}
25all_real_time_dict = {}
26default_real_time = 7983849599000 # 2222-12-31 23:59:59
27
28def compare_timestamp(time1, time2):
29    return float(time1) < float(time2)
30
31def read_files(file, devices_name):
32    traces = []
33    with codecs.open(file, 'r', encoding='utf-8') as fp:
34        for line in fp:
35            if line.find("binder_transaction") > -1 \
36                or line.find("tracing_mark_write") > -1:
37                line = line.replace("\n", "")
38                trace_match = re.match(trace_regex, line)
39                if trace_match:
40                    traces.append((float(trace_match.group(5)), line))
41                if line.find("realtime_ts") > -1:
42                    time_regex = "{}{}".format(trace_regex, \
43                        "tracing_mark_write:\s+trace_event_clock_sync: realtime_ts=(.*)")
44                    time_match = re.match(time_regex, line)
45                    all_real_time_dict[devices_name] = { \
46                        "realtime_ts": int(time_match.group(6)), \
47                        "timestamp": float(time_match.group(5))}
48    if (not all_real_time_dict.__contains__(devices_name)) and traces:
49        line = traces[-1][1]
50        trace_match = re.match(trace_regex, line)
51        all_real_time_dict[devices_name] = { \
52            "realtime_ts": default_real_time, \
53            "timestamp": float(trace_match.group(5))}
54    return traces
55
56def handle_option():
57    if len(sys.argv) < 2:
58        print("eg: python bytrace_multi.py file1.ftrace file2.ftrace ...")
59        exit(0)
60    for i in range(len(sys.argv)):
61        if i == 0:
62            continue
63        if sys.argv[i] == "-h" or sys.argv[i] == "--help":
64            print("eg: python bytrace_multi.py file1.ftrace file2.ftrace ...")
65            exit(0)
66        elif not os.path.exists(sys.argv[i]):
67            print("Warning: {} is not found.".format(sys.argv[i]))
68        else:
69            files.append(sys.argv[i])
70
71def change_trace_time(all_trace_list, \
72                      base_real_time, \
73                      base_time_stamp, \
74                      target_device):
75    target_real_time = all_real_time_dict[target_device]["realtime_ts"]
76    target_time_stamp = all_real_time_dict[target_device]["timestamp"]
77    if not target_real_time == default_real_time:
78        diff_real_time = float(target_real_time - base_real_time) / 1000
79        target_time_stamp_ = base_time_stamp + diff_real_time
80        diff_time = target_time_stamp - target_time_stamp_
81    else:
82        # If the file does not have realtime, the difference is 0.5s.
83        diff_real_time = 0.5
84        target_time_stamp_ = base_time_stamp + diff_real_time
85        diff_time = target_time_stamp - target_time_stamp_
86    traces = all_traces_dict[target_device]
87    for mtuple in traces:
88        timestamp = float(mtuple[0])
89        line = mtuple[1]
90        timestamp_ = "{:.6f}".format(timestamp - diff_time)
91        line_ = target_device + \
92            line.replace("{:.6f}".format(timestamp), str(timestamp_))
93        all_trace_list.append((timestamp_, line_))
94
95def write_to_file(data, file_name):
96    with codecs.open(file_name, 'w+', encoding='utf-8') as fp:
97        fp.write("# tracer: nop\n")
98        fp.write("#\n")
99        fp.write("#                                      _-----=> irqs-off\n")
100        fp.write("#                                     / _----=> need-resched\n")
101        fp.write("#                                    | / _---=> hardirq/softirq\n")
102        fp.write("#                                    || / _--=> preempt-depth\n")
103        fp.write("#                                    ||| /     delay\n")
104        fp.write("#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION\n")
105        fp.write("#              | |        |      |   ||||       |         |\n")
106        for mtuple in data:
107            fp.write(mtuple[1])
108            fp.write("\n")
109
110def main():
111    handle_option()
112    if len(files) == 0:
113        exit(-1)
114
115    for i, val in enumerate(files):
116        device_name = "[device_{}]".format(str(i))
117        all_traces_dict[device_name] = read_files(val, device_name)
118
119    all_time_sorted_list = sorted(all_real_time_dict.items(), key=lambda \
120        all_real_time_dict: all_real_time_dict[1]["realtime_ts"])
121    base_real_time = all_time_sorted_list[0][1]["realtime_ts"]
122    base_time_stamp = all_time_sorted_list[0][1]["timestamp"]
123    all_trace_list = [] # [(timestamp, line)]
124    for mtuple in all_time_sorted_list:
125        target_device = mtuple[0]
126        change_trace_time(all_trace_list, \
127                          base_real_time, \
128                          base_time_stamp, \
129                          target_device)
130    # Sort by timestamp from small to large
131    all_trace_sorted_list = sorted(all_trace_list, key=lambda x: x[0])
132    curtime = time.strftime("%Y%m%d_%H%M%S", time.localtime())
133    write_to_file(all_trace_sorted_list, "multi_trace_{}.ftrace".format(str(curtime)))
134
135if __name__ == '__main__':
136    main()
137