| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 the V8 project authors. All rights reserved. | 2 # Copyright 2014 the V8 project authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """ | 6 """ |
| 7 Performance runner for d8. | 7 Performance runner for d8. |
| 8 | 8 |
| 9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json | 9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json |
| 10 | 10 |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 162 | 162 |
| 163 def __add__(self, other): | 163 def __add__(self, other): |
| 164 self.traces += other.traces | 164 self.traces += other.traces |
| 165 self.errors += other.errors | 165 self.errors += other.errors |
| 166 return self | 166 return self |
| 167 | 167 |
| 168 def __str__(self): # pragma: no cover | 168 def __str__(self): # pragma: no cover |
| 169 return str(self.ToDict()) | 169 return str(self.ToDict()) |
| 170 | 170 |
| 171 | 171 |
| 172 class Measurement(object): |
| 173 """Represents a series of results of one trace. |
| 174 |
| 175 The results are from repetitive runs of the same executable. They are |
| 176 gathered by repeated calls to ConsumeOutput. |
| 177 """ |
| 178 def __init__(self, graphs, units, results_regexp, stddev_regexp): |
| 179 self.name = graphs[-1] |
| 180 self.graphs = graphs |
| 181 self.units = units |
| 182 self.results_regexp = results_regexp |
| 183 self.stddev_regexp = stddev_regexp |
| 184 self.results = [] |
| 185 self.errors = [] |
| 186 self.stddev = "" |
| 187 |
| 188 def ConsumeOutput(self, stdout): |
| 189 try: |
| 190 result = re.search(self.results_regexp, stdout, re.M).group(1) |
| 191 self.results.append(str(float(result))) |
| 192 except ValueError: |
| 193 self.errors.append("Regexp \"%s\" returned a non-numeric for test %s." |
| 194 % (self.results_regexp, self.name)) |
| 195 except: |
| 196 self.errors.append("Regexp \"%s\" didn't match for test %s." |
| 197 % (self.results_regexp, self.name)) |
| 198 |
| 199 try: |
| 200 if self.stddev_regexp and self.stddev: |
| 201 self.errors.append("Test %s should only run once since a stddev " |
| 202 "is provided by the test." % self.name) |
| 203 if self.stddev_regexp: |
| 204 self.stddev = re.search(self.stddev_regexp, stdout, re.M).group(1) |
| 205 except: |
| 206 self.errors.append("Regexp \"%s\" didn't match for test %s." |
| 207 % (self.stddev_regexp, self.name)) |
| 208 |
| 209 def GetResults(self): |
| 210 return Results([{ |
| 211 "graphs": self.graphs, |
| 212 "units": self.units, |
| 213 "results": self.results, |
| 214 "stddev": self.stddev, |
| 215 }], self.errors) |
| 216 |
| 217 |
| 218 def AccumulateResults(graph_names, trace_configs, iter_output, calc_total): |
| 219 """Iterates over the output of multiple benchmark reruns and accumulates |
| 220 results for a configured list of traces. |
| 221 |
| 222 Args: |
| 223 graph_names: List of names that configure the base path of the traces. E.g. |
| 224 ['v8', 'Octane']. |
| 225 trace_configs: List of "TraceConfig" instances. Each trace config defines |
| 226 how to perform a measurement. |
| 227 iter_output: Iterator over the standard output of each test run. |
| 228 calc_total: Boolean flag to speficy the calculation of a summary trace. |
| 229 Returns: A "Results" object. |
| 230 """ |
| 231 measurements = [trace.CreateMeasurement() for trace in trace_configs] |
| 232 for stdout in iter_output(): |
| 233 for measurement in measurements: |
| 234 measurement.ConsumeOutput(stdout) |
| 235 |
| 236 res = reduce(lambda r, m: r + m.GetResults(), measurements, Results()) |
| 237 |
| 238 if not res.traces or not calc_total: |
| 239 return res |
| 240 |
| 241 # Assume all traces have the same structure. |
| 242 if len(set(map(lambda t: len(t["results"]), res.traces))) != 1: |
| 243 res.errors.append("Not all traces have the same number of results.") |
| 244 return res |
| 245 |
| 246 # Calculate the geometric means for all traces. Above we made sure that |
| 247 # there is at least one trace and that the number of results is the same |
| 248 # for each trace. |
| 249 n_results = len(res.traces[0]["results"]) |
| 250 total_results = [GeometricMean(t["results"][i] for t in res.traces) |
| 251 for i in range(0, n_results)] |
| 252 res.traces.append({ |
| 253 "graphs": graph_names + ["Total"], |
| 254 "units": res.traces[0]["units"], |
| 255 "results": total_results, |
| 256 "stddev": "", |
| 257 }) |
| 258 return res |
| 259 |
| 260 |
| 261 def AccumulateGenericResults(graph_names, suite_units, iter_output): |
| 262 """Iterates over the output of multiple benchmark reruns and accumulates |
| 263 generic results. |
| 264 |
| 265 Args: |
| 266 graph_names: List of names that configure the base path of the traces. E.g. |
| 267 ['v8', 'Octane']. |
| 268 suite_units: Measurement default units as defined by the benchmark suite. |
| 269 iter_output: Iterator over the standard output of each test run. |
| 270 Returns: A "Results" object. |
| 271 """ |
| 272 traces = OrderedDict() |
| 273 for stdout in iter_output(): |
| 274 for line in stdout.strip().splitlines(): |
| 275 match = GENERIC_RESULTS_RE.match(line) |
| 276 if match: |
| 277 stddev = "" |
| 278 graph = match.group(1) |
| 279 trace = match.group(2) |
| 280 body = match.group(3) |
| 281 units = match.group(4) |
| 282 match_stddev = RESULT_STDDEV_RE.match(body) |
| 283 match_list = RESULT_LIST_RE.match(body) |
| 284 errors = [] |
| 285 if match_stddev: |
| 286 result, stddev = map(str.strip, match_stddev.group(1).split(",")) |
| 287 results = [result] |
| 288 elif match_list: |
| 289 results = map(str.strip, match_list.group(1).split(",")) |
| 290 else: |
| 291 results = [body.strip()] |
| 292 |
| 293 try: |
| 294 results = map(lambda r: str(float(r)), results) |
| 295 except ValueError: |
| 296 results = [] |
| 297 errors = ["Found non-numeric in %s" % |
| 298 "/".join(graph_names + [graph, trace])] |
| 299 |
| 300 trace_result = traces.setdefault(trace, Results([{ |
| 301 "graphs": graph_names + [graph, trace], |
| 302 "units": (units or suite_units).strip(), |
| 303 "results": [], |
| 304 "stddev": "", |
| 305 }], errors)) |
| 306 trace_result.traces[0]["results"].extend(results) |
| 307 trace_result.traces[0]["stddev"] = stddev |
| 308 |
| 309 return reduce(lambda r, t: r + t, traces.itervalues(), Results()) |
| 310 |
| 311 |
| 172 class Node(object): | 312 class Node(object): |
| 173 """Represents a node in the suite tree structure.""" | 313 """Represents a node in the suite tree structure.""" |
| 174 def __init__(self, *args): | 314 def __init__(self, *args): |
| 175 self._children = [] | 315 self._children = [] |
| 176 | 316 |
| 177 def AppendChild(self, child): | 317 def AppendChild(self, child): |
| 178 self._children.append(child) | 318 self._children.append(child) |
| 179 | 319 |
| 180 | 320 |
| 181 class DefaultSentinel(Node): | 321 class DefaultSentinel(Node): |
| 182 """Fake parent node with all default values.""" | 322 """Fake parent node with all default values.""" |
| 183 def __init__(self): | 323 def __init__(self): |
| 184 super(DefaultSentinel, self).__init__() | 324 super(DefaultSentinel, self).__init__() |
| 185 self.binary = "d8" | 325 self.binary = "d8" |
| 186 self.run_count = 10 | 326 self.run_count = 10 |
| 187 self.timeout = 60 | 327 self.timeout = 60 |
| 188 self.path = [] | 328 self.path = [] |
| 189 self.graphs = [] | 329 self.graphs = [] |
| 190 self.flags = [] | 330 self.flags = [] |
| 191 self.test_flags = [] | 331 self.test_flags = [] |
| 192 self.resources = [] | 332 self.resources = [] |
| 193 self.results_regexp = None | 333 self.results_regexp = None |
| 194 self.stddev_regexp = None | 334 self.stddev_regexp = None |
| 195 self.units = "score" | 335 self.units = "score" |
| 196 self.total = False | 336 self.total = False |
| 197 | 337 |
| 198 | 338 |
| 199 class Graph(Node): | 339 class GraphConfig(Node): |
| 200 """Represents a suite definition. | 340 """Represents a suite definition. |
| 201 | 341 |
| 202 Can either be a leaf or an inner node that provides default values. | 342 Can either be a leaf or an inner node that provides default values. |
| 203 """ | 343 """ |
| 204 def __init__(self, suite, parent, arch): | 344 def __init__(self, suite, parent, arch): |
| 205 super(Graph, self).__init__() | 345 super(GraphConfig, self).__init__() |
| 206 self._suite = suite | 346 self._suite = suite |
| 207 | 347 |
| 208 assert isinstance(suite.get("path", []), list) | 348 assert isinstance(suite.get("path", []), list) |
| 209 assert isinstance(suite["name"], basestring) | 349 assert isinstance(suite["name"], basestring) |
| 210 assert isinstance(suite.get("flags", []), list) | 350 assert isinstance(suite.get("flags", []), list) |
| 211 assert isinstance(suite.get("test_flags", []), list) | 351 assert isinstance(suite.get("test_flags", []), list) |
| 212 assert isinstance(suite.get("resources", []), list) | 352 assert isinstance(suite.get("resources", []), list) |
| 213 | 353 |
| 214 # Accumulated values. | 354 # Accumulated values. |
| 215 self.path = parent.path[:] + suite.get("path", []) | 355 self.path = parent.path[:] + suite.get("path", []) |
| (...skipping 25 matching lines...) Expand all Loading... |
| 241 self.results_regexp = suite.get("results_regexp", regexp_default) | 381 self.results_regexp = suite.get("results_regexp", regexp_default) |
| 242 | 382 |
| 243 # A similar regular expression for the standard deviation (optional). | 383 # A similar regular expression for the standard deviation (optional). |
| 244 if parent.stddev_regexp: | 384 if parent.stddev_regexp: |
| 245 stddev_default = parent.stddev_regexp % re.escape(suite["name"]) | 385 stddev_default = parent.stddev_regexp % re.escape(suite["name"]) |
| 246 else: | 386 else: |
| 247 stddev_default = None | 387 stddev_default = None |
| 248 self.stddev_regexp = suite.get("stddev_regexp", stddev_default) | 388 self.stddev_regexp = suite.get("stddev_regexp", stddev_default) |
| 249 | 389 |
| 250 | 390 |
| 251 class Trace(Graph): | 391 class TraceConfig(GraphConfig): |
| 252 """Represents a leaf in the suite tree structure. | 392 """Represents a leaf in the suite tree structure.""" |
| 393 def __init__(self, suite, parent, arch): |
| 394 super(TraceConfig, self).__init__(suite, parent, arch) |
| 395 assert self.results_regexp |
| 253 | 396 |
| 254 Handles collection of measurements. | 397 def CreateMeasurement(self): |
| 255 """ | 398 return Measurement( |
| 256 def __init__(self, suite, parent, arch): | 399 self.graphs, |
| 257 super(Trace, self).__init__(suite, parent, arch) | 400 self.units, |
| 258 assert self.results_regexp | 401 self.results_regexp, |
| 259 self.results = [] | 402 self.stddev_regexp, |
| 260 self.errors = [] | 403 ) |
| 261 self.stddev = "" | |
| 262 | |
| 263 def ConsumeOutput(self, stdout): | |
| 264 try: | |
| 265 result = re.search(self.results_regexp, stdout, re.M).group(1) | |
| 266 self.results.append(str(float(result))) | |
| 267 except ValueError: | |
| 268 self.errors.append("Regexp \"%s\" returned a non-numeric for test %s." | |
| 269 % (self.results_regexp, self.graphs[-1])) | |
| 270 except: | |
| 271 self.errors.append("Regexp \"%s\" didn't match for test %s." | |
| 272 % (self.results_regexp, self.graphs[-1])) | |
| 273 | |
| 274 try: | |
| 275 if self.stddev_regexp and self.stddev: | |
| 276 self.errors.append("Test %s should only run once since a stddev " | |
| 277 "is provided by the test." % self.graphs[-1]) | |
| 278 if self.stddev_regexp: | |
| 279 self.stddev = re.search(self.stddev_regexp, stdout, re.M).group(1) | |
| 280 except: | |
| 281 self.errors.append("Regexp \"%s\" didn't match for test %s." | |
| 282 % (self.stddev_regexp, self.graphs[-1])) | |
| 283 | |
| 284 def GetResults(self): | |
| 285 return Results([{ | |
| 286 "graphs": self.graphs, | |
| 287 "units": self.units, | |
| 288 "results": self.results, | |
| 289 "stddev": self.stddev, | |
| 290 }], self.errors) | |
| 291 | 404 |
| 292 | 405 |
| 293 class Runnable(Graph): | 406 class RunnableConfig(GraphConfig): |
| 294 """Represents a runnable suite definition (i.e. has a main file). | 407 """Represents a runnable suite definition (i.e. has a main file). |
| 295 """ | 408 """ |
| 296 @property | 409 @property |
| 297 def main(self): | 410 def main(self): |
| 298 return self._suite.get("main", "") | 411 return self._suite.get("main", "") |
| 299 | 412 |
| 300 def ChangeCWD(self, suite_path): | 413 def ChangeCWD(self, suite_path): |
| 301 """Changes the cwd to to path defined in the current graph. | 414 """Changes the cwd to to path defined in the current graph. |
| 302 | 415 |
| 303 The tests are supposed to be relative to the suite configuration. | 416 The tests are supposed to be relative to the suite configuration. |
| 304 """ | 417 """ |
| 305 suite_dir = os.path.abspath(os.path.dirname(suite_path)) | 418 suite_dir = os.path.abspath(os.path.dirname(suite_path)) |
| 306 bench_dir = os.path.normpath(os.path.join(*self.path)) | 419 bench_dir = os.path.normpath(os.path.join(*self.path)) |
| 307 os.chdir(os.path.join(suite_dir, bench_dir)) | 420 os.chdir(os.path.join(suite_dir, bench_dir)) |
| 308 | 421 |
| 309 def GetCommandFlags(self, extra_flags=None): | 422 def GetCommandFlags(self, extra_flags=None): |
| 310 suffix = ["--"] + self.test_flags if self.test_flags else [] | 423 suffix = ["--"] + self.test_flags if self.test_flags else [] |
| 311 return self.flags + (extra_flags or []) + [self.main] + suffix | 424 return self.flags + (extra_flags or []) + [self.main] + suffix |
| 312 | 425 |
| 313 def GetCommand(self, shell_dir, extra_flags=None): | 426 def GetCommand(self, shell_dir, extra_flags=None): |
| 314 # TODO(machenbach): This requires +.exe if run on windows. | 427 # TODO(machenbach): This requires +.exe if run on windows. |
| 315 cmd = [os.path.join(shell_dir, self.binary)] | 428 cmd = [os.path.join(shell_dir, self.binary)] |
| 316 return cmd + self.GetCommandFlags(extra_flags=extra_flags) | 429 return cmd + self.GetCommandFlags(extra_flags=extra_flags) |
| 317 | 430 |
| 318 def Run(self, runner): | 431 def Run(self, runner): |
| 319 """Iterates over several runs and handles the output for all traces.""" | 432 """Iterates over several runs and handles the output for all traces.""" |
| 320 for stdout in runner(): | 433 return AccumulateResults(self.graphs, self._children, runner, self.total) |
| 321 for trace in self._children: | |
| 322 trace.ConsumeOutput(stdout) | |
| 323 res = reduce(lambda r, t: r + t.GetResults(), self._children, Results()) | |
| 324 | 434 |
| 325 if not res.traces or not self.total: | |
| 326 return res | |
| 327 | 435 |
| 328 # Assume all traces have the same structure. | 436 class RunnableTraceConfig(TraceConfig, RunnableConfig): |
| 329 if len(set(map(lambda t: len(t["results"]), res.traces))) != 1: | |
| 330 res.errors.append("Not all traces have the same number of results.") | |
| 331 return res | |
| 332 | |
| 333 # Calculate the geometric means for all traces. Above we made sure that | |
| 334 # there is at least one trace and that the number of results is the same | |
| 335 # for each trace. | |
| 336 n_results = len(res.traces[0]["results"]) | |
| 337 total_results = [GeometricMean(t["results"][i] for t in res.traces) | |
| 338 for i in range(0, n_results)] | |
| 339 res.traces.append({ | |
| 340 "graphs": self.graphs + ["Total"], | |
| 341 "units": res.traces[0]["units"], | |
| 342 "results": total_results, | |
| 343 "stddev": "", | |
| 344 }) | |
| 345 return res | |
| 346 | |
| 347 class RunnableTrace(Trace, Runnable): | |
| 348 """Represents a runnable suite definition that is a leaf.""" | 437 """Represents a runnable suite definition that is a leaf.""" |
| 349 def __init__(self, suite, parent, arch): | 438 def __init__(self, suite, parent, arch): |
| 350 super(RunnableTrace, self).__init__(suite, parent, arch) | 439 super(RunnableTraceConfig, self).__init__(suite, parent, arch) |
| 351 | 440 |
| 352 def Run(self, runner): | 441 def Run(self, runner): |
| 353 """Iterates over several runs and handles the output.""" | 442 """Iterates over several runs and handles the output.""" |
| 443 measurement = self.CreateMeasurement() |
| 354 for stdout in runner(): | 444 for stdout in runner(): |
| 355 self.ConsumeOutput(stdout) | 445 measurement.ConsumeOutput(stdout) |
| 356 return self.GetResults() | 446 return measurement.GetResults() |
| 357 | 447 |
| 358 | 448 |
| 359 class RunnableGeneric(Runnable): | 449 class RunnableGenericConfig(RunnableConfig): |
| 360 """Represents a runnable suite definition with generic traces.""" | 450 """Represents a runnable suite definition with generic traces.""" |
| 361 def __init__(self, suite, parent, arch): | 451 def __init__(self, suite, parent, arch): |
| 362 super(RunnableGeneric, self).__init__(suite, parent, arch) | 452 super(RunnableGenericConfig, self).__init__(suite, parent, arch) |
| 363 | 453 |
| 364 def Run(self, runner): | 454 def Run(self, runner): |
| 365 """Iterates over several runs and handles the output.""" | 455 return AccumulateGenericResults(self.graphs, self.units, runner) |
| 366 traces = OrderedDict() | |
| 367 for stdout in runner(): | |
| 368 for line in stdout.strip().splitlines(): | |
| 369 match = GENERIC_RESULTS_RE.match(line) | |
| 370 if match: | |
| 371 stddev = "" | |
| 372 graph = match.group(1) | |
| 373 trace = match.group(2) | |
| 374 body = match.group(3) | |
| 375 units = match.group(4) | |
| 376 match_stddev = RESULT_STDDEV_RE.match(body) | |
| 377 match_list = RESULT_LIST_RE.match(body) | |
| 378 errors = [] | |
| 379 if match_stddev: | |
| 380 result, stddev = map(str.strip, match_stddev.group(1).split(",")) | |
| 381 results = [result] | |
| 382 elif match_list: | |
| 383 results = map(str.strip, match_list.group(1).split(",")) | |
| 384 else: | |
| 385 results = [body.strip()] | |
| 386 | |
| 387 try: | |
| 388 results = map(lambda r: str(float(r)), results) | |
| 389 except ValueError: | |
| 390 results = [] | |
| 391 errors = ["Found non-numeric in %s" % | |
| 392 "/".join(self.graphs + [graph, trace])] | |
| 393 | |
| 394 trace_result = traces.setdefault(trace, Results([{ | |
| 395 "graphs": self.graphs + [graph, trace], | |
| 396 "units": (units or self.units).strip(), | |
| 397 "results": [], | |
| 398 "stddev": "", | |
| 399 }], errors)) | |
| 400 trace_result.traces[0]["results"].extend(results) | |
| 401 trace_result.traces[0]["stddev"] = stddev | |
| 402 | |
| 403 return reduce(lambda r, t: r + t, traces.itervalues(), Results()) | |
| 404 | 456 |
| 405 | 457 |
| 406 def MakeGraph(suite, arch, parent): | 458 def MakeGraphConfig(suite, arch, parent): |
| 407 """Factory method for making graph objects.""" | 459 """Factory method for making graph configuration objects.""" |
| 408 if isinstance(parent, Runnable): | 460 if isinstance(parent, RunnableConfig): |
| 409 # Below a runnable can only be traces. | 461 # Below a runnable can only be traces. |
| 410 return Trace(suite, parent, arch) | 462 return TraceConfig(suite, parent, arch) |
| 411 elif suite.get("main") is not None: | 463 elif suite.get("main") is not None: |
| 412 # A main file makes this graph runnable. Empty strings are accepted. | 464 # A main file makes this graph runnable. Empty strings are accepted. |
| 413 if suite.get("tests"): | 465 if suite.get("tests"): |
| 414 # This graph has subgraphs (traces). | 466 # This graph has subgraphs (traces). |
| 415 return Runnable(suite, parent, arch) | 467 return RunnableConfig(suite, parent, arch) |
| 416 else: | 468 else: |
| 417 # This graph has no subgraphs, it's a leaf. | 469 # This graph has no subgraphs, it's a leaf. |
| 418 return RunnableTrace(suite, parent, arch) | 470 return RunnableTraceConfig(suite, parent, arch) |
| 419 elif suite.get("generic"): | 471 elif suite.get("generic"): |
| 420 # This is a generic suite definition. It is either a runnable executable | 472 # This is a generic suite definition. It is either a runnable executable |
| 421 # or has a main js file. | 473 # or has a main js file. |
| 422 return RunnableGeneric(suite, parent, arch) | 474 return RunnableGenericConfig(suite, parent, arch) |
| 423 elif suite.get("tests"): | 475 elif suite.get("tests"): |
| 424 # This is neither a leaf nor a runnable. | 476 # This is neither a leaf nor a runnable. |
| 425 return Graph(suite, parent, arch) | 477 return GraphConfig(suite, parent, arch) |
| 426 else: # pragma: no cover | 478 else: # pragma: no cover |
| 427 raise Exception("Invalid suite configuration.") | 479 raise Exception("Invalid suite configuration.") |
| 428 | 480 |
| 429 | 481 |
| 430 def BuildGraphs(suite, arch, parent=None): | 482 def BuildGraphConfigs(suite, arch, parent=None): |
| 431 """Builds a tree structure of graph objects that corresponds to the suite | 483 """Builds a tree structure of graph objects that corresponds to the suite |
| 432 configuration. | 484 configuration. |
| 433 """ | 485 """ |
| 434 parent = parent or DefaultSentinel() | 486 parent = parent or DefaultSentinel() |
| 435 | 487 |
| 436 # TODO(machenbach): Implement notion of cpu type? | 488 # TODO(machenbach): Implement notion of cpu type? |
| 437 if arch not in suite.get("archs", SUPPORTED_ARCHS): | 489 if arch not in suite.get("archs", SUPPORTED_ARCHS): |
| 438 return None | 490 return None |
| 439 | 491 |
| 440 graph = MakeGraph(suite, arch, parent) | 492 graph = MakeGraphConfig(suite, arch, parent) |
| 441 for subsuite in suite.get("tests", []): | 493 for subsuite in suite.get("tests", []): |
| 442 BuildGraphs(subsuite, arch, graph) | 494 BuildGraphConfigs(subsuite, arch, graph) |
| 443 parent.AppendChild(graph) | 495 parent.AppendChild(graph) |
| 444 return graph | 496 return graph |
| 445 | 497 |
| 446 | 498 |
| 447 def FlattenRunnables(node, node_cb): | 499 def FlattenRunnables(node, node_cb): |
| 448 """Generator that traverses the tree structure and iterates over all | 500 """Generator that traverses the tree structure and iterates over all |
| 449 runnables. | 501 runnables. |
| 450 """ | 502 """ |
| 451 node_cb(node) | 503 node_cb(node) |
| 452 if isinstance(node, Runnable): | 504 if isinstance(node, RunnableConfig): |
| 453 yield node | 505 yield node |
| 454 elif isinstance(node, Node): | 506 elif isinstance(node, Node): |
| 455 for child in node._children: | 507 for child in node._children: |
| 456 for result in FlattenRunnables(child, node_cb): | 508 for result in FlattenRunnables(child, node_cb): |
| 457 yield result | 509 yield result |
| 458 else: # pragma: no cover | 510 else: # pragma: no cover |
| 459 raise Exception("Invalid suite configuration.") | 511 raise Exception("Invalid suite configuration.") |
| 460 | 512 |
| 461 | 513 |
| 462 class Platform(object): | 514 class Platform(object): |
| (...skipping 13 matching lines...) Expand all Loading... |
| 476 def __init__(self, options): | 528 def __init__(self, options): |
| 477 super(DesktopPlatform, self).__init__(options) | 529 super(DesktopPlatform, self).__init__(options) |
| 478 | 530 |
| 479 def PreExecution(self): | 531 def PreExecution(self): |
| 480 pass | 532 pass |
| 481 | 533 |
| 482 def PostExecution(self): | 534 def PostExecution(self): |
| 483 pass | 535 pass |
| 484 | 536 |
| 485 def PreTests(self, node, path): | 537 def PreTests(self, node, path): |
| 486 if isinstance(node, Runnable): | 538 if isinstance(node, RunnableConfig): |
| 487 node.ChangeCWD(path) | 539 node.ChangeCWD(path) |
| 488 | 540 |
| 489 def Run(self, runnable, count): | 541 def Run(self, runnable, count): |
| 490 try: | 542 try: |
| 491 output = commands.Execute( | 543 output = commands.Execute( |
| 492 runnable.GetCommand(self.shell_dir, self.extra_flags), | 544 runnable.GetCommand(self.shell_dir, self.extra_flags), |
| 493 timeout=runnable.timeout, | 545 timeout=runnable.timeout, |
| 494 ) | 546 ) |
| 495 except OSError as e: | 547 except OSError as e: |
| 496 print ">>> OSError (#%d):" % (count + 1) | 548 print ">>> OSError (#%d):" % (count + 1) |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 593 "bin", | 645 "bin", |
| 594 skip_if_missing=True, | 646 skip_if_missing=True, |
| 595 ) | 647 ) |
| 596 self._PushFile( | 648 self._PushFile( |
| 597 self.shell_dir, | 649 self.shell_dir, |
| 598 "snapshot_blob.bin", | 650 "snapshot_blob.bin", |
| 599 "bin", | 651 "bin", |
| 600 skip_if_missing=True, | 652 skip_if_missing=True, |
| 601 ) | 653 ) |
| 602 | 654 |
| 603 if isinstance(node, Runnable): | 655 if isinstance(node, RunnableConfig): |
| 604 self._PushFile(bench_abs, node.main, bench_rel) | 656 self._PushFile(bench_abs, node.main, bench_rel) |
| 605 for resource in node.resources: | 657 for resource in node.resources: |
| 606 self._PushFile(bench_abs, resource, bench_rel) | 658 self._PushFile(bench_abs, resource, bench_rel) |
| 607 | 659 |
| 608 def Run(self, runnable, count): | 660 def Run(self, runnable, count): |
| 609 cache = cache_control.CacheControl(self.device) | 661 cache = cache_control.CacheControl(self.device) |
| 610 cache.DropRamCaches() | 662 cache.DropRamCaches() |
| 611 binary_on_device = os.path.join( | 663 binary_on_device = os.path.join( |
| 612 AndroidPlatform.DEVICE_DIR, "bin", runnable.binary) | 664 AndroidPlatform.DEVICE_DIR, "bin", runnable.binary) |
| 613 cmd = [binary_on_device] + runnable.GetCommandFlags(self.extra_flags) | 665 cmd = [binary_on_device] + runnable.GetCommandFlags(self.extra_flags) |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 696 with open(path) as f: | 748 with open(path) as f: |
| 697 suite = json.loads(f.read()) | 749 suite = json.loads(f.read()) |
| 698 | 750 |
| 699 # If no name is given, default to the file name without .json. | 751 # If no name is given, default to the file name without .json. |
| 700 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0]) | 752 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0]) |
| 701 | 753 |
| 702 # Setup things common to one test suite. | 754 # Setup things common to one test suite. |
| 703 platform.PreExecution() | 755 platform.PreExecution() |
| 704 | 756 |
| 705 # Build the graph/trace tree structure. | 757 # Build the graph/trace tree structure. |
| 706 root = BuildGraphs(suite, options.arch) | 758 root = BuildGraphConfigs(suite, options.arch) |
| 707 | 759 |
| 708 # Callback to be called on each node on traversal. | 760 # Callback to be called on each node on traversal. |
| 709 def NodeCB(node): | 761 def NodeCB(node): |
| 710 platform.PreTests(node, path) | 762 platform.PreTests(node, path) |
| 711 | 763 |
| 712 # Traverse graph/trace tree and interate over all runnables. | 764 # Traverse graph/trace tree and interate over all runnables. |
| 713 for runnable in FlattenRunnables(root, NodeCB): | 765 for runnable in FlattenRunnables(root, NodeCB): |
| 714 print ">>> Running suite: %s" % "/".join(runnable.graphs) | 766 print ">>> Running suite: %s" % "/".join(runnable.graphs) |
| 715 | 767 |
| 716 def Runner(): | 768 def Runner(): |
| (...skipping 10 matching lines...) Expand all Loading... |
| 727 | 779 |
| 728 if options.json_test_results: | 780 if options.json_test_results: |
| 729 results.WriteToFile(options.json_test_results) | 781 results.WriteToFile(options.json_test_results) |
| 730 else: # pragma: no cover | 782 else: # pragma: no cover |
| 731 print results | 783 print results |
| 732 | 784 |
| 733 return min(1, len(results.errors)) | 785 return min(1, len(results.errors)) |
| 734 | 786 |
| 735 if __name__ == "__main__": # pragma: no cover | 787 if __name__ == "__main__": # pragma: no cover |
| 736 sys.exit(Main(sys.argv[1:])) | 788 sys.exit(Main(sys.argv[1:])) |
| OLD | NEW |