Home | History | Annotate | Download | only in concurrent_events
      1 //===-- main.cpp ------------------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 // This test is intended to create a situation in which multiple events
     11 // (breakpoints, watchpoints, crashes, and signal generation/delivery) happen
     12 // from multiple threads. The test expects the debugger to set a breakpoint on
     13 // the main thread (before any worker threads are spawned) and modify variables
     14 // which control the number of therads that are spawned for each action.
     15 
     16 #include <atomic>
     17 #include <vector>
     18 using namespace std;
     19 
     20 #include <pthread.h>
     21 
     22 #include <signal.h>
     23 #include <sys/types.h>
     24 #include <unistd.h>
     25 
     26 // Note that although hogging the CPU while waiting for a variable to change
     27 // would be terrible in production code, it's great for testing since it
     28 // avoids a lot of messy context switching to get multiple threads synchronized.
     29 #define do_nothing()
     30 
     31 #define pseudo_barrier_wait(bar) \
     32     --bar;                       \
     33     while (bar > 0)              \
     34         do_nothing();
     35 
     36 #define pseudo_barrier_init(bar, count) (bar = count)
     37 
     38 typedef std::vector<std::pair<unsigned, void*(*)(void*)> > action_counts;
     39 typedef std::vector<pthread_t> thread_vector;
     40 
     41 std::atomic_int g_barrier;
     42 int g_breakpoint = 0;
     43 int g_sigusr1_count = 0;
     44 std::atomic_int g_watchme;
     45 
     46 struct action_args {
     47   int delay;
     48 };
     49 
     50 // Perform any extra actions required by thread 'input' arg
     51 void do_action_args(void *input) {
     52     if (input) {
     53       action_args *args = static_cast<action_args*>(input);
     54       sleep(args->delay);
     55     }
     56 }
     57 
     58 void *
     59 breakpoint_func (void *input)
     60 {
     61     // Wait until all threads are running
     62     pseudo_barrier_wait(g_barrier);
     63     do_action_args(input);
     64 
     65     // Do something
     66     g_breakpoint++;       // Set breakpoint here
     67     return 0;
     68 }
     69 
     70 void *
     71 signal_func (void *input) {
     72     // Wait until all threads are running
     73     pseudo_barrier_wait(g_barrier);
     74     do_action_args(input);
     75 
     76     // Send a user-defined signal to the current process
     77     //kill(getpid(), SIGUSR1);
     78     // Send a user-defined signal to the current thread
     79     pthread_kill(pthread_self(), SIGUSR1);
     80 
     81     return 0;
     82 }
     83 
     84 void *
     85 watchpoint_func (void *input) {
     86     pseudo_barrier_wait(g_barrier);
     87     do_action_args(input);
     88 
     89     g_watchme += 1;     // watchpoint triggers here
     90     return 0;
     91 }
     92 
     93 void *
     94 crash_func (void *input) {
     95     pseudo_barrier_wait(g_barrier);
     96     do_action_args(input);
     97 
     98     int *a = 0;
     99     *a = 5; // crash happens here
    100     return 0;
    101 }
    102 
    103 void sigusr1_handler(int sig) {
    104     if (sig == SIGUSR1)
    105         g_sigusr1_count += 1; // Break here in signal handler
    106 }
    107 
    108 /// Register a simple function for to handle signal
    109 void register_signal_handler(int signal, void (*handler)(int))
    110 {
    111     sigset_t empty_sigset;
    112     sigemptyset(&empty_sigset);
    113 
    114     struct sigaction action;
    115     action.sa_sigaction = 0;
    116     action.sa_mask = empty_sigset;
    117     action.sa_flags = 0;
    118     action.sa_handler = handler;
    119     sigaction(SIGUSR1, &action, 0);
    120 }
    121 
    122 void start_threads(thread_vector& threads,
    123                    action_counts& actions,
    124                    void* args = 0) {
    125     action_counts::iterator b = actions.begin(), e = actions.end();
    126     for(action_counts::iterator i = b; i != e; ++i) {
    127         for(unsigned count = 0; count < i->first; ++count) {
    128             pthread_t t;
    129             pthread_create(&t, 0, i->second, args);
    130             threads.push_back(t);
    131         }
    132     }
    133 }
    134 
    135 int dotest()
    136 {
    137     g_watchme = 0;
    138 
    139     // Actions are triggered immediately after the thread is spawned
    140     unsigned num_breakpoint_threads = 1;
    141     unsigned num_watchpoint_threads = 0;
    142     unsigned num_signal_threads = 1;
    143     unsigned num_crash_threads = 0;
    144 
    145     // Actions below are triggered after a 1-second delay
    146     unsigned num_delay_breakpoint_threads = 0;
    147     unsigned num_delay_watchpoint_threads = 0;
    148     unsigned num_delay_signal_threads = 0;
    149     unsigned num_delay_crash_threads = 0;
    150 
    151     register_signal_handler(SIGUSR1, sigusr1_handler); // Break here and adjust num_[breakpoint|watchpoint|signal|crash]_threads
    152 
    153     unsigned total_threads = num_breakpoint_threads \
    154                              + num_watchpoint_threads \
    155                              + num_signal_threads \
    156                              + num_crash_threads \
    157                              + num_delay_breakpoint_threads \
    158                              + num_delay_watchpoint_threads \
    159                              + num_delay_signal_threads \
    160                              + num_delay_crash_threads;
    161 
    162     // Don't let either thread do anything until they're both ready.
    163     pseudo_barrier_init(g_barrier, total_threads);
    164 
    165     action_counts actions;
    166     actions.push_back(std::make_pair(num_breakpoint_threads, breakpoint_func));
    167     actions.push_back(std::make_pair(num_watchpoint_threads, watchpoint_func));
    168     actions.push_back(std::make_pair(num_signal_threads, signal_func));
    169     actions.push_back(std::make_pair(num_crash_threads, crash_func));
    170 
    171     action_counts delay_actions;
    172     actions.push_back(std::make_pair(num_delay_breakpoint_threads, breakpoint_func));
    173     actions.push_back(std::make_pair(num_delay_watchpoint_threads, watchpoint_func));
    174     actions.push_back(std::make_pair(num_delay_signal_threads, signal_func));
    175     actions.push_back(std::make_pair(num_delay_crash_threads, crash_func));
    176 
    177     // Create threads that handle instant actions
    178     thread_vector threads;
    179     start_threads(threads, actions);
    180 
    181     // Create threads that handle delayed actions
    182     action_args delay_arg;
    183     delay_arg.delay = 1;
    184     start_threads(threads, delay_actions, &delay_arg);
    185 
    186     // Join all threads
    187     typedef std::vector<pthread_t>::iterator thread_iterator;
    188     for(thread_iterator t = threads.begin(); t != threads.end(); ++t)
    189         pthread_join(*t, 0);
    190 
    191     return 0;
    192 }
    193 
    194 int main ()
    195 {
    196     dotest();
    197     return 0; // Break here and verify one thread is active.
    198 }
    199 
    200 
    201