-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathmsg_buckets.py
More file actions
204 lines (171 loc) · 7.32 KB
/
Copy pathmsg_buckets.py
File metadata and controls
204 lines (171 loc) · 7.32 KB
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
"""This class holds a dict of message buckets according to the format defined
upon construction. It is meant to hold all message buckets of a build / tool
run, and provides convenience functions that streamline result aggregation
and printout in the Dvsim flow classes.
"""
import copy
from pathlib import Path
import hjson
from dvsim.logging import log
from dvsim.msg_bucket import MsgBucket
from dvsim.utils.print import print_msg_list
class MsgBuckets:
def __init__(self, bucket_cfgs: list[dict]) -> None:
self.buckets = {
f"{b['category']}_{b['severity']}": MsgBucket(b["category"], b["severity"], b["label"])
for b in bucket_cfgs
}
def clear(self) -> None:
"""Clear all signatures in all buckets."""
for b in self.buckets.values():
b.clear()
def get_labels(self, severity_filter: list[str] | None = None) -> list[str]:
"""Return all bucket labels as a list.
If severity_filter is not empty, only the buckets with the listed
severities will be returned.
"""
if severity_filter is None:
severity_filter = []
for s in severity_filter:
if not MsgBucket.severity_is_known(s):
msg = f"Unknown severity {s}"
raise RuntimeError(msg)
return [
b.label
for b in self.buckets.values()
if not severity_filter or b.severity in severity_filter
]
def get_keys(self, severity_filter: list[str] | None = None) -> list[str]:
"""Returns all bucket keys as a list.
If severity_filter is not empty, only the buckets with the listed
severities will be returned.
"""
if severity_filter is None:
severity_filter = []
for s in severity_filter:
if not MsgBucket.severity_is_known(s):
msg = f"Unknown severity {s}"
raise RuntimeError(msg)
keys = []
for key, b in self.buckets.items():
if not severity_filter or b.severity in severity_filter:
keys.append(key)
return keys
def get_counts(
self, keys: list[str] | None = None, severity_filter: list[str] | None = None
) -> list[int]:
"""Get bucket count totals as a list of integers.
The bucket keys can be supplied externally, in which case the
severity_filter does not apply. If a specific bucket key does not
exist, a 0 count will be returned for that specific key.
If severity_filter is not empty, only the buckets with the listed
severities will be returned.
"""
if severity_filter is None:
severity_filter = []
if not keys:
keys = self.get_keys(severity_filter)
counts = []
for k in keys:
c = self.buckets[k].count() if k in self.buckets else 0
counts.append(c)
return counts
def get_counts_md(
self,
keys: list[str] | None = None,
severity_filter: list[str] | None = None,
colmap: bool = True,
) -> list[str]:
"""Get bucket count totals as a list of strings with optional colormap.
The bucket keys can be supplied externally, in which case the
severity_filter does not apply. If a specific bucket key does not
exist, a "--" string will be returned for that specific key.
If severity_filter is not empty, only the buckets with the listed
severities will be returned.
"""
if severity_filter is None:
severity_filter = []
if not keys:
keys = self.get_keys(severity_filter)
counts = []
for k in keys:
c = self.buckets[k].count_md(colmap) if k in self.buckets else "--"
counts.append(c)
return counts
def has_signatures(self, severity_filter: list[str] | None = None) -> bool:
"""Checks whether there are any signatures with specified severities.
If severity_filter is empty, the method returns true if any of the
buckets contains a nonzero amount of signatures.
"""
if severity_filter is None:
severity_filter = []
return any(self.get_counts(severity_filter=severity_filter))
def print_signatures_md(
self, severity_filter: list[str] | None = None, max_per_bucket: int = -1
) -> str:
"""Render signatures into a string buffer.
The bucket labels are used as subtitles in this printout.
If severity_filter is not empty, only the buckets with the listed
severities will be returned.
The number of messages printed per bucket can be limited by
setting max_per_bucket to a nonnegative value.
"""
if severity_filter is None:
severity_filter = []
msgs = ""
keys = self.get_keys(severity_filter)
for k in keys:
msgs += print_msg_list(
f"#### {self.buckets[k].label}",
self.buckets[k].signatures,
max_per_bucket,
)
return msgs
def merge(self, other) -> None:
"""Merge other MsgBuckets object into this one.
This will append signatures to the corresponding bucket if it exists.
If the bucket does not yet exist it will be created.
"""
for k, b in other.buckets.items():
if k in self.buckets:
self.buckets[k].merge(b)
else:
self.buckets.update({k: copy.deepcopy(b)})
def __add__(self, other):
"""Merges two MsgBucket objects into one.
Buckets will be uniquified, and signatures in buckets with the same
category and severity will be merged.
"""
mb = copy.deepcopy(self)
mb.merge(other)
return mb
# TODO(#9079): remove the method below once the log parsing has been
# merged into the Dvsim core code.
def load_hjson(self, result_path: Path) -> None:
"""Clear internal data structure and initialize with values in Hjson."""
self.clear()
try:
with result_path.open() as results_file:
results_dict = hjson.load(results_file, use_decimal=True)
except OSError as err:
log.warning("%s", err)
if "flow_error" not in self.buckets:
self.buckets.update({"flow_error": MsgBucket("flow", "error")})
self.buckets["flow_error"].signatures.append(f"IOError: {err}")
return
for k, signatures in results_dict.items():
if not isinstance(signatures, list):
msg = f"Signatures in {k} must be a list of strings"
raise RuntimeError(msg)
# check the key is in the bucket list to avoid an exception
if k in self.buckets:
self.buckets[k].signatures.extend(signatures)
else:
# This is the case when a Signature defined in the python
# parser is not in the Hjson. It is ok the other way around,
# i.e. the bucket is in the hjson but in the parser list.
msg = f"Signatures in {k} must be in the Hjson buckets list."
log.warning(msg)