remove dependence of TestGdbRemoteExitCode.py on parent directory source
authorTodd Fiala <todd.fiala@gmail.com>
Mon, 5 Sep 2016 22:03:02 +0000 (22:03 +0000)
committerTodd Fiala <todd.fiala@gmail.com>
Mon, 5 Sep 2016 22:03:02 +0000 (22:03 +0000)
As Pavel pointed out in a comment on llvm.org/pr30271, the VPATH I was
using here to eliminate duplication of a .cpp file had a side effect of
attempting to pull in a .o/.obj file from that same parent dir, where
other tests can be running in parallel.  This is no good.

For now, I have removed the VPATH, which should address
llvm.org/pr30271.  I have also removed the XFAIL.

llvm-svn: 280675

lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/Makefile
lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/TestGdbRemoteExitCode.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/main.cpp [new file with mode: 0644]

index 1370b53..6ff66a3 100644 (file)
@@ -1,7 +1,5 @@
 LEVEL = ../../../make
 
-VPATH = ..
-
 override CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS
 ENABLE_THREADS := YES
 CXX_SOURCES := main.cpp
index 20e4abb..e77f2b7 100644 (file)
@@ -59,7 +59,6 @@ class TestGdbRemoteExitCode(GdbRemoteTestCaseBase):
         self.start_inferior()
 
     @llgs_test
-    @expectedFailureAll(bugnumber="llvm.org/pr30271")
     def test_start_inferior_llgs(self):
         self.init_llgs_test()
         self.build()
@@ -119,7 +118,6 @@ class TestGdbRemoteExitCode(GdbRemoteTestCaseBase):
         self.inferior_exit_42()
 
     @llgs_test
