• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 os
17import sys
18import json
19import gzip
20import shutil
21import argparse
22
23KFILESIGNATURE = "# ninja log v5\n"
24
25
26class StoringDataLine(object):
27    def __init__(self, start, end):
28        self.start = int(start)
29        self.end = int(end)
30        self.target_obj_names = []
31
32    def __str__(self):
33        return "{} {} {} ".format(self.start, self.end, self.target_obj_names)
34
35
36class NinjaToTrace(object):
37    def __init__(self):
38        self.datalist = list()
39        self.durations = list()
40
41    def parse_file(self, filename, ninja_start_time):
42        # ensure file exist
43        if not os.path.exists(filename):
44            print("file: {} not exists".format(filename))
45            return False
46        storing_data = {}
47        with open(filename, mode='r') as f:
48            firstline = f.readline()
49            if firstline != KFILESIGNATURE:
50                print("unrecognized ninja log format, we need {}".format(
51                    KFILESIGNATURE))
52
53            for _, line in enumerate(f.readlines()):
54                start, end, time_stamp, name, cmdhash = line.strip().split(
55                    '\t')
56                if time_stamp > ninja_start_time:
57                    storing_data.setdefault(cmdhash, StoringDataLine(start, end))
58                    storing_data.get(cmdhash).target_obj_names.append(name)
59
60        self.datalist = sorted(storing_data.values(),
61                               key=lambda line: line.start)
62        self.durations = sorted(storing_data.values(),
63                                key=lambda line: line.end - line.start,
64                                reverse=True)
65        return True
66
67    def save_durations(self, duration_file: str):
68        total_time = 0
69        if os.path.exists(duration_file):
70            shutil.move(duration_file, '{}/sorted_action_duration.{}.txt'.format(os.path.dirname(duration_file),
71                os.stat(duration_file).st_mtime))
72        with open(duration_file, 'w') as file:
73            for item in self.durations:
74                duration = item.end - item.start
75                total_time += duration
76                file.write('{}: {}\n'.format(item.target_obj_names[0],
77                                             duration))
78            file.write('total time: {} ms'.format(total_time))
79
80    def trans_to_trace_json(self, dest_file_name: str):
81        counter = CountingTheTid()
82        tracelist = list()
83        for storingdataline in self.datalist:
84            tracelist.append({
85                'name': '%0s' % ', '.join(storingdataline.target_obj_names),
86                'cat': 'targets',
87                'ph': 'X',
88                'ts': str(storingdataline.start * 1000),
89                'dur': str((storingdataline.end - storingdataline.start) * 1000),
90                'pid': str(0),
91                'tid': str(counter.counting_the_new_tid(storingdataline)),
92                'args': {},
93            })
94
95        if not dest_file_name.endswith('.gz'):
96            dest_file_name = dest_file_name + '.gz'
97
98        if os.path.exists(dest_file_name):
99            shutil.move(
100                dest_file_name, '%s/build.trace.%d.gz' %
101                                (os.path.dirname(dest_file_name),
102                                 int(os.stat(dest_file_name).st_mtime)))
103
104        with gzip.open(dest_file_name, "wt") as f:
105            json.dump(tracelist, f)
106
107
108class CountingTheTid(object):
109    def __init__(self):
110        self.tids = []  # store the tid's end time
111
112    def counting_the_new_tid(self, storingdataline):
113        for i, tid in enumerate(self.tids):
114            if tid <= storingdataline.start:
115                self.tids[i] = storingdataline.end
116                return i  # renew the endtime and return the current tid
117
118        # for the end time is newer than all tids so we need a new one
119        self.tids.append(storingdataline.end)
120        return len(self.tids) - 1  # the last index of the tids
121
122
123def main():
124    parser = argparse.ArgumentParser()
125    parser.add_argument('--ninja-log', help='path to ninja log')
126    parser.add_argument('--trace-file', help='path to build trace file')
127    parser.add_argument('--duration-file', help='path to duration file')
128    parser.add_argument(
129        '--ninja-start-time',
130        help='epoch time of "Starting ninja ..." in nanoseconds')
131
132    options = parser.parse_args()
133    myparser = NinjaToTrace()
134    if not myparser.parse_file(options.ninja_log, options.ninja_start_time):
135        print("parse file fail")
136        return
137
138    myparser.trans_to_trace_json(options.trace_file)
139    myparser.save_durations(options.duration_file)
140
141
142if __name__ == '__main__':
143    sys.exit(main())
144