Skip to content

Commit

Permalink
i#95 Linux detach, i#1512 startstop flaky: rewrite api.startstop test
Browse files Browse the repository at this point in the history
Rewrites the api.startstop test to use condition variables for more
reliable and extensive testing of native<->DR transitions.  Adds condition
variable support for Linux and Windows to the tests.  Removes the FLAKY
marker from the test.

Fixes #1512

Review-URL: https://codereview.appspot.com/310160043
  • Loading branch information
derekbruening committed Sep 8, 2016
1 parent a6a062d commit ba6e2e6
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 65 deletions.
5 changes: 2 additions & 3 deletions suite/tests/CMakeLists.txt
Expand Up @@ -584,7 +584,7 @@ function(add_exe test source)
target_link_libraries(${test} ${libmath} ${libdl})
endif ()
if (NOT ANDROID) # pthreads is inside Bionic on Android
if ("${test}" MATCHES "^pthread|api.startstop_FLAKY")
if ("${test}" MATCHES "^pthread|api.startstop")
target_link_libraries(${test} ${libpthread})
endif ()
endif ()
Expand Down Expand Up @@ -2176,8 +2176,7 @@ if (CLIENT_INTERFACE)
endif (ARM)

if (NOT ARM) # FIXME i#1551: fix bugs on ARM
# XXX i#1512: api.startstop is FLAKY
tobuild_api(api.startstop_FLAKY api/startstop.c "" "" OFF OFF)
tobuild_api(api.startstop api/startstop.c "" "" OFF OFF)
endif (NOT ARM)
if (X86) # FIXME i#1551, i#1569: port to ARM and AArch64
# test static decoder library
Expand Down
200 changes: 139 additions & 61 deletions suite/tests/api/startstop.c
Expand Up @@ -34,21 +34,33 @@
#include <assert.h>
#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include "configure.h"
#include "dr_api.h"
#include "tools.h"
#include "condvar.h"
#ifdef WINDOWS
# include <windows.h>
#else
# include <pthread.h>
# include <sched.h>
#endif

#define ITERS 150000
#define VERBOSE 0

#define NUM_THREADS 10
#define START_STOP_ITERS 10
#define COMPUTE_ITERS 150000

#if VERBOSE
# define VPRINT(...) print(__VA_ARGS__)
#else
# define VPRINT(...) /* nothing */
#endif

/* We have event bb look for this to make sure we're instrumenting the sideline
* thread.
*/
/* We could generate this via macros but that gets pretty obtuse */
NOINLINE void func_0(void) { }
NOINLINE void func_1(void) { }
NOINLINE void func_2(void) { }
Expand All @@ -60,32 +72,27 @@ NOINLINE void func_7(void) { }
NOINLINE void func_8(void) { }
NOINLINE void func_9(void) { }

static bool took_over_thread[10];
typedef void (*void_func_t)(void);
static bool took_over_thread[NUM_THREADS];
static void_func_t funcs[NUM_THREADS];

static dr_emit_flags_t
event_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace,
bool translating)
{
int i;
app_pc pc = instr_get_app_pc(instrlist_first(bb));
if (pc == (app_pc)&func_0) took_over_thread[0] = true;
if (pc == (app_pc)&func_1) took_over_thread[1] = true;
if (pc == (app_pc)&func_2) took_over_thread[2] = true;
if (pc == (app_pc)&func_3) took_over_thread[3] = true;
if (pc == (app_pc)&func_4) took_over_thread[4] = true;
if (pc == (app_pc)&func_5) took_over_thread[5] = true;
if (pc == (app_pc)&func_6) took_over_thread[6] = true;
if (pc == (app_pc)&func_7) took_over_thread[7] = true;
if (pc == (app_pc)&func_8) took_over_thread[8] = true;
if (pc == (app_pc)&func_9) took_over_thread[9] = true;
for (i = 0; i < NUM_THREADS; i++) {
if (pc == (app_pc)funcs[i])
took_over_thread[i] = true;
}
return DR_EMIT_DEFAULT;
}

/* This is a thread that spins and calls sideline_func. It will call
* sideline_func at least once before returning and joining the parent.
* We cannot use pthread_cond_var here as we need to keep spinning
* until DR takes over and hits event_bb.
*/
static volatile bool should_spin = true;
static volatile bool sideline_exit = false;
static void *sideline_continue;
static void *go_native;
static void *sideline_ready[NUM_THREADS];

