diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThread.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThread.cc index db0bd9ff5d1..8a9498b22ce 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThread.cc +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThread.cc @@ -1,66 +1,104 @@ -#ifdef __MINGW32__ - #include // MinGW has no POSIX support; use MSVC runtime -#else - #include -#endif #include #include +#include #include "Sleep.h" -#define NUM_THREADS 5 +#include "Thread.h" -#ifdef __MINGW32__ -typedef unsigned int TID; -#else -typedef pthread_t TID; -#endif +static const int NUM_THREADS = 5; +struct PrintHelloArgs { + int thread_id; + ThreadBarrier *barrier_start; + ThreadBarrier *barrier_finish; + ThreadSemaphore *sem_start; +}; -#ifdef __MINGW32__ -unsigned int __stdcall PrintHello(void *threadid) -#else -void *PrintHello(void *threadid) -#endif +static ThreadRet THREAD_CALL_CONV PrintHello(void *void_arg) { - long tid = (long)threadid; - printf("Hello World! It's me, thread #%ld!\n", tid); - SLEEP(2); // keep this thread around for a bit; the tests will check for its existence while the main thread is stopped at a breakpoint + struct PrintHelloArgs *args = (struct PrintHelloArgs *) void_arg; + int thread_id = args->thread_id; + ThreadBarrier *barrier_start = args->barrier_start; + ThreadBarrier *barrier_finish = args->barrier_finish; + ThreadSemaphore *sem_start = args->sem_start; -#ifdef __MINGW32__ - return 0; -#else - pthread_exit(NULL); -#endif + /* Indicate to main thread that the thread is started. */ + ThreadSemaphorePut(sem_start); + + printf("Hello World! It's me, thread #%d!\n", thread_id); + + /* Make sure that all threads are started before the breakpoint in main hits. */ + ThreadBarrierWait(barrier_start); + + printf("Thread %d in the middle\n", thread_id); + + /* Make sure that the thread does not finish before the breakpoint in main hits. */ + ThreadBarrierWait(barrier_finish); + + printf("Goodbye World! From thread #%d\n", thread_id); + + return THREAD_DEFAULT_RET; } int main(int argc, char *argv[]) { - TID threads[NUM_THREADS]; - int t; - for(t=0; t < NUM_THREADS; t++) + ThreadHandle threads[NUM_THREADS]; + struct PrintHelloArgs args[NUM_THREADS]; + + /* Used to make rendez-vous points between all threads. */ + ThreadBarrier barrier_start; + ThreadBarrier barrier_finish; + + /* Used to tell when a thread is started for real. */ + ThreadSemaphore sem_start; + + /* + 1 for main thread */ + ThreadBarrierInit(&barrier_start, NUM_THREADS + 1); + ThreadBarrierInit(&barrier_finish, NUM_THREADS + 1); + + ThreadSemaphoreInit(&sem_start, 0); + + for (int t = 0; t < NUM_THREADS; t++) { - printf("In main: creating thread %d\n", t); -#ifdef __MINGW32__ + printf("In main: creating thread #%d\n", t); + + args[t].thread_id = t; + args[t].barrier_start = &barrier_start; + args[t].barrier_finish = &barrier_finish; + args[t].sem_start = &sem_start; + + int ret = StartThread(PrintHello, &args[t], &threads[t]); + + if (!ret) { - uintptr_t rc = _beginthreadex(NULL, 0, PrintHello, (void*)t, 0, &threads[t]); - SLEEP(1); // debugger should for sure receive thread creation event after stepping over this sleep; not guaranteed to happen simply stepping over the thread creation call - if (rc == 0) - { - printf("ERROR; _beginthreadex() failed. errno = %d\n", errno); + printf("Error: StartThread failed.\n"); exit(-1); - } - } -#else - { - int rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t); - SLEEP(1); // debugger should for sure receive thread creation event after stepping over this sleep; not guaranteed to happen simply stepping over the thread creation call - if (rc) - { - printf("ERROR; return code from pthread_create() is %d\n", rc); - exit(-1); - } } -#endif + + /* Wait until the thread has really started. */ + ThreadSemaphoreTake(&sem_start); + + printf("In main: thread #%d has started\n", t); /* Breakpoint LINE_MAIN_AFTER_THREAD_START */ } - + + /* Let the threads continue to the 'critical' section> */ + ThreadBarrierWait(&barrier_start); + + printf("In main thread, all threads created.\n"); /* main breakpoint here */ + + SLEEP(30); + + /* Unlock the threads and let the program finish. */ + ThreadBarrierWait(&barrier_finish); + + for (int t = 0; t < NUM_THREADS; t++) + { + printf("In main, joining thread #%d\n", t); + JoinThread(threads[t], NULL); + } + + ThreadBarrierDestroy(&barrier_start); + ThreadBarrierDestroy(&barrier_finish); + ThreadSemaphoreDestroy(&sem_start); + return 0; } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThreadRunControl.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThreadRunControl.cc index 30b23e74b2d..4dc7ec628a3 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThreadRunControl.cc +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThreadRunControl.cc @@ -1,73 +1,48 @@ -#ifdef __MINGW32__ - #include // MinGW has no POSIX support; use MSVC runtime -#else - #include -#endif +#include "Thread.h" #include -#include #include "Sleep.h" -#ifdef __MINGW32__ -typedef unsigned int TID; -#else -typedef pthread_t TID; -#endif - // Set a breakpoint here so that both threads stop. void firstBreakpoint(long id) { - printf("First breakpoint method from thread %ld\n", id); -} - - -#ifdef __MINGW32__ -unsigned int __stdcall PrintHello(void *threadId) -#else -void *PrintHello(void *threadId) -#endif -{ - long tId = (long)threadId; - firstBreakpoint(tId); // Stop a first time - - SLEEP(1); // Keep state running a little - - firstBreakpoint(tId); // Stop a second time - - SLEEP(3); // Resuming past this will give us a running thread - -#ifdef __MINGW32__ - return 0; -#else - pthread_exit(NULL); -#endif + printf("First breakpoint method from thread %ld\n", id); } +struct PrintHelloArgs { + int thread_id; +}; + +static ThreadRet THREAD_CALL_CONV PrintHello(void *void_arg) { + struct PrintHelloArgs *args = (struct PrintHelloArgs *) void_arg; + int thread_id = args->thread_id; + + firstBreakpoint(thread_id); // Stop a first time + + SLEEP(1); // Keep state running a little + + firstBreakpoint(thread_id); // Stop a second time + + SLEEP(3); // Resuming past this will give us a running thread + + return THREAD_DEFAULT_RET; +} int main(int argc, char *argv[]) { - TID thread; - int tId = 1; // Break at main will stop here: we have a single thread stopped + ThreadHandle thread; + struct PrintHelloArgs args; + args.thread_id = 1; // Break at main will stop here: we have a single thread stopped SLEEP(1); // When resuming past here, we have a single thread running -#ifdef __MINGW32__ - uintptr_t rc = _beginthreadex(NULL, 0, PrintHello, (void*)tId, 0, &thread); - if (rc == 0) - { - printf("ERROR; _beginthreadex() failed. errno = %d\n", errno); - exit(-1); - } -#else - int rc = pthread_create(&thread, NULL, PrintHello, (void *)tId); - if (rc) - { - printf("ERROR; return code from pthread_create() is %d\n", rc); - exit(-1); - } -#endif - - firstBreakpoint(0); - + int ret = StartThread(PrintHello, &args, &thread); + if (!ret) { + printf("Error: failed to start thread.\n"); + return 1; + } + + firstBreakpoint(0); + SLEEP(1); // Resuming past this will make this thread run, while we stop the second thread SLEEP(3); // Resuming past this will make this thread run, while we also run the second thread diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Thread.h b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Thread.h new file mode 100644 index 00000000000..833a37b7e1a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Thread.h @@ -0,0 +1,52 @@ +#ifndef Thread_h +#define Thread_h + +/* + * This file provides a simple and incomplete platform compatibility + * layer for thread-related operations. This should help avoiding + * platform dependent code in tests. + */ + + +/* First, include type defintions necessary to define the public API. */ +#ifndef __MINGW32__ +#include "ThreadPthreadTypes.h" +#else // __MINGW32__ +#include "ThreadWindowsTypes.h" +#endif + + +/* + * Type of callback functions for threads + * + * Note: if you don't care about the return value of the thread, you can + * return THREAD_DEFAULT_RET instead, which is defined according to the + * platform type. + * + * Otherwise, you'll need to have some #ifdefs in your test code in order + * to return different value types depending on the platform. + */ +typedef ThreadRet (THREAD_CALL_CONV *ThreadFunc)(void *); + + +/* Public API */ +static int StartThread(ThreadFunc func, void *arg, ThreadHandle *handle); +static int JoinThread(ThreadHandle handle, ThreadRet *ret); +static int ThreadBarrierInit(ThreadBarrier *barrier, unsigned int count); +static int ThreadBarrierDestroy(ThreadBarrier *barrier); +static int ThreadBarrierWait(ThreadBarrier *barrier); +static int ThreadSemaphoreInit(ThreadSemaphore *sem, unsigned int count); +static int ThreadSemaphoreTake(ThreadSemaphore *sem); +static int ThreadSemaphorePut(ThreadSemaphore *sem); +static int ThreadSemaphoreDestroy(ThreadSemaphore *sem); +static int ThreadSetName(const char *name); + + +/* Then, include the implemention of the API. */ +#ifndef __MINGW32__ +#include "ThreadPthread.h" +#else // __MINGW32__ +#include "ThreadWindows.h" +#endif + +#endif // Thread_h diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadPthread.h b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadPthread.h new file mode 100644 index 00000000000..16e9e95eb8d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadPthread.h @@ -0,0 +1,58 @@ +#ifndef THREADPTHREAD_H +#define THREADPTHREAD_H + +#include +#include +#include + +/* Thread functions */ + +static int StartThread(ThreadFunc func, void *arg, ThreadHandle *handle) { + return pthread_create(handle, NULL, func, arg) == 0; +} + +static int JoinThread(ThreadHandle handle, ThreadRet *ret) +{ + return pthread_join(handle, ret) == 0; +} + + +/* Barrier functions */ + +static int ThreadBarrierInit(ThreadBarrier *barrier, unsigned int count) +{ + return pthread_barrier_init(barrier, NULL, count) == 0; +} + +static int ThreadBarrierDestroy(ThreadBarrier *barrier) +{ + return pthread_barrier_destroy(barrier) == 0; +} + +static int ThreadBarrierWait(ThreadBarrier *barrier) +{ + int ret = pthread_barrier_wait(barrier); + return ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD; +} + +static int ThreadSemaphoreInit(ThreadSemaphore *sem, unsigned int count) +{ + return sem_init(sem, 0, count) == 0; +} + +static int ThreadSemaphoreTake(ThreadSemaphore *sem) +{ + return sem_wait(sem) == 0; +} + +static int ThreadSemaphorePut(ThreadSemaphore *sem) +{ + return sem_post(sem) == 0; +} + +static int ThreadSemaphoreDestroy(ThreadSemaphore *sem) +{ + return sem_destroy(sem) == 0; +} + +#endif // THREADPTHREAD_H diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadPthreadTypes.h b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadPthreadTypes.h new file mode 100644 index 00000000000..c8e0ce09a1c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadPthreadTypes.h @@ -0,0 +1,16 @@ +#ifndef THREADPTHREADTYPES_H +#define THREADPTHREADTYPES_H + +#include +#include + +/* Type definitions */ + +typedef pthread_t ThreadHandle; +typedef pthread_barrier_t ThreadBarrier; +typedef sem_t ThreadSemaphore; +typedef void *ThreadRet; +static void *THREAD_DEFAULT_RET = NULL; +#define THREAD_CALL_CONV + +#endif // THREADPTHREADTYPES_H diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadWindows.h b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadWindows.h new file mode 100644 index 00000000000..2482495f762 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadWindows.h @@ -0,0 +1,113 @@ +#ifndef THREADWINDOWS_H +#define THREADWINDOWS_H + +#include +#include +#include + +/* Thread functions */ + +static int StartThread(ThreadFunc func, void *arg, ThreadHandle *handle) { + *handle = (HANDLE) _beginthreadex(NULL, 0, func, arg, 0, NULL); + + return *handle != 0; +} + +static int JoinThread(ThreadHandle handle, ThreadRet *ret) +{ + DWORD result = WaitForSingleObject(handle, INFINITE); + if (result != WAIT_OBJECT_0) + return 0; + + BOOL result_b = GetExitCodeThread(handle, (DWORD *) ret); + if (!result) + return 0; + + return 1; +} + + +/* Barrier functions */ + +struct WindowsThreadBarrier +{ + LONG num_threads_to_wait; + // InterlockedIncrement requires the LONG variable to be aligned on a 4-bytes boundary. + LONG num_threads_waiting __attribute__ ((aligned (4))); + HANDLE semaphore; +}; + +static int ThreadBarrierInit(ThreadBarrier *barrier, unsigned int count) +{ + const LONG max_threads = LONG_MAX; + + barrier->semaphore = CreateSemaphore(NULL, 0, max_threads, NULL); + if (!barrier->semaphore) { + return 0; + } + + barrier->num_threads_to_wait = count; + barrier->num_threads_waiting = 0; + + /* Make sure that the 4-bytes alignment directive works properly. */ + assert(((intptr_t) &barrier->num_threads_waiting & 0x3) == 0); + + return 1; +} + +static int ThreadBarrierDestroy(ThreadBarrier *barrier) +{ + CloseHandle(barrier->semaphore); + + return 1; +} + +static int ThreadBarrierWait(ThreadBarrier *barrier) +{ + LONG new_value = InterlockedIncrement(&barrier->num_threads_waiting); + + if (new_value == barrier->num_threads_to_wait) { + // We are the last thread to hit the barrier, release everybody else (count - 1 threads). + BOOL ret = ReleaseSemaphore(barrier->semaphore, barrier->num_threads_to_wait - 1, NULL); + if (!ret) + return 0; + } else { + // We are not the last thread to hit the barrier, wait to get released. + DWORD ret = WaitForSingleObject(barrier->semaphore, INFINITE); + if (ret != WAIT_OBJECT_0) + return 0; + } + + return 1; +} + +static int ThreadSemaphoreInit(ThreadSemaphore *sem, unsigned int initial_count) +{ + *sem = CreateSemaphore(NULL, initial_count, LONG_MAX, NULL); + return *sem != NULL; +} + +static int ThreadSemaphoreTake(ThreadSemaphore *sem) +{ + DWORD result = WaitForSingleObject(*sem, INFINITE); + + return result == WAIT_OBJECT_0; +} + +static int ThreadSemaphorePut(ThreadSemaphore *sem) +{ + return ReleaseSemaphore(*sem, 1, NULL) != 0; +} + +static int ThreadSemaphoreDestroy(ThreadSemaphore *sem) +{ + return CloseHandle(*sem) != 0; +} + +static int ThreadSetName(const char *name) +{ + /* Not supported> */ + return 0; +} + +#endif // THREADWINDOWS_H diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadWindowsTypes.h b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadWindowsTypes.h new file mode 100644 index 00000000000..36dc68701f5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ThreadWindowsTypes.h @@ -0,0 +1,17 @@ +#ifndef THREADWINDOWSTYPES_H +#define THREADWINDOWSTYPES_H + +#include + +/* Type definitions */ + +struct WindowsThreadBarrier; + +typedef HANDLE ThreadHandle; +typedef struct WindowsThreadBarrier ThreadBarrier; +typedef HANDLE ThreadSemaphore; +typedef unsigned ThreadRet; +static ThreadRet THREAD_DEFAULT_RET = 0; +#define THREAD_CALL_CONV __stdcall + +#endif // THREADWINDOWSTYPES_H diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRegistersTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRegistersTest.java index 46c784d42d5..7e154f2ddd7 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRegistersTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRegistersTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2014 Ericsson and others. + * Copyright (c) 2009, 2015 Ericsson and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,6 +9,7 @@ * Ericsson - initial API and implementation * Alvaro Sanchez-Leon (Ericsson) - Make Registers View specific to a frame (Bug 323552) * Alvaro Sanchez-Leon (Ericsson) - Allow user to edit the register groups (Bug 235747) + * Simon Marchi (Ericsson) - Adapt test code to thread platform compatibility layer. *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; @@ -55,7 +56,6 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.gdb.service.GDBRegisters; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; -import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.MIRegisters.MIRegisterDMC; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.mi.service.command.output.MIDataListRegisterNamesInfo; @@ -368,19 +368,7 @@ public class MIRegistersTest extends BaseTestCase { @Test public void compareRegisterForMultipleExecutionContexts() throws Throwable { - // Run past the line that creates a thread and past the sleep that - // follows it. This is a bit tricky because the code that creates the - // thread is conditional depending on environment. Run to the printf - // before it (which is common), then do step operations over the - // non-common code (but same number of lines) - SyncUtil.runToLine(SRC_NAME, MIRunControlTest.LINE_MAIN_PRINTF); - - // Because the program is about to go multi-threaded, we have to select the thread we want to keep stepping. If we don't, we will ask GDB to step - // the entire process which is not what we want. We can fetch the thread from the stopped event but we should do that before the second thread is - // created, to be sure the stopped event is for the main thread. - MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_OVER); // over the printf - SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER); // over the create-thread call - stoppedEvent = SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER, TestsPlugin.massageTimeout(2000)); // over the one second sleep + MIStoppedEvent stoppedEvent = SyncUtil.runToLine(SRC_NAME, MIRunControlTest.LINE_MAIN_ALL_THREADS_STARTED); // Get the thread IDs final IContainerDMContext containerDmc = DMContexts.getAncestorOfType(stoppedEvent.getDMContext(), IContainerDMContext.class); @@ -399,12 +387,8 @@ public class MIRegistersTest extends BaseTestCase { assertNotNull(ctxts); assertTrue(ctxts.length > 1); - int tid1 = ((IMIExecutionDMContext) ctxts[0]).getThreadId(); - int tid2 = ((IMIExecutionDMContext) ctxts[1]).getThreadId(); - - // Get execution context to thread 2 - IExecutionDMContext execDmc = SyncUtil.createExecutionContext(containerDmc, tid2); - IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(execDmc, 0); + // Get stack frame for thread 2 + IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(ctxts[1], 0); String thread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0); String thread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1); @@ -413,9 +397,8 @@ public class MIRegistersTest extends BaseTestCase { String thread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4); String thread2RegVal5 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5); - // Get execution context to thread 1 - execDmc = SyncUtil.createExecutionContext(containerDmc, tid1); - IFrameDMContext frameDmc1 = SyncUtil.getStackFrame(execDmc, 0); + // Get stack frame for thread 1 + IFrameDMContext frameDmc1 = SyncUtil.getStackFrame(ctxts[0], 0); getModelDataForRegisterDataValue(frameDmc1, IFormattedValues.NATURAL_FORMAT, 0); diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRunControlTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRunControlTest.java index 11d6d439881..32ef6acb0eb 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRunControlTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIRunControlTest.java @@ -1,13 +1,14 @@ /******************************************************************************* - * Copyright (c) 2007, 2014 Ericsson and others. + * Copyright (c) 2007, 2015 Ericsson and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Ericsson AB - Initial implementation of Test cases + * Ericsson AB - Initial implementation of Test cases * Simon Marchi (Ericsson) - Add and use runningOnWindows(). + * Simon Marchi (Ericsson) - Adapt test code to thread platform compatibility layer. *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; @@ -82,11 +83,13 @@ public class MIRunControlTest extends BaseTestCase { private IContainerDMContext fContainerDmc; private IExecutionDMContext fThreadExecDmc; - + // line numbers in MultiThread.cc - static final int LINE_MAIN_RETURN = 63; - static final int LINE_MAIN_PRINTF = 41; - + static final int LINE_MAIN_BEFORE_THREAD_START = 69; // Just before StartThread + static final int LINE_MAIN_AFTER_THREAD_START = 80; // Just after StartThread, where the thread is guaranteed to be started. + static final int LINE_MAIN_ALL_THREADS_STARTED = 88; // Where all threads are guaranteed to be started. + + /* * Path to executable */ @@ -270,23 +273,9 @@ public class MIRunControlTest extends BaseTestCase { new ServiceEventWaitor( getGDBLaunch().getSession(), IStartedDMEvent.class); - - // Run past the line that creates a thread and past the sleep that - // follows it. This is a bit tricky because the code that creates the - // thread is conditional depending on environment. Run to the printf - // before it (which is common), then do step operations over the - // non-common code (but same number of lines) - SyncUtil.runToLine(fContainerDmc, SOURCE_NAME, LINE_MAIN_PRINTF, true); - - // Because the program is about to go multi-threaded, we have to select the thread - // we want to keep stepping. If we don't, we will ask GDB to step the entire process - // which is not what we want. We can fetch the thread from the stopped event - // but we should do that before the second thread is created, to be sure the stopped - // event is for the main thread. - MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_OVER); // over the printf - SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER); // over the create-thread call - SyncUtil.step(stoppedEvent.getDMContext(), StepType.STEP_OVER, TestsPlugin.massageTimeout(2000)); // over the one second sleep - + + SyncUtil.runToLine(fContainerDmc, SOURCE_NAME, LINE_MAIN_AFTER_THREAD_START, true); + final IContainerDMContext containerDmc = SyncUtil.getContainerContext(); /* @@ -436,7 +425,7 @@ public class MIRunControlTest extends BaseTestCase { /* * Add a breakpoint */ - SyncUtil.addBreakpoint(SOURCE_NAME + ":" + LINE_MAIN_PRINTF, false); + SyncUtil.addBreakpoint(SOURCE_NAME + ":" + LINE_MAIN_BEFORE_THREAD_START, false); /* * Resume till the breakpoint is hit @@ -687,27 +676,25 @@ public class MIRunControlTest extends BaseTestCase { ISuspendedDMEvent.class); - fRunCtrl.getExecutor().submit(new Runnable() { - @Override + fRunCtrl.getExecutor().submit(new Runnable() { + @Override public void run() { - fRunCtrl.runToLine(fThreadExecDmc, SOURCE_NAME, LINE_MAIN_RETURN, true, - new RequestMonitor(fRunCtrl.getExecutor(), null) { - @Override - protected void handleCompleted() { - wait.waitFinished(getStatus()); - } - }); - } - }); + fRunCtrl.runToLine(fThreadExecDmc, SOURCE_NAME, + LINE_MAIN_ALL_THREADS_STARTED, true, + new RequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + wait.waitFinished(getStatus()); + } + }); + } + }); wait.waitUntilDone(TestsPlugin.massageTimeout(1000)); Assert.assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); - // The program takes five seconds to run. There's five iterations of a - // loop that has a one second sleep in it suspendedEventWaitor.waitForEvent(TestsPlugin.massageTimeout(10000)); - final IContainerDMContext containerDmc = SyncUtil.getContainerContext(); fRunCtrl.getExecutor().submit(new Runnable() { @@ -749,13 +736,16 @@ public class MIRunControlTest extends BaseTestCase { }); } }); - wait.waitUntilDone(TestsPlugin.massageTimeout(1000)); + wait.waitUntilDone(TestsPlugin.massageTimeout(1000)); Assert.assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset(); - // The program takes five seconds to run. There's five iterations of a - // loop that has a one second sleep in it. Wait a second then attempt to - // interrupt the target + // Wait one second and attempt to interrupt the target. + // As of gdb 7.8, interrupting execution after a thread exit does not + // work well. This test works around it by interrupting before threads + // exit. Once the bug in gdb is fixed, we should add a test that + // interrupts after the threads exit. + // Ref: https://sourceware.org/bugzilla/show_bug.cgi?id=17627 Thread.sleep(1000); fRunCtrl.getExecutor().submit(new Runnable() { @Override @@ -769,6 +759,7 @@ public class MIRunControlTest extends BaseTestCase { }); } }); + wait.waitUntilDone(TestsPlugin.massageTimeout(1000)); Assert.assertTrue(wait.getMessage(), wait.isOK()); wait.waitReset();