summaryrefslogtreecommitdiffstats
path: root/lldb/test/tools/lldb-gdbserver/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/test/tools/lldb-gdbserver/main.cpp')
-rw-r--r--lldb/test/tools/lldb-gdbserver/main.cpp135
1 files changed, 114 insertions, 21 deletions
diff --git a/lldb/test/tools/lldb-gdbserver/main.cpp b/lldb/test/tools/lldb-gdbserver/main.cpp
index f32e428593c..e7ae168c2b3 100644
--- a/lldb/test/tools/lldb-gdbserver/main.cpp
+++ b/lldb/test/tools/lldb-gdbserver/main.cpp
@@ -3,13 +3,17 @@
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
+#include <setjmp.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <vector>
-#if defined(__linux__)
+#if defined(__APPLE__)
+__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2)
+int pthread_threadid_np(pthread_t,__uint64_t*);
+#elif defined(__linux__)
#include <sys/syscall.h>
#endif
@@ -20,9 +24,15 @@ static const char *const STDERR_PREFIX = "stderr:";
static const char *const THREAD_PREFIX = "thread:";
static const char *const THREAD_COMMAND_NEW = "new";
static const char *const THREAD_COMMAND_PRINT_IDS = "print-ids";
+static const char *const THREAD_COMMAND_SEGFAULT = "segfault";
static bool g_print_thread_ids = false;
static pthread_mutex_t g_print_mutex = PTHREAD_MUTEX_INITIALIZER;
+static bool g_threads_do_segfault = false;
+
+static pthread_mutex_t g_jump_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
+static jmp_buf g_jump_buffer;
+static bool g_is_segfaulting = false;
static void
print_thread_id ()
@@ -30,7 +40,9 @@ print_thread_id ()
// Put in the right magic here for your platform to spit out the thread id (tid) that debugserver/lldb-gdbserver would see as a TID.
// Otherwise, let the else clause print out the unsupported text so that the unit test knows to skip verifying thread ids.
#if defined(__APPLE__)
- printf ("%" PRIx64, static_cast<uint64_t> (pthread_mach_thread_np(pthread_self())));
+ __uint64_t tid = 0;
+ pthread_threadid_np(pthread_self(), &tid);
+ printf ("%" PRIx64, tid);
#elif defined (__linux__)
// This is a call to gettid() via syscall.
printf ("%" PRIx64, static_cast<uint64_t> (syscall (__NR_gettid)));
@@ -42,36 +54,62 @@ print_thread_id ()
static void
signal_handler (int signo)
{
+ const char *signal_name = NULL;
+ switch (signo)
+ {
+ case SIGUSR1: signal_name = "SIGUSR1"; break;
+ case SIGSEGV: signal_name = "SIGSEGV"; break;
+ default: signal_name = NULL;
+ }
+
+ // Print notice that we received the signal on a given thread.
+ pthread_mutex_lock (&g_print_mutex);
+ if (signal_name)
+ printf ("received %s on thread id: ", signal_name);
+ else
+ printf ("received signo %d (%s) on thread id: ", signo, strsignal (signo));
+ print_thread_id ();
+ printf ("\n");
+ pthread_mutex_unlock (&g_print_mutex);
+
+ // Reset the signal handler if we're one of the expected signal handlers.
switch (signo)
{
+ case SIGSEGV:
+ // Fix up the pointer we're writing to. This needs to happen if nothing intercepts the SIGSEGV
+ // (i.e. if somebody runs this from the command line).
+ longjmp(g_jump_buffer, 1);
+ break;
case SIGUSR1:
- // Print notice that we received the signal on a given thread.
- pthread_mutex_lock (&g_print_mutex);
- printf ("received SIGUSR1 on thread id: ");
- print_thread_id ();
- printf ("\n");
- pthread_mutex_unlock (&g_print_mutex);
-
- // Reset the signal handler.
- sig_t sig_result = signal (SIGUSR1, signal_handler);
- if (sig_result == SIG_ERR)
+ if (g_is_segfaulting)
{
- fprintf(stderr, "failed to set signal handler: errno=%d\n", errno);
- exit (1);
+ // Fix up the pointer we're writing to. This is used to test gdb remote signal delivery.
+ // A SIGSEGV will be raised when the thread is created, switched out for a SIGUSR1, and
+ // then this code still needs to fix the seg fault.
+ // (i.e. if somebody runs this from the command line).
+ longjmp(g_jump_buffer, 1);
}
-
break;
}
+
+ // Reset the signal handler.
+ sig_t sig_result = signal (signo, signal_handler);
+ if (sig_result == SIG_ERR)
+ {
+ fprintf(stderr, "failed to set signal handler: errno=%d\n", errno);
+ exit (1);
+ }
}
static void*
thread_func (void *arg)
{
+ static pthread_mutex_t s_thread_index_mutex = PTHREAD_MUTEX_INITIALIZER;
static int s_thread_index = 1;
- // For now, just sleep for a few seconds.
- // std::cout << "thread " << pthread_self() << ": created" << std::endl;
+ pthread_mutex_lock (&s_thread_index_mutex);
const int this_thread_index = s_thread_index++;
+ pthread_mutex_unlock (&s_thread_index_mutex);
if (g_print_thread_ids)
{
@@ -82,13 +120,50 @@ thread_func (void *arg)
pthread_mutex_unlock (&g_print_mutex);
}
- int sleep_seconds_remaining = 20;
+ if (g_threads_do_segfault)
+ {
+ // Sleep for a number of seconds based on the thread index.
+ // TODO add ability to send commands to test exe so we can
+ // handle timing more precisely. This is clunky. All we're
+ // trying to do is add predictability as to the timing of
+ // signal generation by created threads.
+ int sleep_seconds = 2 * (this_thread_index - 1);
+ while (sleep_seconds > 0)
+ sleep_seconds = sleep(sleep_seconds);
+
+ // Test creating a SEGV.
+ pthread_mutex_lock (&g_jump_buffer_mutex);
+ g_is_segfaulting = true;
+ int *bad_p = NULL;
+ if (setjmp(g_jump_buffer) == 0)
+ {
+ // Force a seg fault signal on this thread.
+ *bad_p = 0;
+ }
+ else
+ {
+ // Tell the system we're no longer seg faulting.
+ // Used by the SIGUSR1 signal handler that we inject
+ // in place of the SIGSEGV so it only tries to
+ // recover from the SIGSEGV if this seg fault code
+ // was in play.
+ g_is_segfaulting = false;
+ }
+ pthread_mutex_unlock (&g_jump_buffer_mutex);
+
+ pthread_mutex_lock (&g_print_mutex);
+ printf ("thread ");
+ print_thread_id ();
+ printf (": past SIGSEGV\n");
+ pthread_mutex_unlock (&g_print_mutex);
+ }
+
+ int sleep_seconds_remaining = 5;
while (sleep_seconds_remaining > 0)
{
sleep_seconds_remaining = sleep (sleep_seconds_remaining);
}
- // std::cout << "thread " << pthread_self() << ": exiting" << std::endl;
return NULL;
}
@@ -98,10 +173,24 @@ int main (int argc, char **argv)
int return_value = 0;
// Set the signal handler.
- sig_t sig_result = signal (SIGUSR1, signal_handler);
+ sig_t sig_result = signal (SIGALRM, signal_handler);
if (sig_result == SIG_ERR)
{
- fprintf(stderr, "failed to set signal handler: errno=%d\n", errno);
+ fprintf(stderr, "failed to set SIGALRM signal handler: errno=%d\n", errno);
+ exit (1);
+ }
+
+ sig_result = signal (SIGUSR1, signal_handler);
+ if (sig_result == SIG_ERR)
+ {
+ fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno);
+ exit (1);
+ }
+
+ sig_result = signal (SIGSEGV, signal_handler);
+ if (sig_result == SIG_ERR)
+ {
+ fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno);
exit (1);
}
@@ -158,6 +247,10 @@ int main (int argc, char **argv)
printf ("\n");
pthread_mutex_unlock (&g_print_mutex);
}
+ else if (std::strstr (argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_SEGFAULT))
+ {
+ g_threads_do_segfault = true;
+ }
else
{
// At this point we don't do anything else with threads.
OpenPOWER on IntegriCloud