aot.rs 28 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1
// Copyright 2017 The Australian National University
2
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
3 4 5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
6
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
9 10 11 12 13 14
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

15 16
extern crate time;

17
use linkutils::*;
18 19
use ast::ir::MuName;
use runtime;
qinsoon's avatar
qinsoon committed
20
use vm::VM;
21
use compiler::backend;
qinsoon's avatar
qinsoon committed
22

23 24 25
use std::path::PathBuf;
use std::process::Command;

26 27
/// links generated code for the given functions, static library of Zebu,
/// and a main function to produce an executable of the given name
28
pub fn link_primordial(funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
29 30 31
    let emit_dir = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);

    // prepare a list of files that need to be compiled and linked together
32
    let files: Vec<PathBuf> = {
33 34 35 36 37 38 39 40 41 42 43 44 45
        use std::fs;

        let mut ret = vec![];

        // all interested mu funcs
        for func in funcs {
            ret.push(get_path_for_mu_func(func, vm));
        }

        // mu context
        ret.push(get_path_for_mu_context(vm));

        // copy primoridal entry
46
        let source = get_path_under_zebu(runtime::PRIMORDIAL_ENTRY);
47 48 49 50 51 52 53 54
        let dest = {
            let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
            ret.push("main.c");

            ret
        };
        trace!("copying from {:?} to {:?}", source, dest);
        match fs::copy(source.as_path(), dest.as_path()) {
55
            Ok(_) => {}
56
            Err(e) => panic!("failed to copy: {}", e)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
        }

        // include the primordial C main
        ret.push(dest);

        // include mu static lib
        ret.push(get_path_under_zebu(if cfg!(debug_assertions) {
            "target/debug/libmu.a"
        } else {
            "target/release/libmu.a"
        }));

        ret
    };

    let mut out_path = emit_dir.clone();
    out_path.push(out);

75 76 77 78
    link_executable_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
79
        out_path
80
    )
81 82
}

83 84 85 86
/// links generated code for the given test's functions, static library of Zebu,
/// and a main function to produce an executable of the given name
pub fn link_test_primordial(funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
    let emit_dir = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
87

88 89 90
    // prepare a list of files that need to be compiled and linked together
    let files: Vec<PathBuf> = {
        use std::fs;
91

92
        let mut ret = vec![];
93

94 95 96 97
        // all interested mu funcs
        for func in funcs {
            ret.push(get_path_for_mu_func(func, vm));
        }
98

99 100
        // mu context
        ret.push(get_path_for_mu_context(vm));
101

102 103 104 105 106
        // copy primoridal entry
        let source = get_path_under_zebu(runtime::PRIMORDIAL_ENTRY);
        let dest = {
            let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
            ret.push("main_test.c");
107

108 109 110 111 112 113 114
            ret
        };
        trace!("copying from {:?} to {:?}", source, dest);
        match fs::copy(source.as_path(), dest.as_path()) {
            Ok(_) => {}
            Err(e) => panic!("failed to copy: {}", e)
        }
115

116 117
        // include the primordial C main
        ret.push(dest);
118

119 120 121 122 123 124
        // include mu static lib
        ret.push(get_path_under_zebu(if cfg!(debug_assertions) {
            "target/debug/libmu.a"
        } else {
            "target/release/libmu.a"
        }));
125

126 127
        ret
    };
128

129 130
    let mut out_path = emit_dir.clone();
    out_path.push(out);
131

132 133 134 135 136 137 138 139
    link_executable_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
        out_path
    )
}

140
/// invokes the C compiler to link code into an executable
141 142 143 144
fn link_executable_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
145
    out: PathBuf
