Added gdb-remote $qMemoryRegionInfo tests for heap and stack.
authorTodd Fiala <todd.fiala@gmail.com>
Wed, 4 Jun 2014 16:42:12 +0000 (16:42 +0000)
committerTodd Fiala <todd.fiala@gmail.com>
Wed, 4 Jun 2014 16:42:12 +0000 (16:42 +0000)
Added two new tests: one to verify that a test exe heap address
returned is readable and writeable, and a similar one to verify
a test exe stack address is readable and writeable.

Ran the main.cpp test exe code through the Xcode re-indenter.
I was using TextMate to edit the test's C++ code alongside the
Python code but last check-in found that it was not handling
tabs/indentation the way I am intending it.

Modified test exe to require C++11.

Refactored gdb remote python code's handling of memory region
info into more re-usable methods.

llvm-svn: 210196

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

index 39ede97..b0285fa 100644 (file)
@@ -1,6 +1,6 @@
 LEVEL = ../../make
 
-CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS
+CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -std=c++11
 LD_EXTRAS := -lpthread
 CXX_SOURCES := main.cpp
 MAKE_DSYM :=NO
index 76f8e4f..ef524dc 100644 (file)
@@ -255,7 +255,34 @@ class LldbGdbServerTestCase(TestBase):
         self.assertTrue("encoding" in reg_info)
         self.assertTrue("format" in reg_info)
 
-    def assert_address_within_range(self, test_address, range_start, range_size):
+    def add_query_memory_region_packets(self, address):
+        self.test_sequence.add_log_lines(
+            ["read packet: $qMemoryRegionInfo:{0:x}#00".format(address),
+             {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"memory_region_response"} }],
+            True)
+        
+    def parse_memory_region_packet(self, context):
+        # Ensure we have a context.
+        self.assertIsNotNone(context.get("memory_region_response"))
+        
+        # Pull out key:value; pairs.
+        mem_region_dict = {match.group(1):match.group(2) for match in re.finditer(r"([^:]+):([^;]+);", context.get("memory_region_response"))}
+
+        # Validate keys are known.
+        for (key, val) in mem_region_dict.items():
+            self.assertTrue(key in ["start", "size", "permissions", "error"])
+            self.assertIsNotNone(val)
+
+        # Return the dictionary of key-value pairs for the memory region.
+        return mem_region_dict
+
+    def assert_address_within_memory_region(self, test_address, mem_region_dict):
+        self.assertIsNotNone(mem_region_dict)
+        self.assertTrue("start" in mem_region_dict)
+        self.assertTrue("size" in mem_region_dict)
+        
+        range_start = int(mem_region_dict["start"], 16)
+        range_size = int(mem_region_dict["size"], 16)
         range_end = range_start + range_size
 
         if test_address < range_start:
@@ -1350,35 +1377,20 @@ class LldbGdbServerTestCase(TestBase):
 
         # Grab memory region info from the inferior.
         self.reset_test_sequence()
-        self.test_sequence.add_log_lines(
-            ["read packet: $qMemoryRegionInfo:{0:x}#00".format(code_address),
-             {"direction":"send", "regex":r"^\$(.+)#[0-9a-fA-F]{2}$", "capture":{1:"memory_region_response"} }],
-            True)
+        self.add_query_memory_region_packets(code_address)
 
         # Run the packet stream.
         context = self.expect_gdbremote_sequence()
         self.assertIsNotNone(context)
-
-        # Parse the memory region response
-        self.assertIsNotNone(context.get("memory_region_response"))
-        mem_region_dict = {match.group(1):match.group(2) for match in re.finditer(r"([^:]+):([^;]+);", context.get("memory_region_response"))}
-        # print "mem_region_dict: {}".format(mem_region_dict)
-        
-        for (key, val) in mem_region_dict.items():
-            self.assertTrue(key in ["start", "size", "permissions", "error"])
-            self.assertIsNotNone(val)
+        mem_region_dict = self.parse_memory_region_packet(context)
 
         # Ensure code address is readable and executable.
         self.assertTrue("permissions" in mem_region_dict)
         self.assertTrue("r" in mem_region_dict["permissions"])
         self.assertTrue("x" in mem_region_dict["permissions"])
-
-        # Ensure it has a start address and a size.
-        self.assertTrue("start" in mem_region_dict)
-        self.assertTrue("size" in mem_region_dict)
         
         # Ensure the start address and size encompass the address we queried.
-        self.assert_address_within_range(code_address, int(mem_region_dict["start"], 16), int(mem_region_dict["size"], 16))
+        self.assert_address_within_memory_region(code_address, mem_region_dict)
         
 
     @debugserver_test
@@ -1398,6 +1410,129 @@ class LldbGdbServerTestCase(TestBase):
         self.set_inferior_startup_launch()
         self.qMemoryRegionInfo_reports_code_address_as_executable()
 
