Added gdb-remote test for software breakpoints.
authorTodd Fiala <todd.fiala@gmail.com>
Thu, 5 Jun 2014 16:34:13 +0000 (16:34 +0000)
committerTodd Fiala <todd.fiala@gmail.com>
Thu, 5 Jun 2014 16:34:13 +0000 (16:34 +0000)
Tests $Z0 and $z0.  Extends test exe get-code-address-hex:
to take a function name.

Enabled for debugserver, disabled for llgs.  Implementing
in llgs branch next.

llvm-svn: 210272

lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py
lldb/test/tools/lldb-gdbserver/main.cpp

index ef524dc..5f0c880 100644 (file)
@@ -49,7 +49,7 @@ class LldbGdbServerTestCase(TestBase):
         self.set_inferior_startup_launch()
 
         # Uncomment this code to force only a single test to run (by name).
-        # if not re.search(r"qMemoryRegionInfo", self._testMethodName):
+        # if not re.search(r"breakpoint", self._testMethodName):
         #     self.skipTest("focusing on one test")
 
     def reset_test_sequence(self):
@@ -1351,7 +1351,7 @@ class LldbGdbServerTestCase(TestBase):
     def qMemoryRegionInfo_reports_code_address_as_executable(self):
         # Start up the inferior.
         procs = self.prep_debug_monitor_and_inferior(
-            inferior_args=["get-code-address-hex:", "sleep:5"])
+            inferior_args=["get-code-address-hex:hello", "sleep:5"])
 
         # Run the process
         self.test_sequence.add_log_lines(
@@ -1534,6 +1534,101 @@ class LldbGdbServerTestCase(TestBase):
         self.set_inferior_startup_launch()
         self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable()
 
+    def software_breakpoint_set_and_remove_work(self):
+        # Start up the inferior.
+        procs = self.prep_debug_monitor_and_inferior(
+            inferior_args=["get-code-address-hex:hello", "sleep:1", "call-function:hello"])
+
+        # Run the process
+        self.test_sequence.add_log_lines(
+            [
+             # Start running after initial stop.
+             "read packet: $c#00",
+             # Match output line that prints the memory address of the function call entry point.
+             # Note we require launch-only testing so we can get inferior otuput.
+             { "type":"output_match", "regex":r"^code address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"function_address"} },
+             # Now stop the inferior.
+             "read packet: {}".format(chr(03)),
+             # And wait for the stop notification.
+             {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }],
+            True)
+
+        # Run the packet stream.
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        # Grab the address.
+        self.assertIsNotNone(context.get("function_address"))
+        function_address = int(context.get("function_address"), 16)
+
+        # Set the breakpoint.
+        # Note this might need to be switched per platform (ARM, mips, etc.).
+        BREAKPOINT_KIND = 1
+        
+        self.reset_test_sequence()
+        self.test_sequence.add_log_lines(
+            [
+             # Set the breakpoint.
+             "read packet: $Z0,{0:x},{1}#00".format(function_address, BREAKPOINT_KIND),
+             # Verify the stub could set it.
+             "send packet: $OK#00",
+             # Continue the inferior.
+             "read packet: $c#00",
+             # Expect a breakpoint stop report.
+             {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} },
+             ], True)
+             
+        # Run the packet stream.
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        # Verify the stop signal reported was the breakpoint signal number.
+        stop_signo = context.get("stop_signo")
+        self.assertIsNotNone(stop_signo)
+        self.assertEquals(int(stop_signo,16), signal.SIGTRAP)
+
+        # Ensure we did not receive any output.  If the breakpoint was not set, we would
+        # see output (from a launched process with captured stdio) printing a hello, world message.
+        # That would indicate the breakpoint didn't take.
+        self.assertEquals(len(context["O_content"]), 0)
+
+        # Verify that a breakpoint unset and continue gets us the expected output.
+        self.reset_test_sequence()
+        self.test_sequence.add_log_lines(
+            [
+             # Remove the breakpoint.
+             "read packet: $z0,{0:x},{1}#00".format(function_address, BREAKPOINT_KIND),
+             # Verify the stub could unset it.
+             "send packet: $OK#00",
+             # Continue running.
+             "read packet: $c#00",
+             # We should now receive the output from the call.
+             { "type":"output_match", "regex":r"^hello, world\r\n$" },
+             # And wait for program completion.
+             {"direction":"send", "regex":r"^\$W00(.*)#00" },
+             ], True)
+
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+        
+
+    @debugserver_test
+    @dsym_test
+    def test_software_breakpoint_set_and_remove_work_debugserver_dsym(self):
+        self.init_debugserver_test()
+        self.buildDsym()
+        self.set_inferior_startup_launch()
+        self.software_breakpoint_set_and_remove_work()
+
+    @llgs_test
+    @dwarf_test
+    @unittest2.expectedFailure()
+    def test_software_breakpoint_set_and_remove_work_llgs_dwarf(self):
+        self.init_llgs_test()
+        self.buildDwarf()
+        self.set_inferior_startup_launch()
+        self.software_breakpoint_set_and_remove_work()
+
 
 if __name__ == '__main__':
     unittest2.main()
index 933d778..1c3b8f0 100644 (file)
@@ -20,14 +20,16 @@ int pthread_threadid_np(pthread_t,__uint64_t*);
 #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 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 GET_MESSAGE_ADDRESS_COMMAND = "get-message-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_COMMAND = "get-code-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";
@@ -64,12 +66,12 @@ print_thread_id ()
 static void
 signal_handler (int signo)
 {
-       const char *signal_name = NULL;
+       const char *signal_name = nullptr;
        switch (signo)
        {
                case SIGUSR1: signal_name = "SIGUSR1"; break;
                case SIGSEGV: signal_name = "SIGSEGV"; break;
-               default:      signal_name = NULL;
+               default:      signal_name = nullptr;
        }
 
        // Print notice that we received the signal on a given thread.
@@ -111,6 +113,14 @@ signal_handler (int signo)
        }
 }
 
