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

Reply via email to