Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | r""" |
| 5 | Class describing simulation configuration object |
| 6 | """ |
| 7 | |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 8 | import os |
Rupert Swarbrick | 299cae5 | 2020-05-05 11:11:06 +0100 | [diff] [blame] | 9 | import shutil |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 10 | import subprocess |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 11 | import sys |
Srikrishna Iyer | 32aae3f | 2020-03-19 17:34:00 -0700 | [diff] [blame] | 12 | from collections import OrderedDict |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 13 | |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 14 | import logging as log |
| 15 | from tabulate import tabulate |
| 16 | |
| 17 | from Deploy import CompileSim, CovAnalyze, CovMerge, CovReport, RunTest, Deploy |
Udi Jonnalagadda | df49fb8 | 2020-03-17 11:05:17 -0700 | [diff] [blame] | 18 | from FlowCfg import FlowCfg |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 19 | from Modes import BuildModes, Modes, Regressions, RunModes, Tests |
| 20 | from testplanner import testplan_utils |
| 21 | from utils import VERBOSE, find_and_substitute_wildcards |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 22 | |
| 23 | |
Rupert Swarbrick | 299cae5 | 2020-05-05 11:11:06 +0100 | [diff] [blame] | 24 | def pick_dump_format(fmts): |
| 25 | '''Choose a supported wave dumping format |
| 26 | |
| 27 | fmts is a list of formats that the chosen tool supports. Return the first |
| 28 | that we think is possible (e.g. not fsdb if Verdi is not installed). |
| 29 | |
| 30 | ''' |
| 31 | assert fmts |
| 32 | fmt = fmts[0] |
| 33 | if fmt == 'fsdb' and not shutil.which('verdi'): |
| 34 | return pick_dump_format(fmts[1:]) |
| 35 | |
| 36 | return fmt |
| 37 | |
| 38 | |
| 39 | def resolve_dump_format(tool, dump): |
| 40 | '''Decide on the correct dumping format |
| 41 | |
| 42 | This is called after reading the config file. tool is the chosen tool, |
| 43 | which will always have been resolved by this point. waves is a boolean |
| 44 | which determines whether waves should be dumped at all (from the --waves |
| 45 | argument). dump is the dumping format chosen on the command line or None. |
| 46 | |
| 47 | ''' |
| 48 | assert tool is not None |
| 49 | |
| 50 | SUPPORTED_DUMP_FMTS = { |
| 51 | 'vcs': ['fsdb', 'vpd'], |
| 52 | 'xcelium': ['fsdb', 'shm', 'vpd'] |
| 53 | } |
| 54 | |
| 55 | # Look up which dumping formats the tool supports |
| 56 | fmts = SUPPORTED_DUMP_FMTS.get(tool) |
| 57 | |
| 58 | if dump is not None: |
| 59 | # If the user has specified their preferred dumping format, use it. As |
| 60 | # a sanity check, error out if the chosen tool doesn't support the |
| 61 | # format, but only if we know about the tool. If not, we'll just assume |
| 62 | # they know what they're doing. |
| 63 | if fmts is not None and dump not in fmts: |
| 64 | log.error('Chosen tool ({}) does not support wave ' |
| 65 | 'dumping format {!r}.' |
| 66 | .format(tool, dump)) |
| 67 | sys.exit(1) |
| 68 | |
| 69 | return dump |
| 70 | |
| 71 | # If the user hasn't specified a dumping format, but has asked for waves, |
| 72 | # we need to decide on a format for them. If fmts is None, we don't know |
| 73 | # about this tool. Maybe it's a new simulator, in which case, default to |
| 74 | # VPD and hope for the best. |
| 75 | if not fmts: |
| 76 | return 'vpd' |
| 77 | |
| 78 | return pick_dump_format(fmts) |
| 79 | |
| 80 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 81 | class SimCfg(FlowCfg): |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 82 | """Simulation configuration object |
| 83 | |
| 84 | A simulation configuration class holds key information required for building a DV |
| 85 | regression framework. |
| 86 | """ |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 87 | def __init__(self, flow_cfg_file, proj_root, args): |
| 88 | super().__init__(flow_cfg_file, proj_root, args) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 89 | # Options set from command line |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 90 | self.tool = args.tool |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 91 | self.build_opts = [] |
| 92 | self.build_opts.extend(args.build_opts) |
Rupert Swarbrick | 6236f07 | 2020-06-02 10:58:21 +0100 | [diff] [blame] | 93 | self.en_build_modes = args.build_modes.copy() |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 94 | self.run_opts = [] |
| 95 | self.run_opts.extend(args.run_opts) |
| 96 | self.en_run_modes = [] |
| 97 | self.en_run_modes.extend(args.run_modes) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 98 | self.build_unique = args.build_unique |
| 99 | self.build_only = args.build_only |
| 100 | self.run_only = args.run_only |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 101 | self.reseed_ovrd = args.reseed |
| 102 | self.reseed_multiplier = args.reseed_multiplier |
| 103 | self.waves = args.waves |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 104 | self.max_waves = args.max_waves |
| 105 | self.cov = args.cov |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 106 | self.cov_merge_previous = args.cov_merge_previous |
Rupert Swarbrick | e83b55e | 2020-05-12 11:44:04 +0100 | [diff] [blame] | 107 | self.profile = args.profile or '(cfg uses profile without --profile)' |
Weicai Yang | ee57d1a | 2020-01-14 17:13:04 -0800 | [diff] [blame] | 108 | self.xprop_off = args.xprop_off |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 109 | self.no_rerun = args.no_rerun |
| 110 | self.verbosity = "{" + args.verbosity + "}" |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 111 | self.verbose = args.verbose |
| 112 | self.dry_run = args.dry_run |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 113 | self.map_full_testplan = args.map_full_testplan |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 114 | |
Srikrishna Iyer | 39ffebd | 2020-03-30 11:53:12 -0700 | [diff] [blame] | 115 | # Disable cov if --build-only is passed. |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 116 | if self.build_only: |
| 117 | self.cov = False |
Srikrishna Iyer | 39ffebd | 2020-03-30 11:53:12 -0700 | [diff] [blame] | 118 | |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 119 | # Set default sim modes for unpacking |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 120 | if self.waves is True: |
| 121 | self.en_build_modes.append("waves") |
| 122 | if self.cov is True: |
| 123 | self.en_build_modes.append("cov") |
Rupert Swarbrick | e83b55e | 2020-05-12 11:44:04 +0100 | [diff] [blame] | 124 | if args.profile is not None: |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 125 | self.en_build_modes.append("profile") |
| 126 | if self.xprop_off is not True: |
| 127 | self.en_build_modes.append("xprop") |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 128 | |
| 129 | # Options built from cfg_file files |
| 130 | self.project = "" |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 131 | self.flow = "" |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 132 | self.flow_makefile = "" |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 133 | self.build_dir = "" |
| 134 | self.run_dir = "" |
| 135 | self.sw_build_dir = "" |
| 136 | self.pass_patterns = [] |
| 137 | self.fail_patterns = [] |
| 138 | self.name = "" |
| 139 | self.dut = "" |
| 140 | self.tb = "" |
| 141 | self.testplan = "" |
| 142 | self.fusesoc_core = "" |
| 143 | self.ral_spec = "" |
| 144 | self.build_modes = [] |
| 145 | self.run_modes = [] |
| 146 | self.regressions = [] |
| 147 | |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 148 | # Options from tools - for building and running tests |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 149 | self.build_cmd = "" |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 150 | self.flist_gen_cmd = "" |
| 151 | self.flist_gen_opts = [] |
| 152 | self.flist_file = "" |
| 153 | self.run_cmd = "" |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 154 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 155 | # Generated data structures |
| 156 | self.links = {} |
| 157 | self.build_list = [] |
| 158 | self.run_list = [] |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 159 | self.cov_merge_deploy = None |
| 160 | self.cov_report_deploy = None |
Srikrishna Iyer | 32aae3f | 2020-03-19 17:34:00 -0700 | [diff] [blame] | 161 | self.results_summary = OrderedDict() |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 162 | |
Scott Johnson | fe79c4b | 2020-07-08 10:31:08 -0700 | [diff] [blame] | 163 | # If is_primary_cfg is set, then each cfg will have its own cov_deploy. |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 164 | # Maintain an array of those in cov_deploys. |
| 165 | self.cov_deploys = [] |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 166 | |
| 167 | # Parse the cfg_file file tree |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 168 | self.parse_flow_cfg(flow_cfg_file) |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 169 | self._post_parse_flow_cfg() |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 170 | |
Rupert Swarbrick | 299cae5 | 2020-05-05 11:11:06 +0100 | [diff] [blame] | 171 | # Choose a dump format now. Note that this has to happen after parsing |
| 172 | # the configuration format because our choice might depend on the |
| 173 | # chosen tool. |
| 174 | self.dump_fmt = (resolve_dump_format(self.tool, args.dump) |
| 175 | if self.waves else 'none') |
| 176 | |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 177 | # If build_unique is set, then add current timestamp to uniquify it |
| 178 | if self.build_unique: |
| 179 | self.build_dir += "_" + self.timestamp |
| 180 | |
Srikrishna Iyer | 3529221 | 2020-01-17 16:19:35 -0800 | [diff] [blame] | 181 | # Process overrides before substituting the wildcards. |
| 182 | self._process_overrides() |
| 183 | |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 184 | # Make substitutions, while ignoring the following wildcards |
| 185 | # TODO: Find a way to set these in sim cfg instead |
| 186 | ignored_wildcards = [ |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 187 | "build_mode", "index", "test", "seed", "uvm_test", "uvm_test_seq", |
Srikrishna Iyer | c612bb0 | 2020-05-12 00:33:27 -0700 | [diff] [blame] | 188 | "cov_db_dirs", "sw_test", "sw_test_is_prebuilt", "sw_build_device" |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 189 | ] |
| 190 | self.__dict__ = find_and_substitute_wildcards(self.__dict__, |
| 191 | self.__dict__, |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 192 | ignored_wildcards, |
Scott Johnson | fe79c4b | 2020-07-08 10:31:08 -0700 | [diff] [blame] | 193 | self.is_primary_cfg) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 194 | |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 195 | # Set the title for simulation results. |
| 196 | self.results_title = self.name.upper() + " Simulation Results" |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 197 | |
Scott Johnson | fe79c4b | 2020-07-08 10:31:08 -0700 | [diff] [blame] | 198 | # Stuff below only pertains to individual cfg (not primary cfg) |
Cindy Chen | f9e7ffe | 2020-03-26 00:07:36 -0700 | [diff] [blame] | 199 | # or individual selected cfgs (if select_cfgs is configured via command line) |
| 200 | # TODO: find a better way to support select_cfgs |
Scott Johnson | fe79c4b | 2020-07-08 10:31:08 -0700 | [diff] [blame] | 201 | if not self.is_primary_cfg and (not self.select_cfgs or |
| 202 | self.name in self.select_cfgs): |
Rupert Swarbrick | e83b55e | 2020-05-12 11:44:04 +0100 | [diff] [blame] | 203 | # If self.tool is None at this point, there was no --tool argument on |
| 204 | # the command line, and there is no default tool set in the config |
Scott Johnson | fe79c4b | 2020-07-08 10:31:08 -0700 | [diff] [blame] | 205 | # file. That's ok if this is a primary config (where the |
Rupert Swarbrick | e83b55e | 2020-05-12 11:44:04 +0100 | [diff] [blame] | 206 | # sub-configurations can choose tools themselves), but not otherwise. |
| 207 | if self.tool is None: |
| 208 | log.error('Config file does not specify a default tool, ' |
| 209 | 'and there was no --tool argument on the command line.') |
| 210 | sys.exit(1) |
| 211 | |
Cindy Chen | f9e7ffe | 2020-03-26 00:07:36 -0700 | [diff] [blame] | 212 | # Print info: |
Srikrishna Iyer | fbaa01a | 2020-03-19 15:32:23 -0700 | [diff] [blame] | 213 | log.info("[scratch_dir]: [%s]: [%s]", self.name, self.scratch_path) |
| 214 | |
| 215 | # Set directories with links for ease of debug / triage. |
| 216 | self.links = { |
| 217 | "D": self.scratch_path + "/" + "dispatched", |
| 218 | "P": self.scratch_path + "/" + "passed", |
| 219 | "F": self.scratch_path + "/" + "failed", |
| 220 | "K": self.scratch_path + "/" + "killed" |
| 221 | } |
| 222 | |
| 223 | # Use the default build mode for tests that do not specify it |
| 224 | if not hasattr(self, "build_mode"): |
Rupert Swarbrick | 3936246 | 2020-06-04 15:26:17 +0100 | [diff] [blame] | 225 | self.build_mode = 'default' |
Srikrishna Iyer | fbaa01a | 2020-03-19 15:32:23 -0700 | [diff] [blame] | 226 | |
| 227 | self._process_exports() |
| 228 | |
| 229 | # Create objects from raw dicts - build_modes, sim_modes, run_modes, |
Scott Johnson | fe79c4b | 2020-07-08 10:31:08 -0700 | [diff] [blame] | 230 | # tests and regressions, only if not a primary cfg obj |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 231 | self._create_objects() |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 232 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 233 | # Post init checks |
| 234 | self.__post_init__() |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 235 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 236 | def __post_init__(self): |
| 237 | # Run some post init checks |
| 238 | super().__post_init__() |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 239 | |
Weicai Yang | fc2ff3b | 2020-03-19 18:05:14 -0700 | [diff] [blame] | 240 | def kill(self): |
| 241 | '''kill running processes and jobs gracefully |
| 242 | ''' |
| 243 | super().kill() |
| 244 | for item in self.cov_deploys: |
| 245 | item.kill() |
| 246 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 247 | # Purge the output directories. This operates on self. |
| 248 | def _purge(self): |
Rupert Swarbrick | 8e57719 | 2020-03-24 14:36:30 +0000 | [diff] [blame] | 249 | if self.scratch_path: |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 250 | try: |
| 251 | log.info("Purging scratch path %s", self.scratch_path) |
| 252 | os.system("/bin/rm -rf " + self.scratch_path) |
| 253 | except IOError: |
| 254 | log.error('Failed to purge scratch directory %s', |
| 255 | self.scratch_path) |
| 256 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 257 | def _create_objects(self): |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 258 | # Create build and run modes objects |
Rupert Swarbrick | 6236f07 | 2020-06-02 10:58:21 +0100 | [diff] [blame] | 259 | self.build_modes = Modes.create_modes(BuildModes, self.build_modes) |
| 260 | self.run_modes = Modes.create_modes(RunModes, self.run_modes) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 261 | |
| 262 | # Walk through build modes enabled on the CLI and append the opts |
| 263 | for en_build_mode in self.en_build_modes: |
Rupert Swarbrick | 6236f07 | 2020-06-02 10:58:21 +0100 | [diff] [blame] | 264 | build_mode_obj = Modes.find_mode(en_build_mode, self.build_modes) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 265 | if build_mode_obj is not None: |
| 266 | self.build_opts.extend(build_mode_obj.build_opts) |
| 267 | self.run_opts.extend(build_mode_obj.run_opts) |
| 268 | else: |
| 269 | log.error( |
| 270 | "Mode \"%s\" enabled on the the command line is not defined", |
| 271 | en_build_mode) |
| 272 | sys.exit(1) |
| 273 | |
| 274 | # Walk through run modes enabled on the CLI and append the opts |
| 275 | for en_run_mode in self.en_run_modes: |
Rupert Swarbrick | 6236f07 | 2020-06-02 10:58:21 +0100 | [diff] [blame] | 276 | run_mode_obj = Modes.find_mode(en_run_mode, self.run_modes) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 277 | if run_mode_obj is not None: |
| 278 | self.run_opts.extend(run_mode_obj.run_opts) |
| 279 | else: |
| 280 | log.error( |
| 281 | "Mode \"%s\" enabled on the the command line is not defined", |
| 282 | en_run_mode) |
| 283 | sys.exit(1) |
| 284 | |
| 285 | # Create tests from given list of items |
Rupert Swarbrick | 3936246 | 2020-06-04 15:26:17 +0100 | [diff] [blame] | 286 | self.tests = Tests.create_tests(self.tests, self) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 287 | |
Srikrishna Iyer | 113d733 | 2020-01-21 14:06:47 -0800 | [diff] [blame] | 288 | # Regressions |
| 289 | # Parse testplan if provided. |
| 290 | if self.testplan != "": |
| 291 | self.testplan = testplan_utils.parse_testplan(self.testplan) |
| 292 | # Extract tests in each milestone and add them as regression target. |
| 293 | self.regressions.extend(self.testplan.get_milestone_regressions()) |
| 294 | |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 295 | # Create regressions |
Rupert Swarbrick | 3936246 | 2020-06-04 15:26:17 +0100 | [diff] [blame] | 296 | self.regressions = Regressions.create_regressions(self.regressions, |
| 297 | self, self.tests) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 298 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 299 | def _print_list(self): |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 300 | for list_item in self.list_items: |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 301 | log.info("---- List of %s in %s ----", list_item, self.name) |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 302 | if hasattr(self, list_item): |
| 303 | items = getattr(self, list_item) |
| 304 | for item in items: |
| 305 | log.info(item) |
| 306 | else: |
| 307 | log.error("Item %s does not exist!", list_item) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 308 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 309 | def _create_build_and_run_list(self): |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 310 | # Walk through the list of items to run and create the build and run |
| 311 | # objects. |
| 312 | # Allow multiple regressions to run as long as the do not enable |
| 313 | # sim_modes or run_modes |
| 314 | def get_overlapping_tests(tests, run_list_names): |
| 315 | overlapping_tests = [] |
| 316 | for test in tests: |
| 317 | if test.name in run_list_names: |
| 318 | overlapping_tests.append(test) |
| 319 | return overlapping_tests |
| 320 | |
| 321 | def prune_items(items, marked_items): |
| 322 | pruned_items = [] |
| 323 | for item in items: |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 324 | if item not in marked_items: |
| 325 | pruned_items.append(item) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 326 | return pruned_items |
| 327 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 328 | # Check if there are items to run |
| 329 | if self.items == []: |
| 330 | log.error( |
| 331 | "No items provided for running this simulation / regression") |
| 332 | sys.exit(1) |
| 333 | |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 334 | items_list = self.items |
| 335 | run_list_names = [] |
| 336 | marked_items = [] |
| 337 | # Process regressions first |
| 338 | for regression in self.regressions: |
| 339 | if regression.name in items_list: |
| 340 | overlapping_tests = get_overlapping_tests( |
| 341 | regression.tests, run_list_names) |
| 342 | if overlapping_tests != []: |
Srikrishna Iyer | c3811a0 | 2020-03-11 14:22:05 -0700 | [diff] [blame] | 343 | log.error( |
| 344 | "Regression \"%s\" added for run contains tests that overlap with " |
| 345 | "other regressions added. This can result in conflicting " |
| 346 | "build / run_opts to be set causing unexpected results.", |
| 347 | regression.name) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 348 | sys.exit(1) |
| 349 | |
| 350 | self.run_list.extend(regression.tests) |
| 351 | # Merge regression's build and run opts with its tests and their |
| 352 | # build_modes |
| 353 | regression.merge_regression_opts() |
| 354 | run_list_names.extend(regression.test_names) |
| 355 | marked_items.append(regression.name) |
| 356 | items_list = prune_items(items_list, marked_items) |
| 357 | |
| 358 | # Process individual tests |
| 359 | for test in self.tests: |
| 360 | if test.name in items_list: |
| 361 | overlapping_tests = get_overlapping_tests([test], |
| 362 | run_list_names) |
| 363 | if overlapping_tests == []: |
| 364 | self.run_list.append(test) |
| 365 | run_list_names.append(test.name) |
| 366 | marked_items.append(test.name) |
| 367 | items_list = prune_items(items_list, marked_items) |
| 368 | |
| 369 | # Merge the global build and run opts |
| 370 | Tests.merge_global_opts(self.run_list, self.build_opts, self.run_opts) |
| 371 | |
Srikrishna Iyer | c3811a0 | 2020-03-11 14:22:05 -0700 | [diff] [blame] | 372 | # Check if all items have been processed |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 373 | if items_list != []: |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 374 | log.error( |
Srikrishna Iyer | c3811a0 | 2020-03-11 14:22:05 -0700 | [diff] [blame] | 375 | "The items %s added for run were not found in \n%s!\n " |
| 376 | "Use the --list switch to see a list of available " |
| 377 | "tests / regressions.", items_list, self.flow_cfg_file) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 378 | |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 379 | # Process reseed override and create the build_list |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 380 | build_list_names = [] |
| 381 | for test in self.run_list: |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 382 | # Override reseed if available. |
Rupert Swarbrick | e83b55e | 2020-05-12 11:44:04 +0100 | [diff] [blame] | 383 | if self.reseed_ovrd is not None: |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 384 | test.reseed = self.reseed_ovrd |
| 385 | |
| 386 | # Apply reseed multiplier if set on the command line. |
| 387 | test.reseed *= self.reseed_multiplier |
| 388 | |
| 389 | # Create the unique set of builds needed. |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 390 | if test.build_mode.name not in build_list_names: |
| 391 | self.build_list.append(test.build_mode) |
| 392 | build_list_names.append(test.build_mode.name) |
| 393 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 394 | def _create_dirs(self): |
| 395 | '''Create initial set of directories |
| 396 | ''' |
| 397 | # Invoking system calls has a performance penalty. |
| 398 | # Construct a single command line chained with '&&' to invoke |
| 399 | # the system call only once, rather than multiple times. |
| 400 | create_link_dirs_cmd = "" |
| 401 | for link in self.links.keys(): |
| 402 | create_link_dirs_cmd += "/bin/rm -rf " + self.links[link] + " && " |
| 403 | create_link_dirs_cmd += "mkdir -p " + self.links[link] + " && " |
| 404 | create_link_dirs_cmd += " true" |
| 405 | |
| 406 | try: |
| 407 | os.system(create_link_dirs_cmd) |
| 408 | except IOError: |
| 409 | log.error("Error running when running the cmd \"%s\"", |
| 410 | create_link_dirs_cmd) |
| 411 | sys.exit(1) |
| 412 | |
Rupert Swarbrick | 7b207b7 | 2020-06-11 09:43:46 +0100 | [diff] [blame] | 413 | def _expand_run_list(self, build_map): |
| 414 | '''Generate a list of tests to be run |
| 415 | |
| 416 | For each test in tests, we add it test.reseed times. The ordering is |
| 417 | interleaved so that we run through all of the tests as soon as |
| 418 | possible. If there are multiple tests and they have different reseed |
| 419 | values, they are "fully interleaved" at the start (so if there are |
| 420 | tests A, B with reseed values of 5 and 2, respectively, then the list |
| 421 | will be ABABAAA). |
| 422 | |
| 423 | build_map is a dictionary from build name to a CompileSim object. Each |
| 424 | test is added to the CompileSim item that it depends on (signifying |
| 425 | that the test should be built once the build on which it depends is |
| 426 | done). |
| 427 | |
| 428 | cfg is a SimCfg object, passed to the RunTest constructor. |
| 429 | |
| 430 | ''' |
| 431 | tagged = [] |
| 432 | for test in self.run_list: |
| 433 | for idx in range(test.reseed): |
| 434 | tagged.append((idx, |
| 435 | test, |
| 436 | RunTest(idx, test, self))) |
| 437 | |
| 438 | # Stably sort the tagged list by the 1st coordinate |
| 439 | tagged.sort(key=lambda x: x[0]) |
| 440 | |
| 441 | # Now iterate over it again, adding tests to build_map (in the |
| 442 | # interleaved order) and collecting up the RunTest objects. |
| 443 | runs = [] |
| 444 | for _, test, run in tagged: |
| 445 | build_map[test.build_mode].sub.append(run) |
| 446 | runs.append(run) |
| 447 | |
| 448 | return runs |
| 449 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 450 | def _create_deploy_objects(self): |
| 451 | '''Create deploy objects from the build and run lists. |
| 452 | ''' |
| 453 | |
| 454 | # Create the build and run list first |
| 455 | self._create_build_and_run_list() |
| 456 | |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 457 | builds = [] |
| 458 | build_map = {} |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 459 | for build in self.build_list: |
| 460 | item = CompileSim(build, self) |
| 461 | builds.append(item) |
| 462 | build_map[build] = item |
| 463 | |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 464 | self.builds = builds |
Rupert Swarbrick | 7b207b7 | 2020-06-11 09:43:46 +0100 | [diff] [blame] | 465 | self.runs = ([] |
| 466 | if self.build_only |
| 467 | else self._expand_run_list(build_map)) |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 468 | if self.run_only is True: |
Rupert Swarbrick | 7b207b7 | 2020-06-11 09:43:46 +0100 | [diff] [blame] | 469 | self.deploy = self.runs |
Srikrishna Iyer | 09a81e9 | 2019-12-30 10:47:57 -0800 | [diff] [blame] | 470 | else: |
| 471 | self.deploy = builds |
| 472 | |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 473 | # Create cov_merge and cov_report objects |
| 474 | if self.cov: |
| 475 | self.cov_merge_deploy = CovMerge(self) |
| 476 | self.cov_report_deploy = CovReport(self) |
| 477 | # Generate reports only if merge was successful; add it as a dependency |
| 478 | # of merge. |
| 479 | self.cov_merge_deploy.sub.append(self.cov_report_deploy) |
| 480 | |
Srikrishna Iyer | 544da8d | 2020-01-14 23:51:41 -0800 | [diff] [blame] | 481 | # Create initial set of directories before kicking off the regression. |
| 482 | self._create_dirs() |
| 483 | |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 484 | def create_deploy_objects(self): |
| 485 | '''Public facing API for _create_deploy_objects(). |
| 486 | ''' |
| 487 | super().create_deploy_objects() |
| 488 | |
| 489 | # Also, create cov_deploys |
| 490 | if self.cov: |
| 491 | for item in self.cfgs: |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 492 | if item.cov: |
| 493 | self.cov_deploys.append(item.cov_merge_deploy) |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 494 | |
| 495 | # deploy additional commands as needed. We do this separated for coverage |
| 496 | # since that needs to happen at the end. |
| 497 | def deploy_objects(self): |
| 498 | '''This is a public facing API, so we use "self.cfgs" instead of self. |
| 499 | ''' |
| 500 | # Invoke the base class method to run the regression. |
| 501 | super().deploy_objects() |
| 502 | |
| 503 | # If coverage is enabled, then deploy the coverage tasks. |
| 504 | if self.cov: |
| 505 | Deploy.deploy(self.cov_deploys) |
| 506 | |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 507 | def _cov_analyze(self): |
| 508 | '''Use the last regression coverage data to open up the GUI tool to |
| 509 | analyze the coverage. |
| 510 | ''' |
| 511 | cov_analyze_deploy = CovAnalyze(self) |
Srikrishna Iyer | 39ffebd | 2020-03-30 11:53:12 -0700 | [diff] [blame] | 512 | self.deploy = [cov_analyze_deploy] |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 513 | |
| 514 | def cov_analyze(self): |
| 515 | '''Public facing API for analyzing coverage. |
| 516 | ''' |
| 517 | for item in self.cfgs: |
| 518 | item._cov_analyze() |
| 519 | |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 520 | def _gen_results(self): |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 521 | ''' |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 522 | The function is called after the regression has completed. It collates the |
| 523 | status of all run targets and generates a dict. It parses the testplan and |
| 524 | maps the generated result to the testplan entries to generate a final table |
| 525 | (list). It also prints the full list of failures for debug / triage. If cov |
| 526 | is enabled, then the summary coverage report is also generated. The final |
| 527 | result is in markdown format. |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 528 | ''' |
| 529 | |
| 530 | # TODO: add support for html |
| 531 | def retrieve_result(name, results): |
| 532 | for item in results: |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 533 | if name == item["name"]: |
| 534 | return item |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 535 | return None |
| 536 | |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 537 | def gen_results_sub(items, results, fail_msgs): |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 538 | ''' |
| 539 | Generate the results table from the test runs (builds are ignored). |
| 540 | The table has 3 columns - name, passing and total as a list of dicts. |
| 541 | This is populated for all tests. The number of passing and total is |
| 542 | in reference to the number of iterations or reseeds for that test. |
| 543 | This list of dicts is directly consumed by the Testplan::results_table |
| 544 | method for testplan mapping / annotation. |
| 545 | ''' |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 546 | for item in items: |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 547 | if item.status == "F": |
| 548 | fail_msgs += item.fail_msg |
| 549 | |
| 550 | # Generate results table for runs. |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 551 | if item.target == "run": |
| 552 | result = retrieve_result(item.name, results) |
| 553 | if result is None: |
| 554 | result = {"name": item.name, "passing": 0, "total": 0} |
| 555 | results.append(result) |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 556 | if item.status == "P": |
| 557 | result["passing"] += 1 |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 558 | result["total"] += 1 |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 559 | (results, fail_msgs) = gen_results_sub(item.sub, results, |
| 560 | fail_msgs) |
| 561 | return (results, fail_msgs) |
| 562 | |
| 563 | regr_results = [] |
| 564 | fail_msgs = "" |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 565 | deployed_items = self.deploy |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 566 | if self.cov: |
| 567 | deployed_items.append(self.cov_merge_deploy) |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 568 | (regr_results, fail_msgs) = gen_results_sub(deployed_items, |
| 569 | regr_results, fail_msgs) |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 570 | |
| 571 | # Add title if there are indeed failures |
| 572 | if fail_msgs != "": |
| 573 | fail_msgs = "\n## List of Failures\n" + fail_msgs |
Srikrishna Iyer | 442d8db | 2020-03-05 15:17:17 -0800 | [diff] [blame] | 574 | self.errors_seen = True |
Srikrishna Iyer | 7cf7cad | 2020-01-08 11:32:53 -0800 | [diff] [blame] | 575 | |
| 576 | # Generate results table for runs. |
Srikrishna Iyer | f578e7c | 2020-01-29 13:11:58 -0800 | [diff] [blame] | 577 | results_str = "## " + self.results_title + "\n" |
| 578 | results_str += "### " + self.timestamp_long + "\n" |
Michael Schaffner | cb61dc4 | 2020-07-10 16:36:39 -0700 | [diff] [blame] | 579 | if self.revision_string: |
| 580 | results_str += "### " + self.revision_string + "\n" |
Srikrishna Iyer | f578e7c | 2020-01-29 13:11:58 -0800 | [diff] [blame] | 581 | |
| 582 | # Add path to testplan. |
Weicai Yang | 2b1f219 | 2020-04-08 17:26:31 -0700 | [diff] [blame] | 583 | if hasattr(self, "testplan_doc_path"): |
Rupert Swarbrick | 3936246 | 2020-06-04 15:26:17 +0100 | [diff] [blame] | 584 | testplan = "https://" + self.doc_server + '/' + self.testplan_doc_path |
Weicai Yang | 2b1f219 | 2020-04-08 17:26:31 -0700 | [diff] [blame] | 585 | else: |
| 586 | testplan = "https://" + self.doc_server + '/' + self.rel_path |
| 587 | testplan = testplan.replace("/dv", "/doc/dv_plan/#testplan") |
| 588 | |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 589 | results_str += "### [Testplan](" + testplan + ")\n" |
| 590 | results_str += "### Simulator: " + self.tool.upper() + "\n\n" |
Srikrishna Iyer | f578e7c | 2020-01-29 13:11:58 -0800 | [diff] [blame] | 591 | |
Srikrishna Iyer | 8ce80d0 | 2020-02-05 10:53:19 -0800 | [diff] [blame] | 592 | if regr_results == []: |
| 593 | results_str += "No results to display.\n" |
| 594 | |
| 595 | else: |
| 596 | # TODO: check if testplan is not null? |
| 597 | # Map regr results to the testplan entries. |
| 598 | results_str += self.testplan.results_table( |
| 599 | regr_results=regr_results, |
| 600 | map_full_testplan=self.map_full_testplan) |
| 601 | results_str += "\n" |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 602 | self.results_summary = self.testplan.results_summary |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 603 | |
Srikrishna Iyer | c3811a0 | 2020-03-11 14:22:05 -0700 | [diff] [blame] | 604 | # Append coverage results of coverage was enabled. |
Srikrishna Iyer | c4e3203 | 2020-03-20 02:43:07 -0700 | [diff] [blame] | 605 | if self.cov: |
| 606 | if self.cov_report_deploy.status == "P": |
| 607 | results_str += "\n## Coverage Results\n" |
Srikrishna Iyer | 39ffebd | 2020-03-30 11:53:12 -0700 | [diff] [blame] | 608 | # Link the dashboard page using "cov_report_page" value. |
Weicai Yang | 025d35d | 2020-04-21 11:15:32 -0700 | [diff] [blame] | 609 | if hasattr(self, "cov_report_page"): |
Srikrishna Iyer | 39ffebd | 2020-03-30 11:53:12 -0700 | [diff] [blame] | 610 | results_str += "\n### [Coverage Dashboard]" |
Srikrishna Iyer | 91e1b76 | 2020-07-30 10:00:04 -0700 | [diff] [blame^] | 611 | if self.args.publish: |
| 612 | cov_report_page_path = "cov_report" |
| 613 | else: |
| 614 | cov_report_page_path = self.cov_report_dir |
| 615 | cov_report_page_path += "/" + self.cov_report_page |
| 616 | results_str += "({})\n\n".format(cov_report_page_path) |
Srikrishna Iyer | c4e3203 | 2020-03-20 02:43:07 -0700 | [diff] [blame] | 617 | results_str += self.cov_report_deploy.cov_results |
| 618 | self.results_summary[ |
| 619 | "Coverage"] = self.cov_report_deploy.cov_total |
Srikrishna Iyer | 39ffebd | 2020-03-30 11:53:12 -0700 | [diff] [blame] | 620 | else: |
| 621 | self.results_summary["Coverage"] = "--" |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 622 | |
Srikrishna Iyer | c3811a0 | 2020-03-11 14:22:05 -0700 | [diff] [blame] | 623 | # append link of detail result to block name |
| 624 | self.results_summary["Name"] = self._get_results_page_link( |
| 625 | self.results_summary["Name"]) |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 626 | |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 627 | # Append failures for triage |
| 628 | self.results_md = results_str + fail_msgs |
Srikrishna Iyer | 442d8db | 2020-03-05 15:17:17 -0800 | [diff] [blame] | 629 | results_str += fail_msgs |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 630 | |
| 631 | # Write results to the scratch area |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 632 | results_file = self.scratch_path + "/results_" + self.timestamp + ".md" |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 633 | f = open(results_file, 'w') |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 634 | f.write(self.results_md) |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 635 | f.close() |
Srikrishna Iyer | 4f0b090 | 2020-01-25 02:28:47 -0800 | [diff] [blame] | 636 | |
| 637 | # Return only the tables |
Srikrishna Iyer | fbaa01a | 2020-03-19 15:32:23 -0700 | [diff] [blame] | 638 | log.info("[results page]: [%s] [%s]", self.name, results_file) |
Srikrishna Iyer | 6400905 | 2020-01-13 11:27:39 -0800 | [diff] [blame] | 639 | return results_str |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 640 | |
Weicai Yang | fd2c22e | 2020-02-11 18:37:14 -0800 | [diff] [blame] | 641 | def gen_results_summary(self): |
| 642 | |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 643 | # sim summary result has 5 columns from each SimCfg.results_summary |
Srikrishna Iyer | 442d8db | 2020-03-05 15:17:17 -0800 | [diff] [blame] | 644 | header = ["Name", "Passing", "Total", "Pass Rate"] |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 645 | if self.cov: |
| 646 | header.append('Coverage') |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 647 | table = [header] |
| 648 | colalign = ("center", ) * len(header) |
Weicai Yang | fd2c22e | 2020-02-11 18:37:14 -0800 | [diff] [blame] | 649 | for item in self.cfgs: |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 650 | row = [] |
| 651 | for title in item.results_summary: |
| 652 | row.append(item.results_summary[title]) |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 653 | if row == []: |
| 654 | continue |
Srikrishna Iyer | 32aae3f | 2020-03-19 17:34:00 -0700 | [diff] [blame] | 655 | table.append(row) |
Srikrishna Iyer | 86f6bce | 2020-02-27 19:02:04 -0800 | [diff] [blame] | 656 | self.results_summary_md = "## " + self.results_title + " (Summary)\n" |
Weicai Yang | 680f7e2 | 2020-02-26 18:10:24 -0800 | [diff] [blame] | 657 | self.results_summary_md += "### " + self.timestamp_long + "\n" |
Michael Schaffner | cb61dc4 | 2020-07-10 16:36:39 -0700 | [diff] [blame] | 658 | if self.revision_string: |
| 659 | self.results_summary_md += "### " + self.revision_string + "\n" |
Weicai Yang | fd2c22e | 2020-02-11 18:37:14 -0800 | [diff] [blame] | 660 | self.results_summary_md += tabulate(table, |
| 661 | headers="firstrow", |
| 662 | tablefmt="pipe", |
| 663 | colalign=colalign) |
| 664 | print(self.results_summary_md) |
| 665 | return self.results_summary_md |
| 666 | |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 667 | def _publish_results(self): |
| 668 | '''Publish coverage results to the opentitan web server.''' |
| 669 | super()._publish_results() |
| 670 | |
| 671 | if self.cov: |
| 672 | results_server_dir_url = self.results_server_dir.replace( |
| 673 | self.results_server_prefix, self.results_server_url_prefix) |
| 674 | |
| 675 | log.info("Publishing coverage results to %s", |
| 676 | results_server_dir_url) |
Rupert Swarbrick | 6cc2011 | 2020-04-24 09:44:35 +0100 | [diff] [blame] | 677 | cmd = (self.results_server_cmd + " -m cp -R " + |
Srikrishna Iyer | 91e1b76 | 2020-07-30 10:00:04 -0700 | [diff] [blame^] | 678 | self.cov_report_dir + " " + self.results_server_dir) |
Srikrishna Iyer | 2a710a4 | 2020-02-10 10:39:15 -0800 | [diff] [blame] | 679 | try: |
| 680 | cmd_output = subprocess.run(args=cmd, |
| 681 | shell=True, |
| 682 | stdout=subprocess.PIPE, |
| 683 | stderr=subprocess.STDOUT) |
| 684 | log.log(VERBOSE, cmd_output.stdout.decode("utf-8")) |
| 685 | except Exception as e: |
| 686 | log.error("%s: Failed to publish results:\n\"%s\"", e, |
| 687 | str(cmd)) |