From 38ca239ad251c7c8d531e562a8166a5bcd024eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Tue, 22 Jan 2019 00:00:00 +0000 Subject: [PATCH] remove --gem5, use --emulator gem5 everywhere Allow passing --emulator multiple times for transparent tests selection just like --arch. --- README.adoc | 236 +++++++++++++++++++++---------------------- bench-boot | 12 +-- common.py | 80 ++++++++++----- gem5-bench-cache | 2 +- gem5-bench-dhrystone | 2 +- run | 7 +- test-gdb | 65 ++++++------ test-userland | 67 ++++++------ 8 files changed, 246 insertions(+), 225 deletions(-) diff --git a/README.adoc b/README.adoc index 57bb4730..fee60d0c 100644 --- a/README.adoc +++ b/README.adoc @@ -342,7 +342,7 @@ If you haven't built Buildroot yet for <>, you can build f .... ./build --download-dependencies gem5-buildroot -./run --gem5 +./run --emulator gem5 .... If you have already built previously, don't be afraid: gem5 and QEMU use almost the same root filesystem and kernel, so `./build` will be fast. @@ -364,7 +364,7 @@ You can quit the shell without killing gem5 by typing tilde followed by a period If you are inside <>, which I highly recommend, you can both run gem5 stdout and open the guest terminal on a split window with: .... -./run --gem5 --tmux +./run --emulator gem5 --tmux .... See also: <>. @@ -381,7 +381,7 @@ but if you look closely, the `PS1` prompt marker `#` is there already, just hit If you forgot to open the shell and gem5 exit, you can inspect the terminal output post-mortem at: .... -less "$(./getvar --gem5 m5out_dir)/system.pc.com_1.device" +less "$(./getvar --emulator gem5 m5out_dir)/system.pc.com_1.device" .... More gem5 information is present at: <> @@ -570,7 +570,7 @@ For gem5, do: git submodule update --init --depth 1 "$(./getvar linux_source_dir)" sudo apt-get install qemu-utils ./build-gem5 -./run --gem5 --prebuilt +./run --emulator gem5 --prebuilt .... `qemu-utils` is required because we currently distribute `.qcow2` files which <>, so we need `qemu-img` to extract them first. @@ -783,7 +783,7 @@ To use gem5 instead of QEMU do: .... ./build --download-dependencies gem5-baremetal -./run --arch aarch64 --baremetal interactive/prompt --gem5 +./run --arch aarch64 --baremetal interactive/prompt --emulator gem5 .... and then <> open a shell with: @@ -798,14 +798,14 @@ Note that `./build-baremetal` requires the `--gem5` option, and generates separa .... echo "$(./getvar --arch aarch64 --baremetal interactive/prompt image)" -echo "$(./getvar --arch aarch64 --baremetal interactive/prompt --gem5 image)" +echo "$(./getvar --arch aarch64 --baremetal interactive/prompt --emulator gem5 image)" .... This is unlike the Linux kernel that has a single image for both QEMU and gem5: .... echo "$(./getvar --arch aarch64 image)" -echo "$(./getvar --arch aarch64 --gem5 image)" +echo "$(./getvar --arch aarch64 --emulator gem5 image)" .... The reason for that is that on baremetal we don't parse the <> from memory like the Linux kernel does, which tells the kernel for example the UART address, and many other system parameters. @@ -813,15 +813,15 @@ The reason for that is that on baremetal we don't parse the <> at bad30f513c46c1b0995d3a10c0d9bc2a33dc4fa0: Automatically run non-interactive userland tests that don't depend on nay kernel modules: .... -./build-userland --all-archs -./build-userland --all-archs --static --userland-build-id static -./test-userland --all-archs +./build-userland --all-archs --all-emulators +./build-userland --all-archs --all-emulators --static --userland-build-id static +./test-userland --all-archs --all-emulators .... Source: link:test-userland[] @@ -3547,7 +3547,7 @@ More concretely, first build the kernel with the <> and then run with: .... -./run --arch aarch64 --dp650 --gem5 --linux-build-id gem5-v4.15 +./run --arch aarch64 --dp650 --emulator gem5 --linux-build-id gem5-v4.15 .... ==== Graphic mode gem5 internals @@ -6733,9 +6733,9 @@ instructions_firmware 20708 gem5: .... -./run --arch aarch64 --gem5 --eval 'm5 exit' +./run --arch aarch64 --emulator gem5 --eval 'm5 exit' # Or: -# ./run --arch aarch64 --gem5 --eval 'm5 exit' -- --cpu-type=HPI --caches +# ./run --arch aarch64 --emulator gem5 --eval 'm5 exit' -- --cpu-type=HPI --caches ./gem5-stat --arch aarch64 sim_insts .... @@ -8281,7 +8281,7 @@ You can still send key presses to QEMU however even without the mouse capture, j Start pdb at the first instruction: .... -./run --gem5 --gem5-exe-args='--pdb' --terminal +./run --emulator gem5 --gem5-exe-args='--pdb' --terminal .... Requires `--terminal` as we must be on foreground. @@ -8295,7 +8295,7 @@ import ipdb; ipdb.set_trace() and then run with: .... -./run --gem5 --terminal +./run --emulator gem5 --terminal .... TODO test PyCharm: https://stackoverflow.com/questions/51982735/writing-gem5-configuration-scripts-with-pycharm @@ -8565,14 +8565,14 @@ just appears to output both cores intertwined without any clear differentiation. gem5 provides also provides a tracing mechanism documented at: link:http://www.gem5.org/Trace_Based_Debugging[]: .... -./run --arch aarch64 --eval 'm5 exit' --gem5 --trace Exec +./run --arch aarch64 --eval 'm5 exit' --emulator gem5 --trace Exec less "$(./getvar --arch aarch64 run_dir)/trace.txt" .... Output the trace to stdout instead of a file: .... -./run --arch aarch64 --eval 'm5 exit' --gem5 --trace Exec --trace-stdout +./run --arch aarch64 --eval 'm5 exit' --emulator gem5 --trace Exec --trace-stdout .... This would produce a lot of output however, so you will likely not want that when tracing a Linux kernel boot instructions. But it can be very convenient for smaller traces. @@ -8580,7 +8580,7 @@ This would produce a lot of output however, so you will likely not want that whe List all available debug flags: .... -./run --arch aarch64 --gem5-exe-args='--debug-help' --gem5 +./run --arch aarch64 --gem5-exe-args='--debug-help' --emulator gem5 .... but to understand most of them you have to look at the source code: @@ -8633,8 +8633,8 @@ The best way to verify all of this is to write some <> Trace the source lines just like <> with: .... -./trace-boot --arch aarch64 --gem5 -./trace2line --arch aarch64 --gem5 +./trace-boot --arch aarch64 --emulator gem5 +./trace2line --arch aarch64 --emulator gem5 less "$(./getvar --arch aarch64 run_dir)/trace-lines.txt" .... @@ -8743,7 +8743,7 @@ Another interesting example can be found at: link:gem5-bench-cache[]. A more naive and simpler to understand approach would be a direct: .... -./run --arch aarch64 --gem5 --eval 'm5 checkpoint;m5 resetstats;dhrystone 10000;m5 exit' +./run --arch aarch64 --emulator gem5 --eval 'm5 checkpoint;m5 resetstats;dhrystone 10000;m5 exit' .... but the problem is that this method does not allow to easily run a different script without running the boot again, see: <>. @@ -8781,7 +8781,7 @@ The rabbit hole is likely deep, but let's scratch a bit of the surface. ===== Number of cores .... -./run --arch arm --cpus 2 --gem5 +./run --arch arm --cpus 2 --emulator gem5 .... Check with: @@ -8801,7 +8801,7 @@ Build the kernel with the <>, and then run: ./run \ --arch aarch64 \ --linux-build-id gem5-v4.15 \ - --gem5 \ + --emulator gem5 \ --cpus 16 \ -- \ --param 'system.realview.gic.gem5_extensions = True' \ @@ -8812,7 +8812,7 @@ Build the kernel with the <>, and then run: https://stackoverflow.com/questions/49624061/how-to-run-gem5-simulator-in-fs-mode-without-cache/49634544#49634544 -A quick `+./run --gem5 -- -h+` leads us to the options: +A quick `+./run --emulator gem5 -- -h+` leads us to the options: .... --caches @@ -8879,37 +8879,37 @@ cat "$(./getvar --arch aarch64 run_dir)/bench-cache.txt" which gives: .... -cmd ./run --gem5 --arch aarch64 --gem5-readfile "dhrystone 1000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024 --l1i_size=1024 --l2_size=1024 --l3_size=1024 --cpu-type=HPI --restore-with-cpu=HPI +cmd ./run --emulator gem5 --arch aarch64 --gem5-readfile "dhrystone 1000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024 --l1i_size=1024 --l2_size=1024 --l3_size=1024 --cpu-type=HPI --restore-with-cpu=HPI time 23.82 exit_status 0 cycles 93284622 instructions 4393457 -cmd ./run --gem5 --arch aarch64 --gem5-readfile "dhrystone 1000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB --cpu-type=HPI --restore-with-cpu=HPI +cmd ./run --emulator gem5 --arch aarch64 --gem5-readfile "dhrystone 1000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB --cpu-type=HPI --restore-with-cpu=HPI time 14.91 exit_status 0 cycles 10128985 instructions 4211458 -cmd ./run --gem5 --arch aarch64 --gem5-readfile "dhrystone 10000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024 --l1i_size=1024 --l2_size=1024 --l3_size=1024 --cpu-type=HPI --restore-with-cpu=HPI +cmd ./run --emulator gem5 --arch aarch64 --gem5-readfile "dhrystone 10000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024 --l1i_size=1024 --l2_size=1024 --l3_size=1024 --cpu-type=HPI --restore-with-cpu=HPI time 51.87 exit_status 0 cycles 188803630 instructions 12401336 -cmd ./run --gem5 --arch aarch64 --gem5-readfile "dhrystone 10000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB --cpu-type=HPI --restore-with-cpu=HPI +cmd ./run --emulator gem5 --arch aarch64 --gem5-readfile "dhrystone 10000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB --cpu-type=HPI --restore-with-cpu=HPI time 35.35 exit_status 0 cycles 20715757 instructions 12192527 -cmd ./run --gem5 --arch aarch64 --gem5-readfile "dhrystone 100000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024 --l1i_size=1024 --l2_size=1024 --l3_size=1024 --cpu-type=HPI --restore-with-cpu=HPI +cmd ./run --emulator gem5 --arch aarch64 --gem5-readfile "dhrystone 100000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024 --l1i_size=1024 --l2_size=1024 --l3_size=1024 --cpu-type=HPI --restore-with-cpu=HPI time 339.07 exit_status 0 cycles 1176559936 instructions 94222791 -cmd ./run --gem5 --arch aarch64 --gem5-readfile "dhrystone 100000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB --cpu-type=HPI --restore-with-cpu=HPI +cmd ./run --emulator gem5 --arch aarch64 --gem5-readfile "dhrystone 100000" --gem5-restore 1 -- --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB --cpu-type=HPI --restore-with-cpu=HPI time 240.37 exit_status 0 cycles 125666679 @@ -8963,7 +8963,7 @@ and also: `gem5-dist`: https://publish.illinois.edu/icsl-pdgem5/ Clock frequency: TODO how does it affect performance in benchmarks? .... -./run --arch aarch64 --gem5 -- --cpu-clock 10000000 +./run --arch aarch64 --emulator gem5 -- --cpu-clock 10000000 .... Check with: @@ -9011,10 +9011,10 @@ Usage: ./run \ --arch aarch64 \ --eval-after '/gem5.sh' \ - --gem5 \ + --emulator gem5 \ --gem5-readfile '/bst_vs_heap.out' \ ; -./bst-vs-heap --arch aarch64 --gem5 > bst_vs_heap.dat +./bst-vs-heap --arch aarch64 --emulator gem5 > bst_vs_heap.dat .... and then feed `bst_vs_heap.dat` into: https://github.com/cirosantilli/cpp-cheat/blob/9d0f77792fc8e55b20b6ee32018761ef3c5a3f2f/cpp/interactive/bst_vs_heap.gnuplot @@ -9116,7 +9116,7 @@ There are two ways to run PARSEC with this repo: .... ./build --arch arm --download-dependencies gem5-buildroot parsec-benchmark ./build-buildroot --arch arm --config 'BR2_PACKAGE_PARSEC_BENCHMARK=y' -./run --arch arm --gem5 +./run --arch arm --emulator gem5 .... Once inside the guest, launch one of the `test` input sized benchmarks manually as in: @@ -9270,7 +9270,7 @@ You may also want to test if your patches are still functionally correct inside Analogous <>: .... -./run --arch arm --kernel-cli 'init=/poweroff.out' --gem5 +./run --arch arm --kernel-cli 'init=/poweroff.out' --emulator gem5 .... Internals: when we give `--command-line=` to gem5, it overrides default command lines, including some mandatory ones which are required to boot properly. @@ -9280,7 +9280,7 @@ Our run script hardcodes the require options in the default `--command-line` and To find the default options in the first place, we removed `--command-line` and ran: .... -./run --arch arm --gem5 +./run --arch arm --emulator gem5 .... and then looked at the line of the Linux kernel that starts with: @@ -9296,13 +9296,13 @@ Kernel command line: Analogous <>, on the first shell: .... -./run --arch arm --wait-gdb --gem5 +./run --arch arm --wait-gdb --emulator gem5 .... On the second shell: .... -./run-gdb --arch arm --gem5 +./run-gdb --arch arm --emulator gem5 .... On a third shell: @@ -9335,7 +9335,7 @@ I press `n`, it just runs the program until the end, instead of stopping on the TODO: .... -./run-gdb-user --arch arm --gem5 gem5-1.0/gem5/util/m5/m5 main +./run-gdb-user --arch arm --emulator gem5 gem5-1.0/gem5/util/m5/m5 main .... breaks when `m5` is run on guest, but does not show the source code. @@ -9347,7 +9347,7 @@ Analogous to QEMU's <>, but better since it can be started from inside Documentation: http://gem5.org/Checkpoints .... -./run --arch arm --gem5 +./run --arch arm --emulator gem5 .... In the guest, wait for the boot to end and run: @@ -9361,7 +9361,7 @@ where <> is a guest utility present inside the gem5 tree which we cross-comp To restore the checkpoint, kill the VM and run: .... -./run --arch arm --gem5 --gem5-restore 1 +./run --arch arm --emulator gem5 --gem5-restore 1 .... The `--gem5-restore` option restores the checkpoint that was created most recently. @@ -9376,7 +9376,7 @@ m5 checkpoint Kill the VM, and try it out: .... -./run --arch arm --gem5 --gem5-restore 1 +./run --arch arm --emulator gem5 --gem5-restore 1 .... Here we use `--gem5-restore 1` again, since the second snapshot we took is now the most recent one @@ -9392,13 +9392,13 @@ contains the `date`. The file `f` wouldn't exist had we used the first checkpoin If you automate things with <> as in: .... -./run --arch arm --eval 'm5 checkpoint;m5 resetstats;dhrystone 1000;m5 exit' --gem5 +./run --arch arm --eval 'm5 checkpoint;m5 resetstats;dhrystone 1000;m5 exit' --emulator gem5 .... Then there is no need to pass the kernel command line again to gem5 for replay: .... -./run --arch arm --gem5 --gem5-restore 1 +./run --arch arm --emulator gem5 --gem5-restore 1 .... since boot has already happened, and the parameters are already in the RAM of the snapshot. @@ -9408,7 +9408,7 @@ since boot has already happened, and the parameters are already in the RAM of th Checkpoints are stored inside the <> at: .... -"$(./getvar --gem5 m5out_dir)/cpt." +"$(./getvar --emulator gem5 m5out_dir)/cpt." .... where `` is the cycle number at which the checkpoint was taken. @@ -9438,19 +9438,19 @@ So we can do it like: .... # Boot, checkpoint and exit. printf 'echo "setup run";m5 exit' > "$(./getvar gem5_readfile)" -./run --gem5 --eval 'm5 checkpoint;m5 readfile > a.sh;sh a.sh' +./run --emulator gem5 --eval 'm5 checkpoint;m5 readfile > a.sh;sh a.sh' # Restore and run the first benchmark. printf 'echo "first benchmark";m5 exit' > "$(./getvar gem5_readfile)" -./run --gem5 --gem5-restore 1 +./run --emulator gem5 --gem5-restore 1 # Restore and run the second benchmark. printf 'echo "second benchmark";m5 exit' > "$(./getvar gem5_readfile)" -./run --gem5 --gem5-restore 1 +./run --emulator gem5 --gem5-restore 1 # If something weird happened, create an interactive shell to examine the system. printf 'sh' > "$(./getvar gem5_readfile)" -./run --gem5 --gem5-restore 1 +./run --emulator gem5 --gem5-restore 1 .... Since this is such a common setup, we provide some helpers for it as described at <>: @@ -9487,7 +9487,7 @@ A common combo is to boot Linux with a fast CPU, make a checkpoint and then repl An illustrative interactive run: .... -./run --arch arm --gem5 +./run --arch arm --emulator gem5 .... In guest: @@ -9499,7 +9499,7 @@ m5 checkpoint And then restore the checkpoint with a different CPU: .... -./run --arch arm --gem5 --gem5-restore 1 -- --caches --restore-with-cpu=HPI +./run --arch arm --emulator gem5 --gem5-restore 1 -- --caches --restore-with-cpu=HPI .... === Pass extra options to gem5 @@ -9509,12 +9509,12 @@ Pass options to the `fs.py` script: * get help: + .... -./run --gem5 -- -h +./run --emulator gem5 -- -h .... * boot with the more detailed and slow `HPI` CPU model: + .... -./run --arch arm --gem5 -- --caches --cpu-type=HPI +./run --arch arm --emulator gem5 -- --caches --cpu-type=HPI .... Pass options to the `gem5` executable itself: @@ -9522,7 +9522,7 @@ Pass options to the `gem5` executable itself: * get help: + .... -./run --gem5-exe-args='-h' --gem5 +./run --gem5-exe-args='-h' --emulator gem5 .... === gem5 exit after a number of instructions @@ -9530,7 +9530,7 @@ Pass options to the `gem5` executable itself: Quit the simulation after `1024` instructions: .... -./run --gem5 -- -I 1024 +./run --emulator gem5 -- -I 1024 .... Can be nicely checked with <>. @@ -9538,7 +9538,7 @@ Can be nicely checked with <>. Cycles instead of instructions: .... -./run --gem5 -- --memory 1024 +./run --emulator gem5 -- --memory 1024 .... Otherwise the simulation runs forever by default. @@ -9619,7 +9619,7 @@ m5 writefile myfileguest myfilehost Host: .... -cat "$(./getvar --arch aarch64 --gem5 m5out_dir)/myfilehost" +cat "$(./getvar --arch aarch64 --emulator gem5 m5out_dir)/myfilehost" .... Does not work for subdirectories, gem5 crashes: @@ -9655,8 +9655,8 @@ Ermm, just another <> that only takes integers and only from CLI op Host: .... -./run --gem5 --gem5-restore 1 -- --initparam 13 -./run --gem5 --gem5-restore 1 -- --initparam 42 +./run --emulator gem5 --gem5-restore 1 -- --initparam 13 +./run --emulator gem5 --gem5-restore 1 -- --initparam 42 .... Guest: @@ -9831,7 +9831,7 @@ It is obviously not possible to understand what they actually do from their comm When you run gem5, it generates an `m5out` directory at: .... -echo $(./getvar --arch arm --gem5 m5out_dir)" +echo $(./getvar --arch arm --emulator gem5 m5out_dir)" .... The location of that directory can be set with `./gem5.opt -d`, and defaults to `./m5out`. @@ -9874,7 +9874,7 @@ Let's have some fun and try to correlate the gem5 cycle count `system.cpu.numCyc .... ./build-userland -- rdtsc -./run --eval '/rdtsc.out;m5 exit;' --gem5 +./run --eval '/rdtsc.out;m5 exit;' --emulator gem5 ./gem5-stat .... @@ -9907,7 +9907,7 @@ TODO We didn't manage to find a working ARM analogue to <>: link:kernel_m The `config.ini` file, contains a very good high level description of the system: .... -less $(./getvar --arch arm --gem5 m5out_dir)" +less $(./getvar --arch arm --emulator gem5 m5out_dir)" .... That file contains a tree representation of the system, sample excerpt: @@ -9945,7 +9945,7 @@ For example, `AtomicSimpleCPU` maps is defined at link:https://github.com/gem5/g You can also get a simplified graphical view of the tree with: .... -xdg-open "$(./getvar --arch arm --gem5 m5out_dir)/config.dot.pdf" +xdg-open "$(./getvar --arch arm --emulator gem5 m5out_dir)/config.dot.pdf" .... Modifying the `config.ini` file manually does nothing since it gets overwritten every time. @@ -10004,7 +10004,7 @@ patch -d "$(./getvar gem5_source_dir)" -p 1 < patches/manual/gem5-biglittle.patc then: .... -./run --arch aarch64 --gem5 --gem5-script biglittle +./run --arch aarch64 --emulator gem5 --gem5-script biglittle .... Advantages over `fs.py`: @@ -10632,18 +10632,18 @@ TODO: why is `arm` stuck at `19` which equals Supervisor mode? In gem5, you can configure the lowest EL with: .... -./run --arch arm --baremetal arch/arm/el --gem5 -cat "$(./getvar --arch arm --gem5 gem5_guest_terminal_file)" -./run --arch arm --baremetal arch/arm/el --gem5 -- --param 'system.have_virtualization = True' -cat "$(./getvar --arch arm --gem5 gem5_guest_terminal_file)" -./run --arch arm --baremetal arch/arm/el --gem5 -- --param 'system.have_security = True' -cat "$(./getvar --arch arm --gem5 gem5_guest_terminal_file)" -./run --arch aarch64 --baremetal arch/aarch64/el --gem5 -cat "$(./getvar --arch aarch64 --gem5 gem5_guest_terminal_file)" -./run --arch aarch64 --baremetal arch/aarch64/el --gem5 -- --param 'system.have_virtualization = True' -cat "$(./getvar --arch aarch64 --gem5 gem5_guest_terminal_file)" -./run --arch aarch64 --baremetal arch/aarch64/el --gem5 -- --param 'system.have_security = True' -cat "$(./getvar --arch aarch64 --gem5 gem5_guest_terminal_file)" +./run --arch arm --baremetal arch/arm/el --emulator gem5 +cat "$(./getvar --arch arm --emulator gem5 gem5_guest_terminal_file)" +./run --arch arm --baremetal arch/arm/el --emulator gem5 -- --param 'system.have_virtualization = True' +cat "$(./getvar --arch arm --emulator gem5 gem5_guest_terminal_file)" +./run --arch arm --baremetal arch/arm/el --emulator gem5 -- --param 'system.have_security = True' +cat "$(./getvar --arch arm --emulator gem5 gem5_guest_terminal_file)" +./run --arch aarch64 --baremetal arch/aarch64/el --emulator gem5 +cat "$(./getvar --arch aarch64 --emulator gem5 gem5_guest_terminal_file)" +./run --arch aarch64 --baremetal arch/aarch64/el --emulator gem5 -- --param 'system.have_virtualization = True' +cat "$(./getvar --arch aarch64 --emulator gem5 gem5_guest_terminal_file)" +./run --arch aarch64 --baremetal arch/aarch64/el --emulator gem5 -- --param 'system.have_security = True' +cat "$(./getvar --arch aarch64 --emulator gem5 gem5_guest_terminal_file)" .... output: @@ -10661,9 +10661,9 @@ output: .... ./run --arch aarch64 --baremetal arch/aarch64/multicore --cpus 2 -./run --arch aarch64 --baremetal arch/aarch64/multicore --cpus 2 --gem5 +./run --arch aarch64 --baremetal arch/aarch64/multicore --cpus 2 --emulator gem5 ./run --arch arm --baremetal arch/aarch64/multicore --cpus 2 -./run --arch arm --baremetal arch/aarch64/multicore --cpus 2 --gem5 +./run --arch arm --baremetal arch/aarch64/multicore --cpus 2 --emulator gem5 .... Sources: @@ -10686,7 +10686,7 @@ and watch it hang forever. Note that if you try the same thing on gem5: .... -./run --arch aarch64 --baremetal arch/aarch64/multicore --cpus 1 --gem5 +./run --arch aarch64 --baremetal arch/aarch64/multicore --cpus 1 --emulator gem5 .... then the gem5 actually exits, but with a different message: @@ -10977,7 +10977,7 @@ time 8.83 exit_status 0 instructions 2244297 -cmd ./run --arch x86_64 --eval 'm5 exit' --gem5 +cmd ./run --arch x86_64 --eval 'm5 exit' --emulator gem5 time 213.39 exit_status 0 instructions 318486337 @@ -10990,12 +10990,12 @@ time 6.90 exit_status 0 instructions 776374 -cmd ./run --arch arm --eval 'm5 exit' --gem5 +cmd ./run --arch arm --eval 'm5 exit' --emulator gem5 time 118.46 exit_status 0 instructions 153023392 -cmd ./run --arch arm --eval 'm5 exit' --gem5 -- --cpu-type=HPI --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB +cmd ./run --arch arm --eval 'm5 exit' --emulator gem5 -- --cpu-type=HPI --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB time 2250.40 exit_status 0 instructions 151981914 @@ -11009,22 +11009,22 @@ time 5.04 exit_status 0 instructions 233162 -cmd ./run --arch aarch64 --eval 'm5 exit' --gem5 +cmd ./run --arch aarch64 --eval 'm5 exit' --emulator gem5 time 70.89 exit_status 0 instructions 124346081 -cmd ./run --arch aarch64 --eval 'm5 exit' --gem5 -- --cpu-type=HPI --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB +cmd ./run --arch aarch64 --eval 'm5 exit' --emulator gem5 -- --cpu-type=HPI --caches --l2cache --l1d_size=1024kB --l1i_size=1024kB --l2_size=1024kB --l3_size=1024kB time 381.86 exit_status 0 instructions 124564620 -cmd ./run --arch aarch64 --eval 'm5 exit' --gem5 --gem5-build-type fast +cmd ./run --arch aarch64 --eval 'm5 exit' --emulator gem5 --gem5-build-type fast time 58.00 exit_status 0 instructions 124346081 -cmd ./run --arch aarch64 --eval 'm5 exit' --gem5 --gem5-build-type debug +cmd ./run --arch aarch64 --eval 'm5 exit' --emulator gem5 --gem5-build-type debug time 1022.03 exit_status 0 instructions 124346081 @@ -11037,7 +11037,7 @@ TODO: aarch64 gem5 and QEMU use the same kernel, so why is the gem5 instruction TODO 62f6870e4e0b384c4bd2d514116247e81b241251 takes 33 minutes to finish at 62f6870e4e0b384c4bd2d514116247e81b241251: .... -cmd ./run --arch arm --eval 'm5 exit' --gem5 -- --caches --cpu-type=HPI +cmd ./run --arch arm --eval 'm5 exit' --emulator gem5 -- --caches --cpu-type=HPI .... while aarch64 only 7 minutes. @@ -11138,7 +11138,7 @@ Sample results at gem5 2a9573f5942b5416fb0570cf5cb6cdecba733392: 10 to 12 minute Get results with: .... -./bench-all --gem5 +./bench-all --emulator gem5 tail -n+1 ../linux-kernel-module-cheat-regression/*/gem5-bench-build-*.txt .... @@ -11420,22 +11420,22 @@ Our scripts solve two difficulties with simultaneous runs: Each run gets a separate output directory. For example: .... -./run --arch aarch64 --gem5 --run-id 0 &>/dev/null & -./run --arch aarch64 --gem5 --run-id 1 &>/dev/null & +./run --arch aarch64 --emulator gem5 --run-id 0 &>/dev/null & +./run --arch aarch64 --emulator gem5 --run-id 1 &>/dev/null & .... produces two separate <>: .... -echo "$(./getvar --arch aarch64 --gem5 --run-id 0 m5out_dir)" -echo "$(./getvar --arch aarch64 --gem5 --run-id 1 m5out_dir)" +echo "$(./getvar --arch aarch64 --emulator gem5 --run-id 0 m5out_dir)" +echo "$(./getvar --arch aarch64 --emulator gem5 --run-id 1 m5out_dir)" .... and the gem5 host executable stdout and stderr can be found at: .... -less "$(./getvar --arch aarch64 --gem5 --run-id 0 termout_file)" -less "$(./getvar --arch aarch64 --gem5 --run-id 1 termout_file)" +less "$(./getvar --arch aarch64 --emulator gem5 --run-id 0 termout_file)" +less "$(./getvar --arch aarch64 --emulator gem5 --run-id 1 termout_file)" .... Each line is prepended with the timestamp in seconds since the start of the program when it appeared. @@ -11443,7 +11443,7 @@ Each line is prepended with the timestamp in seconds since the start of the prog To have more semantic output directories names for later inspection, you can use a non numeric string for the run ID, and indicate the port offset explicitly: .... -./run --arch aarch64 --gem5 --run-id some-experiment --port-offset 1 +./run --arch aarch64 --emulator gem5 --run-id some-experiment --port-offset 1 .... `--port-offset` defaults to the run ID when that is a number. @@ -11540,11 +11540,11 @@ git -C "$(./getvar gem5_source_dir)" checkout some-branch git -C "$(./getvar gem5_source_dir)" checkout - # Run master. -./run --gem5 +./run --emulator gem5 # Run another branch. git -C "$(./getvar gem5_source_dir)" checkout some-branch -./run --gem5-build-id some-branch --gem5 +./run --gem5-build-id some-branch --emulator gem5 .... Don't forget however that gem5 has Python scripts in its source code tree, and that those must match the source code of a given build. @@ -11569,11 +11569,11 @@ cd - ./build-gem5 --gem5-worktree my-new-feature # Run the submodule. -./run --gem5 --run-id 0 &>/dev/null & +./run --emulator gem5 --run-id 0 &>/dev/null & # Run the branch the need to check out anything. # With --gem5-worktree, we can do both runs at the same time! -./run --gem5 --gem5-worktree my-new-feature --run-id 1 &>/dev/null & +./run --emulator gem5 --gem5-worktree my-new-feature --run-id 1 &>/dev/null & .... `--gem5-worktree ` automatically creates: @@ -11628,7 +11628,7 @@ The `gem5.debug` executable has optimizations turned off unlike the default `gem .... ./build-gem5 --arch aarch64 --gem5-build-type debug -./run --arch aarch64 --debug-vm --gem5 --gem5-build-type debug +./run --arch aarch64 --debug-vm --emulator gem5 --gem5-build-type debug .... The build outputs are automatically stored in a different directory from other build types such as `.opt` build, which prevents `.debug` files from overwriting `.opt` ones. @@ -11638,8 +11638,8 @@ Therefore, `--gem5-build-id` is not required. The price to pay for debuggability is high however: a Linux kernel boot was about 14 times slower than opt at 71e927e63bda6507d5a528f22c78d65099bdf36f between the commands: .... -./run --arch aarch64 --eval 'm5 exit' --gem5 --linux-build-id v4.16 -./run --arch aarch64 --eval 'm5 exit' --gem5 --linux-build-id v4.16 --gem5-build-type debug +./run --arch aarch64 --eval 'm5 exit' --emulator gem5 --linux-build-id v4.16 +./run --arch aarch64 --eval 'm5 exit' --emulator gem5 --linux-build-id v4.16 --gem5-build-type debug .... so you will likely only use this when it is unavoidable. @@ -11884,7 +11884,7 @@ We have some link:https://github.com/pexpect/pexpect[pexpect] automated tests fo .... ./build-test-gdb && \ -./test-gdb --all-archs +./test-gdb --all-archs --all-emulators .... Sources: diff --git a/bench-boot b/bench-boot index 410e2a66..82b249e4 100755 --- a/bench-boot +++ b/bench-boot @@ -57,7 +57,7 @@ if [ "$test_size" -ge 2 ]; then bench "$arch --eval 'm5 exit' --gem5" gem5_insts "$arch" fi -#bench "$arch --eval 'm5 exit' --gem5 -- --cpu-type=DerivO3CPU ${caches}" +#bench "$arch --eval 'm5 exit' --emulator gem5 -- --cpu-type=DerivO3CPU ${caches}" #gem5_insts "$arch" arch=arm @@ -69,7 +69,7 @@ if [ "$test_size" -ge 2 ]; then gem5_insts "$arch" fi if [ "$test_size" -ge 3 ]; then - bench "$arch --eval 'm5 exit' --gem5 -- --cpu-type=HPI ${caches}" + bench "$arch --eval 'm5 exit' --emulator gem5 -- --cpu-type=HPI ${caches}" gem5_insts "$arch" fi @@ -83,12 +83,12 @@ if [ "$test_size" -ge 2 ]; then gem5_insts "$arch" fi if [ "$test_size" -ge 3 ]; then - bench "$arch --eval 'm5 exit' --gem5 -- --cpu-type=HPI ${caches}" + bench "$arch --eval 'm5 exit' --emulator gem5 -- --cpu-type=HPI ${caches}" gem5_insts "$arch" - #bench "$arch --eval 'm5 exit' --gem5 --gem5-script biglittle" + #bench "$arch --eval 'm5 exit' --emulator gem5 --gem5-script biglittle" #gem5_insts "$arch" - bench "$arch --eval 'm5 exit' --gem5 --gem5-build-type fast" + bench "$arch --eval 'm5 exit' --emulator gem5 --gem5-build-type fast" gem5_insts "$arch" - bench "$arch --eval 'm5 exit' --gem5 --gem5-build-type debug" + bench "$arch --eval 'm5 exit' --emulator gem5 --gem5-build-type debug" gem5_insts "$arch" fi diff --git a/common.py b/common.py index 5853cc2e..01eecca2 100644 --- a/common.py +++ b/common.py @@ -95,7 +95,15 @@ consts['obj_ext'] = '.o' consts['config_file'] = os.path.join(consts['data_dir'], 'config.py') consts['magic_fail_string'] = b'lkmc_test_fail' consts['baremetal_lib_basename'] = 'lib' -consts['emulators'] = ['qemu', 'gem5'] +consts['emulator_short_to_long_dict'] = collections.OrderedDict([ + ('q', 'qemu'), + ('g', 'gem5'), +]) +consts['all_long_emulators'] = [consts['emulator_short_to_long_dict'][k] for k in consts['emulator_short_to_long_dict']] +consts['emulator_choices'] = set() +for key in consts['emulator_short_to_long_dict']: + consts['emulator_choices'].add(key) + consts['emulator_choices'].add(consts['emulator_short_to_long_dict'][key]) class LkmcCliFunction(cli_function.CliFunction): ''' @@ -130,7 +138,12 @@ Run action for all supported --archs archs. Ignore --archs. '''.format(arches_string) ) self.add_argument( - '-a', '--arch', action='append', default=[consts['default_arch']], dest='archs', + '-a', + '--arch', + action='append', + choices=consts['arch_choices'], + default=[consts['default_arch']], + dest='archs', help='''\ CPU architecture to use. If given multiple times, run the action for each arch sequentially in that order. If one of them fails, stop running. @@ -298,17 +311,29 @@ instances in parallel. Default: the run ID (-n) if that is an integer, otherwise ) # Misc. + emulators = consts['emulator_short_to_long_dict'] + emulators_string = [] + for emulator_short in emulators: + emulator_long = emulators[emulator_short] + emulators_string.append('{} ({})'.format(emulator_long, emulator_short)) + emulators_string = ', '.join(emulators_string) self.add_argument( - '--emulator', choices=consts['emulators'], + '--all-emulators', default=False, help='''\ -Set the emulator to use. Ignore --gem5. -''' +Run action for all supported --emulators emulators. Ignore --emulators. +'''.format(emulators_string) ) self.add_argument( - '-g', '--gem5', default=False, + '-e', + '--emulator', + action='append', + choices=consts['emulator_choices'], + default=['qemu'], + dest='emulators', help='''\ -Use gem5 instead of QEMU. Shortcut for `--emulator gem5`. -''' +Emulator to use. If given multiple times, semantics are similar to --arch. +Valid emulators: {} +'''.format(emulators_string) ) def __call__(self, **kwargs): @@ -328,15 +353,10 @@ Use gem5 instead of QEMU. Shortcut for `--emulator gem5`. ''' def join(*paths): return os.path.join(*paths) - if env['arch'] not in consts['arch_choices']: - raise Exception('Unknown arch: ' + env['arch']) - if env['emulator'] is None: - if env['gem5']: - env['emulator'] = 'gem5' - else: - env['emulator'] = 'qemu' if env['arch'] in env['arch_short_to_long_dict']: env['arch'] = env['arch_short_to_long_dict'][env['arch']] + if env['emulator'] in env['emulator_short_to_long_dict']: + env['emulator'] = env['emulator_short_to_long_dict'][env['emulator']] if env['userland_build_id'] is None: env['userland_build_id'] = env['default_build_id'] env['userland_build_id_given'] = False @@ -722,19 +742,23 @@ Use gem5 instead of QEMU. Shortcut for `--emulator gem5`. env.update(consts) if env['all_archs']: env['archs'] = consts['all_long_archs'] - for arch in env['archs']: - if not env['dry_run']: - start_time = time.time() - env['arch'] = arch - self.env = env.copy() - self._init_env(self.env) - self.sh = shell_helpers.ShellHelpers(dry_run=self.env['dry_run']) - ret = self.timed_main() - if not env['dry_run']: - end_time = time.time() - self._print_time(end_time - start_time) - if ret is not None and ret != 0: - return ret + if env['all_emulators']: + env['emulators'] = consts['all_long_emulators'] + for emulator in env['emulators']: + for arch in env['archs']: + if not env['dry_run']: + start_time = time.time() + env['arch'] = arch + env['emulator'] = emulator + self.env = env.copy() + self._init_env(self.env) + self.sh = shell_helpers.ShellHelpers(dry_run=self.env['dry_run']) + ret = self.timed_main() + if not env['dry_run']: + end_time = time.time() + self._print_time(end_time - start_time) + if ret is not None and ret != 0: + return ret return 0 def make_build_dirs(self): diff --git a/gem5-bench-cache b/gem5-bench-cache index 4267680b..d1d0476d 100755 --- a/gem5-bench-cache +++ b/gem5-bench-cache @@ -13,7 +13,7 @@ while getopts "C" OPT; do esac done shift "$(($OPTIND - 1))" -common_opts="--gem5 $@" +common_opts="--emulator gem5 $@" # Vars cmd="./run ${common_opts}" diff --git a/gem5-bench-dhrystone b/gem5-bench-dhrystone index 0910c4c8..df3d90b6 100755 --- a/gem5-bench-dhrystone +++ b/gem5-bench-dhrystone @@ -6,7 +6,7 @@ set -eu root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" outfile="${root_dir}/out/gem5-bench-dhrystone.txt" arch=aarch64 -cmd="./run -a '$arch' --gem5 --eval-busybox '/gem5.sh'" +cmd="./run --arch '$arch' --emulator gem5 --eval-busybox '/gem5.sh'" # These cache sizes roughly match the ARM Cortex A75 # https://en.wikipedia.org/wiki/ARM_Cortex-A75 diff --git a/run b/run index 7ad94f63..e9da024e 100755 --- a/run +++ b/run @@ -59,7 +59,7 @@ See: https://github.com/cirosantilli/linux-kernel-module-cheat#replace-init ''' ) self.add_argument( - '-e', '--kernel-cli', + '--kernel-cli', help='''\ Pass an extra Linux kernel command line options, and place them before the dash separator `-`. Only options that come before the `-`, i.e. @@ -76,14 +76,13 @@ See: https://github.com/cirosantilli/linux-kernel-module-cheat#init-busybox ''' ) self.add_argument( - '-f', '--kernel-cli-after-dash', + '--kernel-cli-after-dash', help='''\ Pass an extra Linux kernel command line options, add a dash `-` separator, and place the options after the dash. Intended for custom options understood by our `init` scripts, most of which are prefixed by `lkmc_`. Example: `./run --kernel-cli-after-dash 'lkmc_eval="wget google.com" lkmc_lala=y'` -Mnenomic: `-f` comes after `-e`. ''' ) self.add_argument( @@ -92,7 +91,7 @@ Mnenomic: `-f` comes after `-e`. Pass extra options to the gem5 executable. Do not confuse with the arguments passed to config scripts, like `fs.py`. Example: -./run -G '--debug-flags=Exec --debug' --gem5 -- --cpu-type=HPI --caches +./run --emulator gem5 --gem5-exe-args '--debug-flags=Exec --debug' -- --cpu-type=HPI --caches will run: gem.op5 --debug-flags=Exec fs.py --cpu-type=HPI --caches ''' diff --git a/test-gdb b/test-gdb index cfde9f29..27ed0931 100755 --- a/test-gdb +++ b/test-gdb @@ -21,41 +21,40 @@ found by searching for the Python test files. def timed_main(self): run = self.import_path('run').Main() run_gdb = self.import_path('run-gdb').Main() - for emulator in self.env['emulators']: - if self.env['arch'] in self.env['crosstool_ng_supported_archs']: - if self.env['tests'] == []: - test_scripts_noext = [] - for f in os.listdir(self.env['baremetal_src_dir']): + if self.env['arch'] in self.env['crosstool_ng_supported_archs']: + if self.env['tests'] == []: + test_scripts_noext = [] + for f in os.listdir(self.env['baremetal_src_dir']): + base, ext = os.path.splitext(f) + if ext == '.py': + test_scripts_noext.append(base) + for root, dirs, files in os.walk(os.path.join(self.env['baremetal_src_dir'], 'arch', self.env['arch'])): + for f in files: base, ext = os.path.splitext(f) if ext == '.py': - test_scripts_noext.append(base) - for root, dirs, files in os.walk(os.path.join(self.env['baremetal_src_dir'], 'arch', self.env['arch'])): - for f in files: - base, ext = os.path.splitext(f) - if ext == '.py': - full_path = os.path.join(root, base) - relpath = os.path.relpath(full_path, self.env['baremetal_src_dir']) - test_scripts_noext.append(relpath) - else: - test_scripts_noext = self.env['tests'] - for test_script_noext in test_scripts_noext: - run_thread = threading.Thread(target=lambda: run( - archs=[self.env['arch']], - background=True, - baremetal=test_script_noext, - dry_run=self.env['dry_run'], - emulator=emulator, - wait_gdb=True - )) - run_thread.start() - run_gdb( - archs=[self.env['arch']], - baremetal=test_script_noext, - dry_run=self.env['dry_run'], - emulator=emulator, - test=True, - ) - run_thread.join() + full_path = os.path.join(root, base) + relpath = os.path.relpath(full_path, self.env['baremetal_src_dir']) + test_scripts_noext.append(relpath) + else: + test_scripts_noext = self.env['tests'] + for test_script_noext in test_scripts_noext: + run_thread = threading.Thread(target=lambda: run( + archs=[self.env['arch']], + background=True, + baremetal=test_script_noext, + dry_run=self.env['dry_run'], + emulators=self.env['emulator'], + wait_gdb=True + )) + run_thread.start() + run_gdb( + archs=[self.env['arch']], + baremetal=test_script_noext, + dry_run=self.env['dry_run'], + emulators=self.env['emulator'], + test=True, + ) + run_thread.join() if __name__ == '__main__': Main().cli() diff --git a/test-userland b/test-userland index ecca540c..b8b237ec 100755 --- a/test-userland +++ b/test-userland @@ -13,40 +13,39 @@ class Main(common.LkmcCliFunction): 'hello_cpp.cpp', 'print_argv.c', ] - for emulator in self.env['emulators']: - if emulator == 'gem5': - extra_args = { - 'userland_build_id': 'static', - } - else: - extra_args = {} - if self.env['arch'] == 'x86_64': - arch_sources = [ - 'asm_hello' - ] - elif self.env['arch'] == 'aarch64': - arch_sources = [ - 'asm_hello' - ] - else: - arch_sources = [] - arch_sources[:] = [os.path.join('arch', self.env['arch'], arch_source) for arch_source in arch_sources] - for source in sources + arch_sources: - exit_status = run( - archs=[self.env['arch']], - dry_run=self.env['dry_run'], - userland=source, - emulator=emulator, - **extra_args, - ) - # TODO forward all args attempt. In particular, --dry-run. - #new_env = self.env.copy() - #new_env['userland'] = source - #new_env['emulator'] = emulator - #new_env.update(extra_args) - #exit_status = run(**new_env) - if exit_status != 0: - raise Exception('Test failed: {} {} {} {}'.format(emulator, arch, source, exit_status)) + if self.env['emulator'] == 'gem5': + extra_args = { + 'userland_build_id': 'static', + } + else: + extra_args = {} + if self.env['arch'] == 'x86_64': + arch_sources = [ + 'asm_hello' + ] + elif self.env['arch'] == 'aarch64': + arch_sources = [ + 'asm_hello' + ] + else: + arch_sources = [] + arch_sources[:] = [os.path.join('arch', self.env['arch'], arch_source) for arch_source in arch_sources] + for source in sources + arch_sources: + exit_status = run( + archs=[self.env['arch']], + dry_run=self.env['dry_run'], + userland=source, + emulators=[self.env['emulator']], + **extra_args, + ) + # TODO forward all args attempt. In particular, --dry-run. + #new_env = self.env.copy() + #new_env['userland'] = source + #new_env['emulator'] = emulator + #new_env.update(extra_args) + #exit_status = run(**new_env) + if exit_status != 0: + raise Exception('Test failed: {} {} {} {}'.format(emulator, arch, source, exit_status)) if __name__ == '__main__': Main().cli()