def run(self, task: Any = None, cwd: str = ".") -> None: """Actually run the test. Calls Popen with the command to fork a new process. Then, this function polls the process every 5 seconds to check if it has finished or not. Each time it checks, it dumps the json info so other applications can poll those files. task is the celery task that is running this gem5 instance. cwd is the directory to change to before running. This allows a server process to run in a different directory than the running process. Note that only the spawned process runs in the new directory. """ # Check if the run is already in the database db = artifact.getDBConnection() if self.hash in db: print(f"Error: Have already run {self.command}. Exiting!") return self._run(task, cwd)
def run(self, task: Any = None, cwd: str = '.') -> None: """Actually run the test. Calls Popen with the command to fork a new process. Then, this function polls the process every 5 seconds to check if it has finished or not. Each time it checks, it dumps the json info so other applications can poll those files. task is the celery task that is running this gem5 instance. cwd is the directory to change to before running. This allows a server process to run in a different directory than the running process. Note that only the spawned process runs in the new directory. """ # Check if the run is already in the database db = artifact.getDBConnection() if self.hash in db: print(f"Error: Have already run {self.command}. Exiting!") return self.status = "Begin run" self.dumpJson('info.json') if not self.checkArtifacts(cwd): self.dumpJson('info.json') return self.status = "Spawning" self.start_time = time.time() self.task_id = task.request.id if task else None self.dumpJson('info.json') # Start running the gem5 command proc = subprocess.Popen(self.command, cwd=cwd) # Register handler in case this process is killed while the gem5 # instance is running. Note: there's a bit of a race condition here, # but hopefully it's not a big deal def handler(signum, frame): proc.kill() self.kill_reason = 'sigterm' self.dumpJson('info.json') # Note: We'll fall out of the while loop after this. # This makes it so if you term *this* process, it will actually kill # the subprocess and then this process will die. signal.signal(signal.SIGTERM, handler) # Do this until the subprocess is done (successfully or not) while proc.poll() is None: self.status = "Running" # Still running self.current_time = time.time() self.pid = proc.pid self.running = True if self.current_time - self.start_time > self.timeout: proc.kill() self.kill_reason = 'timeout' if self.checkKernelPanic(): proc.kill() self.kill_reason = 'kernel panic' self.dumpJson('info.json') # Check again in five seconds time.sleep(5) print("Done running {}".format(' '.join(self.command))) # Done executing self.running = False self.end_time = time.time() self.return_code = proc.returncode if self.return_code == 0: self.status = "Finished" else: self.status = "Failed" self.dumpJson('info.json') self.saveResults() # Store current gem5 run in the database db.put(self._id, self._getSerializable()) print("Done storing the results of {}".format(' '.join(self.command)))
import hashlib import json import os from pathlib import Path import signal import subprocess import time from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from uuid import UUID, uuid4 import zipfile from gem5art import artifact from gem5art.artifact import Artifact _db: artifact._artifactdb.ArtifactDB = artifact.getDBConnection() class gem5Run: """ This class holds all of the info required to run gem5. """ _id: UUID hash: str type: str name: str gem5_binary: Path run_script: Path gem5_artifact: Artifact gem5_git_artifact: Artifact run_script_git_artifact: Artifact
def _run(self, task: Any = None, cwd: str = ".") -> None: """Actually run the test. Calls Popen with the command to fork a new process. Then, this function polls the process every 5 seconds to check if it has finished or not. Each time it checks, it dumps the json info so other applications can poll those files. task is the celery task that is running this gem5 instance. cwd is the directory to change to before running. This allows a server process to run in a different directory than the running process. Note that only the spawned process runs in the new directory. """ # Connect to the database db = artifact.getDBConnection() self.status = "Begin run" self.dumpJson("info.json") if not self.checkArtifacts(cwd): self.dumpJson("info.json") return self.status = "Spawning" self.start_time = time.time() self.task_id = task.request.id if task else None self.dumpJson("info.json") # Start running the gem5 command proc = subprocess.Popen(self.command, cwd=cwd) # Register handler in case this process is killed while the gem5 # instance is running. Note: there's a bit of a race condition here, # but hopefully it's not a big deal def handler(signum, frame): proc.kill() self.kill_reason = "sigterm" self.dumpJson("info.json") # Note: We'll fall out of the while loop after this. # This makes it so if you term *this* process, it will actually kill # the subprocess and then this process will die. signal.signal(signal.SIGTERM, handler) # Do this until the subprocess is done (successfully or not) while proc.poll() is None: self.status = "Running" # Still running self.current_time = time.time() self.pid = proc.pid self.running = True if self.current_time - self.start_time > self.timeout: proc.kill() self.kill_reason = "timeout" if self.checkKernelPanic(): proc.kill() self.kill_reason = "kernel panic" # Assigning a function/lambda to an object variable does not make # the function/lambda become a bound one. Therefore, the # user-defined function must pass `self` in. # Here, mypy classifies self.check_failure() as a bound function, # so we tell mypy to ignore it./ if self.check_failure(self): # type: ignore proc.kill() self.kill_reason = "User defined kill" self.dumpJson("info.json") # Check again in five seconds time.sleep(5) print("Done running {}".format(" ".join(self.command))) # Done executing self.running = False self.end_time = time.time() self.return_code = proc.returncode if self.return_code == 0: self.status = "Finished" else: self.status = "Failed" self.dumpJson("info.json") self.saveResults() # Store current gem5 run in the database db.put(self._id, self._getSerializable()) print("Done storing the results of {}".format(" ".join(self.command)))