gem5: centralize information on simulate() time reached

This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-07-25 00:00:00 +00:00
parent 402059ed22
commit 6f5a1a3e83
6 changed files with 138 additions and 10 deletions

View File

@@ -10009,6 +10009,12 @@ Breakdown:
* `25007500`: time count in some unit. Note how the microops execute at further timestamps. * `25007500`: time count in some unit. Note how the microops execute at further timestamps.
* `system.cpu`: distinguishes between CPUs when there are more than one. For example, running xref:arm-multicore[xrefstyle=full] with two cores produces `system.cpu0` and `system.cpu1` * `system.cpu`: distinguishes between CPUs when there are more than one. For example, running xref:arm-multicore[xrefstyle=full] with two cores produces `system.cpu0` and `system.cpu1`
* `T0`: thread number. TODO: https://superuser.com/questions/133082/hyper-threading-and-dual-core-whats-the-difference/995858#995858[hyperthread]? How to play with it? * `T0`: thread number. TODO: https://superuser.com/questions/133082/hyper-threading-and-dual-core-whats-the-difference/995858#995858[hyperthread]? How to play with it?
+
`config`.ini has `--param 'system.multi_thread = True' --param 'system.cpu[0].numThreads = 2'`, but in <<arm-multicore>> the first one alone does not produce `T1`, and with the second one simulation blows up with:
+
....
fatal: fatal condition interrupts.size() != numThreads occurred: CPU system.cpu has 1 interrupt controllers, but is expecting one per thread (2)
....
* `@start_kernel`: we are in the `start_kernel` function. Awesome feature! Implemented with libelf https://sourceforge.net/projects/elftoolchain/ copy pasted in-tree `ext/libelf`. To get raw addresses, remove the `ExecSymbol`, which is enabled by `Exec`. This can be done with `Exec,-ExecSymbol`. * `@start_kernel`: we are in the `start_kernel` function. Awesome feature! Implemented with libelf https://sourceforge.net/projects/elftoolchain/ copy pasted in-tree `ext/libelf`. To get raw addresses, remove the `ExecSymbol`, which is enabled by `Exec`. This can be done with `Exec,-ExecSymbol`.
* `.1` as in `@start_kernel.1`: index of the microop * `.1` as in `@start_kernel.1`: index of the microop
* `stp`: instruction disassembly. Note however that the disassembly of many instructions are very broken as of 2019q2, and you can't just trust them blindly. * `stp`: instruction disassembly. Note however that the disassembly of many instructions are very broken as of 2019q2, and you can't just trust them blindly.
@@ -10387,6 +10393,8 @@ Exiting @ tick 18446744073709551615 because simulate() limit reached
See bug report at: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/81 See bug report at: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/81
Related: <<gem5-simulate-limit-reached>>.
====== gem5 ARM full system with more than 8 cores ====== gem5 ARM full system with more than 8 cores
https://stackoverflow.com/questions/50248067/how-to-run-a-gem5-arm-aarch64-full-system-simulation-with-fs-py-with-more-than-8 https://stackoverflow.com/questions/50248067/how-to-run-a-gem5-arm-aarch64-full-system-simulation-with-fs-py-with-more-than-8
@@ -11755,6 +11763,93 @@ Running the larger regression tests is exposed with:
but TODO: those require magic blobs on `M5_PATH` that we don't currently automate. but TODO: those require magic blobs on `M5_PATH` that we don't currently automate.
=== gem5 simulate() limit reached
This error happens when the following instruction limits are reached:
....
system.cpu[0].max_insts_all_threads
system.cpu[0].max_insts_any_thread
....
If the parameter is not set, it defaults to `0`, which is magic and means the huge maximum value of `uint64_t`: 0xFFFFFFFFFFFFFFFF, which in practice would require a very long simulation if at least one CPU were live.
So this usually means all CPUs are in a sleep state, and no events are scheduled in the future, which usually indicates a bug in either gem5 or guest code, leading gem5 to blow up.
Still, fs.py at gem5 08c79a194d1a3430801c04f37d13216cc9ec1da3 does not exit with non-zero status due to this... and so we just parse it out just as for <<m5-fail>>...
A trivial and very direct way to see message would be:
....
./run \
--emulator gem5 \
--static \
--userland \userland/arch/x86_64/freestanding/linux/hello.S \
--trace-insts-stdout \
-- \
--param 'system.cpu[0].max_insts_all_threads = 3' \
;
....
which as of lkmc 402059ed22432bb351d42eb10900e5a8e06aa623 runs only the first three instructions and quits!
....
info: Entering event queue @ 0. Starting simulation...
0: system.cpu A0 T0 : @asm_main_after_prologue : mov rdi, 0x1
0: system.cpu A0 T0 : @asm_main_after_prologue.0 : MOV_R_I : limm rax, 0x1 : IntAlu : D=0x0000000000000001 flags=(IsInteger|IsMicroop|IsLastMicroop|IsFirstMicroop)
1000: system.cpu A0 T0 : @asm_main_after_prologue+7 : mov rdi, 0x1
1000: system.cpu A0 T0 : @asm_main_after_prologue+7.0 : MOV_R_I : limm rdi, 0x1 : IntAlu : D=0x0000000000000001 flags=(IsInteger|IsMicroop|IsLastMicroop|IsFirstMicroop)
2000: system.cpu A0 T0 : @asm_main_after_prologue+14 : lea rsi, DS:[rip + 0x19]
2000: system.cpu A0 T0 : @asm_main_after_prologue+14.0 : LEA_R_P : rdip t7, %ctrl153, : IntAlu : D=0x000000000040008d flags=(IsInteger|IsMicroop|IsDelayedCommit|IsFirstMicroop)
2500: system.cpu A0 T0 : @asm_main_after_prologue+14.1 : LEA_R_P : lea rsi, DS:[t7 + 0x19] : IntAlu : D=0x00000000004000a6 flags=(IsInteger|IsMicroop|IsLastMicroop)
Exiting @ tick 3000 because all threads reached the max instruction count
....
The message also shows on <<user-mode-simulation>> deadlocks, for example in link:userland/posix/pthread_deadlock.c[]:
....
./run \
--emulator gem5 \
--static \
--userland userland/posix/pthread_deadlock.c \
--userland-args 1 \
;
....
ends in:
....
Exiting @ tick 18446744073709551615 because simulate() limit reached
....
where 18446744073709551615 is 0xFFFFFFFFFFFFFFFF in decimal.
And there is a <<baremetal>> example at link:baremetal/arch/aarch64/no_bootloader/wfe_loop.S[] that dies on <<arm-wfe-and-sev-instructions,WFE>>:
....
./run \
--arch aarch64 \
--baremetal baremetal/arch/aarch64/no_bootloader/wfe_loop.S \
--emulator gem5 \
--trace-insts-stdout \
;
....
which gives:
....
info: Entering event queue @ 0. Starting simulation...
0: system.cpu A0 T0 : @lkmc_start : wfe : IntAlu : D=0x0000000000000000 flags=(IsSerializeAfter|IsNonSpeculative|IsQuiesce|IsUnverifiable)
1000: system.cpu A0 T0 : @lkmc_start+4 : b <lkmc_start> : IntAlu : flags=(IsControl|IsDirectControl|IsUncondControl)
1500: system.cpu A0 T0 : @lkmc_start : wfe : IntAlu : D=0x0000000000000000 flags=(IsSerializeAfter|IsNonSpeculative|IsQuiesce|IsUnverifiable)
Exiting @ tick 18446744073709551615 because simulate() limit reached
....
Other examples of the message:
* <<arm-multicore>> with a single CPU stays stopped at an WFE sleep instruction
* this sample bug on se.py multithreading: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/81
=== gem5 build options === gem5 build options
In order to use different build options, you might also want to use <<gem5-build-variants>> to keep the build outputs separate from one another. In order to use different build options, you might also want to use <<gem5-build-variants>> to keep the build outputs separate from one another.
@@ -12307,6 +12402,7 @@ These links provide a clear overview of what POSIX is:
POSIX' multithreading API. This was for a looong time the only "portable" multithreading alternative, until <<cpp-multithreading,C++11 finally added threads>>, thus also extending the portability to Windows. POSIX' multithreading API. This was for a looong time the only "portable" multithreading alternative, until <<cpp-multithreading,C++11 finally added threads>>, thus also extending the portability to Windows.
* link:userland/posix/pthread_count.c[] * link:userland/posix/pthread_count.c[]
* link:userland/posix/pthread_deadlock.c[]
* link:userland/posix/pthread_self.c[] * link:userland/posix/pthread_self.c[]
==== sysconf ==== sysconf
@@ -14847,6 +14943,8 @@ This is specially interesting for the executables that don't use the bootloader
The cool thing about those examples is that you start at the very first instruction of your program, which gives more control. The cool thing about those examples is that you start at the very first instruction of your program, which gives more control.
Examples without bootloader are somewhat analogous to user mode <<freestanding-programs>>.
=== Baremetal bootloaders === Baremetal bootloaders
As can be seen from <<baremetal-gdb-step-debug>>, all examples under link:baremetal/[], with the exception of `baremetal/arch/<arch>/no_bootloader`, start from our tiny bootloaders: As can be seen from <<baremetal-gdb-step-debug>>, all examples under link:baremetal/[], with the exception of `baremetal/arch/<arch>/no_bootloader`, start from our tiny bootloaders:
@@ -15432,13 +15530,7 @@ Note that if you try the same thing on gem5:
./run --arch aarch64 --baremetal baremetal/arch/aarch64/multicore.S --cpus 1 --emulator gem5 ./run --arch aarch64 --baremetal baremetal/arch/aarch64/multicore.S --cpus 1 --emulator gem5
.... ....
then the gem5 actually exits, but with a different message: then the gem5 actually exits with <<gem5-simulate-limit-reached>> as opposed to the expected:
....
Exiting @ tick 18446744073709551615 because simulate() limit reached
....
as opposed to the expected:
.... ....
Exiting @ tick 36500 because m5_exit instruction encountered Exiting @ tick 36500 because m5_exit instruction encountered

