Added: trunk/PerformanceTests/JetStream2/LuaJSFight/richards.js (0 => 248326)
--- trunk/PerformanceTests/JetStream2/LuaJSFight/richards.js (rev 0)
+++ trunk/PerformanceTests/JetStream2/LuaJSFight/richards.js 2019-08-06 23:24:26 UTC (rev 248326)
@@ -0,0 +1,537 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// This is a _javascript_ implementation of the Richards
+// benchmark from:
+//
+// http://www.cl.cam.ac.uk/~mr10/Bench.html
+//
+// The benchmark was originally implemented in BCPL by
+// Martin Richards.
+
+
+/**
+ * The Richards benchmark simulates the task dispatcher of an
+ * operating system.
+ **/
+function runRichards() {
+ var scheduler = new Scheduler();
+ scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
+
+ var queue = new Packet(null, ID_WORKER, KIND_WORK);
+ queue = new Packet(queue, ID_WORKER, KIND_WORK);
+ scheduler.addWorkerTask(ID_WORKER, 1000, queue);
+
+ queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+ scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
+
+ queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+ scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
+
+ scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
+
+ scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
+
+ scheduler.schedule();
+
+ if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
+ scheduler.holdCount != EXPECTED_HOLD_COUNT) {
+ var msg =
+ "Error during execution: queueCount = " + scheduler.queueCount +
+ ", holdCount = " + scheduler.holdCount + ".";
+ throw new Error(msg);
+ }
+}
+
+var COUNT = 1000;
+
+/**
+ * These two constants specify how many times a packet is queued and
+ * how many times a task is put on hold in a correct run of richards.
+ * They don't have any meaning a such but are characteristic of a
+ * correct run so if the actual queue or hold count is different from
+ * the expected there must be a bug in the implementation.
+ **/
+var EXPECTED_QUEUE_COUNT = 2322;
+var EXPECTED_HOLD_COUNT = 928;
+
+
+/**
+ * A scheduler can be used to schedule a set of tasks based on their relative
+ * priorities. Scheduling is done by maintaining a list of task control blocks
+ * which holds tasks and the data queue they are processing.
+ * @constructor
+ */
+function Scheduler() {
+ this.queueCount = 0;
+ this.holdCount = 0;
+ this.blocks = new Array(NUMBER_OF_IDS);
+ this.list = null;
+ this.currentTcb = null;
+ this.currentId = null;
+}
+
+var ID_IDLE = 0;
+var ID_WORKER = 1;
+var ID_HANDLER_A = 2;
+var ID_HANDLER_B = 3;
+var ID_DEVICE_A = 4;
+var ID_DEVICE_B = 5;
+var NUMBER_OF_IDS = 6;
+
+var KIND_DEVICE = 0;
+var KIND_WORK = 1;
+
+/**
+ * Add an idle task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {int} count the number of times to schedule the task
+ */
+Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
+ this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
+};
+
+/**
+ * Add a work task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
+ this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
+ this.addTask(id, priority, queue, new HandlerTask(this));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
+ this.addTask(id, priority, queue, new DeviceTask(this))
+};
+
+/**
+ * Add the specified task and mark it as running.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
+ this.addTask(id, priority, queue, task);
+ this.currentTcb.setRunning();
+};
+
+/**
+ * Add the specified task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addTask = function (id, priority, queue, task) {
+ this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
+ this.list = this.currentTcb;
+ this.blocks[id] = this.currentTcb;
+};
+
+/**
+ * Execute the tasks managed by this scheduler.
+ */
+Scheduler.prototype.schedule = function () {
+ this.currentTcb = this.list;
+ while (this.currentTcb != null) {
+ if (this.currentTcb.isHeldOrSuspended()) {
+ this.currentTcb = this.currentTcb.link;
+ } else {
+ this.currentId = this.currentTcb.id;
+ this.currentTcb = this.currentTcb.run();
+ }
+ }
+};
+
+/**
+ * Release a task that is currently blocked and return the next block to run.
+ * @param {int} id the id of the task to suspend
+ */
+Scheduler.prototype.release = function (id) {
+ var tcb = this.blocks[id];
+ if (tcb == null) return tcb;
+ tcb.markAsNotHeld();
+ if (tcb.priority > this.currentTcb.priority) {
+ return tcb;
+ } else {
+ return this.currentTcb;
+ }
+};
+
+/**
+ * Block the currently executing task and return the next task control block
+ * to run. The blocked task will not be made runnable until it is explicitly
+ * released, even if new work is added to it.
+ */
+Scheduler.prototype.holdCurrent = function () {
+ this.holdCount++;
+ this.currentTcb.markAsHeld();
+ return this.currentTcb.link;
+};
+
+/**
+ * Suspend the currently executing task and return the next task control block
+ * to run. If new work is added to the suspended task it will be made runnable.
+ */
+Scheduler.prototype.suspendCurrent = function () {
+ this.currentTcb.markAsSuspended();
+ return this.currentTcb;
+};
+
+/**
+ * Add the specified packet to the end of the worklist used by the task
+ * associated with the packet and make the task runnable if it is currently
+ * suspended.
+ * @param {Packet} packet the packet to add
+ */
+Scheduler.prototype.queue = function (packet) {
+ var t = this.blocks[packet.id];
+ if (t == null) return t;
+ this.queueCount++;
+ packet.link = null;
+ packet.id = this.currentId;
+ return t.checkPriorityAdd(this.currentTcb, packet);
+};
+
+/**
+ * A task control block manages a task and the queue of work packages associated
+ * with it.
+ * @param {TaskControlBlock} link the preceding block in the linked block list
+ * @param {int} id the id of this block
+ * @param {int} priority the priority of this block
+ * @param {Packet} queue the queue of packages to be processed by the task
+ * @param {Task} task the task
+ * @constructor
+ */
+function TaskControlBlock(link, id, priority, queue, task) {
+ this.link = link;
+ this.id = id;
+ this.priority = priority;
+ this.queue = queue;
+ this.task = task;
+ if (queue == null) {
+ this.state = STATE_SUSPENDED;
+ } else {
+ this.state = STATE_SUSPENDED_RUNNABLE;
+ }
+}
+
+/**
+ * The task is running and is currently scheduled.
+ */
+var STATE_RUNNING = 0;
+
+/**
+ * The task has packets left to process.
+ */
+var STATE_RUNNABLE = 1;
+
+/**
+ * The task is not currently running. The task is not blocked as such and may
+* be started by the scheduler.
+ */
+var STATE_SUSPENDED = 2;
+
+/**
+ * The task is blocked and cannot be run until it is explicitly released.
+ */
+var STATE_HELD = 4;
+
+var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
+var STATE_NOT_HELD = ~STATE_HELD;
+
+TaskControlBlock.prototype.setRunning = function () {
+ this.state = STATE_RUNNING;
+};
+
+TaskControlBlock.prototype.markAsNotHeld = function () {
+ this.state = this.state & STATE_NOT_HELD;
+};
+
+TaskControlBlock.prototype.markAsHeld = function () {
+ this.state = this.state | STATE_HELD;
+};
+
+TaskControlBlock.prototype.isHeldOrSuspended = function () {
+ return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
+};
+
+TaskControlBlock.prototype.markAsSuspended = function () {
+ this.state = this.state | STATE_SUSPENDED;
+};
+
+TaskControlBlock.prototype.markAsRunnable = function () {
+ this.state = this.state | STATE_RUNNABLE;
+};
+
+/**
+ * Runs this task, if it is ready to be run, and returns the next task to run.
+ */
+TaskControlBlock.prototype.run = function () {
+ var packet;
+ if (this.state == STATE_SUSPENDED_RUNNABLE) {
+ packet = this.queue;
+ this.queue = packet.link;
+ if (this.queue == null) {
+ this.state = STATE_RUNNING;
+ } else {
+ this.state = STATE_RUNNABLE;
+ }
+ } else {
+ packet = null;
+ }
+ return this.task.run(packet);
+};
+
+/**
+ * Adds a packet to the worklist of this block's task, marks this as runnable if
+ * necessary, and returns the next runnable object to run (the one
+ * with the highest priority).
+ */
+TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
+ if (this.queue == null) {
+ this.queue = packet;
+ this.markAsRunnable();
+ if (this.priority > task.priority) return this;
+ } else {
+ this.queue = packet.addTo(this.queue);
+ }
+ return task;
+};
+
+TaskControlBlock.prototype.toString = function () {
+ return "tcb { " + this.task + "@" + this.state + " }";
+};
+
+/**
+ * An idle task doesn't do any work itself but cycles control between the two
+ * device tasks.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed value that controls how the device tasks are scheduled
+ * @param {int} count the number of times this task should be scheduled
+ * @constructor
+ */
+function IdleTask(scheduler, v1, count) {
+ this.scheduler = scheduler;
+ this.v1 = v1;
+ this.count = count;
+}
+
+IdleTask.prototype.run = function (packet) {
+ this.count--;
+ if (this.count == 0) return this.scheduler.holdCurrent();
+ if ((this.v1 & 1) == 0) {
+ this.v1 = this.v1 >> 1;
+ return this.scheduler.release(ID_DEVICE_A);
+ } else {
+ this.v1 = (this.v1 >> 1) ^ 0xD008;
+ return this.scheduler.release(ID_DEVICE_B);
+ }
+};
+
+IdleTask.prototype.toString = function () {
+ return "IdleTask"
+};
+
+/**
+ * A task that suspends itself after each time it has been run to simulate
+ * waiting for data from an external device.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function DeviceTask(scheduler) {
+ this.scheduler = scheduler;
+ this.v1 = null;
+}
+
+DeviceTask.prototype.run = function (packet) {
+ if (packet == null) {
+ if (this.v1 == null) return this.scheduler.suspendCurrent();
+ var v = this.v1;
+ this.v1 = null;
+ return this.scheduler.queue(v);
+ } else {
+ this.v1 = packet;
+ return this.scheduler.holdCurrent();
+ }
+};
+
+DeviceTask.prototype.toString = function () {
+ return "DeviceTask";
+};
+
+/**
+ * A task that manipulates work packets.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed used to specify how work packets are manipulated
+ * @param {int} v2 another seed used to specify how work packets are manipulated
+ * @constructor
+ */
+function WorkerTask(scheduler, v1, v2) {
+ this.scheduler = scheduler;
+ this.v1 = v1;
+ this.v2 = v2;
+}
+
+WorkerTask.prototype.run = function (packet) {
+ if (packet == null) {
+ return this.scheduler.suspendCurrent();
+ } else {
+ if (this.v1 == ID_HANDLER_A) {
+ this.v1 = ID_HANDLER_B;
+ } else {
+ this.v1 = ID_HANDLER_A;
+ }
+ packet.id = this.v1;
+ packet.a1 = 0;
+ for (var i = 0; i < DATA_SIZE; i++) {
+ this.v2++;
+ if (this.v2 > 26) this.v2 = 1;
+ packet.a2[i] = this.v2;
+ }
+ return this.scheduler.queue(packet);
+ }
+};
+
+WorkerTask.prototype.toString = function () {
+ return "WorkerTask";
+};
+
+/**
+ * A task that manipulates work packets and then suspends itself.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function HandlerTask(scheduler) {
+ this.scheduler = scheduler;
+ this.v1 = null;
+ this.v2 = null;
+}
+
+HandlerTask.prototype.run = function (packet) {
+ if (packet != null) {
+ if (packet.kind == KIND_WORK) {
+ this.v1 = packet.addTo(this.v1);
+ } else {
+ this.v2 = packet.addTo(this.v2);
+ }
+ }
+ if (this.v1 != null) {
+ var count = this.v1.a1;
+ var v;
+ if (count < DATA_SIZE) {
+ if (this.v2 != null) {
+ v = this.v2;
+ this.v2 = this.v2.link;
+ v.a1 = this.v1.a2[count];
+ this.v1.a1 = count + 1;
+ return this.scheduler.queue(v);
+ }
+ } else {
+ v = this.v1;
+ this.v1 = this.v1.link;
+ return this.scheduler.queue(v);
+ }
+ }
+ return this.scheduler.suspendCurrent();
+};
+
+HandlerTask.prototype.toString = function () {
+ return "HandlerTask";
+};
+
+/* --- *
+ * P a c k e t
+ * --- */
+
+var DATA_SIZE = 4;
+
+/**
+ * A simple package of data that is manipulated by the tasks. The exact layout
+ * of the payload data carried by a packet is not importaint, and neither is the
+ * nature of the work performed on packets by the tasks.
+ *
+ * Besides carrying data, packets form linked lists and are hence used both as
+ * data and worklists.
+ * @param {Packet} link the tail of the linked list of packets
+ * @param {int} id an ID for this packet
+ * @param {int} kind the type of this packet
+ * @constructor
+ */
+function Packet(link, id, kind) {
+ this.link = link;
+ this.id = id;
+ this.kind = kind;
+ this.a1 = 0;
+ this.a2 = new Array(DATA_SIZE);
+}
+
+/**
+ * Add this packet to the end of a worklist, and return the worklist.
+ * @param {Packet} queue the worklist to add this packet to
+ */
+Packet.prototype.addTo = function (queue) {
+ this.link = null;
+ if (queue == null) return this;
+ var peek, next = queue;
+ while ((peek = next.link) != null)
+ next = peek;
+ next.link = this;
+ return queue;
+};
+
+Packet.prototype.toString = function () {
+ return "Packet";
+};
+
+
+runRichards();
Added: trunk/PerformanceTests/JetStream2/RAMification.py (0 => 248326)
--- trunk/PerformanceTests/JetStream2/RAMification.py (rev 0)
+++ trunk/PerformanceTests/JetStream2/RAMification.py 2019-08-06 23:24:26 UTC (rev 248326)
@@ -0,0 +1,347 @@
+# Copyright (C) 2018-2019 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import collections
+import json
+import math
+import os
+import re
+import subprocess
+
+jitTests = ["3d-cube-SP", "3d-raytrace-SP", "acorn-wtb", "ai-astar", "Air", "async-fs", "Babylon", "babylon-wtb", "base64-SP", "Basic", "Box2D", "cdjs", "chai-wtb", "coffeescript-wtb", "crypto", "crypto-aes-SP", "crypto-md5-SP", "crypto-sha1-SP", "date-format-tofte-SP", "date-format-xparb-SP", "delta-blue", "earley-boyer", "espree-wtb", "first-inspector-code-load", "FlightPlanner", "float-mm.c", "gaussian-blur", "gbemu", "gcc-loops-wasm", "hash-map", "HashSet-wasm", "jshint-wtb", "json-parse-inspector", "json-stringify-inspector", "lebab-wtb", "mandreel", "ML", "multi-inspector-code-load", "n-body-SP"
;, "navier-stokes", "octane-code-load", "octane-zlib", "OfflineAssembler", "pdfjs", "prepack-wtb", "quicksort-wasm", "raytrace", "regex-dna-SP", "regexp", "richards", "richards-wasm", "splay", "stanford-crypto-aes", "stanford-crypto-pbkdf2", "stanford-crypto-sha256", "string-unpack-code-SP", "tagcloud-SP", "tsf-wasm", "typescript", "uglify-js-wtb", "UniPoker", "WSL"]
+
+nonJITTests = ["3d-cube-SP", "3d-raytrace-SP", "acorn-wtb", "ai-astar", "Air", "async-fs", "Babylon", "babylon-wtb", "base64-SP", "Basic", "Box2D", "cdjs", "chai-wtb", "coffeescript-wtb", "crypto-aes-SP", "delta-blue", "earley-boyer", "espree-wtb", "first-inspector-code-load", "gaussian-blur", "gbemu", "hash-map", "jshint-wtb", "json-parse-inspector", "json-stringify-inspector", "lebab-wtb", "mandreel", "ML", "multi-inspector-code-load", "octane-code-load", "OfflineAssembler", "pdfjs", "prepack-wtb", "raytrace", "regex-dna-SP", "regexp", "splay", "stanford-crypto-aes", "string-unpack-code-SP", "
tagcloud-SP", "typescript", "uglify-js-wtb"]
+
+# Run two groups of tests with each group in a single JSC instance to see how well memory recovers between tests.
+groupTests = ["typescript,acorn-wtb,Air,pdfjs,crypto-aes-SP", "splay,FlightPlanner,prepack-wtb,octane-zlib,3d-cube-SP"]
+
+luaTests = [("hello_world-LJF", "LuaJSFight/hello_world.js", 5), ("list_search-LJF", "LuaJSFight/list_search.js", 5), ("lists-LJF", "LuaJSFight/lists.js", 5), ("string_lists-LJF", "LuaJSFight/string_lists.js", 5), ("richards", "LuaJSFight/richards.js", 5)]
+
+_oneMB_ = float(1024 * 1024)
+footprintRE = re.compile("Current Footprint: (\d+(?:.\d+)?)")
+peakFootprintRE = re.compile("Peak Footprint: (\d+(?:.\d+)?)")
+
+TestResult = collections.namedtuple("TestResult", ["name", "returnCode", "footprint", "peakFootprint"])
+
+ramification_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+
+remoteHooks = {}
+
+
+def mean(values):
+ if not len(values):
+ return None
+
+ return sum(values) / len(values)
+
+
+def geomean(values):
+ if not len(values):
+ return None
+
+ product = 1.0
+
+ for x in values:
+ product *= x
+
+ return math.pow(product, (1.0 / len(values)))
+
+
+def frameworkPathFromExecutablePath(execPath):
+ if not os.path.abspath(execPath):
+ execPath = os.path.isabs(execPath)
+
+ pathMatch = re.match("(.*?/WebKitBuild/(Release|Debug)+)/([a-zA-Z]+)$", execPath)
+ if pathMatch:
+ return pathMatch.group(1)
+
+ pathMatch = re.match("(.*?)/_javascript_Core.framework/Resources/([a-zA-Z]+)$", execPath)
+ if pathMatch:
+ return pathMatch.group(1)
+
+ pathMatch = re.match("(.*?/(Release|Debug)+)/([a-zA-Z]+)$", execPath)
+ if pathMatch:
+ return pathMatch.group(1)
+
+ pathMatch = re.match("(.*?)/_javascript_Core.framework/(.*?)/Resources/([a-zA-Z]+)$", execPath)
+ if pathMatch:
+ return pathMatch.group(1)
+
+
+def parseArgs(parser=None):
+ def optStrToBool(arg):
+ if arg.lower() in ("true", "t", "yes", "y"):
+ return True
+ if arg.lower() in ("false", "f", "no", "n"):
+ return False
+
+ raise argparse.ArgumentTypeError("Boolean value expected")
+
+ if not parser:
+ parser = argparse.ArgumentParser(description="RAMification benchmark driver script")
+ parser.set_defaults(runner=LocalRunner)
+
+ verbosityGroup = parser.add_mutually_exclusive_group()
+ verbosityGroup.add_argument("-q", "--quiet", dest="verbose", action="" help="Provide less output")
+ verbosityGroup.add_argument("-v", "--verbose", dest="verbose", action="" default=True, help="Provide more output")
+
+ parser.add_argument("-c", "--jsc", dest="jscCommand", type=str, default="/usr/local/bin/jsc", metavar="path-to-jsc", help="Path to jsc command")
+ parser.add_argument("-d", "--jetstream2-dir", dest="testDir", type=str, default=ramification_dir, metavar="path-to-JetStream2-files", help="JetStream2 root directory")
+ parser.add_argument("-e", "--env-var", dest="extraEnvVars", action="" default=[], metavar="env-var=value", help="Specify additional environment variables")
+ parser.add_argument("-f", "--format-json", dest="formatJSON", action="" default=False, help="Format JSON with whitespace")
+ parser.add_argument("-g", "--run-grouped-tests", dest="runGroupedTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run grouped tests [default]")
+ parser.add_argument("-j", "--run-jit", dest="runJITTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run JIT tests [default]")
+ parser.add_argument("-l", "--lua", dest="runLuaTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run Lua comparison tests [default]")
+ parser.add_argument("-n", "--run-no-jit", dest="runNoJITTests", nargs="?", const=True, default=None, type=optStrToBool, metavar="true / false", help="Run no JIT tests [default]")
+ parser.add_argument("-o", "--output", dest="jsonFilename", type=str, default=None, metavar="JSON-output-file", help="Path to JSON output")
+
+ args = parser.parse_args()
+
+ subtestArgs = [args.runGroupedTests, args.runJITTests, args.runLuaTests, args.runNoJITTests]
+ allDefault = all([arg is None for arg in subtestArgs])
+ anyTrue = any([arg is True for arg in subtestArgs])
+ anyFalse = any([arg is False for arg in subtestArgs])
+
+ # Default behavior is to run all subtests.
+ # If a test is explicitly specified not to run, skip that test and use the default behavior for the remaining tests.
+ # If tests are explicitly specified to run, only run those tests.
+ # If there is a mix of tests specified to run and not to run, also do not run any unspecified tests.
+ getArgValue = lambda arg: True if allDefault else False if arg is None and anyTrue else True if arg is None and anyFalse else arg
+
+ args.runJITTests = getArgValue(args.runJITTests)
+ args.runNoJITTests = getArgValue(args.runNoJITTests)
+ args.runLuaTests = getArgValue(args.runLuaTests)
+ args.runGroupedTests = getArgValue(args.runGroupedTests)
+
+ return args
+
+
+class BaseRunner:
+ def __init__(self, args):
+ self.rootDir = args.testDir
+ self.environmentVars = {}
+
+ def setup(self):
+ pass
+
+ def setEnv(self, variable, value):
+ self.environmentVars[variable] = value
+
+ def unsetEnv(self, variable):
+ self.environmentVars.pop(variable, None)
+
+ def resetForTest(self, testName):
+ self.testName = testName
+ self.footprint = None
+ self.peakFootprint = None
+ self.returnCode = 0
+
+ def processLine(self, line):
+ line = line.strip()
+
+ footprintMatch = re.match(footprintRE, line)
+ if footprintMatch:
+ self.footprint = int(footprintMatch.group(1))
+ return
+
+ peakFootprintMatch = re.match(peakFootprintRE, line)
+ if peakFootprintMatch:
+ self.peakFootprint = int(peakFootprintMatch.group(1))
+
+ def setReturnCode(self, returnCode):
+ self.returnCode = returnCode
+
+ def getResults(self):
+ return TestResult(name=self.testName, returnCode=self.returnCode, footprint=self.footprint, peakFootprint=self.peakFootprint)
+
+
+class LocalRunner(BaseRunner):
+ def __init__(self, args):
+ BaseRunner.__init__(self, args)
+ self.jscCommand = args.jscCommand
+
+ def runOneTest(self, test, extraOptions=None, useJetStream2Harness=True):
+ self.resetForTest(test)
+
+ args = [self.jscCommand]
+ if extraOptions:
+ args.extend(extraOptions)
+
+ if useJetStream2Harness:
+ args.extend(["-e", "testList='{test}'; runMode='RAMification'".format(test=test), "cli.js"])
+ else:
+ args.extend(["--footprint", "{test}".format(test=test)])
+
+ self.resetForTest(test)
+
+ proc = subprocess.Popen(args, cwd=self.rootDir, env=self.environmentVars, stdout=subprocess.PIPE, stderr=None, shell=False)
+ while True:
+ line = proc.stdout.readline()
+ self.processLine(line)
+
+ if line == "":
+ break
+
+ self.setReturnCode(proc.wait())
+
+ return self.getResults()
+
+
+def main(parser=None):
+ footprintValues = []
+ peakFootprintValues = []
+ testResultsDict = {}
+ hasFailedRuns = False
+
+ args = parseArgs(parser=parser)
+
+ testRunner = args.runner(args)
+
+ dyldFrameworkPath = frameworkPathFromExecutablePath(args.jscCommand)
+ if dyldFrameworkPath:
+ testRunner.setEnv("DYLD_FRAMEWORK_PATH", dyldFrameworkPath)
+
+ for envVar in args.extraEnvVars:
+ envVarParts = envVar.split("=")
+ if len(envVarParts) == 1:
+ envVarParts[1] = "1"
+ testRunner.setEnv(envVarParts[0], envVarParts[1])
+
+ testRunner.setup()
+
+ def runTestList(testList, extraOptions=None, useJetStream2Harness=True):
+ testScoresDict = {}
+
+ for testInfo in testList:
+ footprintScores = []
+ peakFootprintScores = []
+ if isinstance(testInfo, tuple):
+ testName, test, weight = testInfo
+ else:
+ testName, test, weight = testInfo, testInfo, 1
+
+ print "Running {}...".format(testName),
+ testResult = testRunner.runOneTest(test, extraOptions, useJetStream2Harness)
+
+ if testResult.returnCode == 0 and testResult.footprint and testResult.peakFootprint:
+ if args.verbose:
+ print "footprint: {}, peak footprint: {}".format(testResult.footprint, testResult.peakFootprint)
+ else:
+ print
+
+ if testResult.footprint:
+ footprintScores.append(int(testResult.footprint))
+ for count in range(0, weight):
+ footprintValues.append(testResult.footprint / oneMB)
+ if testResult.peakFootprint:
+ peakFootprintScores.append(int(testResult.peakFootprint))
+ for count in range(0, weight):
+ peakFootprintValues.append(testResult.peakFootprint / oneMB)
+ else:
+ hasFailedRuns = True
+ print "failed",
+ if testResult.returnCode != 0:
+ print " exit code = {}".format(testResult.returnCode),
+ if not testResult.footprint:
+ print " footprint = {}".format(testResult.footprint),
+ if not testResult.peakFootprint:
+ print " peak footprint = {}".format(testResult.peakFootprint),
+ print
+
+ testScoresDict[test] = {"metrics": {"Allocations": ["Geometric"]}, "tests": {"end": {"metrics": {"Allocations": {"current": footprintScores}}}, "peak": {"metrics": {"Allocations": {"current": peakFootprintScores}}}}}
+
+ return testScoresDict
+
+ current_path = os.getcwd()
+ os.chdir(ramification_dir) # To allow JS libraries to load
+
+ if args.runLuaTests:
+ if args.verbose:
+ print "== LuaJSFight No JIT tests =="
+
+ # Use system malloc for LuaJSFight tests
+ testRunner.setEnv("Malloc", "X")
+
+ scoresDict = runTestList(luaTests, ["--useJIT=false", "--forceMiniVMMode=true"], useJetStream2Harness=False)
+
+ testResultsDict["LuaJSFight No JIT Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
+
+ testRunner.unsetEnv("Malloc")
+
+ if args.runGroupedTests:
+ if args.verbose:
+ print "== Grouped tests =="
+
+ scoresDict = runTestList(groupTests)
+
+ testResultsDict["Grouped Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
+
+ if args.runJITTests:
+ if args.verbose:
+ print "== JIT tests =="
+
+ scoresDict = runTestList(jitTests)
+
+ testResultsDict["JIT Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
+
+ if args.runNoJITTests:
+ if args.verbose:
+ print "== No JIT tests =="
+
+ scoresDict = runTestList(nonJITTests, ["--useJIT=false", "-e", "testIterationCount=1"])
+
+ testResultsDict["No JIT Tests"] = {"metrics": {"Allocations": ["Geometric"]}, "tests": scoresDict}
+
+ footprintGeomean = int(geomean(footprintValues) * oneMB)
+ peakFootprintGeomean = int(geomean(peakFootprintValues) * oneMB)
+ totalScore = int(geomean([footprintGeomean, peakFootprintGeomean]))
+
+ if footprintGeomean:
+ print "Footprint geomean: {} ({:.3f} MB)".format(footprintGeomean, footprintGeomean / oneMB)
+
+ if peakFootprintGeomean:
+ print "Peak Footprint geomean: {} ({:.3f} MB)".format(peakFootprintGeomean, peakFootprintGeomean / oneMB)
+
+ if footprintGeomean and peakFootprintGeomean:
+ print "Score: {} ({:.3f} MB)".format(totalScore, totalScore / oneMB)
+
+ resultsDict = {"RAMification": {"metrics": {"Allocations": {"current": [totalScore]}}, "tests": testResultsDict}}
+
+ os.chdir(current_path) # Reset the path back to what it was before
+
+ if args.jsonFilename:
+ with open(args.jsonFilename, "w") as jsonFile:
+ if args.formatJSON:
+ json.dump(resultsDict, jsonFile, indent=4, separators=(',', ': '))
+ else:
+ json.dump(resultsDict, jsonFile)
+
+ if hasFailedRuns:
+ print "Detected failed run(s), exiting with non-zero return code"
+
+ return hasFailedRuns
+
+
+if __name__ == "__main__":
+ exit(main())