blob: 0733e4c9e9a497ae2f2784ebf216495a98bd4424 [file] [log] [blame]
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// 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 <atomic>
#include <vector>
using namespace std;
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
// Note that although hogging the CPU while waiting for a variable to change
// would be terrible in production code, it's great for testing since it
// avoids a lot of messy context switching to get multiple threads synchronized.
#define do_nothing()
#define pseudo_barrier_wait(bar) \
--bar; \
while (bar > 0) \
do_nothing();
#define pseudo_barrier_init(bar, count) (bar = count)
typedef std::vector<std::pair<unsigned, void*(*)(void*)> > action_counts;
typedef std::vector<pthread_t> thread_vector;
std::atomic_int g_barrier;
int g_breakpoint = 0;
int g_sigusr1_count = 0;
std::atomic_int 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;
actions.push_back(std::make_pair(num_delay_breakpoint_threads, breakpoint_func));
actions.push_back(std::make_pair(num_delay_watchpoint_threads, watchpoint_func));
actions.push_back(std::make_pair(num_delay_signal_threads, signal_func));
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.
}