146
) -> PathBuf {
147 148 149
    info!("output as {:?}", out.as_path());

    let mut cc = Command::new(get_c_compiler());
150

151 152 153 154 155 156
    // external libs
    for path in libpath.iter() {
        cc.arg(format!("-L{}", path));
    }
    for l in lib.iter() {
        cc.arg(format!("-l{}", l));
157 158
    }

159
    // dylibs used for linux
160
    if cfg!(target_os = "linux") {
161 162 163 164
        cc.arg("-ldl");
        cc.arg("-lrt");
        cc.arg("-lm");
        cc.arg("-lpthread");
165
        cc.arg("-lz");
166 167 168 169 170 171 172 173
    } else if cfg!(target_os = "macos") {
        cc.arg("-liconv");
        cc.arg("-framework");
        cc.arg("Security");
        cc.arg("-framework");
        cc.arg("CoreFoundation");
        cc.arg("-lz");
        cc.arg("-lSystem");
174
        cc.arg("-lresolv");
175 176
        cc.arg("-lc");
        cc.arg("-lm");
177
    }
178

179
    // all the source code
180 181 182 183 184
    for file in files {
        info!("link with {:?}", file.as_path());
        cc.arg(file.as_path());
    }

185
    // flag to allow find symbols in the executable
186
    cc.arg("-rdynamic");
187 188

    // specified output
189 190
    cc.arg("-o");
    cc.arg(out.as_os_str());
191

192 193
    // execute and check results
    assert!(exec_cmd(cc).status.success(), "failed to link code");
194 195 196
    out
}

197 198
/// links generated code for the given functions to produce a dynamic
/// library of the given name
199
pub fn link_dylib(funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
200 201 202 203 204
    link_dylib_with_extra_srcs(funcs, vec![], out, vm)
}

/// links generated code for the given functions with a few external sources
/// to produce a dynamic library of the given name
205 206 207 208
pub fn link_dylib_with_extra_srcs(
    funcs: Vec<MuName>,
    srcs: Vec<String>,
    out: &str,
209
    vm: &VM
210
) -> PathBuf {
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    let files = {
        let mut ret = vec![];

        for func in funcs {
            ret.push(get_path_for_mu_func(func, vm));
        }

        for src in srcs {
            ret.push(PathBuf::from(src));
        }

        ret.push(get_path_for_mu_context(vm));

        ret
    };

    let mut out_path = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
    out_path.push(out);

230 231 232 233
    link_dylib_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
234
        out_path
235
    )
236 237 238
}

/// invokes the C compiler to link code into a dynamic library
239 240 241 242
fn link_dylib_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
243
    out: PathBuf
244 245
) -> PathBuf {
    let mut object_files: Vec<PathBuf> = vec![];
246

247
    // compile each single source file
248
    for file in files {
249
        let mut cc = Command::new(get_c_compiler());
250

251
        // output object file
252
        cc.arg("-c");
253
        // position independent code
254
        cc.arg("-fPIC");
255 256 257 258

        let mut out = file.clone();
        out.set_extension("o");

259 260 261
        cc.arg(file.as_os_str());
        cc.arg("-o");
        cc.arg(out.as_os_str());
262 263

        object_files.push(out);
264
        exec_cmd(cc);
265 266
    }

267 268
    // link object files into a dynamic library
    let mut cc = Command::new(get_c_compiler());
269 270 271 272 273 274 275

    // external libs
    for path in libpath.iter() {
        cc.arg(format!("-L{}", path));
    }
    for l in lib.iter() {
        cc.arg(format!("-l{}", l));
276 277
    }

278 279
    // options:
    // shared library
280
    cc.arg("-shared");
281
    // position independent code
282
    cc.arg("-fPIC");
283
    // allow undefined symbol
284 285
    cc.arg("-Wl");
    cc.arg("-undefined");
286
    // allow dynamic lookup symbols
287
    cc.arg("dynamic_lookup");
qinsoon's avatar
qinsoon committed
288 289

    // all object files
290
    for obj in object_files {
291
        cc.arg(obj.as_os_str());
292
    }
293

qinsoon's avatar
qinsoon committed
294
    // output
295 296
    cc.arg("-o");
    cc.arg(out.as_os_str());
297

298
    exec_cmd(cc);
299 300 301 302

    out
}

