aot.rs 28.5 KB
Newer Older
1
// Copyright 2017 The Australian National University
2
//
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
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
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
        }

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

        // include mu static lib
qinsoon's avatar
qinsoon committed
63
        if vm.vm_options.flag_aot_link_static {
64 65 66 67 68 69
            ret.push(get_path_under_zebu(if cfg!(debug_assertions) {
                "target/debug/libmu.a"
            } else {
                "target/release/libmu.a"
            }));
        }
70 71 72 73 74 75 76

        ret
    };

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

77
    link_executable_internal(
qinsoon's avatar
qinsoon committed
78
        !vm.vm_options.flag_aot_link_static,
79 80 81
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
82
        out_path
83
    )
84 85
}

86 87 88 89
/// 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);
90

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

95
        let mut ret = vec![];
96

97 98 99 100
        // all interested mu funcs
        for func in funcs {
            ret.push(get_path_for_mu_func(func, vm));
        }
101

102 103
        // mu context
        ret.push(get_path_for_mu_context(vm));
104

105
        // copy primoridal entry
qinsoon's avatar
qinsoon committed
106
        let source = get_path_under_zebu(runtime::TEST_PRIMORDIAL_ENTRY);
107 108 109
        let dest = {
            let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
            ret.push("main_test.c");
110

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

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

122
        // include mu static lib
qinsoon's avatar
qinsoon committed
123
        if vm.vm_options.flag_aot_link_static {
124 125 126 127 128 129
            ret.push(get_path_under_zebu(if cfg!(debug_assertions) {
                "target/debug/libmu.a"
            } else {
                "target/release/libmu.a"
            }));
        }
130

131 132
        ret
    };
133

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

137
    link_executable_internal(
qinsoon's avatar
qinsoon committed
138
        !vm.vm_options.flag_aot_link_static,
139 140 141 142 143 144 145
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
        out_path
    )
}

146
/// invokes the C compiler to link code into an executable
147
fn link_executable_internal(
148
    link_dynamicly: bool,
149 150 151
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
152
    out: PathBuf
153
) -> PathBuf {
154 155 156
    info!("output as {:?}", out.as_path());

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

158 159 160 161 162 163
    // external libs
    for path in libpath.iter() {
        cc.arg(format!("-L{}", path));
    }
    for l in lib.iter() {
        cc.arg(format!("-l{}", l));
164 165
    }

166 167 168 169 170 171 172 173 174 175 176 177
    if link_dynamicly {
        cc.arg(format!(
            "-L{}",
            get_path_under_zebu(if cfg!(debug_assertions) {
                "target/debug"
            } else {
                "target/release"
            }).to_str()
                .unwrap()
        ));
        cc.arg("-lmu");
    }
178
    // dylibs used for linux
179
    if cfg!(target_os = "linux") {
180
        cc.arg("-lpthread");
181
    } else if cfg!(target_os = "macos") {   // TODO macos args need to be updated
182 183 184 185 186 187 188
        cc.arg("-liconv");
        cc.arg("-framework");
        cc.arg("Security");
        cc.arg("-framework");
        cc.arg("CoreFoundation");
        cc.arg("-lz");
        cc.arg("-lSystem");
189
        cc.arg("-lresolv");
190 191
        cc.arg("-lc");
        cc.arg("-lm");
192
    }
193
    // all the source code
194 195 196 197 198
    for file in files {
        info!("link with {:?}", file.as_path());
        cc.arg(file.as_path());
    }

199
    // flag to allow find symbols in the executable
200
    cc.arg("-rdynamic");
201 202

    // specified output
203 204
    cc.arg("-o");
    cc.arg(out.as_os_str());
205

206 207
    // execute and check results
    assert!(exec_cmd(cc).status.success(), "failed to link code");
208 209 210
    out
}

211 212
/// links generated code for the given functions to produce a dynamic
/// library of the given name
213
pub fn link_dylib(funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
214 215 216 217 218
    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
219 220 221 222
pub fn link_dylib_with_extra_srcs(
    funcs: Vec<MuName>,
    srcs: Vec<String>,
    out: &str,
223
    vm: &VM
224
) -> PathBuf {
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
    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);

244 245 246 247
    link_dylib_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
248
        out_path
249
    )
250 251 252
}

/// invokes the C compiler to link code into a dynamic library
253 254 255 256
fn link_dylib_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
257
    out: PathBuf