+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)
 {
@@ -144,7 +154,7 @@ thread_func (void *arg)
                // Test creating a SEGV.
                pthread_mutex_lock (&g_jump_buffer_mutex);
                g_is_segfaulting = true;
-               int *bad_p = NULL;
+               int *bad_p = nullptr;
                if (setjmp(g_jump_buffer) == 0)
                {
                        // Force a seg fault signal on this thread.
@@ -174,7 +184,7 @@ thread_func (void *arg)
                sleep_seconds_remaining = sleep (sleep_seconds_remaining);
        }
 
-       return NULL;
+       return nullptr;
 }
 
 int main (int argc, char **argv)
@@ -263,12 +273,30 @@ int main (int argc, char **argv)
             printf ("stack address: %p\n", &return_value);
                        pthread_mutex_unlock (&g_print_mutex);
         }
-        else if (std::strstr (argv[i], GET_CODE_ADDRESS_COMMAND))
+        else if (std::strstr (argv[i], GET_CODE_ADDRESS_PREFIX))
         {
+            // Defaut to providing the address of main.
+            void (*func_p)() = nullptr;
+
+            if (std::strstr (argv[i] + strlen (GET_CODE_ADDRESS_PREFIX), "hello"))
+                func_p = hello;
+
                        pthread_mutex_lock (&g_print_mutex);
-            printf ("code address: %p\n", main);
+            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
+            {
+                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.
@@ -276,7 +304,7 @@ int main (int argc, char **argv)
                        {
                                // Create a new thread.
                                pthread_t new_thread;
-                               const int err = ::pthread_create (&new_thread, NULL, thread_func, NULL);
+                               const int err = ::pthread_create (&new_thread, nullptr, thread_func, nullptr);
                            if (err)
                                {
                                        fprintf (stderr, "pthread_create() failed with error code %d\n", err);
@@ -316,7 +344,7 @@ int main (int argc, char **argv)
        // If we launched any threads, join them
        for (std::vector<pthread_t>::iterator it = threads.begin (); it != threads.end (); ++it)
        {
-               void *thread_retval = NULL;
+               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);