303 304 305 306 307 308 309 310 311 312 313 314 315 316
/// builds a bundle (that contains a function), compiles it,
/// links and loads it as a dynamic library
/// This function is used to test compiler.
//  TODO: should think about using make_boot_image() instead of this adhoc code (Issue #52)
pub fn compile_fnc<'a>(fnc_name: &'static str, build_fnc: &'a Fn() -> VM) -> ll::Library {
    VM::start_logging_trace();

    let vm = Arc::new(build_fnc());
    let compiler = Compiler::new(CompilerPolicy::default(), &vm);
    let func_id = vm.id_of(fnc_name);
    {
        let funcs = vm.funcs().read().unwrap();
        let func = match funcs.get(&func_id) {
            Some(func) => func.read().unwrap(),
317
            None => panic!("cannot find function {}", fnc_name)
318 319 320 321
        };

        let cur_ver = match func.cur_ver {
            Some(v) => v,
322 323 324 325 326 327
            None => {
                panic!(
                    "function {} does not have a defined current version",
                    fnc_name
                )
            }
328 329 330 331 332
        };

        let func_vers = vm.func_vers().read().unwrap();
        let mut func_ver = match func_vers.get(&cur_ver) {
            Some(fv) => fv.write().unwrap(),
333
            None => panic!("cannot find function version {}", cur_ver)
334 335 336 337 338 339 340 341 342 343 344 345 346
        };
        compiler.compile(&mut func_ver);
    }
    backend::emit_context(&vm);
    let libname = &get_dylib_name(fnc_name);
    let dylib = aot::link_dylib(vec![Mu(fnc_name)], libname, &vm);
    ll::Library::new(dylib.as_os_str()).unwrap()
}

/// builds a bundle (that contains several functions), compiles them,
/// links and loads it as a dynamic library
/// This function is used to test compiler.
//  TODO: should think about using make_boot_image() instead of this adhoc code (Issue #52)
347 348 349
pub fn compile_fncs<'a>(
    entry: &'static str,
    fnc_names: Vec<&'static str>,
350
    build_fnc: &'a Fn() -> VM
351
) -> ll::Library {
352 353 354 355 356 357 358 359 360 361
    VM::start_logging_trace();

    let vm = Arc::new(build_fnc());
    let compiler = Compiler::new(CompilerPolicy::default(), &vm);

    for func in fnc_names.iter() {
        let func_id = vm.id_of(func);
        let funcs = vm.funcs().read().unwrap();
        let func = funcs.get(&func_id).unwrap().read().unwrap();
        let func_vers = vm.func_vers().read().unwrap();
362 363 364 365 366
        let mut func_ver = func_vers
            .get(&func.cur_ver.unwrap())
            .unwrap()
            .write()
            .unwrap();
367 368 369 370 371 372 373 374 375 376 377
        compiler.compile(&mut func_ver);
    }

    backend::emit_context(&vm);

    let libname = &get_dylib_name(entry);
    let dylib = aot::link_dylib(fnc_names.iter().map(|x| Mu(x)).collect(), libname, &vm);
    ll::Library::new(dylib.as_os_str()).unwrap()
}

/// gets the path for the generated code of a Mu function
378
fn get_path_for_mu_func(f: MuName, vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
379
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
380
    ret.push((*f).clone() + ".S");
381 382 383 384

    ret
}

385
/// gets the path for generated Mu context (persisted VM/heap)
386
fn get_path_for_mu_context(vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
387
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
388 389
    ret.push(backend::AOT_EMIT_CONTEXT_FILE);
    ret
390
}
391 392

#[cfg(not(feature = "sel4-rumprun"))]
393 394 395
pub fn run_test(vm: &VM, test_name: &str, tester_name: &str) {
    let output_name = test_name.to_string() + "_" + tester_name;
    let executable = link_test_primordial(
396 397 398 399
        vec![
            Arc::new(test_name.to_string()),
            Arc::new(tester_name.to_string()),
        ],
400 401 402
        output_name.as_str(),
        vm
    );
403 404 405 406
    self::super::exec_path(executable);
}

