GitLab will be upgraded on June 2nd 2020 at 2.00 pm (AEDT) to 3.00 pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to local Gitlab admin team.

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);
}