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