View File

@@ -0,0 +1,8 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#gem5-simulate-time-reached */
#include <lkmc/m5ops.h>
.global lkmc_start
lkmc_start:
wfe
b lkmc_start

View File

@@ -1,7 +1,5 @@
#ifndef LKMC_BOARD_H #ifndef LKMC_BOARD_H
#define LKMC_BOARD_H #define LKMC_BOARD_H
#if LKMC_GEM5
#else
# define QEMU_VIRT_GIC_BASE (0x08000000) # define QEMU_VIRT_GIC_BASE (0x08000000)
# define QEMU_VIRT_GIC_INT_MAX (64) # define QEMU_VIRT_GIC_INT_MAX (64)
# define QEMU_VIRT_GIC_PRIO_MAX (16) # define QEMU_VIRT_GIC_PRIO_MAX (16)
@@ -21,4 +19,3 @@
# define GIC_PRI_MASK (0x0f) # define GIC_PRI_MASK (0x0f)
# define TIMER_IRQ (27) /** Timer IRQ */ # define TIMER_IRQ (27) /** Timer IRQ */
#endif #endif
#endif

View File

@@ -306,6 +306,7 @@ path_properties_tuples = (
{ {
'gem5_exit.S': {'requires_m5ops': True}, 'gem5_exit.S': {'requires_m5ops': True},
'semihost_exit.S': {'requires_semihosting': True}, 'semihost_exit.S': {'requires_semihosting': True},
'wfe_loop.S': {'more_than_1s': True},
} }
), ),
'return1.S': {'exit_status': 1}, 'return1.S': {'exit_status': 1},

View File

@@ -1,4 +1,6 @@
/* count to infinity in n threads. /* count to infinity in n threads.
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#pthreads
* *
* Useful if you need to keep several threads around * Useful if you need to keep several threads around
* to test something. * to test something.

View File

@@ -0,0 +1,28 @@
/* Let's see a trivial deadlock in action to feel the joys of multithreading.
*
* https://github.com/cirosantilli/linux-kernel-module-cheat#pthreads
*
* Exit successfully immediately without any arguments:
*
* ./pthread_deadlock.out
*
* Hang forever in a deadlock if one argument is given:
*
* ....
* ./pthread_deadlock.out 0
* ....
*/
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
int main(int argc, char **argv) {
pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
(void)(argv);
pthread_mutex_lock(&main_thread_mutex);
if (argc > 1)
pthread_mutex_lock(&main_thread_mutex);
return EXIT_SUCCESS;
}