#[cfg(feature = "sel4-rumprun")]
407 408
pub fn run_test(vm: &VM, test_name: &str, tester_name: &str) {

409
    use std::fs::File;
410

411 412 413 414 415 416 417 418
    //  emit/add.s
    let test_asm_file = "emit/".to_string() + test_name + ".S";
    //  emit/add_test1.s
    let tester_asm_file = "emit/".to_string() + tester_name + ".S";
    //  emit/context.s
    let context_asm_file = "emit/".to_string() + "context.S";
    //  emit/mu_sym_table.s
    let mu_sym_table_asm_file = "emit/".to_string() + "mu_sym_table.S";
419

420 421 422 423 424 425 426
    // clean the destination first
    let destination_prefix = "../rumprun-sel4/apps/zebu_rt/src/emit/";
    let output = Command::new("rm")
        .arg("-R")
        .arg(destination_prefix)
        .output()
        .expect("failed to RM dest emit");
427

428
    assert!(output.status.success());
429

430 431 432 433 434
    //  recreate the emit folder, deleted by the previous command
    let output = Command::new("mkdir")
        .arg(destination_prefix)
        .output()
        .expect("failed to RM dest emit");
435

436
    assert!(output.status.success());
437 438


439 440 441
    // above file will be pasted in \
    //  rumprun-sel4/apps/zebu_rt/src + the above Strings
    let destination_prefix = "../rumprun-sel4/apps/zebu_rt/src/";
442

443 444 445 446
    let dest_test_asm_file = destination_prefix.to_string() + &test_asm_file;
    let dest_tester_asm_file = destination_prefix.to_string() + &tester_asm_file;
    let dest_context_asm_file = destination_prefix.to_string() + &context_asm_file;
    let dest_mu_sym_table_asm_file = destination_prefix.to_string() + &mu_sym_table_asm_file;
447

448 449 450 451 452
    /*
        The following 4 commands, copy 4 asm source files to \
        the proper location of filesystem for sel4-rumprun runtime
        This is currently src/emit
    */
453

454 455 456 457 458
    let output = Command::new("cp")
        .arg(&test_asm_file)
        .arg(&dest_test_asm_file)
        .output()
        .expect("failed to copy test_asm_file");
459

460
    assert!(output.status.success());
461

462 463 464 465 466
    let output = Command::new("cp")
        .arg(&tester_asm_file)
        .arg(&dest_tester_asm_file)
        .output()
        .expect("failed to copy tester_asm_file");
467

468
    assert!(output.status.success());
469

470 471 472 473 474
    let output = Command::new("cp")
        .arg(&context_asm_file)
        .arg(&dest_context_asm_file)
        .output()
        .expect("failed to copy context_asm_file");
475

476
    assert!(output.status.success());
477

478 479 480 481 482
    let output = Command::new("cp")
        .arg(&mu_sym_table_asm_file)
        .arg(&dest_mu_sym_table_asm_file)
        .output()
        .expect("failed to copy dest_mu_sym_table_asm_file");
483

484
    assert!(output.status.success());
485 486


487 488 489 490
    /*
        Everything is ready for our sel4-rumprun Zebu runtime
        to start building the final test executable(s)
    */
491

492 493
    use std::os::unix::io::FromRawFd;
    use std::os::unix::io::AsRawFd;
494 495


496 497 498 499
    let output = Command::new("rm")
        .arg("outputs.txt")
        .output()
        .expect("failed to change directory2");
500

501
    let mut outputs_file = File::create("outputs.txt").unwrap();
502

503
    let rawfd = outputs_file.as_raw_fd();
504
    //    let rawfd = unsafe { File::from_raw_fd(rawfd) };
505
    let rawfd = unsafe { Stdio::from_raw_fd(rawfd) };
506

507 508 509 510 511
    let output = Command::new("bash")
        .arg("build_for_sel4_rumprun.sh")
        .stdout(Stdio::inherit())
        .output()
        .expect("failed to Build");
512

513 514 515
    println!("****************************************");
    println!("Build Output -{:?}-", output);
    println!("****************************************");
516

517
    assert!(output.status.success());
518

519 520
    // First create a child process which runs qemu for testing
    // Then, create a watchdog to check if test is finished
521

522 523 524 525 526 527 528 529 530 531 532 533 534
    let mut tester_proc = Command::new("qemu-system-x86_64")
        .arg("-nographic")
        .arg("-m")
        .arg("512")
        .arg("-kernel")
        .arg("../rumprun-sel4/images/kernel-x86_64-pc99")
        .arg("-initrd")
        .arg("../rumprun-sel4/images/roottask-image-x86_64-pc99")
        .arg("-cpu")
        .arg("Haswell")
        .stdout(rawfd)
        .spawn()
        .expect("failed to RUN");
535

536 537 538
    use std::thread;
    use std::io;
    use std::io::prelude::*;
539

540 541 542
    let mut child_proc_finished = 0;
    let mut test_succeeded = 0;
    let mut test_length = 0;
543 544
    let test_length_max = 60; // Maximum allowed length for a test is currently 60 seconds

545 546 547 548 549 550 551 552 553
    // This loop checks the output file to recognize when qemu vm \
    // which is running the test, should be terminated
    while child_proc_finished == 0 {
        thread::sleep_ms(5000);
        test_length += 5;
        {
            let mut results_file = File::open("outputs.txt");
            let mut results_file = match results_file {
                Ok(the_file) => the_file,
554 555 556
                Err(error) => {
                    panic!("Checking outputs file failed with error -{}-", error);
                }
557 558
            };
            let mut file_content = String::new();
559

560
            results_file.read_to_string(&mut file_content);
561

562 563 564 565
            if file_content.contains("bmk_platform_halt@kernel.c:95 All is well in the universe.") {
                child_proc_finished = 1;
                if file_content.contains("@#$%PASSED%$#@") {
                    test_succeeded = 1;
566 567 568 569
                } else if file_content.contains("@#$%FAILED%$#@") {
                    test_succeeded = 0;
                } else {
                    panic!("Invalid test outcome!");
570
                }
571 572
            } else {
                continue;
573
            }
574

575 576
            use std::str::FromStr;
            use std::fs::OpenOptions;
577

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
            let mut lines = file_content.lines();
            let mut search_finished = 0;
            let mut test_name = String::new();
            while search_finished == 0 {
                let mut current_line = lines.next();
                if current_line == None {
                    panic!("Test name not found in outputs.txt");
                }
                let current_line = current_line.unwrap();
                println!("{}", current_line);
                if current_line.contains("**TEST**") {
                    search_finished = 1;
                    test_name = String::from_str(lines.next().unwrap()).unwrap();
                }
            }
593

594
            //            let mut log_file = File::create("results_log.txt");
595 596 597 598
            let mut log_file = OpenOptions::new()
                .write(true)
                .append(true)
                .open("results_log.txt");
599 600
            let mut log_file = match log_file {
                Ok(the_file) => the_file,
601 602 603
                Err(error) => {
                    panic!("Creating-Opening log file failed with error -{}-", error);
                }
604
            };
605 606 607 608 609 610 611 612 613 614

            log_file
                .write_fmt(format_args!("******************************\n"))
                .unwrap();
            log_file
                .write_fmt(format_args!("Test time : {}\n", time::now_utc().ctime()))
                .unwrap();
            log_file
                .write_fmt(format_args!("Test name : {}\n", test_name))
                .unwrap();
615
            if test_succeeded == 1 {
616 617 618 619 620 621 622
                log_file
                    .write_fmt(format_args!("Test result : PASSED\n"))
                    .unwrap();
            } else {
                log_file
                    .write_fmt(format_args!("Test result : FAILED\n"))
                    .unwrap();
623
            }
624 625 626
            log_file
                .write_fmt(format_args!("******************************"))
                .unwrap();
627 628 629 630 631 632 633 634 635
        }
        println!("+ 5 secs");
        if test_length == test_length_max {
            let output = Command::new("kill")
                .arg("-15")
                .arg("--")
                .arg(tester_proc.id().to_string())
                .output()
                .expect("failed to kill TO");
636

637
            assert!(output.status.success());
638

639 640 641
            panic!("Test Timed Out!");
        }
    }
642

643 644 645 646 647 648 649
    // Terminate the test proc
    let output = Command::new("kill")
        .arg("-15")
        .arg("--")
        .arg(tester_proc.id().to_string())
        .output()
        .expect("failed to kill");
650

651
    assert!(output.status.success());
652

653 654 655 656
    assert!(test_succeeded == 1);
}