-    @expectedFailureAll(bugnumber="llvm.org/pr30271")
     def test_inferior_exit_42_llgs(self):
         self.init_llgs_test()
         self.build()
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/main.cpp b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/main.cpp
new file mode 100644 (file)
index 0000000..b97c6eb
--- /dev/null
@@ -0,0 +1,391 @@
+#include <cstdlib>
+#include <cstring>
+#include <errno.h>
+#include <inttypes.h>
+#include <memory>
+#include <pthread.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <vector>
+
+#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
+
+static const char *const RETVAL_PREFIX               = "retval:";
+static const char *const SLEEP_PREFIX                = "sleep:";
+static const char *const STDERR_PREFIX               = "stderr:";
+static const char *const SET_MESSAGE_PREFIX          = "set-message:";
+static const char *const PRINT_MESSAGE_COMMAND       = "print-message:";
+static const char *const GET_DATA_ADDRESS_PREFIX     = "get-data-address-hex:";
+static const char *const GET_STACK_ADDRESS_COMMAND   = "get-stack-address-hex:";
+static const char *const GET_HEAP_ADDRESS_COMMAND    = "get-heap-address-hex:";
+
+static const char *const GET_CODE_ADDRESS_PREFIX     = "get-code-address-hex:";
+static const char *const CALL_FUNCTION_PREFIX        = "call-function:";
+
+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 char g_message[256];
+
+static volatile char g_c1 = '0';
+static volatile char g_c2 = '1';
+
+static void
+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__)
+       __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)));
+#else
+       printf("{no-tid-support}");
+#endif
+}
+
+static void
+signal_handler (int signo)
+{
+       const char *signal_name = nullptr;
+       switch (signo)
+       {
+               case SIGUSR1: signal_name = "SIGUSR1"; break;
+               case SIGSEGV: signal_name = "SIGSEGV"; break;
+               default:      signal_name = nullptr;
+       }
+
+       // 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:
+               if (g_is_segfaulting)
+               {
+                   // 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:
+            if (g_is_segfaulting)
+            {
+                // 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
+swap_chars ()
+{
+    g_c1 = '1';
+    g_c2 = '0';
+
+    g_c1 = '0';
+    g_c2 = '1';
+}
+
+static void
+hello ()
+{
+    pthread_mutex_lock (&g_print_mutex);
+    printf ("hello, world\n");
+    pthread_mutex_unlock (&g_print_mutex);
+}
+
+static void*
+thread_func (void *arg)
+{
+       static pthread_mutex_t s_thread_index_mutex = PTHREAD_MUTEX_INITIALIZER;
+       static int s_thread_index = 1;
+
+       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)
+       {
+               pthread_mutex_lock (&g_print_mutex);
+               printf ("thread %d id: ", this_thread_index);
+               print_thread_id ();
+               printf ("\n");
+               pthread_mutex_unlock (&g_print_mutex);
+       }
+
+       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 = nullptr;
+               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 = 60;
+       while (sleep_seconds_remaining > 0)
+       {
+               sleep_seconds_remaining = sleep (sleep_seconds_remaining);
+       }
+
+       return nullptr;
+}
+
+int main (int argc, char **argv)
+{
+       lldb_enable_attach();
+
+       std::vector<pthread_t> threads;
+       std::unique_ptr<uint8_t[]> heap_array_up;
+    int return_value = 0;
+
+       // Set the signal handler.
+       sig_t sig_result = signal (SIGALRM, signal_handler);
+       if (sig_result == SIG_ERR)
+       {
+               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);
+       }
+
+       // Process command line args.
+    for (int i = 1; i < argc; ++i)
+    {
+        if (std::strstr (argv[i], STDERR_PREFIX))
+        {
+            // Treat remainder as text to go to stderr.
+            fprintf (stderr, "%s\n", (argv[i] + strlen (STDERR_PREFIX)));
+        }
+        else if (std::strstr (argv[i], RETVAL_PREFIX))
+        {
+            // Treat as the return value for the program.
+            return_value = std::atoi (argv[i] + strlen (RETVAL_PREFIX));
+        }
+        else if (std::strstr (argv[i], SLEEP_PREFIX))
+        {
+            // Treat as the amount of time to have this process sleep (in seconds).
+            int sleep_seconds_remaining = std::atoi (argv[i] + strlen (SLEEP_PREFIX));
+
+                       // Loop around, sleeping until all sleep time is used up.  Note that
+                       // signals will cause sleep to end early with the number of seconds remaining.
+                       for (int i = 0; sleep_seconds_remaining > 0; ++i)
+                       {
+                               sleep_seconds_remaining = sleep (sleep_seconds_remaining);
+                               // std::cout << "sleep result (call " << i << "): " << sleep_seconds_remaining << std::endl;
+                       }
+        }
+               else if (std::strstr (argv[i], SET_MESSAGE_PREFIX))
+               {
+                       // Copy the contents after "set-message:" to the g_message buffer.
+                       // Used for reading inferior memory and verifying contents match expectations.
+            strncpy (g_message, argv[i] + strlen (SET_MESSAGE_PREFIX), sizeof (g_message));
+
+                       // Ensure we're null terminated.
+                       g_message[sizeof (g_message) - 1] = '\0';
+
+               }
+               else if (std::strstr (argv[i], PRINT_MESSAGE_COMMAND))
+               {
+                       pthread_mutex_lock (&g_print_mutex);
+            printf ("message: %s\n", g_message);
+                       pthread_mutex_unlock (&g_print_mutex);
+               }
+        else if (std::strstr (argv[i], GET_DATA_ADDRESS_PREFIX))
+        {
+            volatile void *data_p = nullptr;
+
+            if (std::strstr (argv[i] + strlen (GET_DATA_ADDRESS_PREFIX), "g_message"))
+                data_p = &g_message[0];
+            else if (std::strstr (argv[i] + strlen (GET_DATA_ADDRESS_PREFIX), "g_c1"))
+                data_p = &g_c1;
+            else if (std::strstr (argv[i] + strlen (GET_DATA_ADDRESS_PREFIX), "g_c2"))
+                data_p = &g_c2;
+
+                       pthread_mutex_lock (&g_print_mutex);
+            printf ("data address: %p\n", data_p);
+                       pthread_mutex_unlock (&g_print_mutex);
+        }
+        else if (std::strstr (argv[i], GET_HEAP_ADDRESS_COMMAND))
+        {
+                       // Create a byte array if not already present.
+                       if (!heap_array_up)
+                               heap_array_up.reset (new uint8_t[32]);
+
+                       pthread_mutex_lock (&g_print_mutex);
+            printf ("heap address: %p\n", heap_array_up.get ());
+                       pthread_mutex_unlock (&g_print_mutex);
+        }
+        else if (std::strstr (argv[i], GET_STACK_ADDRESS_COMMAND))
+        {
+                       pthread_mutex_lock (&g_print_mutex);
+            printf ("stack address: %p\n", &return_value);
+                       pthread_mutex_unlock (&g_print_mutex);
+        }
+        else if (std::strstr (argv[i], GET_CODE_ADDRESS_PREFIX))
+        {
+            void (*func_p)() = nullptr;
+
+            if (std::strstr (argv[i] + strlen (GET_CODE_ADDRESS_PREFIX), "hello"))
+                func_p = hello;
+            else if (std::strstr (argv[i] + strlen (GET_CODE_ADDRESS_PREFIX), "swap_chars"))
+                func_p = swap_chars;
+
+                       pthread_mutex_lock (&g_print_mutex);
+            printf ("code address: %p\n", func_p);
+                       pthread_mutex_unlock (&g_print_mutex);
+        }
+        else if (std::strstr (argv[i], CALL_FUNCTION_PREFIX))
+        {
+            // Defaut to providing the address of main.
+            if (std::strcmp (argv[i] + strlen (CALL_FUNCTION_PREFIX), "hello") == 0)
+                hello();
+            else if (std::strcmp (argv[i] + strlen (CALL_FUNCTION_PREFIX), "swap_chars") == 0)
+                swap_chars();
+            else
+            {
+                pthread_mutex_lock (&g_print_mutex);
+                printf ("unknown function: %s\n", argv[i] + strlen (CALL_FUNCTION_PREFIX));
+                pthread_mutex_unlock (&g_print_mutex);
+            }
+        }
+               else if (std::strstr (argv[i], THREAD_PREFIX))
+               {
+                       // Check if we're creating a new thread.
+                       if (std::strstr (argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_NEW))
+                       {
+                               // Create a new thread.
+                               pthread_t new_thread;
+                               const int err = ::pthread_create (&new_thread, nullptr, thread_func, nullptr);
+                           if (err)
+                               {
+                                       fprintf (stderr, "pthread_create() failed with error code %d\n", err);
+                                       exit (err);
+                               }
+                               threads.push_back (new_thread);
+                       }
+                       else if (std::strstr (argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_PRINT_IDS))
+                       {
+                               // Turn on thread id announcing.
+                               g_print_thread_ids = true;
+
+                               // And announce us.
+                               pthread_mutex_lock (&g_print_mutex);
+                               printf ("thread 0 id: ");
+                               print_thread_id ();
+                               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.
+                               // Later use thread index and send command to thread.
+                       }
+               }
+        else
+        {
+            // Treat the argument as text for stdout.
+            printf("%s\n", argv[i]);
+        }
+    }
+
+       // If we launched any threads, join them
+       for (std::vector<pthread_t>::iterator it = threads.begin (); it != threads.end (); ++it)
+       {
+               void *thread_retval = nullptr;
+               const int err = ::pthread_join (*it, &thread_retval);
+           if (err != 0)
+                       fprintf (stderr, "pthread_join() failed with error code %d\n", err);
+       }
+    
+    return return_value;
+}