2013-07-11 22:14:47 +00:00
|
|
|
// 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
|
2014-06-27 02:42:12 +00:00
|
|
|
// which control the number of threads that are spawned for each action.
|
2013-07-09 00:08:01 +00:00
|
|
|
|
Centralize libc++ test skipping logic
Summary:
This aims to replace the different decorators we've had on each libc++
test with a single solution. Each libc++ will be assigned to the
"libc++" category and a single central piece of code will decide whether
we are actually able to run libc++ test in the given configuration by
enabling or disabling the category (while giving the user the
opportunity to override this).
I started this effort because I wanted to get libc++ tests running on
android, and none of the existing decorators worked for this use case:
- skipIfGcc - incorrect, we can build libc++ executables on android
with gcc (in fact, after this, we can now do it on linux as well)
- lldbutil.skip_if_library_missing - this checks whether libc++.so is
loaded in the proces, which fails in case of a statically linked
libc++ (this makes copying executables to the remote target easier to
manage).
To make this work I needed to split out the pseudo_barrier code from the
force-included file, as libc++'s atomic does not play well with gcc on
linux, and this made every test fail, even though we need the code only
in the threading tests.
So far, I am only annotating one of the tests with this category. If
this does not break anything, I'll proceed to update the rest.
Reviewers: jingham, zturner, EricWF
Subscribers: srhines, lldb-commits
Differential Revision: https://reviews.llvm.org/D30984
llvm-svn: 299028
2017-03-29 21:01:14 +00:00
|
|
|
#include "pseudo_barrier.h"
|
2013-07-09 00:08:01 +00:00
|
|
|
#include <vector>
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
|
2016-05-10 07:54:25 +00:00
|
|
|
pseudo_barrier_t g_barrier;
|
2013-07-09 00:08:01 +00:00
|
|
|
int g_breakpoint = 0;
|
|
|
|
|
int g_sigusr1_count = 0;
|
"Fix" concurrent events test for arm
Summary:
The test incremented an atomic varible to trigger the watchpoint event.
On arm64 this compiled to a ldaxr/stlxr loop, with the watchpoint being
triggered in the middle of the loop. Hitting the watchpoint resets the
exclusive monitor, and forces the process to loop one more time, hitting
the watchpoint again, etc.
While it would be nice if the debugger was able to resume from this
situation, this is not trivial, and is not what this test is about.
Therefore, I propose to change this to a simple store to a normal
variable (which should still trip the watchpoint everywhere, but without
atomic loops) and file a bug to investigate the possibilities of
handling the watchpoints in atomic loops in a more reasonable way.
Reviewers: clayborg
Subscribers: aemerson, kristof.beyls, lldb-commits
Differential Revision: https://reviews.llvm.org/D39680
llvm-svn: 317561
2017-11-07 10:36:36 +00:00
|
|
|
uint32_t g_watchme;
|
2013-07-09 00:08:01 +00:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2013-07-09 17:36:18 +00:00
|
|
|
// Wait until all threads are running
|
2013-07-09 00:08:01 +00:00
|
|
|
pseudo_barrier_wait(g_barrier);
|
|
|
|
|
do_action_args(input);
|
|
|
|
|
|
|
|
|
|
// Do something
|
|
|
|
|
g_breakpoint++; // Set breakpoint here
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
signal_func (void *input) {
|
2013-07-09 17:36:18 +00:00
|
|
|
// Wait until all threads are running
|
2013-07-09 00:08:01 +00:00
|
|
|
pseudo_barrier_wait(g_barrier);
|
|
|
|
|
do_action_args(input);
|
|
|
|
|
|
2013-07-11 22:14:47 +00:00
|
|
|
// 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);
|
2013-07-09 00:08:01 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
watchpoint_func (void *input) {
|
|
|
|
|
pseudo_barrier_wait(g_barrier);
|
|
|
|
|
do_action_args(input);
|
|
|
|
|
|
"Fix" concurrent events test for arm
Summary:
The test incremented an atomic varible to trigger the watchpoint event.
On arm64 this compiled to a ldaxr/stlxr loop, with the watchpoint being
triggered in the middle of the loop. Hitting the watchpoint resets the
exclusive monitor, and forces the process to loop one more time, hitting
the watchpoint again, etc.
While it would be nice if the debugger was able to resume from this
situation, this is not trivial, and is not what this test is about.
Therefore, I propose to change this to a simple store to a normal
variable (which should still trip the watchpoint everywhere, but without
atomic loops) and file a bug to investigate the possibilities of
handling the watchpoints in atomic loops in a more reasonable way.
Reviewers: clayborg
Subscribers: aemerson, kristof.beyls, lldb-commits
Differential Revision: https://reviews.llvm.org/D39680
llvm-svn: 317561
2017-11-07 10:36:36 +00:00
|
|
|
g_watchme = 1; // watchpoint triggers here
|
2013-07-09 00:08:01 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-29 23:13:08 +00:00
|
|
|
int dotest()
|
2013-07-09 00:08:01 +00:00
|
|
|
{
|
|
|
|
|
g_watchme = 0;
|
|
|
|
|
|
2013-07-11 22:14:47 +00:00
|
|
|
// Actions are triggered immediately after the thread is spawned
|
2013-07-09 00:08:01 +00:00
|
|
|
unsigned num_breakpoint_threads = 1;
|
2013-07-12 19:46:47 +00:00
|
|
|
unsigned num_watchpoint_threads = 0;
|
|
|
|
|
unsigned num_signal_threads = 1;
|
2013-07-11 22:14:47 +00:00
|
|
|
unsigned num_crash_threads = 0;
|
2013-07-09 00:08:01 +00:00
|
|
|
|
2013-07-11 22:14:47 +00:00
|
|
|
// Actions below are triggered after a 1-second delay
|
2013-07-09 00:08:01 +00:00
|
|
|
unsigned num_delay_breakpoint_threads = 0;
|
2013-07-12 19:46:47 +00:00
|
|
|
unsigned num_delay_watchpoint_threads = 0;
|
2013-07-09 00:08:01 +00:00
|
|
|
unsigned num_delay_signal_threads = 0;
|
|
|
|
|
unsigned num_delay_crash_threads = 0;
|
|
|
|
|
|
2013-07-12 19:46:47 +00:00
|
|
|
register_signal_handler(SIGUSR1, sigusr1_handler); // Break here and adjust num_[breakpoint|watchpoint|signal|crash]_threads
|
2013-07-11 22:14:47 +00:00
|
|
|
|
2013-07-09 00:08:01 +00:00
|
|
|
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;
|
2014-11-13 04:00:23 +00:00
|
|
|
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));
|
2013-07-09 00:08:01 +00:00
|
|
|
|
|
|
|
|
// Create threads that handle instant actions
|
2013-07-12 19:46:47 +00:00
|
|
|
thread_vector threads;
|
2013-07-09 00:08:01 +00:00
|
|
|
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);
|
|
|
|
|
|
2013-07-29 23:13:08 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main ()
|
|
|
|
|
{
|
|
|
|
|
dotest();
|
2013-07-11 22:14:47 +00:00
|
|
|
return 0; // Break here and verify one thread is active.
|
2013-07-09 00:08:01 +00:00
|
|
|
}
|
2013-07-29 23:13:08 +00:00
|
|
|
|
|
|
|
|
|