Commit ff8e7c4d by Aldy Hernandez Committed by Aldy Hernandez

gcc-simulate-thread.exp: New.

        * lib/gcc-simulate-thread.exp: New.
        * gcc.dg/simulate-thread/guality.h: New.
        * gcc.dg/simulate-thread/simulate-thread.h: New.
        * gcc.dg/simulate-thread/simulate-thread.exp: New.
        * gcc.dg/simulate-thread/simulate-thread.gdb: New.
        * gcc.dg/simulate-thread/README: New.
        * g++.dg/simulate-thread/guality.h: New.
        * g++.dg/simulate-thread/simulate-thread.h: New.
        * g++.dg/simulate-thread/simulate-thread.exp: New.
        * g++.dg/simulate-thread/simulate-thread.gdb: New.
        * c-c++-common/cxxbitfields-2.c: Remove.
        * c-c++-common/cxxbitfields.c: Remove.
        * c-c++-common/cxxbitfields-4.c: Remove.
        * c-c++-common/cxxbitfields-5.c: Remove.
        * c-c++-common/simulate-thread/bitfields-1.c: New.
        * c-c++-common/simulate-thread/bitfields-2.c: New.
        * c-c++-common/simulate-thread/bitfields-3.c: New.
        * c-c++-common/simulate-thread/bitfields-4.c: New.

