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