• 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                storing_data.setdefault(cmdhash, StoringDataLine(start, end))
57                storing_data.get(cmdhash).target_obj_names.append(name)
58
59        self.datalist = sorted(storing_data.values(),
60                               key=lambda line: line.start)
61        self.durations = sorted(storing_data.values(),
62                                key=lambda line: line.end - line.start,
63                                reverse=True)
64        return True
65
66    def save_durations(self, duration_file):
67        total_time = 0
68        with open(duration_file, 'w') as file:
69            for item in self.durations:
70                duration = item.end - item.start
71                total_time += duration
72                file.write('{}: {}\n'.format(item.target_obj_names[0],
73                                             duration))
74            file.write('total time: {} ms'.format(total_time))
75
76    def trans_to_trace_json(self, dest_file_name):
77        counter = CountingTheTid()
78        tracelist = list()
79        for storingdataline in self.datalist:
80            tracelist.append({
81                'name': '%0s' % ', '.join(storingdataline.target_obj_names),
82                'cat': 'targets',
83                'ph': 'X',
84                'ts': str(storingdataline.start * 1000),
85                'dur': str((storingdataline.end - storingdataline.start) * 1000),
86                'pid': str(0),
87                'tid': str(counter.counting_the_new_tid(storingdataline)),
88                'args': {},
89            })
90
91        if not dest_file_name.endswith('.gz'):
92            dest_file_name = dest_file_name + '.gz'
93
94        if os.path.exists(dest_file_name):
95            shutil.move(
96                dest_file_name, '%s/build.trace.%d.gz' %
97                                (os.path.dirname(dest_file_name),
98                                 int(os.stat(dest_file_name).st_mtime)))
99
100        with gzip.open(dest_file_name, "wt") as f:
101            json.dump(tracelist, f)
102
103
104class CountingTheTid(object):
105    def __init__(self):
106        self.tids = []  # store the tid's end time
107
108    def counting_the_new_tid(self, storingdataline):
109        for i in range(len(self.tids)):
110            if self.tids[i] <= storingdataline.start:
111                self.tids[i] = storingdataline.end
112                return i  # renew the endtime and return the current tid
113
114        # for the end time is newer than all tids so we need a new one
115        self.tids.append(storingdataline.end)
116        return len(self.tids) - 1  # the last index of the tids
117
118
119def main():
120    parser = argparse.ArgumentParser()
121    parser.add_argument('--ninja-log', help='path to ninja log')
122    parser.add_argument('--trace-file', help='path to build trace file')
123    parser.add_argument('--duration-file', help='path to duration file')
124    parser.add_argument(
125        '--ninja-start-time',
126        help='epoch time of "Starting ninja ..." in nanoseconds')
127
128    options = parser.parse_args()
129    myparser = NinjaToTrace()
130    if not myparser.parse_file(options.ninja_log, options.ninja_start_time):
131        print("parse file fail")
132        return
133
134    myparser.trans_to_trace_json(options.trace_file)
135    myparser.save_durations(options.duration_file)
136
137
138if __name__ == '__main__':
139    sys.exit(main())
140