# 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/.
"""
Module to handle chrom* profiling.
"""
import json
import os
import zipfile
from pathlib
import Path
import mozfile
from logger.logger
import RaptorLogger
here = Path(__file__).resolve().parent
LOG = RaptorLogger(component=
"raptor-chrome-trace")
from raptor_profiling
import RaptorProfiling
class ChromeTrace(RaptorProfiling):
"""
Handle Tracing
for chrom* applications.
This allows us to collect Chrome profiling data
and to zip results into one file.
"""
def __init__(self, upload_dir, raptor_config, test_config):
super().__init__(upload_dir, raptor_config, test_config)
# define the key in the results json for traces
self.profile_entry_string =
"timeline"
# Make sure no archive already exists in the location where
# we plan to output our profiler archive
self.profile_arcname = Path(
self.upload_dir,
"profile_{0}.zip".format(self.test_config[
"name"])
)
LOG.info(
"Clearing archive {0}".format(self.profile_arcname))
mozfile.remove(self.profile_arcname)
LOG.info(
"Activating chrome tracing! temp profile dir: {0}".format(
self.temp_profile_dir
)
)
@property
def _is_extra_profiler_run(self):
return self.raptor_config.get(
"extra_profiler_run",
False)
def output_trace(self):
"""Collect and output Chrome Trace data for one pagecycle
Write the profiles into a set of folders formatted
as:
<TEST-NAME>-<TEST-RUN-TYPE>.
<TEST-RUN-TYPE> can be pageload-{warm,cold}
or {test-type}
only
for the tests that are
not a pageload test.
For example,
"cnn-pageload-warm".
The file names are formatted
as <ITERATION-TYPE>-<ITERATION>
to clearly indicate without redundant information.
For example,
"browser-cycle-1".
"""
profiles = self.collect_profiles()
if len(profiles) == 0:
if self._is_extra_profiler_run:
LOG.info(
"No profiles collected in the extra profiler run")
else:
LOG.error(
"No profiles collected")
return
test_type = self.test_config.get(
"type",
"pageload")
try:
mode = zipfile.ZIP_DEFLATED
except NameError:
mode = zipfile.ZIP_STORED
with zipfile.ZipFile(self.profile_arcname,
"a", mode)
as arc:
for profile_info
in profiles:
profile_path = profile_info[
"path"]
LOG.info(
"Opening profile at %s" % profile_path)
try:
profile = self._open_profile_file(profile_path)
except FileNotFoundError:
if self._is_extra_profiler_run:
LOG.info(
"Trace not found on extra profiler run.")
else:
LOG.error(
"Trace not found.")
continue
try:
test_run_type = (
"{0}-{1}".format(test_type, profile_info[
"type"])
if test_type ==
"pageload"
else test_type
)
folder_name =
"%s-%s" % (self.test_config[
"name"], test_run_type)
iteration = Path(profile_path).parts[-1].split(
"-")[-1]
if test_type ==
"pageload" and profile_info[
"type"] ==
"cold":
iteration_type =
"browser-cycle"
elif profile_info[
"type"] ==
"warm":
iteration_type =
"page-cycle"
else:
iteration_type =
"iteration"
profile_name =
"-".join([iteration_type, iteration])
path_in_zip = Path(folder_name, profile_name)
LOG.info(
"Adding profile %s to archive %s as %s"
% (profile_path, self.profile_arcname, path_in_zip)
)
arc.writestr(
str(path_in_zip),
json.dumps(profile, ensure_ascii=
False).encode(
"utf-8"),
)
except Exception:
LOG.exception(
"Failed to add profile %s to archive %s"
% (profile_path, self.profile_arcname)
)
raise
# save the latest chrome trace archive to an env var, so later on
# it can be viewed automatically via the view-gecko-profile tool.
# since we are using the firefox profiler to view the trace, it
# is convenient to just use the same env var.
os.environ[
"RAPTOR_LATEST_GECKO_PROFILE_ARCHIVE"] = str(
self.profile_arcname
)
# convert posixpath object to string for environment
def clean(self):
"""
Clean up temp folders created
with the instance creation.
"""
mozfile.remove(self.temp_profile_dir)