I've been toying with the simulate-thread framework a bit. With the
timeout threshold is back to where it originally was, the spectre of
huge log files when an infinite loop happens is back.
This patch has the following modifications:
1) An instruction count threshold has been added.
This will truly prevent infinite loops which were the original
issue. If the count reaches the threshold value, the test case fails.
I've set the current limit to 10,000, which I would expect to be
sufficient. The highest Ive seen so far is 877, but I'm sure someone
will find something higher :-). A testcase can override this value if
it wishes. If we encounter targets where this is not high enough, we
can raise it further. The main point is to avoid generating 10's of
gigabytes of log files when a fast machine hits an infinite loop and the
time based timeout doesnt kick in quickly enough.
If a test does fail for this reason, the log file will issue a FAIL
message indicating the instruction count threshold was exceeded.
2) I tweaked the atomic-load-int128.c testcase to avoid an inadvertent
hostile thread situation that was not intended.
3) The speculative-store.c testcase had a bug in it where the verify
function was not returning a value when successful. ThIs resulted in an
UNSUPPORTED result occasionally because the testcase didn't run far
enough to indicate GDB had successfully run, yet no FAIL was issued.
4) I lowered the hostile thread threshold by an order of magnitude so
that if a hostile thread is encountered frequently, it wont rapidly
approach the new instruction count threshold. This could happen on
compare_and_swap loop targets.
I've tried this on x86_64-unknown-linux-gnu and everything seems good.
If anyone wants to try it on their more stressed arch, all you need to
do is apply the patch to the testsuite and run 'make check-gcc
RUNTESTFLAGS=simulate-thread.exp' to see if the results are as expected.
OK for mainline?
Andrew
* gcc.dg/simulate-thread/simulate-thread.gdb: Use return value from
simulate_thread_wrapper_other_threads
* gcc.dg/simulate-thread/atomic-load-int128.c (simulate_thread_main):
Move initialization of 'value' to main().
(main): Initialize 'value';
* gcc.dg/simulate-thread/speculative-store.c
(simulate_thread_step_verify): Return 0 when successful.
* gcc.dg/simulate-thread/simulate-thread.h (HOSTILE_THREAD_THRESHOLD):
Reduce threshold.
(INSN_COUNT_THRESHOLD): New. Instruction limit to terminate test.
(simulate_thread_wrapper_other_threads): Return a success/fail value
and issue an error if the instruction count threshold is exceeded.
Index: testsuite/gcc.dg/simulate-thread/simulate-thread.gdb
===================================================================
*** testsuite/gcc.dg/simulate-thread/simulate-thread.gdb (revision 184447)
--- testsuite/gcc.dg/simulate-thread/simulate-thread.gdb (working copy)
*************** run
*** 5,11 ****
set $ret = 0
while (simulate_thread_fini != 1) && (! $ret)
! call simulate_thread_wrapper_other_threads()
stepi
set $ret |= simulate_thread_step_verify()
end
--- 5,11 ----
set $ret = 0
while (simulate_thread_fini != 1) && (! $ret)
! set $ret |= simulate_thread_wrapper_other_threads()
stepi
set $ret |= simulate_thread_step_verify()
end
Index: testsuite/gcc.dg/simulate-thread/atomic-load-int128.c
===================================================================
*** testsuite/gcc.dg/simulate-thread/atomic-load-int128.c (revision 184447)
--- testsuite/gcc.dg/simulate-thread/atomic-load-int128.c (working copy)
*************** void simulate_thread_main()
*** 105,113 ****
{
int x;
- /* Make sure value starts with an atomic value now. */
- __atomic_store_n (&value, ret, __ATOMIC_SEQ_CST);
-
/* Execute loads with value changing at various cyclic values. */
for (table_cycle_size = 16; table_cycle_size > 4 ; table_cycle_size--)
{
--- 105,110 ----
*************** void simulate_thread_main()
*** 126,131 ****
--- 123,132 ----
main()
{
fill_table ();
+
+ /* Make sure value starts with an atomic value from the table. */
+ __atomic_store_n (&value, table[0], __ATOMIC_SEQ_CST);
+
simulate_thread_main ();
simulate_thread_done ();
return 0;
Index: testsuite/gcc.dg/simulate-thread/speculative-store.c
===================================================================
*** testsuite/gcc.dg/simulate-thread/speculative-store.c (revision 184447)
--- testsuite/gcc.dg/simulate-thread/speculative-store.c (working copy)
*************** int simulate_thread_step_verify()
*** 24,29 ****
--- 24,30 ----
printf("FAIL: global variable was assigned to. \n");
return 1;
}
+ return 0;
}
int simulate_thread_final_verify()
Index: testsuite/gcc.dg/simulate-thread/simulate-thread.h
===================================================================
*** testsuite/gcc.dg/simulate-thread/simulate-thread.h (revision 184447)
--- testsuite/gcc.dg/simulate-thread/simulate-thread.h (working copy)
*************** simulate_thread_done ()
*** 37,43 ****
infinite loop to be avoided.
If the testcase defines HOSTILE_PAUSE_ERROR, then it will be
! considered an RUNTIME FAILURE if the hostile pause is triggered.
This will allow to test for guaranteed forward progress routines.
If the default values for HOSTILE_THREAD_THRESHOLD or
--- 37,43 ----
infinite loop to be avoided.
If the testcase defines HOSTILE_PAUSE_ERROR, then it will be
! considered a RUNTIME FAILURE if the hostile pause is triggered.
This will allow to test for guaranteed forward progress routines.
If the default values for HOSTILE_THREAD_THRESHOLD or
*************** simulate_thread_done ()
*** 50,66 ****
hostile condition is interferring. */
! /* Define the threshold to start pausing the hostile thread. */
#if !defined (HOSTILE_THREAD_THRESHOLD)
! #define HOSTILE_THREAD_THRESHOLD 500
#endif
/* Define the length of pause in cycles for the hostile thread to pause to
! allow forward progress to be made. */
#if !defined (HOSTILE_THREAD_PAUSE)
#define HOSTILE_THREAD_PAUSE 20
#endif
void simulate_thread_other_threads (void);
int simulate_thread_final_verify (void);
--- 50,78 ----
hostile condition is interferring. */
! /* Define the threshold instruction count to start pausing the hostile
! thread. To avoid huge potential log files when things are not going well,
! set this number very low. If a test specifically requires that the forward
! progress guarantee is made, this number should be raised by the testcase. */
#if !defined (HOSTILE_THREAD_THRESHOLD)
! #define HOSTILE_THREAD_THRESHOLD 50
#endif
/* Define the length of pause in cycles for the hostile thread to pause to
! allow forward progress to be made. If this number is too low, a
! compare_and_swap loop may not have time to finish, especially on a
! 128 bit operation. */
#if !defined (HOSTILE_THREAD_PAUSE)
#define HOSTILE_THREAD_PAUSE 20
#endif
+ /* Define the number of instructions which are allowed to be executed before
+ the testcase is deemed to fail. This is primarily to avoid huge log files
+ when a testcase goes into an infinte loop. */
+ #if !defined (INSN_COUNT_THRESHOLD)
+ #define INSN_COUNT_THRESHOLD 10000
+ #endif
+
void simulate_thread_other_threads (void);
int simulate_thread_final_verify (void);
*************** static int simulate_thread_hostile_pause
*** 71,96 ****
is reached, the other_thread process is paused for
HOSTILE_THREAD_PAUSE cycles before resuming, and the counters start
again. */
! void
simulate_thread_wrapper_other_threads()
{
! static int count = 0;
! static int pause = 0;
! if (++count >= HOSTILE_THREAD_THRESHOLD)
{
if (!simulate_thread_hostile_pause)
simulate_thread_hostile_pause = 1;
/* Count cycles before calling the hostile thread again. */
! if (pause++ < HOSTILE_THREAD_PAUSE)
! return;
/* Reset the pause counter, as well as the thread counter. */
! pause = 0;
! count = 0;
}
simulate_thread_other_threads ();
}
--- 83,116 ----
is reached, the other_thread process is paused for
HOSTILE_THREAD_PAUSE cycles before resuming, and the counters start
again. */
! int
simulate_thread_wrapper_other_threads()
{
! static int insn_count = 0;
! static int hostile_count = 0;
! static int hostile_pause = 0;
!
! if (++insn_count >= INSN_COUNT_THRESHOLD)
! {
! printf ("FAIL: Testcase exceeded maximum instruction count threshold\n");
! return 1;
! }
! if (++hostile_count >= HOSTILE_THREAD_THRESHOLD)
{
if (!simulate_thread_hostile_pause)
simulate_thread_hostile_pause = 1;
/* Count cycles before calling the hostile thread again. */
! if (hostile_pause++ < HOSTILE_THREAD_PAUSE)
! return 0;
/* Reset the pause counter, as well as the thread counter. */
! hostile_pause = 0;
! hostile_count = 0;
}
simulate_thread_other_threads ();
+ return 0;
}