xref: /build/scripts/ninja2trace.py (revision 5f9996aa)
15f9996aaSopenharmony_ci#!/usr/bin/env python
25f9996aaSopenharmony_ci# -*- coding: utf-8 -*-
35f9996aaSopenharmony_ci# Copyright (c) 2021 Huawei Device Co., Ltd.
45f9996aaSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
55f9996aaSopenharmony_ci# you may not use this file except in compliance with the License.
65f9996aaSopenharmony_ci# You may obtain a copy of the License at
75f9996aaSopenharmony_ci#
85f9996aaSopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
95f9996aaSopenharmony_ci#
105f9996aaSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
115f9996aaSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
125f9996aaSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f9996aaSopenharmony_ci# See the License for the specific language governing permissions and
145f9996aaSopenharmony_ci# limitations under the License.
155f9996aaSopenharmony_ci
165f9996aaSopenharmony_ciimport os
175f9996aaSopenharmony_ciimport sys
185f9996aaSopenharmony_ciimport json
195f9996aaSopenharmony_ciimport gzip
205f9996aaSopenharmony_ciimport shutil
215f9996aaSopenharmony_ciimport argparse
225f9996aaSopenharmony_ci
235f9996aaSopenharmony_ciKFILESIGNATURE = "# ninja log v5\n"
245f9996aaSopenharmony_ci
255f9996aaSopenharmony_ci
265f9996aaSopenharmony_ciclass StoringDataLine(object):
275f9996aaSopenharmony_ci    def __init__(self, start, end):
285f9996aaSopenharmony_ci        self.start = int(start)
295f9996aaSopenharmony_ci        self.end = int(end)
305f9996aaSopenharmony_ci        self.target_obj_names = []
315f9996aaSopenharmony_ci
325f9996aaSopenharmony_ci    def __str__(self):
335f9996aaSopenharmony_ci        return "{} {} {} ".format(self.start, self.end, self.target_obj_names)
345f9996aaSopenharmony_ci
355f9996aaSopenharmony_ci
365f9996aaSopenharmony_ciclass NinjaToTrace(object):
375f9996aaSopenharmony_ci    def __init__(self):
385f9996aaSopenharmony_ci        self.datalist = list()
395f9996aaSopenharmony_ci        self.durations = list()
405f9996aaSopenharmony_ci
415f9996aaSopenharmony_ci    def parse_file(self, filename, ninja_start_time):
425f9996aaSopenharmony_ci        # ensure file exist
435f9996aaSopenharmony_ci        if not os.path.exists(filename):
445f9996aaSopenharmony_ci            print("file: {} not exists".format(filename))
455f9996aaSopenharmony_ci            return False
465f9996aaSopenharmony_ci        storing_data = {}
475f9996aaSopenharmony_ci        with open(filename, mode='r') as f:
485f9996aaSopenharmony_ci            firstline = f.readline()
495f9996aaSopenharmony_ci            if firstline != KFILESIGNATURE:
505f9996aaSopenharmony_ci                print("unrecognized ninja log format, we need {}".format(
515f9996aaSopenharmony_ci                    KFILESIGNATURE))
525f9996aaSopenharmony_ci
535f9996aaSopenharmony_ci            for _, line in enumerate(f.readlines()):
545f9996aaSopenharmony_ci                start, end, time_stamp, name, cmdhash = line.strip().split(
555f9996aaSopenharmony_ci                    '\t')
565f9996aaSopenharmony_ci                if time_stamp > ninja_start_time:
575f9996aaSopenharmony_ci                    storing_data.setdefault(cmdhash, StoringDataLine(start, end))
585f9996aaSopenharmony_ci                    storing_data.get(cmdhash).target_obj_names.append(name)
595f9996aaSopenharmony_ci
605f9996aaSopenharmony_ci        self.datalist = sorted(storing_data.values(),
615f9996aaSopenharmony_ci                               key=lambda line: line.start)
625f9996aaSopenharmony_ci        self.durations = sorted(storing_data.values(),
635f9996aaSopenharmony_ci                                key=lambda line: line.end - line.start,
645f9996aaSopenharmony_ci                                reverse=True)
655f9996aaSopenharmony_ci        return True
665f9996aaSopenharmony_ci
675f9996aaSopenharmony_ci    def save_durations(self, duration_file: str):
685f9996aaSopenharmony_ci        total_time = 0
695f9996aaSopenharmony_ci        with open(duration_file, 'w') as file:
705f9996aaSopenharmony_ci            for item in self.durations:
715f9996aaSopenharmony_ci                duration = item.end - item.start
725f9996aaSopenharmony_ci                total_time += duration
735f9996aaSopenharmony_ci                file.write('{}: {}\n'.format(item.target_obj_names[0],
745f9996aaSopenharmony_ci                                             duration))
755f9996aaSopenharmony_ci            file.write('total time: {} ms'.format(total_time))
765f9996aaSopenharmony_ci
775f9996aaSopenharmony_ci    def trans_to_trace_json(self, dest_file_name: str):
785f9996aaSopenharmony_ci        counter = CountingTheTid()
795f9996aaSopenharmony_ci        tracelist = list()
805f9996aaSopenharmony_ci        for storingdataline in self.datalist:
815f9996aaSopenharmony_ci            tracelist.append({
825f9996aaSopenharmony_ci                'name': '%0s' % ', '.join(storingdataline.target_obj_names),
835f9996aaSopenharmony_ci                'cat': 'targets',
845f9996aaSopenharmony_ci                'ph': 'X',
855f9996aaSopenharmony_ci                'ts': str(storingdataline.start * 1000),
865f9996aaSopenharmony_ci                'dur': str((storingdataline.end - storingdataline.start) * 1000),
875f9996aaSopenharmony_ci                'pid': str(0),
885f9996aaSopenharmony_ci                'tid': str(counter.counting_the_new_tid(storingdataline)),
895f9996aaSopenharmony_ci                'args': {},
905f9996aaSopenharmony_ci            })
915f9996aaSopenharmony_ci
925f9996aaSopenharmony_ci        if not dest_file_name.endswith('.gz'):
935f9996aaSopenharmony_ci            dest_file_name = dest_file_name + '.gz'
945f9996aaSopenharmony_ci
955f9996aaSopenharmony_ci        if os.path.exists(dest_file_name):
965f9996aaSopenharmony_ci            shutil.move(
975f9996aaSopenharmony_ci                dest_file_name, '%s/build.trace.%d.gz' %
985f9996aaSopenharmony_ci                                (os.path.dirname(dest_file_name),
995f9996aaSopenharmony_ci                                 int(os.stat(dest_file_name).st_mtime)))
1005f9996aaSopenharmony_ci
1015f9996aaSopenharmony_ci        with gzip.open(dest_file_name, "wt") as f:
1025f9996aaSopenharmony_ci            json.dump(tracelist, f)
1035f9996aaSopenharmony_ci
1045f9996aaSopenharmony_ci
1055f9996aaSopenharmony_ciclass CountingTheTid(object):
1065f9996aaSopenharmony_ci    def __init__(self):
1075f9996aaSopenharmony_ci        self.tids = []  # store the tid's end time
1085f9996aaSopenharmony_ci
1095f9996aaSopenharmony_ci    def counting_the_new_tid(self, storingdataline):
1105f9996aaSopenharmony_ci        for i, tid in enumerate(self.tids):
1115f9996aaSopenharmony_ci            if tid <= storingdataline.start:
1125f9996aaSopenharmony_ci                self.tids[i] = storingdataline.end
1135f9996aaSopenharmony_ci                return i  # renew the endtime and return the current tid
1145f9996aaSopenharmony_ci
1155f9996aaSopenharmony_ci        # for the end time is newer than all tids so we need a new one
1165f9996aaSopenharmony_ci        self.tids.append(storingdataline.end)
1175f9996aaSopenharmony_ci        return len(self.tids) - 1  # the last index of the tids
1185f9996aaSopenharmony_ci
1195f9996aaSopenharmony_ci
1205f9996aaSopenharmony_cidef main():
1215f9996aaSopenharmony_ci    parser = argparse.ArgumentParser()
1225f9996aaSopenharmony_ci    parser.add_argument('--ninja-log', help='path to ninja log')
1235f9996aaSopenharmony_ci    parser.add_argument('--trace-file', help='path to build trace file')
1245f9996aaSopenharmony_ci    parser.add_argument('--duration-file', help='path to duration file')
1255f9996aaSopenharmony_ci    parser.add_argument(
1265f9996aaSopenharmony_ci        '--ninja-start-time',
1275f9996aaSopenharmony_ci        help='epoch time of "Starting ninja ..." in nanoseconds')
1285f9996aaSopenharmony_ci
1295f9996aaSopenharmony_ci    options = parser.parse_args()
1305f9996aaSopenharmony_ci    myparser = NinjaToTrace()
1315f9996aaSopenharmony_ci    if not myparser.parse_file(options.ninja_log, options.ninja_start_time):
1325f9996aaSopenharmony_ci        print("parse file fail")
1335f9996aaSopenharmony_ci        return
1345f9996aaSopenharmony_ci
1355f9996aaSopenharmony_ci    myparser.trans_to_trace_json(options.trace_file)
1365f9996aaSopenharmony_ci    myparser.save_durations(options.duration_file)
1375f9996aaSopenharmony_ci
1385f9996aaSopenharmony_ci
1395f9996aaSopenharmony_ciif __name__ == '__main__':
1405f9996aaSopenharmony_ci    sys.exit(main())
141