# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. """ Superclass to handle profiling in Raptor-Browsertime. """ import gzip import json import os from logger.logger import RaptorLogger here = os.path.dirname(os.path.realpath(__file__)) LOG = RaptorLogger(component="raptor-profiling") import tempfile class RaptorProfiling: """ Superclass for handling profling for Firefox and Chrom* applications. """ def __init__(self, upload_dir, raptor_config, test_config): self.upload_dir = upload_dir self.raptor_config = raptor_config self.test_config = test_config # Create a temporary directory into which the tests can put # their profiles. These files will be assembled into one big # zip file later on, which is put into the MOZ_UPLOAD_DIR. self.temp_profile_dir = tempfile.mkdtemp() def _open_profile_file(self, profile_path): """Open a profile file given a path and return the contents.""" if profile_path.endswith(".gz"): with gzip.open(profile_path, "r") as profile_file: profile = json.load(profile_file) else: with open(profile_path, "r", encoding="utf-8") as profile_file: profile = json.load(profile_file) return profile def collect_profiles(self): """Collect and return all profile files""" def __get_test_type(): """Returns the type of test that was run. For benchmark/scenario tests, we return those specific types, but for pageloads we return cold or warm depending on the --cold flag. """ if self.test_config.get("type", "pageload") not in ( "benchmark", "scenario", ): return "cold" if self.raptor_config.get("cold", False) else "warm" else: return self.test_config.get("type", "benchmark") res = [] if self.raptor_config.get("browsertime"): topdir = self.raptor_config.get("browsertime_result_dir") # Get the browsertime.json file along with the cold/warm splits # if they exist from a chimera test results = {"main": None, "cold": None, "warm": None} profiling_dir = os.path.join(topdir, "profiling") result_dir = profiling_dir if self._is_extra_profiler_run else topdir if not os.path.isdir(result_dir): # Result directory not found. Return early. Caller will decide # if this should throw an error or not. LOG.info("Could not find the result directory.") return [] for filename in os.listdir(result_dir): if filename == "browsertime.json": results["main"] = os.path.join(result_dir, filename) elif filename == "cold-browsertime.json": results["cold"] = os.path.join(result_dir, filename) elif filename == "warm-browsertime.json": results["warm"] = os.path.join(result_dir, filename) if all(results.values()): break if not any(results.values()): if self._is_extra_profiler_run: LOG.info( "Could not find any browsertime result JSONs in the artifacts " " for the extra profiler run" ) return [] else: raise Exception( "Could not find any browsertime result JSONs in the artifacts" ) profile_locations = [] if self.raptor_config.get("chimera", False): if results["warm"] is None or results["cold"] is None: if self._is_extra_profiler_run: LOG.info( "The test ran in chimera mode but we found no cold " "and warm browsertime JSONs. Cannot collect profiles. " "Failing silently because this is an extra profiler run." ) return [] else: raise Exception( "The test ran in chimera mode but we found no cold " "and warm browsertime JSONs. Cannot collect profiles." ) profile_locations.extend( [("cold", results["cold"]), ("warm", results["warm"])] ) else: # When we don't run in chimera mode, it means that we # either ran a benchmark, scenario test or separate # warm/cold pageload tests. profile_locations.append( ( __get_test_type(), results["main"], ) ) for testtype, results_json in profile_locations: with open(results_json, encoding="utf-8") as f: data = json.load(f) results_dir = os.path.dirname(results_json) for entry in data: try: for rel_profile_path in entry["files"][ self.profile_entry_string ]: res.append( { "path": os.path.join(results_dir, rel_profile_path), "type": testtype, } ) except KeyError: if self._is_extra_profiler_run: LOG.info("Failed to find profiles for extra profiler run.") else: LOG.error("Failed to find profiles.") else: # Raptor-webext stores its profiles in the self.temp_profile_dir # directory for profile in os.listdir(self.temp_profile_dir): res.append( { "path": os.path.join(self.temp_profile_dir, profile), "type": __get_test_type(), } ) LOG.info("Found %s profiles: %s" % (len(res), str(res))) return res