diff options
Diffstat (limited to 'lldb/test/tools/lldb-gdbserver/main.cpp')
-rw-r--r-- | lldb/test/tools/lldb-gdbserver/main.cpp | 135 |
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. |