aot.rs 28.3 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;

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

qinsoon's avatar
qinsoon committed
23
24
25
use std::path::PathBuf;
use std::process::Command;

qinsoon's avatar
qinsoon committed
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 {
qinsoon's avatar
qinsoon committed
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> = {
qinsoon's avatar
qinsoon committed
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);
qinsoon's avatar
qinsoon committed
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)
qinsoon's avatar
qinsoon committed
57
58
59
60
61
62
        }

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

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

        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
    )
qinsoon's avatar
qinsoon committed
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
        /*// include mu static lib
120
121
122
123
        ret.push(get_path_under_zebu(if cfg!(debug_assertions) {
            "target/debug/libmu.a"
        } else {
            "target/release/libmu.a"
124
        }));*/
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
    )
}

qinsoon's avatar
qinsoon committed
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 {
qinsoon's avatar
qinsoon committed
147
148
149
    info!("output as {:?}", out.as_path());

    let mut cc = Command::new(get_c_compiler());
qinsoon's avatar
qinsoon committed
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));
qinsoon's avatar
qinsoon committed
157
158
    }

159
160
161
162
163
164
165
166
167
    cc.arg(format!(
        "-L{}",
        get_path_under_zebu(if cfg!(debug_assertions) {
            "target/debug"
        } else {
            "target/release"
        }).to_str()
            .unwrap()
    ));
168

qinsoon's avatar
qinsoon committed
169
    // dylibs used for linux
qinsoon's avatar
qinsoon committed
170
    if cfg!(target_os = "linux") {
171
172
173
174
        cc.arg("-ldl");
        cc.arg("-lrt");
        cc.arg("-lm");
        cc.arg("-lpthread");
175
        cc.arg("-lz");
176
        cc.arg("-lmu");
177
178
179
180
181
182
183
184
    } 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");
185
        cc.arg("-lresolv");
186
187
        cc.arg("-lc");
        cc.arg("-lm");
188
        cc.arg("-lmu");
qinsoon's avatar
qinsoon committed
189
    }
190

qinsoon's avatar
qinsoon committed
191
    // all the source code
192
193
194
195
196
    for file in files {
        info!("link with {:?}", file.as_path());
        cc.arg(file.as_path());
    }

qinsoon's avatar
qinsoon committed
197
    // flag to allow find symbols in the executable
198
    cc.arg("-rdynamic");
qinsoon's avatar
qinsoon committed
199
200

    // specified output
201
202
    cc.arg("-o");
    cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
203

qinsoon's avatar
qinsoon committed
204
205
    // execute and check results
    assert!(exec_cmd(cc).status.success(), "failed to link code");
qinsoon's avatar
qinsoon committed
206
207
208
    out
}

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

242
243
244
245
    link_dylib_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
246
        out_path
247
    )
qinsoon's avatar
qinsoon committed
248
249
250
}

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

qinsoon's avatar
qinsoon committed
259
    // compile each single source file
qinsoon's avatar
qinsoon committed
260
    for file in files {
qinsoon's avatar
qinsoon committed
261
        let mut cc = Command::new(get_c_compiler());
qinsoon's avatar
qinsoon committed
262

qinsoon's avatar
qinsoon committed
263
        // output object file
264
        cc.arg("-c");
qinsoon's avatar
qinsoon committed
265
        // position independent code
266
        cc.arg("-fPIC");
qinsoon's avatar
qinsoon committed
267
268
269
270

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

271
272
273
        cc.arg(file.as_os_str());
        cc.arg("-o");
        cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
274
275

        object_files.push(out);
qinsoon's avatar
qinsoon committed
276
        exec_cmd(cc);
277
278
    }

qinsoon's avatar
qinsoon committed
279
280
    // link object files into a dynamic library
    let mut cc = Command::new(get_c_compiler());
281
282
283
284
285
286
287

    // external libs
    for path in libpath.iter() {
        cc.arg(format!("-L{}", path));
    }
    for l in lib.iter() {
        cc.arg(format!("-l{}", l));
qinsoon's avatar
qinsoon committed
288
289
    }

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

    // all object files
qinsoon's avatar
qinsoon committed
302
    for obj in object_files {
303
        cc.arg(obj.as_os_str());
qinsoon's avatar
qinsoon committed
304
    }
305

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

qinsoon's avatar
qinsoon committed
310
    exec_cmd(cc);
qinsoon's avatar
qinsoon committed
311
312
313
314

    out
}

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

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

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

    ret
}

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

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

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

421
    use std::fs::File;
422

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

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

440
    assert!(output.status.success());
441

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

448
    assert!(output.status.success());
449
450


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

455
456
457
458
    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;
459

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

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

472
    assert!(output.status.success());
473

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

480
    assert!(output.status.success());
481

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

488
    assert!(output.status.success());
489

490
491
492
493
494
    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");
495

496
    assert!(output.status.success());
497
498


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

504
505
    use std::os::unix::io::FromRawFd;
    use std::os::unix::io::AsRawFd;
506
507


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

513
    let mut outputs_file = File::create("outputs.txt").unwrap();
514

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

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

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

529
    assert!(output.status.success());
530

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

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

548
549
550
    use std::thread;
    use std::io;
    use std::io::prelude::*;
551

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

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

572
            results_file.read_to_string(&mut file_content);
573

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

587
588
            use std::str::FromStr;
            use std::fs::OpenOptions;
589

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

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

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

649
            assert!(output.status.success());
650

651
652
653
            panic!("Test Timed Out!");
        }
    }
654

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

663
    assert!(output.status.success());
664

665
666
667
668
    assert!(test_succeeded == 1);
}

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

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

686
    use std::fs::File;
687

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

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

707
    assert!(output.status.success());
708

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

715
    assert!(output.status.success());
716
717


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

722
723
724
725
726
    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;
727

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

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

740
    assert!(output.status.success());
741

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

748
    assert!(output.status.success());
749

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

756
    assert!(output.status.success());
757

758
759
760
761
762
    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");
763

764
    assert!(output.status.success());
765

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

772
    assert!(output.status.success());
773

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

779
780
    use std::os::unix::io::FromRawFd;
    use std::os::unix::io::AsRawFd;
781
782


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

788
    let mut outputs_file = File::create("outputs.txt").unwrap();
789

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

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

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

804
    assert!(output.status.success());
805

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

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

823
824
825
    use std::thread;
    use std::io;
    use std::io::prelude::*;
826

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

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

847
            results_file.read_to_string(&mut file_content);
848

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

862
863
            use std::str::FromStr;
            use std::fs::OpenOptions;
864

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

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

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

924
            assert!(output.status.success());
925

926
927
928
            panic!("Test Timed Out!");
        }
    }
929

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

938
    assert!(output.status.success());
939

940
941
    assert!(test_succeeded == 1);
}