258 259
) -> PathBuf {
    let mut object_files: Vec<PathBuf> = vec![];
260

261
    // compile each single source file
262
    for file in files {
263
        let mut cc = Command::new(get_c_compiler());
264

265
        // output object file
266
        cc.arg("-c");
267
        // position independent code
268
        cc.arg("-fPIC");
269 270 271 272

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

273 274 275
        cc.arg(file.as_os_str());
        cc.arg("-o");
        cc.arg(out.as_os_str());
276 277

        object_files.push(out);
278
        exec_cmd(cc);
279 280
    }

281 282
    // link object files into a dynamic library
    let mut cc = Command::new(get_c_compiler());
283 284 285 286 287 288 289

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

292 293
    // options:
    // shared library
294
    cc.arg("-shared");
295
    // position independent code
296
    cc.arg("-fPIC");
297
    // allow undefined symbol
298 299
    cc.arg("-Wl");
    cc.arg("-undefined");
300
    // allow dynamic lookup symbols
301
    cc.arg("dynamic_lookup");
qinsoon's avatar
qinsoon committed
302 303

    // all object files
304
    for obj in object_files {
305
        cc.arg(obj.as_os_str());
306
    }
307

qinsoon's avatar
qinsoon committed
308
    // output
309 310
    cc.arg("-o");
    cc.arg(out.as_os_str());
311

312
    exec_cmd(cc);
313 314 315 316

    out
}

317 318 319 320 321 322 323 324 325 326 327 328 329 330
/// 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(),
331
            None => panic!("cannot find function {}", fnc_name)
332 333 334 335
        };

        let cur_ver = match func.cur_ver {
            Some(v) => v,
336 337 338 339 340 341
            None => {
                panic!(
                    "function {} does not have a defined current version",
                    fnc_name
                )
            }
342 343 344 345 346
        };

        let func_vers = vm.func_vers().read().unwrap();
        let mut func_ver = match func_vers.get(&cur_ver) {
            Some(fv) => fv.write().unwrap(),
347
            None => panic!("cannot find function version {}", cur_ver)
348 349 350 351 352 353 354 355 356 357 358 359 360
        };
        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)
361 362 363
pub fn compile_fncs<'a>(
    entry: &'static str,
    fnc_names: Vec<&'static str>,
364
    build_fnc: &'a Fn() -> VM
365
) -> ll::Library {
366 367 368 369 370 371 372 373 374 375
    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();
376 377 378 379 380
        let mut func_ver = func_vers
            .get(&func.cur_ver.unwrap())
            .unwrap()
            .write()
            .unwrap();
381 382 383 384 385 386 387 388 389 390 391
        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
392
fn get_path_for_mu_func(f: MuName, vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
393
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
394
    ret.push((*f).clone() + ".S");
395 396 397 398

    ret
}

399
/// gets the path for generated Mu context (persisted VM/heap)
400
fn get_path_for_mu_context(vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
401
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
402 403
    ret.push(backend::AOT_EMIT_CONTEXT_FILE);
    ret
404
}
405 406

#[cfg(not(feature = "sel4-rumprun"))]
407 408 409
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(
410 411 412 413
        vec![
            Arc::new(test_name.to_string()),
            Arc::new(tester_name.to_string()),
        ],
414 415 416
        output_name.as_str(),
        vm
    );
417 418 419 420
    self::super::exec_path(executable);
}

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

423
    use std::fs::File;
424

425 426 427 428 429 430 431 432
    //  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";
433

434 435 436 437 438 439 440
    // 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");
441

442
    assert!(output.status.success());
443

444 445 446 447 448
    //  recreate the emit folder, deleted by the previous command
    let output = Command::new("mkdir")
        .arg(destination_prefix)
        .output()
        .expect("failed to RM dest emit");
449

450
    assert!(output.status.success());
451 452


453 454 455
    // above file will be pasted in \
    //  rumprun-sel4/apps/zebu_rt/src + the above Strings
    let destination_prefix = "../rumprun-sel4/apps/zebu_rt/src/";
456

457 458 459 460
    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;
461

462 463 464 465 466
    /*
        The following 4 commands, copy 4 asm source files to \
        the proper location of filesystem for sel4-rumprun runtime
        This is currently src/emit
    */
467

468 469 470 471 472
    let output = Command::new("cp")
        .arg(&test_asm_file)
        .arg(&dest_test_asm_file)
        .output()
        .expect("failed to copy test_asm_file");
473

474
    assert!(output.status.success());
475

476 477 478 479 480
    let output = Command::new("cp")
        .arg(&tester_asm_file)
        .arg(&dest_tester_asm_file)
        .output()
        .expect("failed to copy tester_asm_file");
481

482
    assert!(output.status.success());
483

484 485 486 487 488
    let output = Command::new("cp")
        .arg(&context_asm_file)
        .arg(&dest_context_asm_file)
        .output()
        .expect("failed to copy context_asm_file");
489

490
    assert!(output.status.success());
491

492 493 494 495 496
    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");
497

498
    assert!(output.status.success());
499 500


