1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// 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 = 0;
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 = 1;
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.
}
|