#ifdef WINDOWS
int __stdcall
Expand All @@ -94,15 +101,44 @@ void *
#endif
sideline_spinner(void *arg)
{
void (*sideline_func)(void) = (void (*)(void))arg;
do {
sideline_func();
#ifdef WINDOWS
Sleep(0);
#else
sched_yield();
#endif
} while (should_spin);
unsigned int idx = (unsigned int)(uintptr_t)arg;
void_func_t sideline_func = funcs[idx];
if (dr_app_running_under_dynamorio())
print("ERROR: thread %d should NOT be under DynamoRIO\n", idx);
VPRINT("%d signaling sideline_ready\n", idx);
signal_cond_var(sideline_ready[idx]);

VPRINT("%d waiting for continue\n", idx);
wait_cond_var(sideline_continue);
sideline_func();
VPRINT("%d signaling sideline_ready\n", idx);
signal_cond_var(sideline_ready[idx]);

VPRINT("%d waiting for native\n", idx);
wait_cond_var(go_native);
VPRINT("%d signaling sideline_ready\n", idx);
signal_cond_var(sideline_ready[idx]);

while (!sideline_exit) {
VPRINT("%d waiting for continue\n", idx);
wait_cond_var(sideline_continue);
if (sideline_exit)
break;

if (!dr_app_running_under_dynamorio())
print("ERROR: thread %d should be under DynamoRIO\n", idx);
VPRINT("%d signaling sideline_ready\n", idx);
signal_cond_var(sideline_ready[idx]);

VPRINT("%d waiting for native\n", idx);
wait_cond_var(go_native);
if (dr_app_running_under_dynamorio())
print("ERROR: thread %d should NOT be under DynamoRIO\n", idx);
VPRINT("%d signaling sideline_ready\n", idx);
signal_cond_var(sideline_ready[idx]);
}
VPRINT("%d exiting\n", idx);

#ifdef WINDOWS
return 0;
#else
Expand All @@ -120,50 +156,78 @@ int main(void)
int i,j;
void *stack = NULL;
#ifdef UNIX
pthread_t pt[10]; /* On Linux, the tid. */
pthread_t pt[NUM_THREADS]; /* On Linux, the tid. */
#else
uintptr_t thread[10]; /* _beginthreadex doesn't return HANDLE? */
uint tid[10];
uintptr_t thread[NUM_THREADS]; /* _beginthreadex doesn't return HANDLE? */
uint tid[NUM_THREADS];
#endif

/* Create spinning sideline threads. */
/* We could generate this via macros but that gets pretty obtuse */
funcs[0] = &func_0;
funcs[1] = &func_1;
funcs[2] = &func_2;
funcs[3] = &func_3;
funcs[4] = &func_4;
funcs[5] = &func_5;
funcs[6] = &func_6;
funcs[7] = &func_7;
funcs[8] = &func_8;
funcs[9] = &func_9;

sideline_continue = create_cond_var();
go_native = create_cond_var();

for (i = 0; i < NUM_THREADS; i++) {
sideline_ready[i] = create_cond_var();
#ifdef UNIX
pthread_create(&pt[0], NULL, sideline_spinner, (void*)func_0);
pthread_create(&pt[1], NULL, sideline_spinner, (void*)func_1);
pthread_create(&pt[2], NULL, sideline_spinner, (void*)func_2);
pthread_create(&pt[3], NULL, sideline_spinner, (void*)func_3);
pthread_create(&pt[4], NULL, sideline_spinner, (void*)func_4);
pthread_create(&pt[5], NULL, sideline_spinner, (void*)func_5);
pthread_create(&pt[6], NULL, sideline_spinner, (void*)func_6);
pthread_create(&pt[7], NULL, sideline_spinner, (void*)func_7);
pthread_create(&pt[8], NULL, sideline_spinner, (void*)func_8);
pthread_create(&pt[9], NULL, sideline_spinner, (void*)func_9);
pthread_create(&pt[i], NULL, sideline_spinner, (void*)(uintptr_t)i);
#else
thread[0] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_0, 0, &tid[0]);
thread[1] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_1, 0, &tid[1]);
thread[2] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_2, 0, &tid[2]);
thread[3] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_3, 0, &tid[3]);
thread[4] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_4, 0, &tid[4]);
thread[5] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_5, 0, &tid[5]);
thread[6] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_6, 0, &tid[6]);
thread[7] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_7, 0, &tid[7]);
thread[8] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_8, 0, &tid[8]);
thread[9] = _beginthreadex(NULL, 0, sideline_spinner, (void*)func_9, 0, &tid[9]);
thread[i] = _beginthreadex(NULL, 0, sideline_spinner, (void*)(uintptr_t)i,
0, &tid[i]);
#endif
}

