From 6a9f851b5a4acd6f70f7f0b113818f8775911884 Mon Sep 17 00:00:00 2001 From: Nathan Pemberton Date: Wed, 12 Dec 2018 18:21:54 -0500 Subject: [PATCH] Add uart logging and file outputs --- runOutput/.gitignore | 2 ++ sw-manager.py | 67 +++++++++++++++++++++++--------------------- test/command.json | 2 +- test/smoke2.json | 9 ++++-- util/config.py | 7 +++-- util/util.py | 26 ++++++++++++----- 6 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 runOutput/.gitignore diff --git a/runOutput/.gitignore b/runOutput/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/runOutput/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/sw-manager.py b/sw-manager.py index 1f30291d..1946c960 100755 --- a/sw-manager.py +++ b/sw-manager.py @@ -48,12 +48,8 @@ def main(): help="Launch the specified job. Defaults to running the base image.") launch_parser.add_argument('-i', '--initramfs', action='store_true', help="Launch the initramfs version of this workload") - # # Init Command - # init_parser = subparsers.add_parser( - # 'init', help="Initialize workloads (using 'host_init' script)") - # init_parser.set_defaults(func=handleInit) - args = parser.parse_args() + setRunName(args) args.workdir = os.path.abspath(args.workdir) # args.config_file = os.path.join(args.workdir, args.config_file) @@ -220,16 +216,15 @@ def handleBuild(args, cfgs): # The order isn't critical here, we should have defined the dependencies correctly in loader doit.doit_cmd.DoitMain(loader).run(binList + imgList) -def launchSpike(config, initramfs=False): - log = logging.getLogger() +def getSpikeCmd(config, initramfs=False): if initramfs: - sp.check_call(['spike', '-p4', '-m4096', config['bin'] + '-initramfs']) + return ['spike', '-p4', '-m4096', config['bin'] + '-initramfs'] elif 'img' not in config: - sp.check_call(['spike', '-p4', '-m4096', config['bin']]) + return ['spike', '-p4', '-m4096', config['bin']] else: raise ValueError("Spike does not support disk-based configurations") -def launchQemu(config, initramfs=False): +def getQemuCmd(config, initramfs=False): log = logging.getLogger() if initramfs: @@ -251,9 +246,9 @@ def launchQemu(config, initramfs=False): if 'img' in config and not initramfs: cmd = cmd + ['-device', 'virtio-blk-device,drive=hd0', '-drive', 'file=' + config['img'] + ',format=raw,id=hd0'] - cmd = cmd + ['-append', 'ro root=/dev/vda'] + cmd = cmd + ['-append', '"ro root=/dev/vda"'] - sp.check_call(cmd) + return cmd def handleLaunch(args, cfgs): log = logging.getLogger() @@ -264,24 +259,26 @@ def handleLaunch(args, cfgs): else: # Run the base image config = cfgs[args.config_file] - + + runResDir = os.path.join(res_dir, getRunName(), config['name']) + uartLog = os.path.join(runResDir, "uartlog") + os.makedirs(runResDir) + if args.spike: if 'img' in config and 'initramfs' not in config: sys.exit("Spike currently does not support disk-based " + "configurations. Please use an initramfs based image.") - launchSpike(config, args.initramfs) + cmd = getSpikeCmd(config, args.initramfs) else: - launchQemu(config, args.initramfs) - -# def handleInit(args, cfgs): -# log = logging.getLogger() -# config = cfgs[args.config_file] -# if 'host-init' in config: -# log.info("Applying host-init: " + config['host-init']) -# if not os.path.exists(config['host-init']): -# raise ValueError("host-init script " + config['host-init'] + " not found.") -# -# run([config['host-init']], cwd=config['workdir']) + cmd = getQemuCmd(config, args.initramfs) + + sp.check_call(" ".join(cmd) + " | tee " + uartLog, shell=True) + + if 'outputs' in config: + outputSpec = [ FileSpec(src=f, dst=runResDir + "/") for f in config['outputs']] + copyImgFiles(config['img'], outputSpec, direction='out') + + log.info("Run output available in: " + runResDir) # Now build linux/bbl def makeBin(config, initramfs=False): @@ -312,8 +309,6 @@ def makeBin(config, initramfs=False): shutil.copy('riscv-pk/build/bbl', config['bin'] + '-initramfs') else: shutil.copy('riscv-pk/build/bbl', config['bin']) - # elif config['distro'] != 'bare': - # raise ValueError("No linux config defined. This is only supported for workloads based on 'bare'") def makeImage(config): log = logging.getLogger() @@ -330,7 +325,7 @@ def makeImage(config): if 'files' in config: log.info("Applying file list: " + str(config['files'])) - applyFiles(config['img'], config['files']) + copyImgFiles(config['img'], config['files'], 'in') if 'guest-init' in config: log.info("Applying init script: " + config['guest-init']) @@ -366,10 +361,13 @@ def makeImage(config): # Note that all paths must be absolute def applyOverlay(img, overlay): log = logging.getLogger() - applyFiles(img, [FileSpec(src=os.path.join(overlay, "*"), dst='/')]) + copyImgFiles(img, [FileSpec(src=os.path.join(overlay, "*"), dst='/')], 'in') -# Copies a list of type FileSpec ('files') into the destination image (img) -def applyFiles(img, files): +# Copies a list of type FileSpec ('files') to/from the destination image (img) +# img - path to image file to use +# files - list of FileSpecs to use +# direction - "in" or "out" for copying files into or out of the image (respectively) +def copyImgFiles(img, files, direction): log = logging.getLogger() if not os.path.exists(mnt): @@ -388,7 +386,12 @@ def applyFiles(img, files): # Note: shell=True because f.src is allowed to contain globs # Note: os.path.join can't handle overlay-style concats (e.g. join('foo/bar', '/baz') == '/baz') # run('cp -a ' + f.src + " " + os.path.normpath(mnt + f.dst), shell=True) - run('sudo rsync -a --chown=root:root ' + f.src + " " + os.path.normpath(mnt + f.dst), shell=True) + if direction == 'in': + run('sudo rsync -a --chown=root:root ' + f.src + " " + os.path.normpath(mnt + f.dst), shell=True) + elif direction == 'out': + run('sudo rsync -a --chown=root:root ' + os.path.normpath(mnt + f.src) + " " + f.dst, shell=True) + else: + raise ValueError("direction option must be either 'in' or 'out'") finally: # run(['guestunmount', mnt]) # run(['fusermount', '-u', mnt]) diff --git a/test/command.json b/test/command.json index 7f11eada..be473e3a 100644 --- a/test/command.json +++ b/test/command.json @@ -1,5 +1,5 @@ { "name" : "command", "base" : "br-base.json", - "command" : "echo I Ran!" + "command" : "echo Global: command >> /root/runOutput && cat /root/runOutput" } diff --git a/test/smoke2.json b/test/smoke2.json index 34715b8a..914c9948 100644 --- a/test/smoke2.json +++ b/test/smoke2.json @@ -2,19 +2,22 @@ "name" : "smoke2", "base" : "br-base.json", "overlay" : "overlay", - "guest-init" : "guest-init.sh", + "guest-init" : "init.sh", + "outputs" : [ "/root/runOutput" ], "run" : "run.sh", "jobs" : [ { "name" : "j0", "files" : [ ["j0_output", "/root/"] ], - "guest-init" : "guest-init0.sh", + "guest-init" : "init0.sh", + "outputs" : [ "/root/j0_output" ], "run" : "run0.sh" }, { "name" : "j1", "files" : [ ["j1_output", "/root/"] ], - "guest-init" : "guest-init1.sh", + "outputs" : [ "/root/j1_output" ], + "guest-init" : "init1.sh", "run" : "run1.sh" } ] diff --git a/util/config.py b/util/config.py index 464eb6d3..a5fee303 100644 --- a/util/config.py +++ b/util/config.py @@ -25,8 +25,11 @@ 'host-init', # Path to folder containing overlay files to apply to img 'overlay', - # List of tuples of files to add [(dest_dir, srcFile),...] + # List of tuples of files to add [(guest_dest, host_src),...] 'files', + # List of files to copy out of the image after running it. Files will + # be flattened into results dir. [guest_src0, guest_src1, ...] + 'outputs', # Path to script to run on the guest every time it boots 'run', # An inline command to run at startup (cannot be set along with 'run') @@ -56,7 +59,7 @@ # These are the options that should be inherited from base configs (if not # explicitly provided) -configInherit = ['runSpec', 'files', 'linux-config', 'builder', 'distro'] +configInherit = ['runSpec', 'files', 'outputs', 'linux-config', 'builder', 'distro'] # These are the permissible base-distributions to use (they get treated special) distros = { diff --git a/util/util.py b/util/util.py index fbae83cf..2c7c0b71 100644 --- a/util/util.py +++ b/util/util.py @@ -10,10 +10,27 @@ root_dir = os.getcwd() image_dir = os.path.join(root_dir, "images") linux_dir = os.path.join(root_dir, "riscv-linux") +log_dir = os.path.join(root_dir, "logs") +res_dir = os.path.join(root_dir, "runOutput") mnt = os.path.join(root_dir, "disk-mount") commandScript = os.path.join(root_dir, "_command.sh") - jlevel = "-j" + str(os.cpu_count()) +runName = "" + +# Create a unique run name +def setRunName(args): + global runName + + timeline = time.strftime("%Y-%m-%d--%H-%M-%S", time.gmtime()) + randname = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(16)) + + runName = os.path.splitext(os.path.basename(args.config_file))[0] + \ + "-" + args.command + \ + "-" + timeline + \ + "-" + randname + +def getRunName(): + return runName # logging setup def initLogging(args): @@ -21,12 +38,7 @@ def initLogging(args): rootLogger.setLevel(logging.NOTSET) # capture everything # Create a unique log name - timeline = time.strftime("%Y-%m-%d--%H-%M-%S", time.gmtime()) - randname = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(16)) - - logPath = os.path.join(root_dir, "logs", os.path.splitext(os.path.basename(args.config_file))[0] + - "-" + timeline + "-" + - "-" + randname + ".log") + logPath = os.path.join(log_dir, getRunName() + ".log") # formatting for log to file fileHandler = logging.FileHandler(str(logPath))