501 502 503 504
    /*
        Everything is ready for our sel4-rumprun Zebu runtime
        to start building the final test executable(s)
    */
505

506 507
    use std::os::unix::io::FromRawFd;
    use std::os::unix::io::AsRawFd;
508 509


510 511 512 513
    let output = Command::new("rm")
        .arg("outputs.txt")
        .output()
        .expect("failed to change directory2");
514

515
    let mut outputs_file = File::create("outputs.txt").unwrap();
516

517
    let rawfd = outputs_file.as_raw_fd();
518
    //    let rawfd = unsafe { File::from_raw_fd(rawfd) };
519
    let rawfd = unsafe { Stdio::from_raw_fd(rawfd) };
520

521 522 523 524 525
    let output = Command::new("bash")
        .arg("build_for_sel4_rumprun.sh")
        .stdout(Stdio::inherit())
        .output()
        .expect("failed to Build");
526

527 528 529
    println!("****************************************");
    println!("Build Output -{:?}-", output);
    println!("****************************************");
530

531
    assert!(output.status.success());
532

533 534
    // First create a child process which runs qemu for testing
    // Then, create a watchdog to check if test is finished
535

536 537 538 539 540 541 542 543 544 545 546 547 548
    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");
549

550 551 552
    use std::thread;
    use std::io;
    use std::io::prelude::*;
553

554 555 556
    let mut child_proc_finished = 0;
    let mut test_succeeded = 0;
    let mut test_length = 0;
557 558
    let test_length_max = 60; // Maximum allowed length for a test is currently 60 seconds

559 560 561 562 563 564 565 566 567
    // 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,
568 569 570
                Err(error) => {
                    panic!("Checking outputs file failed with error -{}-", error);
                }
571 572
            };
            let mut file_content = String::new();
573

574
            results_file.read_to_string(&mut file_content);
575

576 577 578 579
            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;
580 581 582 583
                } else if file_content.contains("@#$%FAILED%$#@") {
                    test_succeeded = 0;
                } else {
                    panic!("Invalid test outcome!");
584
                }
585 586
            } else {
                continue;
587
            }
588

589 590
            use std::str::FromStr;
            use std::fs::OpenOptions;
591

592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
            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();
                }
            }
607

608
            //            let mut log_file = File::create("results_log.txt");
609 610 611 612
            let mut log_file = OpenOptions::new()
                .write(true)
                .append(true)
                .open("results_log.txt");
613 614
            let mut log_file = match log_file {
                Ok(the_file) => the_file,
615 616 617
                Err(error) => {
                    panic!("Creating-Opening log file failed with error -{}-", error);
                }
618
            };
619 620 621 622 623 624 625 626 627 628

            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();
629
            if test_succeeded == 1 {
630 631 632 633 634 635 636
                log_file
                    .write_fmt(format_args!("Test result : PASSED\n"))
                    .unwrap();
            } else {
                log_file
                    .write_fmt(format_args!("Test result : FAILED\n"))
                    .unwrap();
637
            }
638 639 640
            log_file
                .write_fmt(format_args!("******************************"))
                .unwrap();
641 642 643 644 645 646 647 648 649
        }
        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");
650

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

653 654 655
            panic!("Test Timed Out!");
        }
    }
656

657 658 659 660 661 662 663
    // Terminate the test proc
    let output = Command::new("kill")
        .arg("-15")
        .arg("--")
        .arg(tester_proc.id().to_string())
        .output()
        .expect("failed to kill");
664

665
    assert!(output.status.success());
666

667 668 669 670
    assert!(test_succeeded == 1);
}

#[cfg(not(feature = "sel4-rumprun"))]
671 672 673 674
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![
675 676 677
            Arc::new(dep_name.to_string()),
            Arc::new(test_name.to_string()),
            Arc::new(tester_name.to_string()),
678 679 680 681
        ],
        output_name.as_str(),
        vm
    );
682 683 684 685
    self::super::exec_path(executable);
}

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

688
    use std::fs::File;
689

690 691 692 693 694 695 696 697 698 699
    //  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";
700

701 702 703 704 705 706 707
    // 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");
708

709
    assert!(output.status.success());
710

711 712 713 714 715
    //  recreate the emit folder, deleted by the previous command
    let output = Command::new("mkdir")
        .arg(destination_prefix)
        .output()
        .expect("failed to RM dest emit");
716

717
    assert!(output.status.success());
718 719


720 721 722
    // above file will be pasted in \
    //  rumprun-sel4/apps/zebu_rt/src + the above Strings
    let destination_prefix = "../rumprun-sel4/apps/zebu_rt/src/";
723

724 725 726 727 728
    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;
729