From-SVN: r179751
parent 53fbb724
2011-10-10 Aldy Hernandez <aldyh@redhat.com>
* lib/gcc-simulate-thread.exp: New.
* gcc.dg/simulate-thread/guality.h: New.
* gcc.dg/simulate-thread/simulate-thread.h: New.
* gcc.dg/simulate-thread/simulate-thread.exp: New.
* gcc.dg/simulate-thread/simulate-thread.gdb: New.
* gcc.dg/simulate-thread/README: New.
* g++.dg/simulate-thread/guality.h: New.
* g++.dg/simulate-thread/simulate-thread.h: New.
* g++.dg/simulate-thread/simulate-thread.exp: New.
* g++.dg/simulate-thread/simulate-thread.gdb: New.
* c-c++-common/cxxbitfields-2.c: Remove.
* c-c++-common/cxxbitfields.c: Remove.
* c-c++-common/cxxbitfields-4.c: Remove.
* c-c++-common/cxxbitfields-5.c: Remove.
* c-c++-common/simulate-thread/bitfields-1.c: New.
* c-c++-common/simulate-thread/bitfields-2.c: New.
* c-c++-common/simulate-thread/bitfields-3.c: New.
* c-c++-common/simulate-thread/bitfields-4.c: New.
2011-10-09 Paolo Carlini <paolo.carlini@oracle.com> 2011-10-09 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/38980 PR c++/38980
......
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
/* { dg-options "-O2 --param allow-store-data-races=0" } */
/* Test that we don't store past VAR.K. */
struct S
{
volatile int i;
volatile int j: 32;
volatile int k: 15;
volatile char c[2];
} var;
void setit()
{
var.k = 13;
}
/* { dg-final { scan-assembler-not "movl.*, var" } } */
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
/* { dg-options "-O2 --param allow-store-data-races=0" } */
struct bits
{
char a;
int b:7;
int c:9;
unsigned char d;
} x;
/* Store into <c> should not clobber <d>. */
void update_c(struct bits *p, int val)
{
p -> c = val;
}
/* { dg-final { scan-assembler "mov\[bw\]" } } */
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
/* { dg-options "-O2 --param allow-store-data-races=0" } */
#include <stdlib.h>
struct bits
{
char a;
int b:7;
int c:9;
unsigned char d;
} x;
struct bits *p;
static void allocit()
{
p = (struct bits *) malloc (sizeof (struct bits));
}
/* Store into <c> should not clobber <d>. */
/* We should not use a 32-bit move to store into p->, but a smaller move. */
void foo()
{
allocit();
p -> c = 55;
}
/* { dg-final { scan-assembler "mov\[bw\]" } } */
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
/* { dg-options "-O2 --param allow-store-data-races=0" } */
/* Test that we don't store past VAR.A. */
struct S
{
volatile unsigned int a : 4;
unsigned char b;
unsigned int c : 6;
} var;
void set_a()
{
var.a = 12;
}
/* { dg-final { scan-assembler-not "movl.*, var" } } */
/* { dg-do link } */
/* { dg-options "--param allow-store-data-races=0" } */
/* { dg-final { simulate-thread } } */
#include <stdio.h>
#include "../../gcc.dg/simulate-thread/simulate-thread.h"
/* Test that we don't store past VAR.A. */
struct S
{
volatile unsigned int a : 4;
unsigned char b;
unsigned int c : 6;
} var = { 1, 2, 3 };
static int global = 0;
/* Called before each instruction, simulating another thread
executing. */
void simulate_thread_other_threads()
{
global++;
var.b = global;
/* Don't go past the 6 bits var.c can hold. */
var.c = global % 64;
}
/* Called after each instruction. Returns 1 if any inconsistency is
found, 0 otherwise. */
int simulate_thread_step_verify()
{
int ret = 0;
if (var.b != global)
{
printf("FAIL: invalid intermediate value for <b>.\n");
ret = 1;
}
if (var.c != global % 64)
{
printf("FAIL: invalid intermediate value for <c>.\n");
ret = 1;
}
return ret;
}
/* Called at the end of the program (simulate_thread_fini == 1). Verifies
the state of the program and returns 1 if any inconsistency is
found, 0 otherwise. */
int simulate_thread_final_verify()
{
if (var.a != 12)
{
printf("FAIL: invalid final result for <a>.\n");
return 1;
}
return 0;
}
__attribute__((noinline))
void simulate_thread_main()
{
var.a = 12;
}
int main()
{
simulate_thread_main();
simulate_thread_done();
return 0;
}
/* { dg-do link } */
/* { dg-options "--param allow-store-data-races=0" } */
/* { dg-final { simulate-thread } } */
#include <stdio.h>
#include "../../gcc.dg/simulate-thread/simulate-thread.h"
/* Test that we don't store past VAR.K. */
struct S
{
volatile int i;
volatile int j: 32;
volatile int k: 15;
volatile unsigned char c[2];
} var;
static int global = 0;
void simulate_thread_other_threads()
{
global++;
var.c[0] = global % 256;
var.c[1] = global % 256;
}
int simulate_thread_step_verify()
{
if (var.c[0] != global % 256
|| var.c[1] != global % 256)
{
printf("FAIL: invalid intermediate result for <var.c[]>.\n");
return 1;
}
return 0;
}
int simulate_thread_final_verify()
{
if (var.k != 13)
{
printf("FAIL: invalid final result\n");
return 1;
}
return 0;
}
__attribute__((noinline))
void simulate_thread_main()
{
var.k = 13;
}
int main()
{
simulate_thread_main();
simulate_thread_done();
return 0;
}
/* { dg-do link } */
/* { dg-options "--param allow-store-data-races=0" } */
/* { dg-final { simulate-thread } } */
#include <stdio.h>
#include "../../gcc.dg/simulate-thread/simulate-thread.h"
/* Store into <c> should not clobber <d>. */
struct bits
{
char a;
int b:7;
int c:9;
unsigned char d;
} var;
static int global = 0;
void simulate_thread_other_threads()
{
global++;
var.d = global;
}
int simulate_thread_step_verify()
{
if (var.d != global)
{
printf("FAIL: invalid intermediate result\n");
return 1;
}
return 0;
}
int simulate_thread_final_verify()
{
if (var.c != 5)
{
printf("FAIL: invalid final result\n");
return 1;
}
return 0;
}
__attribute__((noinline))
void update_c(struct bits *p, int val)
{
p -> c = val;
}
__attribute__((noinline))
void simulate_thread_main()
{
update_c(&var, 5);
}
int main()
{
simulate_thread_main();
simulate_thread_done();
return 0;
}
/* { dg-do link } */
/* { dg-options "--param allow-store-data-races=0" } */
/* { dg-final { simulate-thread } } */
#include <stdio.h>
#include <stdlib.h>
#include "../../gcc.dg/simulate-thread/simulate-thread.h"
struct bits
{
char a;
int b:7;
int c:9;
unsigned char d;
} *p;
static int global = 0;
void simulate_thread_other_threads()
{
global++;
p->d = global % 256;
}
int simulate_thread_step_verify()
{
if (p->d != global % 256)
{
printf("FAIL: invalid intermediate result\n");
return 1;
}
return 0;
}
int simulate_thread_final_verify()
{
if (p->c != 55)
{
printf("FAIL: invalid final result\n");
return 1;
}
return 0;
}
/* Store into <c> should not clobber <d>. */
/* We should not use a 32-bit move to store into p->, but a smaller move. */
__attribute__((noinline))
void simulate_thread_main()
{
p -> c = 55;
}
int main()
{
p = (struct bits *) calloc (1, sizeof (struct bits));
simulate_thread_main();
simulate_thread_done();
return 0;
}
# Copyright (C) 2011 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
# Your run of the mill dg test, but verify that we have a working GDB first.
load_lib g++-dg.exp
load_lib gcc-simulate-thread.exp
load_lib torture-options.exp
dg-init
torture-init
set-torture-options [list \
{ -O0 -g } \
{ -O1 -g } \
{ -O2 -g } \
{ -O3 -g } \
{ -Os -g } ]
if [gdb-exists] {
gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] ""
gcc-dg-runtest [lsort [glob $srcdir/c-c++-common/simulate-thread/*.c]] ""
}
torture-finish
dg-finish
source ../../gcc.dg/simulate-thread/simulate-thread.gdb
#include "../../gcc.dg/simulate-thread/simulate-thread.h"
OVERVIEW
--------
This is a harness to test the atomicity of certain operations, and to
make sure the compiler does not introduce data races in a
multi-threaded environment.
The basic premise is that we set up testcases such that the thing we
want test, say an atomic instruction which stores a double word is in
a function of its own. We then run this testcase within GDB,
controlled by a gdb script (simulate-thread.gdb). The gdb script will
break on the function to be tested, and then single step through every
machine instruction in the function. We set this up so GDB can make a
couple of inferior function calls before and after each of these
single step instructions for a couple of purposes:
1. One of the calls simulates another thread running in the
process which changes or access memory.
2. The other calls are used to verify that we always get the
expected behavior.
For example, in the case of an atomic store, anyone looking at the
memory associated with an atomic variable should never see any in
between states. If you have an atomic long long int, and it starts
with the value 0, and you write the value MAX_LONG_LONG, any other
thread looking at that variable should never see anything other than 0
or MAX_LONG_LONG. If you implement the atomic write as a sequence of
2 stores, it is possible for another thread to read the location after
the first store, but before the second one is complete. That thread
would then see an in-between state (one word would still be 0).
We simulate this in the testcase by having GDB step through the
program, instruction by instruction, and after each step, making an
inferior function call which looks at the value of the atomic variable
and verifies that it sees either 0 or MAX_LONG_LONG. If it sees any
other value, it fails the testcase.
This way, we are *sure* there is no in between state because we
effectively acted like an OS and switched to another thread after
every single instruction of the routine is executed and looked at the
results each time.
We use the same idea to test for data races to see if an illegal load
has been hoisted, or that two parallel bitfield writes don't overlap
in a data race.
Below is a skeleton of how a test should look like. For more details,
look at the tests themselves.
ANATOMY OF A TEST
-----------------
/* { dg-do link } */
/* { dg-options "-some-flags" } */
/* { dg-final { simulate-thread } } */
/* NOTE: Any failure must be indicated by displaying "FAIL:". */
#include "simulate-thread.h"
/* Called before each instruction, simulating another thread executing. */
void simulate_thread_other_threads()
{
}
/* Called after each instruction. Returns 1 if any inconsistency is
found, 0 otherwise. */
int simulate_thread_step_verify()
{
if (some_problem)
{
printf("FAIL: reason\n");
return 1;
}
return 0;
}
/* Called at the end of the program (simulate_thread_fini == 1). Verifies
the state of the program and returns 1 if any inconsistency is
found, 0 otherwise. */
int simulate_thread_final_verify()
{
if (some_problem)
{
printf("FAIL: reason\n");
return 1;
}
return 0;
}
/* The gdb script will break on simulate_thread_main(), so make sure
GCC does not inline it, thus making the break point fail. */
__attribute__((noinline))
void simulate_thread_main()
{
/* Do stuff. */
}
int main()
{
/* Perform any setup code that will run outside of the testing
harness. Put code here that you do NOT want to be interrupted on
an instruction basis. E.g., setup code, and system library
calls. */
/* Do un-instrumented stuff. */
/* ... */
/* Start the instrumented show. */
simulate_thread_main();
/* Must be called at the end of the test. */
simulate_thread_done();
return 0;
}
# Copyright (C) 2011 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
# Your run of the mill dg test, but verify that we have a working GDB first.
load_lib gcc-dg.exp
load_lib gcc-simulate-thread.exp
load_lib torture-options.exp
dg-init
torture-init
set-torture-options [list \
{ -O0 -g } \
{ -O1 -g } \
{ -O2 -g } \
{ -O3 -g } \
{ -Os -g } ]
if [gdb-exists] {
gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] ""
gcc-dg-runtest [lsort [glob $srcdir/c-c++-common/simulate-thread/*.c]] ""
}
torture-finish
dg-finish
set height 0
break simulate_thread_main
disp/i $pc
run
set $ret = 0
while (simulate_thread_fini != 1) && (! $ret)
call simulate_thread_other_threads()
stepi
set $ret |= simulate_thread_step_verify()
end
if (! $ret)
set $ret |= simulate_thread_final_verify()
end
continue
quit $ret
int simulate_thread_fini = 0;
void __attribute__((noinline))
simulate_thread_done ()
{
simulate_thread_fini = 1;
}
...@@ -747,4 +747,26 @@ proc dg-message { args } { ...@@ -747,4 +747,26 @@ proc dg-message { args } {
process-message saved-dg-warning "" $args process-message saved-dg-warning "" $args
} }
# Check the existence of a gdb in the path, and return true if there
# is one.
#
# Set env(GDB_FOR_GCC_TESTING) accordingly.
proc gdb-exists { args } {
if ![info exists ::env(GDB_FOR_GCC_TESTING)] {
global GDB
if ![info exists ::env(GDB_FOR_GCC_TESTING)] {
if [info exists GDB] {
setenv GDB_FOR_GCC_TESTING "$GDB"
} else {
setenv GDB_FOR_GCC_TESTING "[transform gdb]"
}
}
}
if { [which $::env(GDB_FOR_GCC_TESTING)] != 0 } {
return 1;
}
return 0;
}
set additional_prunes "" set additional_prunes ""
# Copyright (C) 2011 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
# Utility for running a given test through the simulate-thread harness
# using gdb. This is invoked via dg-final.
#
# Adapted from the guality harness.
#
# Call 'fail' if a given test printed "FAIL:", otherwise call 'pass'.
proc simulate-thread { args } {
if { ![isnative] || [is_remote target] } { return }
if { [llength $args] == 1 } {
switch [dg-process-target [lindex $args 0]] {
"F" { setup_xfail "*-*-*" }
}
}
# This assumes that we are three frames down from dg-test, and that
# it still stores the filename of the testcase in a local variable "name".
# A cleaner solution would require a new DejaGnu release.
upvar 2 name testcase
upvar 2 prog prog
upvar 2 srcdir testsuite_dir
set gdb_name $::env(GDB_FOR_GCC_TESTING)
set exec_file "[file rootname [file tail $prog]].exe"
set cmd_file "$testsuite_dir/gcc.dg/simulate-thread/simulate-thread.gdb"
if ![file exists $exec_file] {
return
}
send_log "Spawning: $gdb_name -nx -nw -quiet -x $cmd_file ./$exec_file\n"
set res [remote_spawn target "$gdb_name -nx -nw -x $cmd_file ./$exec_file"]
if { $res < 0 || $res == "" } {
unsupported "$testcase"
return
}
set gdb_worked 0
remote_expect target [timeout_value] {
# Too old GDB
-re "Unhandled dwarf expression|Error in sourced command file" {
unsupported "$testcase"
remote_close target
return
}
-re "FAIL:" {
fail "$testcase"
remote_close target
return
}
# If the gdb output contained simulate_thread_main, assume
# that at the very least, we had a working gdb that was able
# to break in simulate_thread_main.
-re "simulate_thread_main" {
set gdb_worked 1
exp_continue
}
timeout {
unsupported "$testcase"
remote_close target
return
}
}
remote_close target
if {$gdb_worked} {
pass "$testcase"
} else {
# Fail in the absence of a sane GDB.
fail "$testcase"
}
return
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment