summaryrefslogtreecommitdiffstats
path: root/testing/awsy/awsy/process_perf_data.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/awsy/awsy/process_perf_data.py')
-rw-r--r--testing/awsy/awsy/process_perf_data.py214
1 files changed, 214 insertions, 0 deletions
diff --git a/testing/awsy/awsy/process_perf_data.py b/testing/awsy/awsy/process_perf_data.py
new file mode 100644
index 0000000000..32ec9655b0
--- /dev/null
+++ b/testing/awsy/awsy/process_perf_data.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python
+# 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/.
+
+import glob
+import json
+import math
+import os
+import sys
+
+import six
+
+AWSY_PATH = os.path.dirname(os.path.realpath(__file__))
+if AWSY_PATH not in sys.path:
+ sys.path.append(AWSY_PATH)
+
+import parse_about_memory
+
+# A description of each checkpoint and the root path to it.
+CHECKPOINTS = [
+ {"name": "Fresh start", "path": "memory-report-Start-0.json.gz"},
+ {"name": "Fresh start [+30s]", "path": "memory-report-StartSettled-0.json.gz"},
+ {"name": "After tabs open", "path": "memory-report-TabsOpen-4.json.gz"},
+ {
+ "name": "After tabs open [+30s]",
+ "path": "memory-report-TabsOpenSettled-4.json.gz",
+ },
+ {
+ "name": "After tabs open [+30s, forced GC]",
+ "path": "memory-report-TabsOpenForceGC-4.json.gz",
+ },
+ {
+ "name": "Tabs closed extra processes",
+ "path": "memory-report-TabsClosedExtraProcesses-4.json.gz",
+ },
+ {"name": "Tabs closed", "path": "memory-report-TabsClosed-4.json.gz"},
+ {"name": "Tabs closed [+30s]", "path": "memory-report-TabsClosedSettled-4.json.gz"},
+ {
+ "name": "Tabs closed [+30s, forced GC]",
+ "path": "memory-report-TabsClosedForceGC-4.json.gz",
+ },
+]
+
+# A description of each perfherder suite and the path to its values.
+PERF_SUITES = [
+ {"name": "Resident Memory", "node": "resident"},
+ {"name": "Explicit Memory", "node": "explicit/"},
+ {"name": "Heap Unclassified", "node": "explicit/heap-unclassified"},
+ {"name": "JS", "node": "js-main-runtime/"},
+ {"name": "Images", "node": "explicit/images/"},
+]
+
+
+def median(values):
+ sorted_ = sorted(values)
+ # pylint --py3k W1619
+ med = int(len(sorted_) / 2)
+
+ if len(sorted_) % 2:
+ return sorted_[med]
+ # pylint --py3k W1619
+ return (sorted_[med - 1] + sorted_[med]) / 2
+
+
+def update_checkpoint_paths(checkpoint_files, checkpoints):
+ """
+ Updates checkpoints with memory report file fetched in data_path
+ :param checkpoint_files: list of files in data_path
+ :param checkpoints: The checkpoints to update the path of.
+ """
+ target_path = [
+ ["Start-", 0],
+ ["StartSettled-", 0],
+ ["TabsOpen-", -1],
+ ["TabsOpenSettled-", -1],
+ ["TabsOpenForceGC-", -1],
+ ["TabsClosedExtraProcesses-", -1],
+ ["TabsClosed-", -1],
+ ["TabsClosedSettled-", -1],
+ ["TabsClosedForceGC-", -1],
+ ]
+ for i in range(len(target_path)):
+ (name, idx) = target_path[i]
+ paths = sorted([x for x in checkpoint_files if name in x])
+ if paths:
+ indices = [i for i, x in enumerate(checkpoints) if name in x["path"]]
+ if indices:
+ checkpoints[indices[0]]["path"] = paths[idx]
+ else:
+ print("found files but couldn't find {}".format(name))
+
+
+def create_suite(
+ name, node, data_path, checkpoints=CHECKPOINTS, alertThreshold=None, extra_opts=None
+):
+ """
+ Creates a suite suitable for adding to a perfherder blob. Calculates the
+ geometric mean of the checkpoint values and adds that to the suite as
+ well.
+
+ :param name: The name of the suite.
+ :param node: The path of the data node to extract data from.
+ :param data_path: The directory to retrieve data from.
+ :param checkpoints: Which checkpoints to include.
+ :param alertThreshold: The percentage of change that triggers an alert.
+ """
+ suite = {"name": name, "subtests": [], "lowerIsBetter": True, "unit": "bytes"}
+
+ if alertThreshold:
+ suite["alertThreshold"] = alertThreshold
+
+ opts = []
+ if extra_opts:
+ opts.extend(extra_opts)
+
+ if "DMD" in os.environ and os.environ["DMD"]:
+ opts.append("dmd")
+
+ if len(opts) > 0:
+ suite["extraOptions"] = opts
+
+ update_checkpoint_paths(
+ glob.glob(os.path.join(data_path, "memory-report*")), checkpoints
+ )
+
+ total = 0
+ for checkpoint in checkpoints:
+ memory_report_path = os.path.join(data_path, checkpoint["path"])
+
+ name_filter = checkpoint.get("name_filter", None)
+ if checkpoint.get("median"):
+ process = median
+ else:
+ process = sum
+
+ if node != "resident":
+ totals = parse_about_memory.calculate_memory_report_values(
+ memory_report_path, node, name_filter
+ )
+ value = process(totals.values())
+ else:
+ # For "resident" we really want RSS of the chrome ("Main") process
+ # and USS of the child processes. We'll still call it resident
+ # for simplicity (it's nice to be able to compare RSS of non-e10s
+ # with RSS + USS of e10s).
+ totals_rss = parse_about_memory.calculate_memory_report_values(
+ memory_report_path, node, ["Main"]
+ )
+ totals_uss = parse_about_memory.calculate_memory_report_values(
+ memory_report_path, "resident-unique"
+ )
+ value = list(totals_rss.values())[0] + sum(
+ [v for k, v in six.iteritems(totals_uss) if "Main" not in k]
+ )
+
+ subtest = {
+ "name": checkpoint["name"],
+ "value": value,
+ "lowerIsBetter": True,
+ "unit": "bytes",
+ }
+ suite["subtests"].append(subtest)
+ total += math.log(subtest["value"])
+
+ # Add the geometric mean. For more details on the calculation see:
+ # https://en.wikipedia.org/wiki/Geometric_mean#Relationship_with_arithmetic_mean_of_logarithms
+ # pylint --py3k W1619
+ suite["value"] = math.exp(total / len(checkpoints))
+
+ return suite
+
+
+def create_perf_data(
+ data_path, perf_suites=PERF_SUITES, checkpoints=CHECKPOINTS, extra_opts=None
+):
+ """
+ Builds up a performance data blob suitable for submitting to perfherder.
+ """
+ if ("GCOV_PREFIX" in os.environ) or ("JS_CODE_COVERAGE_OUTPUT_DIR" in os.environ):
+ print(
+ "Code coverage is being collected, performance data will not be gathered."
+ )
+ return {}
+
+ perf_blob = {"framework": {"name": "awsy"}, "suites": []}
+
+ for suite in perf_suites:
+ perf_blob["suites"].append(
+ create_suite(
+ suite["name"],
+ suite["node"],
+ data_path,
+ checkpoints,
+ suite.get("alertThreshold"),
+ extra_opts,
+ )
+ )
+
+ return perf_blob
+
+
+if __name__ == "__main__":
+ args = sys.argv[1:]
+ if not args:
+ print("Usage: process_perf_data.py data_path")
+ sys.exit(1)
+
+ # Determine which revisions we need to process.
+ data_path = args[0]
+ perf_blob = create_perf_data(data_path)
+ print("PERFHERDER_DATA: {}".format(json.dumps(perf_blob)))
+
+ sys.exit(0)