730 731 732 733 734
    /*
        The following 4 commands, copy 4 asm source files to \
        the proper location of filesystem for sel4-rumprun runtime
        This is currently src/emit
    */
735

736 737 738 739 740
    let output = Command::new("cp")
        .arg(&test_asm_file)
        .arg(&dest_test_asm_file)
        .output()
        .expect("failed to copy test_asm_file");
741

742
    assert!(output.status.success());
743

744 745 746 747 748
    let output = Command::new("cp")
        .arg(&tester_asm_file)
        .arg(&dest_tester_asm_file)
        .output()
        .expect("failed to copy tester_asm_file");
749

750
    assert!(output.status.success());
751

752 753 754 755 756
    let output = Command::new("cp")
        .arg(&context_asm_file)
        .arg(&dest_context_asm_file)
        .output()
        .expect("failed to copy context_asm_file");
757

758
    assert!(output.status.success());
759

760 761 762 763 764
    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");
765

766
    assert!(output.status.success());
767

768 769 770 771 772
    let output = Command::new("cp")
        .arg(&dep_asm_file)
        .arg(&dest_dep_asm_file)
        .output()
        .expect("failed to copy dep_asm_file");
773

774
    assert!(output.status.success());
775

776 777 778 779
    /*
        Everything is ready for our sel4-rumprun Zebu runtime
        to start building the final test executable(s)
    */
780

781 782
    use std::os::unix::io::FromRawFd;
    use std::os::unix::io::AsRawFd;
783 784


785 786 787 788
    let output = Command::new("rm")
        .arg("outputs.txt")
        .output()
        .expect("failed to change directory2");
789

790
    let mut outputs_file = File::create("outputs.txt").unwrap();
791

792
    let rawfd = outputs_file.as_raw_fd();
793
    //    let rawfd = unsafe { File::from_raw_fd(rawfd) };
794
    let rawfd = unsafe { Stdio::from_raw_fd(rawfd) };
795

796 797 798 799 800
    let output = Command::new("bash")
        .arg("build_for_sel4_rumprun.sh")
        .stdout(Stdio::inherit())
        .output()
        .expect("failed to Build");
801

802 803 804
    println!("****************************************");
    println!("Build Output -{:?}-", output);
    println!("****************************************");
805

806
    assert!(output.status.success());
807

808 809
    // First create a child process which runs qemu for testing
    // Then, create a watchdog to check if test is finished
810

811 812 813 814 815 816 817 818 819 820 821 822 823
    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");
824

825 826 827
    use std::thread;
    use std::io;
    use std::io::prelude::*;
828

829 830 831
    let mut child_proc_finished = 0;
    let mut test_succeeded = 0;
    let mut test_length = 0;
832 833
    let test_length_max = 60; // Maximum allowed length for a test is currently 60 seconds

834 835 836 837 838 839 840 841 842
    // 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,
843 844 845
                Err(error) => {
                    panic!("Checking outputs file failed with error -{}-", error);
                }
846 847
            };
            let mut file_content = String::new();
848

849
            results_file.read_to_string(&mut file_content);
850

851 852 853 854
            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;
855 856 857 858
                } else if file_content.contains("@#$%FAILED%$#@") {
                    test_succeeded = 0;
                } else {
                    panic!("Invalid test outcome!");
859
                }
860 861
            } else {
                continue;
862
            }
863

864 865
            use std::str::FromStr;
            use std::fs::OpenOptions;
866

867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
            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();
                }
            }
882

883
            //            let mut log_file = File::create("results_log.txt");
884 885 886 887
            let mut log_file = OpenOptions::new()
                .write(true)
                .append(true)
                .open("results_log.txt");
888 889
            let mut log_file = match log_file {
                Ok(the_file) => the_file,
890 891 892
                Err(error) => {
                    panic!("Creating-Opening log file failed with error -{}-", error);
                }
893
            };
894 895 896 897 898 899 900 901 902 903

            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();
904
            if test_succeeded == 1 {
905 906 907 908 909 910 911
                log_file
                    .write_fmt(format_args!("Test result : PASSED\n"))
                    .unwrap();
            } else {
                log_file
                    .write_fmt(format_args!("Test result : FAILED\n"))
                    .unwrap();
912
            }
913 914 915
            log_file
                .write_fmt(format_args!("******************************"))
                .unwrap();
916 917 918 919 920 921 922 923 924
        }
        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");
925

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

928 929 930
            panic!("Test Timed Out!");
        }
    }
931

932 933 934 935 936 937 938
    // Terminate the test proc
    let output = Command::new("kill")
        .arg("-15")
        .arg("--")
        .arg(tester_proc.id().to_string())
        .output()
        .expect("failed to kill");
939

940
    assert!(output.status.success());
941

942 943
    assert!(test_succeeded == 1);
}