blob: 86a3a29aacb6f4e8ed6d97f9841a701e0b3a7cc3 [file] [log] [blame]
Srikrishna Iyerdf0936b2021-02-14 16:36:02 -08001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
4
5import logging as log
6import sys
7
8try:
9 import enlighten
10 ENLIGHTEN_EXISTS = True
11except ImportError:
12 ENLIGHTEN_EXISTS = False
13
14
15class StatusPrinter:
16 '''Abstraction for printing the current target status onto the console.
17
18 Targets are ASIC tool flow steps such as build, run, cov etc. These steps
19 are sequenced by the Scheduler. There may be multiple jobs running in
20 parallel in each target. This class provides a mechanism to peridically
21 print the completion status of each target onto the terminal. Messages
22 printed by this class are rather static in nature - all the necessary
23 computations of how the jobs are progressing need to be handled externally.
24
25 The following are the 'fields' accepted by this class:
26 hms: Elapsed time in hh:mm:ss.
27 target: The tool flow step.
28 msg: The completion status message (set externally).
29 perc: Percentage of completion.
30 '''
31
32 # Print elapsed time in bold.
33 hms_fmt = ''.join(['\033[1m', u'{hms:9s}', '\033[0m'])
34 header_fmt = hms_fmt + u' [{target:^13s}]: [{msg}]'
35 status_fmt = header_fmt + u' {perc:3.0f}%'
36
37 def __init__(self):
38 # Once a target is complete, we no longer need to update it - we can
39 # just skip it. Maintaining this here provides a way to print the status
40 # one last time when it reaches 100%. It is much easier to do that here
41 # than in the Scheduler class.
42 self.target_done = {}
43
44 def print_header(self, msg):
45 '''Initilize / print the header bar.
46
47 The header bar contains an introductory message such as the legend of
48 what Q, D, ... mean.'''
49
50 log.info(self.header_fmt.format(hms="", target="legend", msg=msg))
51
52 def init_target(self, target, msg):
53 '''Initialize the status bar for each target.'''
54
55 self.target_done[target] = False
56
57 def update_target(self, target, hms, msg, perc):
58 '''Periodically update the status bar for each target.'''
59
60 if self.target_done[target]:
61 return
62
63 log.info(
64 self.status_fmt.format(hms=hms, target=target, msg=msg, perc=perc))
65 if perc == 100:
66 self.target_done[target] = True
67
68 def exit(self):
69 '''Do cleanup activities before exitting.'''
70
71 pass
72
73
74class EnlightenStatusPrinter(StatusPrinter):
75 '''Abstraction for printing status using Enlighten.
76
77 Enlighten is a third party progress bar tool. Documentation:
78 https://python-enlighten.readthedocs.io/en/stable/
79
80 Though it offers very fancy progress bar visualization, we stick to a
81 simple status bar 'pinned' to the bottom of the screen for each target
82 that displays statically, a pre-prepared message. We avoid the progress bar
83 visualization since it requires enlighten to perform some computations the
84 Scheduler already does. It also helps keep the overhead to a minimum.
85
86 Enlighten does not work if the output of dvsim is redirected to a file, for
87 example - it needs to be attached to a TTY enabled stream.
88 '''
89 def __init__(self):
90 super().__init__()
91
92 # Initialize the status_bars for header and the targets .
93 self.manager = enlighten.get_manager()
94 self.status_header = None
95 self.status_target = {}
96
97 def print_header(self, msg):
98 self.status_header = self.manager.status_bar(
99 status_format=self.header_fmt,
100 hms="",
101 target="legend",
102 msg=
103 "Q: queued, D: dispatched, P: passed, F: failed, K: killed, T: total"
104 )
105
106 def init_target(self, target, msg):
107 super().init_target(target, msg)
108 self.status_target[target] = self.manager.status_bar(
109 status_format=self.status_fmt,
110 hms="",
111 target=target,
112 msg=msg,
113 perc=0.0)
114
115 def update_target(self, target, hms, msg, perc):
116 if self.target_done[target]:
117 return
118
119 self.status_target[target].update(hms=hms, msg=msg, perc=perc)
120 if perc == 100:
121 self.target_done[target] = True
122
123 def exit(self):
124 self.status_header.close()
125 for target in self.status_target:
126 self.status_target[target].close()
127
128
129def get_status_printer():
130 """Factory method that returns a status printer instance.
131
132 If ENLIGHTEN_EXISTS (enlighten is installed) and stdout is a TTY, then
133 return an instance of EnlightenStatusPrinter, else return an instance of
134 StatusPrinter.
135 """
136 if ENLIGHTEN_EXISTS and sys.stdout.isatty():
137 return EnlightenStatusPrinter()
138 else:
139 return StatusPrinter()