/* Initialized DR */
dr_app_setup();
/* XXX: Calling the client interface from the app is not supported. We're
* just using it for testing.
*/
dr_register_bb_event(event_bb);

for (j=0; j<10; j++) {
/* Wait for all the threads to be scheduled */
VPRINT("waiting for ready\n");
for (i=0; i<NUM_THREADS; i++) {
wait_cond_var(sideline_ready[i]);
reset_cond_var(sideline_ready[i]);
}
/* Now get each thread to call its func_N under DR */
dr_app_start();
VPRINT("signaling continue\n");
signal_cond_var(sideline_continue);
VPRINT("waiting for ready\n");
for (i=0; i<NUM_THREADS; i++) {
wait_cond_var(sideline_ready[i]);
reset_cond_var(sideline_ready[i]);
}
reset_cond_var(sideline_continue);
dr_app_stop();
VPRINT("signaling native\n");
signal_cond_var(go_native);

for (j=0; j<START_STOP_ITERS; j++) {
for (i=0; i<NUM_THREADS; i++) {
wait_cond_var(sideline_ready[i]);
reset_cond_var(sideline_ready[i]);
}
reset_cond_var(go_native);
if (dr_app_running_under_dynamorio())
print("ERROR: should not be under DynamoRIO before dr_app_start!\n");
dr_app_start();
if (!dr_app_running_under_dynamorio())
print("ERROR: should be under DynamoRIO after dr_app_start!\n");
for (i=0; i<ITERS; i++) {
VPRINT("loop %d signaling continue\n", j);
signal_cond_var(sideline_continue);
for (i=0; i<COMPUTE_ITERS; i++) {
if (i % 2 == 0) {
res += cos(1./(double)(i+1));
} else {
Expand All @@ -173,24 +237,33 @@ int main(void)
foo();
if (!dr_app_running_under_dynamorio())
print("ERROR: should be under DynamoRIO before dr_app_stop!\n");
/* FIXME i#95: On Linux dr_app_stop only makes the current thread run
* native. We should revisit this while implementing full detach for
* Linux.
*/
for (i=0; i<NUM_THREADS; i++) {
wait_cond_var(sideline_ready[i]);
reset_cond_var(sideline_ready[i]);
}
reset_cond_var(sideline_continue);
dr_app_stop();
if (dr_app_running_under_dynamorio())
print("ERROR: should not be under DynamoRIO after dr_app_stop!\n");
VPRINT("loop %d signaling native\n", j);
signal_cond_var(go_native);
}
/* PR : we get different floating point results on different platforms,
* so we no longer print out res */
print("all done: %d iters\n", i);
print("all done: %d iters\n", j);
for (i=0; i<NUM_THREADS; i++) {
wait_cond_var(sideline_ready[i]);
reset_cond_var(sideline_ready[i]);
}
reset_cond_var(go_native);

/* On x64 Linux it's OK if we call pthread_join natively, but x86-32 has
* problems. We start and stop to bracket it.
*/
dr_app_start();
should_spin = false; /* Break the loops. */
for (i = 0; i < 10; i++) {
sideline_exit = true; /* Break the loops. */
signal_cond_var(sideline_continue);
for (i = 0; i < NUM_THREADS; i++) {
#ifdef UNIX
pthread_join(pt[i], NULL);
#else
Expand All @@ -204,5 +277,10 @@ int main(void)
dr_app_stop();
dr_app_cleanup();

destroy_cond_var(sideline_continue);
destroy_cond_var(go_native);
for (i=0; i<NUM_THREADS; i++)
destroy_cond_var(sideline_ready[i]);

return 0;
}
2 changes: 1 addition & 1 deletion suite/tests/api/startstop.expect
@@ -1 +1 @@
all done: 150000 iters
all done: 10 iters

0 comments on commit ba6e2e6

Please sign in to comment.