aboutsummaryrefslogtreecommitdiff
path: root/scripts/parse_firrtl_transform_log.py
blob: 16ea77cd029cb879b5e8bf31896f38d5f8946d06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3

import sys
import os
import re
from itertools import *
from collections import namedtuple
import argparse

Transform = namedtuple('Transform', ['name', 'children', 'runtime'])

start_xform_re = re.compile(r'\s*=+\s+Starting\s+Transform\s+(\S+)\s+=+\s*')
finish_xform_re = re.compile(r'\s*=+\s+Finished\s+Transform\s+(\S+)\s+=+\s*')
time_re = re.compile(r'\s*Time:\s*(\d+(\.\d+)?)\s*ms\s*')


def get_start(line):
    if line is not None:
        m = start_xform_re.match(line)
        if m:
            return m.group(1)
    return None


def get_finish(line):
    m = finish_xform_re.match(line)
    if m:
        return m.group(1)
    return None


def get_time(line):
    m = time_re.match(line)
    if m:
        return m.group(1)
    return None


def safe_next(it):
    try:
        return next(it)
    except StopIteration:
        return None


def non_empty(it):
    return any(True for _ in it)


def read_transform(name, lines):
    # The log may be malformed so we just catch that whenever
    try:
        children = []
        time = None
        while True:
            line = next(lines)
            start_opt = get_start(line)
            if start_opt is not None:
                child = read_transform(start_opt, lines)
                children.append(child)
            else:
                time_opt = get_time(line)
                if time_opt is not None:
                    time = time_opt
                else:
                    end_opt = get_finish(line)
                    if end_opt is not None:
                        assert end_opt == name
                        return Transform(name, children, time)
    except StopIteration:
        return None


def read_top_transform(it):
    # Find start
    lines = dropwhile(lambda line: not start_xform_re.match(line), it)
    name = get_start(safe_next(lines))
    if name is not None:
        return read_transform(name, lines)
    else:
        return None


def pretty_transform(xform):
    time_str = " ({} ms)".format(xform.runtime) if xform.runtime is not None else ""
    lines = [xform.name + time_str]
    nchildren = len(xform.children)
    for i in range(0, nchildren):
        child = xform.children[i]
        child_lines = pretty_transform(child)
        if i == nchildren - 1:
            branch = "└── "
            indent = "    "
        else:
            branch = "├── "
            indent = "│   "
        first = True
        for line in child_lines:
            prefix = branch if first else indent
            lines.append(prefix + line)
            first = False
    return lines


def remove_time(xform):
    children = [remove_time(child) for child in xform.children]
    return Transform(xform.name, children, None)


def is_existing_file(name):
    if not os.path.isfile(name):
        msg = "File {} does not exist!".format(name)
        raise argparse.ArgumentTypeError(msg)
    else:
        return name


def get_parser():
    parser = argparse.ArgumentParser(description='Parse FIRRTL transform log')
    parser.add_argument('file', help='log file',
                        type=is_existing_file)
    parser.add_argument('-s', '--strip-time', action='store_true', default=False,
                         help='Strip time values')
    return parser


if __name__ == "__main__":
    parser = get_parser()
    args = parser.parse_args()

    transforms = []
    with open(args.file, "r") as f:
        while True:
            xform = read_top_transform(f)
            if xform is None:
                break
            else:
                transforms.append(xform)
    if len(transforms) == 0:
        print("{} does not contain a FIRRTL Transform log!".format(args.file))
        sys.exit(-1)
    top = Transform("firrtl.stage.transforms.Compiler", transforms, None)
    if args.strip_time:
        top = remove_time(top)
    pretty = pretty_transform(top)
    for line in pretty:
        print(line)