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

source code of lldb/test/API/functionalities/thread/concurrent_events/main.cpp