URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [gcc/] [testsuite/] [gcc.dg/] [simulate-thread/] [README] - Rev 689
Compare with Previous | Blame | View Log
OVERVIEW--------This is a harness to test the atomicity of certain operations, and tomake sure the compiler does not introduce data races in amulti-threaded environment.The basic premise is that we set up testcases such that the thing wewant test, say an atomic instruction which stores a double word is ina function of its own. We then run this testcase within GDB,controlled by a gdb script (simulate-thread.gdb). The gdb script willbreak on the function to be tested, and then single step through everymachine instruction in the function. We set this up so GDB can make acouple of inferior function calls before and after each of thesesingle step instructions for a couple of purposes:1. One of the calls simulates another thread running in theprocess which changes or access memory.2. The other calls are used to verify that we always get theexpected behavior.For example, in the case of an atomic store, anyone looking at thememory associated with an atomic variable should never see any inbetween states. If you have an atomic long long int, and it startswith the value 0, and you write the value MAX_LONG_LONG, any otherthread looking at that variable should never see anything other than 0or MAX_LONG_LONG. If you implement the atomic write as a sequence of2 stores, it is possible for another thread to read the location afterthe first store, but before the second one is complete. That threadwould then see an in-between state (one word would still be 0).We simulate this in the testcase by having GDB step through theprogram, instruction by instruction, and after each step, making aninferior function call which looks at the value of the atomic variableand verifies that it sees either 0 or MAX_LONG_LONG. If it sees anyother value, it fails the testcase.This way, we are *sure* there is no in between state because weeffectively acted like an OS and switched to another thread afterevery single instruction of the routine is executed and looked at theresults each time.We use the same idea to test for data races to see if an illegal loadhas been hoisted, or that two parallel bitfield writes don't overlapin 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 isfound, 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). Verifiesthe state of the program and returns 1 if any inconsistency isfound, 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 sureGCC 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 testingharness. Put code here that you do NOT want to be interrupted onan instruction basis. E.g., setup code, and system librarycalls. *//* 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;}