+    def qMemoryRegionInfo_reports_stack_address_as_readable_writeable(self):
+        # Start up the inferior.
+        procs = self.prep_debug_monitor_and_inferior(
+            inferior_args=["get-stack-address-hex:", "sleep:5"])
+
+        # 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 message buffer within the inferior. 
+             # Note we require launch-only testing so we can get inferior otuput.
+             { "type":"output_match", "regex":r"^stack address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"stack_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("stack_address"))
+        stack_address = int(context.get("stack_address"), 16)
+
+        # Grab memory region info from the inferior.
+        self.reset_test_sequence()
+        self.add_query_memory_region_packets(stack_address)
+
+        # Run the packet stream.
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+        mem_region_dict = self.parse_memory_region_packet(context)
+
+        # Ensure address is readable and executable.
+        self.assertTrue("permissions" in mem_region_dict)
+        self.assertTrue("r" in mem_region_dict["permissions"])
+        self.assertTrue("w" in mem_region_dict["permissions"])
+
+        # Ensure the start address and size encompass the address we queried.
+        self.assert_address_within_memory_region(stack_address, mem_region_dict)
+
+
+    @debugserver_test
+    @dsym_test
+    def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_debugserver_dsym(self):
+        self.init_debugserver_test()
+        self.buildDsym()
+        self.set_inferior_startup_launch()
+        self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable()
+
+    @llgs_test
+    @dwarf_test
+    @unittest2.expectedFailure()
+    def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_llgs_dwarf(self):
+        self.init_llgs_test()
+        self.buildDwarf()
+        self.set_inferior_startup_launch()
+        self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable()
+
+    def qMemoryRegionInfo_reports_heap_address_as_readable_writeable(self):
+        # Start up the inferior.
+        procs = self.prep_debug_monitor_and_inferior(
+            inferior_args=["get-heap-address-hex:", "sleep:5"])
+
+        # 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 message buffer within the inferior. 
+             # Note we require launch-only testing so we can get inferior otuput.
+             { "type":"output_match", "regex":r"^heap address: 0x([0-9a-fA-F]+)\r\n$", "capture":{ 1:"heap_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("heap_address"))
+        heap_address = int(context.get("heap_address"), 16)
+
+        # Grab memory region info from the inferior.
+        self.reset_test_sequence()
+        self.add_query_memory_region_packets(heap_address)
+
+        # Run the packet stream.
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+        mem_region_dict = self.parse_memory_region_packet(context)
+
+        # Ensure address is readable and executable.
+        self.assertTrue("permissions" in mem_region_dict)
+        self.assertTrue("r" in mem_region_dict["permissions"])
+        self.assertTrue("w" in mem_region_dict["permissions"])
+
+        # Ensure the start address and size encompass the address we queried.
+        self.assert_address_within_memory_region(heap_address, mem_region_dict)
+
+
+    @debugserver_test
+    @dsym_test
+    def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_debugserver_dsym(self):
+        self.init_debugserver_test()
+        self.buildDsym()
+        self.set_inferior_startup_launch()
+        self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable()
+
+    @llgs_test
+    @dwarf_test
+    @unittest2.expectedFailure()
+    def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_llgs_dwarf(self):
+        self.init_llgs_test()
+        self.buildDwarf()
+        self.set_inferior_startup_launch()
+        self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable()
 
 
 if __name__ == '__main__':
index 4a2784a..933d778 100644 (file)
@@ -2,6 +2,7 @@
 #include <cstring>
 #include <errno.h>
 #include <inttypes.h>
+#include <memory>
 #include <pthread.h>
 #include <setjmp.h>
 #include <signal.h>
@@ -29,9 +30,9 @@ 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 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 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;
@@ -84,21 +85,21 @@ signal_handler (int signo)
        // 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:
-               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;
+        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:
+            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.
@@ -139,7 +140,7 @@ thread_func (void *arg)
                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;
@@ -166,7 +167,7 @@ thread_func (void *arg)
                printf (": past SIGSEGV\n");
                pthread_mutex_unlock (&g_print_mutex);
        }
-       
+
        int sleep_seconds_remaining = 5;
        while (sleep_seconds_remaining > 0)
        {
@@ -203,7 +204,7 @@ int main (int argc, char **argv)
                fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno);
                exit (1);
        }
-       
+
        // Process command line args.
     for (int i = 1; i < argc; ++i)
     {
@@ -221,7 +222,7 @@ int main (int argc, char **argv)
         {
             // 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)
@@ -238,7 +239,7 @@ int main (int argc, char **argv)
 
                        // Ensure we're null terminated.
                        g_message[sizeof (g_message) - 1] = '\0';
-                       
+
                }
         else if (std::strstr (argv[i], GET_MESSAGE_ADDRESS_COMMAND))
         {
@@ -251,7 +252,7 @@ int main (int argc, char **argv)
                        // 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);
@@ -287,7 +288,7 @@ int main (int argc, char **argv)
                        {
                                // Turn on thread id announcing.
                                g_print_thread_ids = true;
-                               
+
                                // And announce us.
                                pthread_mutex_lock (&g_print_mutex);
                                printf ("thread 0 id: ");
@@ -320,6 +321,6 @@ int main (int argc, char **argv)
            if (err != 0)
                        fprintf (stderr, "pthread_join() failed with error code %d\n", err);
        }
-
+    
     return return_value;
 }