From 3d608ab128aa5ef2aeae5590c6253f7a4c13647d Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 30 Oct 2015 17:53:42 +0100 Subject: [PATCH] Update towards having out of order runs. In the meantime organize php code better --- slave/submitter.py | 49 +++++++++++++-- website/UPDATE.php | 96 +++++++++++++++++------------ website/internals.php | 9 --- website/lib/BashInterpreter.php | 8 ++- website/lib/DB/Build.php | 45 ++++++++++++++ website/lib/DB/DB.php | 12 ++++ website/lib/DB/Mode.php | 41 ++++++------ website/lib/DB/Run.php | 73 ++++++++++++++++++++++ website/lib/DB/Vendor.php | 18 ++++++ website/lib/ManipulateTask.php | 26 ++++++-- website/lib/RetriggerController.php | 11 ++-- website/lib/RunReporter.php | 78 +++++++++++++++++++++++ website/lib/VersionControl.php | 42 +++++++++++++ 13 files changed, 426 insertions(+), 82 deletions(-) create mode 100644 website/lib/DB/Build.php create mode 100644 website/lib/DB/DB.php create mode 100644 website/lib/DB/Run.php create mode 100644 website/lib/DB/Vendor.php create mode 100644 website/lib/RunReporter.php create mode 100644 website/lib/VersionControl.php diff --git a/slave/submitter.py b/slave/submitter.py index 3b9970d..fbf23ba 100644 --- a/slave/submitter.py +++ b/slave/submitter.py @@ -67,6 +67,27 @@ class RemoteSubmitter(Submitter): except urllib2.URLError: self.runIds[i] = None + def startOutOfOrder(self, mode, revision, run_before, run_after): + self.assertMachine() + for i in range(len(self.urls)): + try: + url = self.urls[i] + url += '?run=ooo' + url += '&MACHINE=' + str(self.machine) + url += '&mode=' + str(mode) + url += '&revision=' + str(revision) + url += '&run_before_id=' + str(run_before) + url += '&run_after_id=' + str(run_after) + url = urllib2.urlopen(url) + contents = url.read() + m = re.search('id=(\d+)', contents) + if m == None: + self.runIds[i] = None + else: + self.runIds[i] = int(m.group(1)) + except urllib2.URLError: + self.runIds[i] = None + def createBuild(self, engine_type, config, cset): mode = self.mode(engine_type, config) for i in range(len(self.urls)): @@ -190,12 +211,25 @@ if __name__ == "__main__": from optparse import OptionParser parser = OptionParser(usage="usage: %prog [options]") - parser.add_option("-f", "--finish", action="store_true", dest="finish", default=False) - parser.add_option("-s", "--session", dest="session", type="string") - + # create a session arguments parser.add_option("-c", "--create", action="store_true", dest="create", default=False) + parser.add_option("-o", "--output", dest="output", type="string", + help="The place to write the session too.") parser.add_option("-m", "--machine", dest="machine", type="int", help="Give the machine number to submit to.") + parser.add_option("--mode", dest="mode", type="string", + help="OutOfOrder: Give the mode") + parser.add_option("--revision", dest="rev", type="string", + help="OutOfOrder: Give the revision") + parser.add_option("--run_before", dest="run_before", type="id", + help="OutOfOrder: Give the run id whereafter this new run needs to come") + parser.add_option("--run_after", dest="run_after", type="id", + help="OutOfOrder: Give the run id before which this new run needs to come") + + # all other cases need session info + parser.add_option("-s", "--session", dest="session", type="string") + + parser.add_option("-f", "--finish", action="store_true", dest="finish", default=False) (options, args) = parser.parse_args() utils.config.init("awfy.config") @@ -203,8 +237,13 @@ if __name__ == "__main__": if options.create: submitter = RemoteSubmitter() submitter.setMachine(options.machine) - submitter.start() - print json.dumps(submitter.runIds) + if options.mode: + submitter.startOutOfOrder(options.mode, options.rev, options.run_before, options.run_after) + else: + submitter.start() + + fp = open(options.output, "w") + json.dump(submitter.runIds, fp) elif options.finish: fp = open(options.session, "r") session = json.load(fp) diff --git a/website/UPDATE.php b/website/UPDATE.php index b983bb3..c39e3e6 100644 --- a/website/UPDATE.php +++ b/website/UPDATE.php @@ -7,61 +7,77 @@ require_once("internals.php"); init_database(); +require_once("lib/DB/Run.php"); +require_once("lib/DB/Build.php"); +require_once("lib/DB/Mode.php"); +require_once("lib/RunReporter.php"); + // Start a full benchmark run. Request a token/number used to report/group // benchmark scores. -if (isset($_GET['run']) && $_GET['run'] == 'yes') { - // TODO: sort_order is not a date anymore. Adjust this logic to not take an - // order, but only set this afterwards. - $MACHINE = GET_int('MACHINE'); - $stamp = GET_int('stamp'); +if (GET_string("run") == 'yes') { + $machine_id = GET_int('MACHINE'); - $query = mysql_query("SELECT max(sort_order) as maximum - FROM awfy_run - WHERE machine = '".$MACHINE."'") or die(mysql_error()); - $run = mysql_fetch_object($query); + $run = RunReporter::createForMachine($machine_id); + echo "id=".$run->id; + + die(); +} + +// Start an out of order run. Retriggering a particular mode. +if (GET_string("run") == 'ooo') { + $machine_id = GET_int('MACHINE'); + $mode = Mode::FromMode(GET_string('name')); + $revision = GET_string('revision'); + $run_before_id = GET_int('run_before_id'); + $run_after_id = GET_int('run_after_id'); + + $run = RunReporter::createOutOfOrder($machine_id, $mode->id, $revision, + $run_before_id, $run_after_id); + echo "id=".$run->id; - mysql_query("INSERT INTO awfy_run (machine, sort_order, approx_stamp) - VALUES - ($MACHINE, {$run->maximum}, UNIX_TIMESTAMP())") - or die("ERROR: " . mysql_error()); - print("id=" . mysql_insert_id()); die(); } // Finish a full benchmark run. Scores will only become visible from now on // (when status equals 1). -if (isset($_GET['run']) && $_GET['run'] == 'finish') { - $runid = GET_run_id('runid'); +if (GET_string("run") == 'finish') { + $run_id = GET_int('runid'); $status = GET_int('status'); - if (isset($_GET['error'])) - $error = '\'' . mysql_real_escape_string(GET_string('error')) . '\''; - else - $error = 'NULL'; - mysql_query("UPDATE awfy_run - SET status = $status, - error = $error, - finish_stamp = UNIX_TIMESTAMP() - WHERE id = $runid") - or die("ERROR: " . mysql_error()); + + $run = new Run($run_id); + if ($run->isFinished() || $run->hasError()) + throw new Error("Run was already finished or error'ed"); + + $error = GET_string('error'); + $run->finish($status, $error); die(); } -if (isset($_GET['run']) && $_GET['run'] == 'addEngine') { - $runid = GET_run_id('runid'); - $mode_id = find_mode(GET_string('name')); - $cset = mysql_real_escape_string(GET_string('cset')); - mysql_query("INSERT INTO awfy_build - (run_id, mode_id, cset) - VALUES - ($runid, $mode_id, '$cset')") - or die("ERROR: " . mysql_error()); +if (GET_string("run") == 'addEngine') { + $run = new Run(GET_int('runid')); + $revision = GET_string('cset'); + $mode = Mode::FromMode(GET_string('name')); + + if ($run->isFinished() || $run->hasError()) + throw new Error("Run was already finished or error'ed"); + + if ($run->isOutOfOrder()) { + // out of order builds cannot add extra modes. The available + // mode have already been added. + if (!Build::withRunAndMode($run->id, $mode->id)) + $run->finish(0, "Tried to add extra modes to out of order run."); + die(); + } + + Build::insert($run, $mode->id, $revision); + die(); } // Report that a slave is still awake when there are no benchmarks results // to send. -if (isset($_GET['awake']) && $_GET['awake'] == 'yes') { +if (GET_string('awake') == 'yes') { $MACHINE = GET_int('MACHINE'); mysql_query("UPDATE awfy_machine SET last_checked = UNIX_TIMESTAMP() @@ -71,12 +87,16 @@ if (isset($_GET['awake']) && $_GET['awake'] == 'yes') { } // Report score of a benchmark total or subtest. +$run_id = GET_int('run'); +$run = new Run($run_id); +if ($run->isFinished() || $run->hasError()) + throw new Error("Run was already finished or error'ed"); + $time = mysql_real_escape_string(GET_string('time')); $mode_id = find_mode(GET_string('mode')); -$run = GET_run_id('run'); $version = GET_string('suite'); $score = GET_int('score'); -$build = find_build($run, $mode_id); +$build = find_build($run_id, $mode_id); if (isset($_GET['version'])) $version = GET_string('version'); $suite_version_id = find_or_add_suite_version(GET_string('suite'), $version); diff --git a/website/internals.php b/website/internals.php index a07f3fd..57c5abb 100755 --- a/website/internals.php +++ b/website/internals.php @@ -206,14 +206,5 @@ function awfy_query($query) return $result; } -function GET_run_id($name) -{ - $runid = GET_int($name); - $results = mysql_query("SELECT id from awfy_run WHERE id = $runid AND status = 0"); - if (!$results || mysql_num_rows($results) < 1) - return 0; - return $runid; -} - // Init $config = new Config("/etc/awfy-server.config"); diff --git a/website/lib/BashInterpreter.php b/website/lib/BashInterpreter.php index 793eb82..d067c9c 100644 --- a/website/lib/BashInterpreter.php +++ b/website/lib/BashInterpreter.php @@ -3,8 +3,8 @@ class BashInterpreter { public static function matchCommand($text, $command) { $command = str_replace("/", "\/", $command); - preg_match_all("/".$command." .*[;|$|\r\n]/", $text, $matches); - return $matches[0]; + preg_match_all("/(".$command." .*)[;$\r\n#]/", $text, $matches); + return $matches[1]; } public static function matchFlag($command, $flag) { @@ -23,6 +23,10 @@ class BashInterpreter { return str_replace($command, $command." ".$full_flag, $text); } + public static function addFlagToCommand($text, $full_command, $full_flag) { + return str_replace($full_command, $full_command." ".$full_flag, $text); + } + public static function removeFlagFromCommands($text, $command, $full_flag) { foreach(BashInterpreter::matchCommand($text, $command) as $match) { $text = BashInterpreter::removeFlagFromCommand($text, $match, $full_flag); diff --git a/website/lib/DB/Build.php b/website/lib/DB/Build.php new file mode 100644 index 0000000..2788c66 --- /dev/null +++ b/website/lib/DB/Build.php @@ -0,0 +1,45 @@ +id = $id; + } + + public static function withRunAndMode($run_id, $mode_id) { + $qBuild = mysql_query("SELECT id FROM awfy_build + WHERE run_id = $run_id AND + mode_id = $mode_id + LIMIT 1") or die(mysql_error()); + $build = mysql_fetch_object($qBuild); + return new Build($build->id); + } + + public static function insert($run, $mode_id, $revision) { + if ($run->isFinished()) + throw new Exception("Cannot info to a run that is finished."); + + mysql_query("INSERT INTO awfy_build + (run_id, mode_id, revision) + VALUES + ({$run->id}, $mode_id, '".mysql_real_escape_string($revision)."')") + or die("ERROR: " . mysql_error()); + return new Build(mysql_insert_id()); + } + + public function revision() { + return $this->select("revision"); + } + + public function run_id() { + return $this->select("run_id"); + } + + public function mode_id() { + return $this->select("mode_id"); + } +} diff --git a/website/lib/DB/DB.php b/website/lib/DB/DB.php new file mode 100644 index 0000000..efd7784 --- /dev/null +++ b/website/lib/DB/DB.php @@ -0,0 +1,12 @@ +id}") or die(mysql_error()); + $field = mysql_fetch_object($qField); + return $field->field; + } +} + diff --git a/website/lib/DB/Mode.php b/website/lib/DB/Mode.php index 5bd4b0f..ad45bc3 100644 --- a/website/lib/DB/Mode.php +++ b/website/lib/DB/Mode.php @@ -1,30 +1,35 @@ id = $id; } - function vendor() { - $qMode = mysql_query("SELECT vendor_id - FROM awfy_mode - WHERE id = {$this->id}"); - $mode = mysql_fetch_object($qMode); - return $mode->vendor_id; + public static function FromMode($mode) { + $query = "SELECT id FROM awfy_mode + WHERE mode = '" . mysql_real_escape_string($mode) . "'"; + $results = mysql_query($query) or die(mysql_error()); + if (!$results || mysql_num_rows($results) < 1) + return null; + $row = mysql_fetch_array($results); + return new Mode($row[0]); } - function mode() { - $qMode = mysql_query("SELECT mode - FROM awfy_mode - WHERE id = {$this->id}"); - $mode = mysql_fetch_object($qMode); - return $mode->mode; + public function vendor_id() { + return $this->select("vendor_id"); } - function id() { - return $this->id; + public function vendor() { + return new Vendor($this->vendor_id()); + } + + public function mode() { + return $this->select("mode"); } } - diff --git a/website/lib/DB/Run.php b/website/lib/DB/Run.php new file mode 100644 index 0000000..f6e3403 --- /dev/null +++ b/website/lib/DB/Run.php @@ -0,0 +1,73 @@ +id = $id; + } + + public static function insert($machine_id, $sort_order, $approx_stamp = 0) { + $approx_stamp = intval($approx_stamp); + if ($approx_stamp == 0) + $approx_stamp = "UNIX_TIMESTAMP()"; + + mysql_query("INSERT INTO awfy_run + (machine, sort_order, approx_stamp) + VALUES + ($machine_id, $sort_order, $approx_stamp)") + or die("ERROR: " . mysql_error()); + return new Run(mysql_insert_id()); + } + + public function finish($status, $error = "") { + if (empty($error)) + $error = "NULL"; + else + $error = "'" . mysql_real_escape_string($error) . "'"; + + mysql_query("UPDATE awfy_run + SET status = $status, + error = $error, + finish_stamp = UNIX_TIMESTAMP() + WHERE id = $runid") + or die("ERROR: " . mysql_error()); + } + + public function isFinished() { + return $this->select("status") > 0; + } + + public function isOutOfOrder() { + return !!$this->select("out_of_order"); + } + + public function hasError() { + return $this->select("error") != ""; + } + + public function sort_order() { + return $this->select("sort_order"); + } + + public function approx_stamp() { + return $this->select("approx_stamp"); + } + + public function builds() { + $qRun = mysql_query("SELECT approx_stamp from awfy_builds + WHERE id = {$this->id}") or die(mysql_error()); + $run = mysql_fetch_object($qRun); + return $run->approx_stamp; + } + + public function isBuildInfoComplete() { + // The set of builds cannot change anymore, when the run is finished + // or if this run is an out of order run. Out of order runs immediately + // add their build info. + return $this->isFinished() || $this->isOutOfOrder() || $this->hasError(); + } +} diff --git a/website/lib/DB/Vendor.php b/website/lib/DB/Vendor.php new file mode 100644 index 0000000..2ec94ea --- /dev/null +++ b/website/lib/DB/Vendor.php @@ -0,0 +1,18 @@ +id = $id; + } + + function csetURL() { + $qVendor = mysql_query("SELECT csetURL + FROM awfy_vendor + WHERE id = {$this->id}"); + $vendor = mysql_fetch_object($qVendor); + return $vendor->csetURL; + } + +} diff --git a/website/lib/ManipulateTask.php b/website/lib/ManipulateTask.php index 81d7a1f..3f89976 100644 --- a/website/lib/ManipulateTask.php +++ b/website/lib/ManipulateTask.php @@ -38,7 +38,6 @@ class ManipulateTask extends Task { $source_matches = BashInterpreter::matchFlag($command, "-s"); $source_rules = $this->source_rules(); $engine = $source_rules[$source_matches[0]]; - print_r($removed_engines); if (in_array($engine, $removed_engines)) { $this->removeBuildOrDownloadCommand($command); } @@ -63,16 +62,31 @@ class ManipulateTask extends Task { $this->update_configs($configs); } - public function setTipRevision() { - $this->removeRevisionInfo(); + public function setBuildRevisionToTip() { + $this->removeBuildRevisionInfo(); } - public function setRevision($new_revision) { - $this->removeRevisionInfo(); + public function setBuildRevision($new_revision) { + $this->removeBuildRevisionInfo(); $this->task = BashInterpreter::addFlagToCommands($this->task, "python build.py", "-r ".$new_revision); } - private function removeRevisionInfo() { + public function setSubmitterOutOfOrder($mode_name, $revision, $run_before_id, $run_after_id) { + $commands = BashInterpreter::matchCommand($this->task, "python submitter.py"); + foreach ($commands as $command) { + if (count(BashInterpreter::matchFlag($command, "-c")) > 0 || + count(BashInterpreter::matchFlag($command, "--create")) > 0) + { + $flag = "--revision ".$revision." "; + $flag .= "--mode ".$mode_name." "; + $flag .= "--run_before ".$run_before_id." "; + $flag .= "--run_after ".$run_after_id; + $this->task = BashInterpreter::addFlagToCommand($this->task, $command, $flag); + } + } + } + + private function removeBuildRevisionInfo() { $commands = BashInterpreter::matchCommand($this->task, "python download.py"); if (count($commands) != 0) throw new Exception("Not yet supported to specify revision for downloaded builds."); diff --git a/website/lib/RetriggerController.php b/website/lib/RetriggerController.php index 0c6008d..d7f8f81 100644 --- a/website/lib/RetriggerController.php +++ b/website/lib/RetriggerController.php @@ -33,7 +33,7 @@ class RetriggerController { $task = new ManipulateTask($task->task); if ($mode_id != 0) - $task->update_modes($mode->mode()); + $task->update_modes(Array($mode->mode())); $retrigger->tasks[] = $task; @@ -45,9 +45,13 @@ class RetriggerController { return $retrigger; } - public function setRevision($revision) { + public function convertToRevision($mode_id, $revision, $run_before_id, $run_after_id) { + $mode = new Mode($mode_id); + foreach ($this->tasks as $task) { - $task->setRevision($revision); + $task->update_modes(Array("jmim"/*$mode->mode()*/)); + $task->setBuildRevision($revision); + $task->setSubmitterOutOfOrder("jmim"/*$mode->mode()*/, $revision, $run_before_id, $run_after_id); } } @@ -62,4 +66,3 @@ class RetriggerController { } } } - diff --git a/website/lib/RunReporter.php b/website/lib/RunReporter.php new file mode 100644 index 0000000..582a849 --- /dev/null +++ b/website/lib/RunReporter.php @@ -0,0 +1,78 @@ +maximum); + } + + public static function createOutOfOrder($machine_id, $mode_id, $revision, + $run_before_id, $run_after_id) + { + $run_before = new Run($run_before_id); + $run_after = new Run($run_after_id); + + // Find the sorting order where we could add this revision; + $sort_order = RunReporter::findSortOrder($run_before, $mode_id, $revision); + + // sanity check. + if ($sort_order >= run_after->sort_order()) + throw new Exception("Given run bounds were incorrect."); + + // Create a space at the given sort_order, by shifting all sort_order, + // equal or higher than the given sort_order. + RunReporter::increaseNextSortOrder($machine_id, $sort_order); + + $run = Run::insert($machine_id, $sort_order, $run_before->approx_stamp()); + $build = Build::insert($run, $mode_id, $revision); + return $run; + } + + private static function findSortOrder($run, $mode_id, $revision) + { + $version_control = VersionControl::forMode($mode_id); + + while (True) { + if (!$run->isBuildInfoComplete()) + throw new Exception("Encountered an incomplete run."); + + $build = Build::withRunAndMode($run_before->id, $mode_id); + + // We can safely ignore runs that have no results with the requested mode_id + if (!$build) { + $run = $run->next(); + continue; + } + + // We skip to the next run, if revisions are the same. + // To make sure that new runs are shown after existing ones. + if ($version_control->equal($build->revision(), $revision)) { + $run = $run->next(); + continue; + } + + // Using version control take a peek if the revision + // is later/earlier than this one. + if ($version_control->isAfter($build->revision(), $revision)) + return $run->sort_order(); + + $run = $run->next(); + } + } + + private static function increaseNextSortOrder($machine_id, $sort_order) { + mysql_query("UPDATE awfy_run + SET sort_order = sort_order + 1 + WHERE machine = $machine_id AND + sort_order >= $sort_order") or die(mysql_error()); + } +} diff --git a/website/lib/VersionControl.php b/website/lib/VersionControl.php new file mode 100644 index 0000000..f712664 --- /dev/null +++ b/website/lib/VersionControl.php @@ -0,0 +1,42 @@ +vendor_id()); + + $url = $vendor->csetURL(); + if (strpos($url, "hg.mozilla.org") !== false) + return new HGWeb($url); + + throw new Exception("Not implemented version control system."); + } + +} + +class HGWeb { + + public function __construct($url) { + $this->url = str_replace("/rev/", "/", $url); + } + + public function equal($revision1, $revision2) { + return $revision1 == $revision2; + } + + public function isAfter($revision1, $revision2) { + // test if is before + $html = file_get_contents($this->url."pushloghtml?fromchange=$revision1&tochange=$revision2"); + if (strpos($html, "pushlogentry") !== false) + return false; + + // test if is after + $html = file_get_contents($this->url."pushloghtml?fromchange=$revision2&tochange=$revision1"); + if (strpos($html, "pushlogentry") !== false) + return true; + + throw new Exception("Could find relationship between $revision1 and $revision2."); + } + +}