#[cfg(not(feature = "sel4-rumprun"))]
657 658 659 660
pub fn run_test_2f(vm: &VM, test_name: &str, dep_name: &str, tester_name: &str) {
    let output_name = test_name.to_string() + "_" + tester_name;
    let executable = link_test_primordial(
        vec![
661 662 663
            Arc::new(dep_name.to_string()),
            Arc::new(test_name.to_string()),
            Arc::new(tester_name.to_string()),
664 665 666 667
        ],
        output_name.as_str(),
        vm
    );
668 669 670 671
    self::super::exec_path(executable);
}

#[cfg(feature = "sel4-rumprun")]
672 673
pub fn run_test_2f(vm: &VM, test_name: &str, dep_name: &str, tester_name: &str) {

674
    use std::fs::File;
675

676 677 678 679 680 681 682 683 684 685
    //  emit/add.s
    let test_asm_file = "emit/".to_string() + test_name + ".S";
    //  emit/add_test1.s
    let tester_asm_file = "emit/".to_string() + tester_name + ".S";
    //  emit/context.s
    let context_asm_file = "emit/".to_string() + "context.S";
    //  emit/mu_sym_table.s
    let mu_sym_table_asm_file = "emit/".to_string() + "mu_sym_table.S";
    //  something like emit/dummy_call.s
    let dep_asm_file = "emit/".to_string() + dep_name + ".S";
686

687 688 689 690 691 692 693
    // clean the destination first
    let destination_prefix = "../rumprun-sel4/apps/zebu_rt/src/emit/";
    let output = Command::new("rm")
        .arg("-R")
        .arg(destination_prefix)
        .output()
        .expect("failed to RM dest emit");
694

695
    assert!(output.status.success());
696

697 698 699 700 701
    //  recreate the emit folder, deleted by the previous command
    let output = Command::new("mkdir")
        .arg(destination_prefix)
        .output()
        .expect("failed to RM dest emit");
702

703
    assert!(output.status.success());
704 705


706 707 708
    // above file will be pasted in \
    //  rumprun-sel4/apps/zebu_rt/src + the above Strings
    let destination_prefix = "../rumprun-sel4/apps/zebu_rt/src/";
709

710 711 712 713 714
    let dest_test_asm_file = destination_prefix.to_string() + &test_asm_file;
    let dest_tester_asm_file = destination_prefix.to_string() + &tester_asm_file;
    let dest_context_asm_file = destination_prefix.to_string() + &context_asm_file;
    let dest_mu_sym_table_asm_file = destination_prefix.to_string() + &mu_sym_table_asm_file;
    let dest_dep_asm_file = destination_prefix.to_string() + &dep_asm_file;
715

716 717 718 719 720
    /*
        The following 4 commands, copy 4 asm source files to \
        the proper location of filesystem for sel4-rumprun runtime
        This is currently src/emit
    */
721

722 723 724 725 726
    let output = Command::new("cp")
        .arg(&test_asm_file)
        .arg(&dest_test_asm_file)
        .output()
        .expect("failed to copy test_asm_file");
727

728
    assert!(output.status.success());
729

730 731 732 733 734
    let output = Command::new("cp")
        .arg(&tester_asm_file)
        .arg(&dest_tester_asm_file)
        .output()
        .expect("failed to copy tester_asm_file");
735

736
    assert!(output.status.success());
737

738 739 740 741 742
    let output = Command::new("cp")
        .arg(&context_asm_file)
        .arg(&dest_context_asm_file)
        .output()
        .expect("failed to copy context_asm_file");
743

744
    assert!(output.status.success());
745

746 747 748 749 750
    let output = Command::new("cp")
        .arg(&mu_sym_table_asm_file)
        .arg(&dest_mu_sym_table_asm_file)
        .output()
        .expect("failed to copy dest_mu_sym_table_asm_file");
751

752
    assert!(output.status.success());
753

754 755 756 757 758
    let output = Command::new("cp")
        .arg(&dep_asm_file)
        .arg(&dest_dep_asm_file)
        .output()
        .expect("failed to copy dep_asm_file");
759

760
    assert!(output.status.success());
761

762 763 764 765
    /*
        Everything is ready for our sel4-rumprun Zebu runtime
        to start building the final test executable(s)
    */
766

767 768
    use std::os::unix::io::FromRawFd;
    use std::os::unix::io::AsRawFd;
769 770


771 772 773 774
    let output = Command::new("rm")
        .arg("outputs.txt")
        .output()
        .expect("failed to change directory2");
775

776
    let mut outputs_file = File::create("outputs.txt").unwrap();
777

778
    let rawfd = outputs_file.as_raw_fd();
779
    //    let rawfd = unsafe { File::from_raw_fd(rawfd) };
780
    let rawfd = unsafe { Stdio::from_raw_fd(rawfd) };
781

782 783 784 785 786
    let output = Command::new("bash")
        .arg("build_for_sel4_rumprun.sh")
        .stdout(Stdio::inherit())
        .output()
        .expect("failed to Build");
787

788 789 790
    println!("****************************************");
    println!("Build Output -{:?}-", output);
    println!("****************************************");
791

792
    assert!(output.status.success());
793

794 795
    // First create a child process which runs qemu for testing
    // Then, create a watchdog to check if test is finished
796

797 798 799 800 801 802 803 804 805 806 807 808 809
    let mut tester_proc = Command::new("qemu-system-x86_64")
        .arg("-nographic")
        .arg("-m")
        .arg("512")
        .arg("-kernel")
        .arg("../rumprun-sel4/images/kernel-x86_64-pc99")
        .arg("-initrd")
        .arg("../rumprun-sel4/images/roottask-image-x86_64-pc99")
        .arg("-cpu")
        .arg("Haswell")
        .stdout(rawfd)
        .spawn()
        .expect("failed to RUN");
810

811 812 813
    use std::thread;
    use std::io;
    use std::io::prelude::*;
814

815 816 817
    let mut child_proc_finished = 0;
    let mut test_succeeded = 0;
    let mut test_length = 0;
818 819
    let test_length_max = 60; // Maximum allowed length for a test is currently 60 seconds

820 821 822 823 824 825 826 827 828
    // This loop checks the output file to recognize when qemu vm \
    // which is running the test, should be terminated
    while child_proc_finished == 0 {
        thread::sleep_ms(5000);
        test_length += 5;
        {
            let mut results_file = File::open("outputs.txt");
            let mut results_file = match results_file {
                Ok(the_file) => the_file,
829 830 831
                Err(error) => {
                    panic!("Checking outputs file failed with error -{}-", error);
                }
832 833
            };
            let mut file_content = String::new();
834

835
            results_file.read_to_string(&mut file_content);
836

837 838 839 840
            if file_content.contains("bmk_platform_halt@kernel.c:95 All is well in the universe.") {
                child_proc_finished = 1;
                if file_content.contains("@#$%PASSED%$#@") {
                    test_succeeded = 1;
841 842 843 844
                } else if file_content.contains("@#$%FAILED%$#@") {
                    test_succeeded = 0;
                } else {
                    panic!("Invalid test outcome!");
845
                }
846 847
            } else {
                continue;
848
            }
849

850 851
            use std::str::FromStr;
            use std::fs::OpenOptions;
852

853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
            let mut lines = file_content.lines();
            let mut search_finished = 0;
            let mut test_name = String::new();
            while search_finished == 0 {
                let mut current_line = lines.next();
                if current_line == None {
                    panic!("Test name not found in outputs.txt");
                }
                let current_line = current_line.unwrap();
                println!("{}", current_line);
                if current_line.contains("**TEST**") {
                    search_finished = 1;
                    test_name = String::from_str(lines.next().unwrap()).unwrap();
                }
            }
868

869
            //            let mut log_file = File::create("results_log.txt");
870 871 872 873
            let mut log_file = OpenOptions::new()
                .write(true)
                .append(true)
                .open("results_log.txt");
874 875
            let mut log_file = match log_file {
                Ok(the_file) => the_file,
876 877 878
                Err(error) => {
                    panic!("Creating-Opening log file failed with error -{}-", error);
                }
879
            };
880 881 882 883 884 885 886 887 888 889

            log_file
                .write_fmt(format_args!("******************************\n"))
                .unwrap();
            log_file
                .write_fmt(format_args!("Test time : {}\n", time::now_utc().ctime()))
                .unwrap();
            log_file
                .write_fmt(format_args!("Test name : {}\n", test_name))
                .unwrap();
890
            if test_succeeded == 1 {
891 892 893 894 895 896 897
                log_file
                    .write_fmt(format_args!("Test result : PASSED\n"))
                    .unwrap();
            } else {
                log_file
                    .write_fmt(format_args!("Test result : FAILED\n"))
                    .unwrap();
898
            }
899 900 901
            log_file
                .write_fmt(format_args!("******************************"))
                .unwrap();
902 903 904 905 906 907 908 909 910
        }
        println!("+ 5 secs");
        if test_length == test_length_max {
            let output = Command::new("kill")
                .arg("-15")
                .arg("--")
                .arg(tester_proc.id().to_string())
                .output()
                .expect("failed to kill TO");
911

912
            assert!(output.status.success());
913

914 915 916
            panic!("Test Timed Out!");
        }
    }
917

918 919 920 921 922 923 924
    // Terminate the test proc
    let output = Command::new("kill")
        .arg("-15")
        .arg("--")
        .arg(tester_proc.id().to_string())
        .output()
        .expect("failed to kill");
925

926
    assert!(output.status.success());
927

928 929
    assert!(test_succeeded == 1);
}