179 lines
5.2 KiB
C++
179 lines
5.2 KiB
C++
// This test is intended to create a situation in which multiple events
|
|
// (breakpoints, watchpoints, crashes, and signal generation/delivery) happen
|
|
// from multiple threads. The test expects the debugger to set a breakpoint on
|
|
// the main thread (before any worker threads are spawned) and modify variables
|
|
// which control the number of threads that are spawned for each action.
|
|
|
|
#include "pseudo_barrier.h"
|
|
#include <vector>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
typedef std::vector<std::pair<unsigned, void*(*)(void*)> > action_counts;
|
|
typedef std::vector<pthread_t> thread_vector;
|
|
|
|
pseudo_barrier_t g_barrier;
|
|
int g_breakpoint = 0;
|
|
int g_sigusr1_count = 0;
|
|
uint32_t g_watchme;
|
|
|
|
struct action_args {
|
|
int delay;
|
|
};
|
|
|
|
// Perform any extra actions required by thread 'input' arg
|
|
void do_action_args(void *input) {
|
|
if (input) {
|
|
action_args *args = static_cast<action_args*>(input);
|
|
sleep(args->delay);
|
|
}
|
|
}
|
|
|
|
void *
|
|
breakpoint_func (void *input)
|
|
{
|
|
// Wait until all threads are running
|
|
pseudo_barrier_wait(g_barrier);
|
|
do_action_args(input);
|
|
|
|
// Do something
|
|
g_breakpoint++; // Set breakpoint here
|
|
return 0;
|
|
}
|
|
|
|
void *
|
|
signal_func (void *input) {
|
|
// Wait until all threads are running
|
|
pseudo_barrier_wait(g_barrier);
|
|
do_action_args(input);
|
|
|
|
// Send a user-defined signal to the current process
|
|
//kill(getpid(), SIGUSR1);
|
|
// Send a user-defined signal to the current thread
|
|
pthread_kill(pthread_self(), SIGUSR1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void *
|
|
watchpoint_func (void *input) {
|
|
pseudo_barrier_wait(g_barrier);
|
|
do_action_args(input);
|
|
|
|
g_watchme = 1; // watchpoint triggers here
|
|
return 0;
|
|
}
|
|
|
|
void *
|
|
crash_func (void *input) {
|
|
pseudo_barrier_wait(g_barrier);
|
|
do_action_args(input);
|
|
|
|
int *a = 0;
|
|
*a = 5; // crash happens here
|
|
return 0;
|
|
}
|
|
|
|
void sigusr1_handler(int sig) {
|
|
if (sig == SIGUSR1)
|
|
g_sigusr1_count += 1; // Break here in signal handler
|
|
}
|
|
|
|
/// Register a simple function for to handle signal
|
|
void register_signal_handler(int signal, void (*handler)(int))
|
|
{
|
|
sigset_t empty_sigset;
|
|
sigemptyset(&empty_sigset);
|
|
|
|
struct sigaction action;
|
|
action.sa_sigaction = 0;
|
|
action.sa_mask = empty_sigset;
|
|
action.sa_flags = 0;
|
|
action.sa_handler = handler;
|
|
sigaction(SIGUSR1, &action, 0);
|
|
}
|
|
|
|
void start_threads(thread_vector& threads,
|
|
action_counts& actions,
|
|
void* args = 0) {
|
|
action_counts::iterator b = actions.begin(), e = actions.end();
|
|
for(action_counts::iterator i = b; i != e; ++i) {
|
|
for(unsigned count = 0; count < i->first; ++count) {
|
|
pthread_t t;
|
|
pthread_create(&t, 0, i->second, args);
|
|
threads.push_back(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
int dotest()
|
|
{
|
|
g_watchme = 0;
|
|
|
|
// Actions are triggered immediately after the thread is spawned
|
|
unsigned num_breakpoint_threads = 1;
|
|
unsigned num_watchpoint_threads = 0;
|
|
unsigned num_signal_threads = 1;
|
|
unsigned num_crash_threads = 0;
|
|
|
|
// Actions below are triggered after a 1-second delay
|
|
unsigned num_delay_breakpoint_threads = 0;
|
|
unsigned num_delay_watchpoint_threads = 0;
|
|
unsigned num_delay_signal_threads = 0;
|
|
unsigned num_delay_crash_threads = 0;
|
|
|
|
register_signal_handler(SIGUSR1, sigusr1_handler); // Break here and adjust num_[breakpoint|watchpoint|signal|crash]_threads
|
|
|
|
unsigned total_threads = num_breakpoint_threads \
|
|
+ num_watchpoint_threads \
|
|
+ num_signal_threads \
|
|
+ num_crash_threads \
|
|
+ num_delay_breakpoint_threads \
|
|
+ num_delay_watchpoint_threads \
|
|
+ num_delay_signal_threads \
|
|
+ num_delay_crash_threads;
|
|
|
|
// Don't let either thread do anything until they're both ready.
|
|
pseudo_barrier_init(g_barrier, total_threads);
|
|
|
|
action_counts actions;
|
|
actions.push_back(std::make_pair(num_breakpoint_threads, breakpoint_func));
|
|
actions.push_back(std::make_pair(num_watchpoint_threads, watchpoint_func));
|
|
actions.push_back(std::make_pair(num_signal_threads, signal_func));
|
|
actions.push_back(std::make_pair(num_crash_threads, crash_func));
|
|
|
|
action_counts delay_actions;
|
|
delay_actions.push_back(std::make_pair(num_delay_breakpoint_threads, breakpoint_func));
|
|
delay_actions.push_back(std::make_pair(num_delay_watchpoint_threads, watchpoint_func));
|
|
delay_actions.push_back(std::make_pair(num_delay_signal_threads, signal_func));
|
|
delay_actions.push_back(std::make_pair(num_delay_crash_threads, crash_func));
|
|
|
|
// Create threads that handle instant actions
|
|
thread_vector threads;
|
|
start_threads(threads, actions);
|
|
|
|
// Create threads that handle delayed actions
|
|
action_args delay_arg;
|
|
delay_arg.delay = 1;
|
|
start_threads(threads, delay_actions, &delay_arg);
|
|
|
|
// Join all threads
|
|
typedef std::vector<pthread_t>::iterator thread_iterator;
|
|
for(thread_iterator t = threads.begin(); t != threads.end(); ++t)
|
|
pthread_join(*t, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main ()
|
|
{
|
|
dotest();
|
|
return 0; // Break here and verify one thread is active.
|
|
}
|
|
|
|
|