diff options
Diffstat (limited to 'packages/Python/lldbsuite/test/tools/lldb-server')
15 files changed, 1027 insertions, 721 deletions
diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/.clang-format b/packages/Python/lldbsuite/test/tools/lldb-server/.clang-format new file mode 100644 index 0000000000000..9b3aa8b7213b2 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/host-info/TestGdbRemoteHostInfo.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteHostInfo.py index 5089ee85773f5..5089ee85773f5 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/host-info/TestGdbRemoteHostInfo.py +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteHostInfo.py diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py index 57d4d5ab4bb1c..b361b9e6d9157 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py @@ -1,11 +1,13 @@ from __future__ import print_function +import json +import re + import gdbremote_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil - class TestGdbRemoteThreadsInStopReply( gdbremote_testcase.GdbRemoteTestCaseBase): @@ -16,7 +18,8 @@ class TestGdbRemoteThreadsInStopReply( "send packet: $OK#00", ] - def gather_stop_reply_threads(self, post_startup_log_lines, thread_count): + def gather_stop_reply_fields(self, post_startup_log_lines, thread_count, + field_names): # Set up the inferior args. inferior_args = [] for i in range(thread_count - 1): @@ -25,6 +28,9 @@ class TestGdbRemoteThreadsInStopReply( procs = self.prep_debug_monitor_and_inferior( inferior_args=inferior_args) + self.add_register_info_collection_packets() + self.add_process_info_collection_packets() + # Assumes test_sequence has anything added needed to setup the initial state. # (Like optionally enabling QThreadsInStopReply.) if post_startup_log_lines: @@ -34,6 +40,7 @@ class TestGdbRemoteThreadsInStopReply( ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) + hw_info = self.parse_hw_info(context) # Give threads time to start up, then break. time.sleep(1) @@ -77,14 +84,89 @@ class TestGdbRemoteThreadsInStopReply( kv_dict = self.parse_key_val_dict(key_vals_text) self.assertIsNotNone(kv_dict) + result = dict(); + result["pc_register"] = hw_info["pc_register"] + result["little_endian"] = hw_info["little_endian"] + for key_field in field_names: + result[key_field] = kv_dict.get(key_field) + + return result + + def gather_stop_reply_threads(self, post_startup_log_lines, thread_count): # Pull out threads from stop response. - stop_reply_threads_text = kv_dict.get("threads") + stop_reply_threads_text = self.gather_stop_reply_fields( + post_startup_log_lines, thread_count, ["threads"])["threads"] if stop_reply_threads_text: return [int(thread_id, 16) for thread_id in stop_reply_threads_text.split(",")] else: return [] + def gather_stop_reply_pcs(self, post_startup_log_lines, thread_count): + results = self.gather_stop_reply_fields( post_startup_log_lines, + thread_count, ["threads", "thread-pcs"]) + if not results: + return [] + + threads_text = results["threads"] + pcs_text = results["thread-pcs"] + thread_ids = threads_text.split(",") + pcs = pcs_text.split(",") + self.assertTrue(len(thread_ids) == len(pcs)) + + thread_pcs = dict() + for i in range(0, len(pcs)): + thread_pcs[int(thread_ids[i], 16)] = pcs[i] + + result = dict() + result["thread_pcs"] = thread_pcs + result["pc_register"] = results["pc_register"] + result["little_endian"] = results["little_endian"] + return result + + def switch_endian(self, egg): + return "".join(reversed(re.findall("..", egg))) + + def parse_hw_info(self, context): + self.assertIsNotNone(context) + process_info = self.parse_process_info_response(context) + endian = process_info.get("endian") + reg_info = self.parse_register_info_packets(context) + (pc_lldb_reg_index, pc_reg_info) = self.find_pc_reg_info(reg_info) + + hw_info = dict() + hw_info["pc_register"] = pc_lldb_reg_index + hw_info["little_endian"] = (endian == "little") + return hw_info + + def gather_threads_info_pcs(self, pc_register, little_endian): + self.reset_test_sequence() + self.test_sequence.add_log_lines( + [ + "read packet: $jThreadsInfo#c1", + { + "direction": "send", + "regex": r"^\$(.*)#[0-9a-fA-F]{2}$", + "capture": { + 1: "threads_info"}}, + ], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + threads_info = context.get("threads_info") + register = str(pc_register) + # The jThreadsInfo response is not valid JSON data, so we have to + # clean it up first. + jthreads_info = json.loads(re.sub(r"}]", "}", threads_info)) + thread_pcs = dict() + for thread_info in jthreads_info: + tid = thread_info["tid"] + pc = thread_info["registers"][register] + thread_pcs[tid] = self.switch_endian(pc) if little_endian else pc + + return thread_pcs + def QListThreadsInStopReply_supported(self): procs = self.prep_debug_monitor_and_inferior() self.test_sequence.add_log_lines( @@ -183,3 +265,34 @@ class TestGdbRemoteThreadsInStopReply( self.build() self.set_inferior_startup_launch() self.stop_reply_reports_correct_threads(5) + + def stop_reply_contains_thread_pcs(self, thread_count): + results = self.gather_stop_reply_pcs( + self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, thread_count) + stop_reply_pcs = results["thread_pcs"] + pc_register = results["pc_register"] + little_endian = results["little_endian"] + self.assertEqual(len(stop_reply_pcs), thread_count) + + threads_info_pcs = self.gather_threads_info_pcs(pc_register, + little_endian) + + self.assertEqual(len(threads_info_pcs), thread_count) + for thread_id in stop_reply_pcs: + self.assertTrue(thread_id in threads_info_pcs) + self.assertTrue(int(stop_reply_pcs[thread_id], 16) + == int(threads_info_pcs[thread_id], 16)) + + @llgs_test + def test_stop_reply_contains_thread_pcs_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.stop_reply_contains_thread_pcs(5) + + @debugserver_test + def test_stop_reply_contains_thread_pcs_debugserver(self): + self.init_debugserver_test() + self.build() + self.set_inferior_startup_launch() + self.stop_reply_contains_thread_pcs(5) diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py b/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py index b69ac3f8d2a30..6f36fbea470b0 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py +++ b/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py @@ -1082,7 +1082,7 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcod self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() - def software_breakpoint_set_and_remove_work(self): + def breakpoint_set_and_remove_work(self, want_hardware=False): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=[ @@ -1126,15 +1126,27 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcod self.assertIsNotNone(context.get("function_address")) function_address = int(context.get("function_address"), 16) + # Get current target architecture + target_arch = self.getArchitecture() + # Set the breakpoint. - if self.getArchitecture() == "arm": + if (target_arch == "arm") or (target_arch == "aarch64"): # TODO: Handle case when setting breakpoint in thumb code BREAKPOINT_KIND = 4 else: BREAKPOINT_KIND = 1 + + # Set default packet type to Z0 (software breakpoint) + z_packet_type = 0 + + # If hardware breakpoint is requested set packet type to Z1 + if want_hardware == True: + z_packet_type = 1 + self.reset_test_sequence() self.add_set_breakpoint_packets( function_address, + z_packet_type, do_continue=True, breakpoint_kind=BREAKPOINT_KIND) @@ -1182,13 +1194,15 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcod # Verify that a breakpoint remove and continue gets us the expected # output. self.reset_test_sequence() + + # Add breakpoint remove packets + self.add_remove_breakpoint_packets( + function_address, + z_packet_type, + breakpoint_kind=BREAKPOINT_KIND) + 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#63", # We should now receive the output from the call. @@ -1209,7 +1223,7 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcod else: self.build() self.set_inferior_startup_launch() - self.software_breakpoint_set_and_remove_work() + self.breakpoint_set_and_remove_work(want_hardware=False) @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") @@ -1221,7 +1235,35 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcod else: self.build() self.set_inferior_startup_launch() - self.software_breakpoint_set_and_remove_work() + self.breakpoint_set_and_remove_work(want_hardware=False) + + @debugserver_test + @skipUnlessPlatform(oslist=['linux']) + @expectedFailureAndroid + @skipIf(archs=no_match(['arm', 'aarch64'])) + def test_hardware_breakpoint_set_and_remove_work_debugserver(self): + self.init_debugserver_test() + if self.getArchitecture() == "arm": + # TODO: Handle case when setting breakpoint in thumb code + self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) + else: + self.build() + self.set_inferior_startup_launch() + self.breakpoint_set_and_remove_work(want_hardware=True) + + @llgs_test + @skipUnlessPlatform(oslist=['linux']) + @expectedFailureAndroid + @skipIf(archs=no_match(['arm', 'aarch64'])) + def test_hardware_breakpoint_set_and_remove_work_llgs(self): + self.init_llgs_test() + if self.getArchitecture() == "arm": + # TODO: Handle case when setting breakpoint in thumb code + self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) + else: + self.build() + self.set_inferior_startup_launch() + self.breakpoint_set_and_remove_work(want_hardware=True) def qSupported_returns_known_stub_features(self): # Start up the stub and start/prep the inferior. diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/main.cpp index b97c6ebc18e33..a3691a8d42b90 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/main.cpp +++ b/packages/Python/lldbsuite/test/tools/lldb-server/exit-code/main.cpp @@ -15,22 +15,24 @@ #if defined(__APPLE__) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2) -int pthread_threadid_np(pthread_t,__uint64_t*); +int pthread_threadid_np(pthread_t, __uint64_t *); #elif defined(__linux__) #include <sys/syscall.h> +#elif defined(__NetBSD__) +#include <lwp.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 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 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"; @@ -50,342 +52,304 @@ 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. +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))); + __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))); +#elif defined(__NetBSD__) + // Technically lwpid_t is 32-bit signed integer + printf("%" PRIx64, static_cast<uint64_t>(_lwp_self())); #else - printf("{no-tid-support}"); + 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 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'; +static void swap_chars() { + g_c1 = '1'; + g_c2 = '0'; - g_c1 = '0'; - g_c2 = '1'; + 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 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; +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]); +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; + } + + // 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; } diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py index ef6a0a21aaeb5..23cb869358572 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ b/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -883,12 +883,13 @@ class GdbRemoteTestCaseBase(TestBase): def add_set_breakpoint_packets( self, address, + z_packet_type=0, do_continue=True, breakpoint_kind=1): self.test_sequence.add_log_lines( [ # Set the breakpoint. - "read packet: $Z0,{0:x},{1}#00".format( - address, breakpoint_kind), + "read packet: $Z{2},{0:x},{1}#00".format( + address, breakpoint_kind, z_packet_type), # Verify the stub could set it. "send packet: $OK#00", ], True) @@ -904,11 +905,15 @@ class GdbRemoteTestCaseBase(TestBase): 2: "stop_thread_id"}}, ], True) - def add_remove_breakpoint_packets(self, address, breakpoint_kind=1): + def add_remove_breakpoint_packets( + self, + address, + z_packet_type=0, + breakpoint_kind=1): self.test_sequence.add_log_lines( [ # Remove the breakpoint. - "read packet: $z0,{0:x},{1}#00".format( - address, breakpoint_kind), + "read packet: $z{2},{0:x},{1}#00".format( + address, breakpoint_kind, z_packet_type), # Verify the stub could unset it. "send packet: $OK#00", ], True) @@ -929,7 +934,8 @@ class GdbRemoteTestCaseBase(TestBase): "qXfer:libraries:read", "qXfer:libraries-svr4:read", "qXfer:features:read", - "qEcho" + "qEcho", + "QPassSignals" ] def parse_qSupported_response(self, context): diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp index 69d60071aa455..ced7f7125088e 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp +++ b/packages/Python/lldbsuite/test/tools/lldb-server/inferior-crash/main.cpp @@ -2,38 +2,30 @@ #include <cstring> #include <iostream> -namespace -{ - const char *const SEGFAULT_COMMAND = "segfault"; - const char *const ABORT_COMMAND = "abort"; +namespace { +const char *const SEGFAULT_COMMAND = "segfault"; +const char *const ABORT_COMMAND = "abort"; } -int main (int argc, char **argv) -{ - if (argc < 2) - { - std::cout << "expected at least one command provided on the command line" << std::endl; - } +int main(int argc, char **argv) { + if (argc < 2) { + std::cout << "expected at least one command provided on the command line" + << std::endl; + } - // Process command line args. - for (int i = 1; i < argc; ++i) - { - const char *const command = argv[i]; - if (std::strstr (command, SEGFAULT_COMMAND)) - { - // Perform a null pointer access. - int *const null_int_ptr = nullptr; - *null_int_ptr = 0xDEAD; - } - else if (std::strstr (command, ABORT_COMMAND)) - { - std::abort(); - } - else - { - std::cout << "Unsupported command: " << command << std::endl; - } + // Process command line args. + for (int i = 1; i < argc; ++i) { + const char *const command = argv[i]; + if (std::strstr(command, SEGFAULT_COMMAND)) { + // Perform a null pointer access. + int *const null_int_ptr = nullptr; + *null_int_ptr = 0xDEAD; + } else if (std::strstr(command, ABORT_COMMAND)) { + std::abort(); + } else { + std::cout << "Unsupported command: " << command << std::endl; } + } - return 0; + return 0; } diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/main.cpp index b97c6ebc18e33..a574b41abf670 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/main.cpp +++ b/packages/Python/lldbsuite/test/tools/lldb-server/main.cpp @@ -1,3 +1,12 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + #include <cstdlib> #include <cstring> #include <errno.h> @@ -15,22 +24,24 @@ #if defined(__APPLE__) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2) -int pthread_threadid_np(pthread_t,__uint64_t*); +int pthread_threadid_np(pthread_t, __uint64_t *); #elif defined(__linux__) #include <sys/syscall.h> +#elif defined(__NetBSD__) +#include <lwp.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 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 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"; @@ -50,342 +61,304 @@ 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. +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))); + __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))); +#elif defined(__NetBSD__) + // Technically lwpid_t is 32-bit signed integer + printf("%" PRIx64, static_cast<uint64_t>(_lwp_self())); #else - printf("{no-tid-support}"); + 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 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'; +static void swap_chars() { + g_c1 = '1'; + g_c2 = '0'; - g_c1 = '0'; - g_c2 = '1'; + 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 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; +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]); +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; + } + + // 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; } diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp index 70ae5094fde77..c7ebe0759a403 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp +++ b/packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp @@ -1,7 +1,6 @@ #include <cstdio> -int main (int argc, char **argv) -{ - printf("argc: %d\n", argc); - return argv[0][0]; +int main(int argc, char **argv) { + printf("argc: %d\n", argc); + return argv[0][0]; } diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/Makefile b/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/Makefile new file mode 100644 index 0000000000000..314f1cb2f077b --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/TestGdbRemote_QPassSignals.py b/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/TestGdbRemote_QPassSignals.py new file mode 100644 index 0000000000000..7105bcb078bc6 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/TestGdbRemote_QPassSignals.py @@ -0,0 +1,115 @@ +# This test makes sure that lldb-server supports and properly handles +# QPassSignals GDB protocol package. +from __future__ import print_function + +import gdbremote_testcase +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestGdbRemote_QPassSignals(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def expect_signal(self, expected_signo): + self.test_sequence.add_log_lines(["read packet: $vCont;c#a8", + {"direction": "send", + "regex": r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", + "capture": {1: "hex_exit_code"}}, + ], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + hex_exit_code = context.get("hex_exit_code") + self.assertIsNotNone(hex_exit_code) + self.assertEqual(int(hex_exit_code, 16), expected_signo) + + def expect_exit_code(self, exit_code): + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#a8", + "send packet: $W{0:02x}#00".format(exit_code)], + True) + self.expect_gdbremote_sequence() + + + def ignore_signals(self, signals): + def signal_name_to_hex(signame): + return format(lldbutil.get_signal_number(signame), 'x') + signals_str = ";".join(map(signal_name_to_hex, signals)) + + self.test_sequence.add_log_lines(["read packet: $QPassSignals:" + + signals_str + " #00", + "send packet: $OK#00"], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + @llgs_test + @skipUnlessPlatform(["linux", "android"]) + def test_q_pass_signals(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + expected_signals = ["SIGSEGV", + "SIGALRM", "SIGFPE", "SIGBUS", "SIGINT", "SIGHUP"] + signals_to_ignore = ["SIGUSR1", "SIGUSR2"] + self.ignore_signals(signals_to_ignore) + for signal_name in expected_signals: + signo = lldbutil.get_signal_number(signal_name) + self.expect_signal(signo) + self.expect_exit_code(len(signals_to_ignore)) + + @llgs_test + @skipUnlessPlatform(["linux", "android"]) + def test_change_signals_at_runtime(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + expected_signals = ["SIGSEGV", "SIGUSR1", "SIGUSR2", + "SIGALRM", "SIGHUP"] + signals_to_ignore = ["SIGFPE", "SIGBUS", "SIGINT"] + + for signal_name in expected_signals: + signo = lldbutil.get_signal_number(signal_name) + self.expect_signal(signo) + if signal_name == "SIGALRM": + self.ignore_signals(signals_to_ignore) + self.expect_exit_code(len(signals_to_ignore)) + + @llgs_test + def test_default_signals_behavior(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + expected_signals = ["SIGSEGV", "SIGUSR1", "SIGUSR2", + "SIGALRM", "SIGFPE", "SIGBUS", "SIGINT", "SIGHUP"] + for signal_name in expected_signals: + signo = lldbutil.get_signal_number(signal_name) + self.expect_signal(signo) + self.expect_exit_code(0) + + + @llgs_test + @skipUnlessPlatform(["linux", "android"]) + def test_support_q_pass_signals(self): + self.init_llgs_test() + self.build() + + # Start up the stub and start/prep the inferior. + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + self.add_qSupported_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Retrieve the qSupported features and check QPassSignals+ + supported_dict = self.parse_qSupported_response(context) + self.assertEqual(supported_dict["QPassSignals"], "+") diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/main.cpp new file mode 100644 index 0000000000000..4991259fe46ed --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/main.cpp @@ -0,0 +1,37 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <signal.h> +#include <stdio.h> +#include <vector> + +static int signal_counter = 0; + +static void count_signal(int signo) { + ++signal_counter; + printf("Signal %d\n", signo); +} + +static void raise_signals() { + std::vector<int> signals( + {SIGSEGV, SIGUSR1, SIGUSR2, SIGALRM, SIGFPE, SIGBUS, SIGINT, SIGHUP}); + + for (int signal_num : signals) { + signal(signal_num, count_signal); + } + + for (int signal_num : signals) { + raise(signal_num); + } +} + +int main() { + raise_signals(); + return signal_counter; +} diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/host-info/Makefile b/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/Makefile index 1370b53b5a67a..8817fff55e8c0 100644 --- a/packages/Python/lldbsuite/test/tools/lldb-server/host-info/Makefile +++ b/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/Makefile @@ -1,10 +1,6 @@ LEVEL = ../../../make -VPATH = .. - -override CFLAGS_EXTRAS += -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS ENABLE_THREADS := YES CXX_SOURCES := main.cpp -MAKE_DSYM :=NO include $(LEVEL)/Makefile.rules diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/TestGdbRemoteThreadName.py b/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/TestGdbRemoteThreadName.py new file mode 100644 index 0000000000000..5bfcd660c2ace --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/TestGdbRemoteThreadName.py @@ -0,0 +1,41 @@ +from __future__ import print_function + +import gdbremote_testcase +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestGdbRemoteThreadName(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def run_and_check_name(self, expected_name): + self.test_sequence.add_log_lines(["read packet: $vCont;c#a8", + {"direction": "send", + "regex": + r"^\$T([0-9a-fA-F]{2})([^#]+)#[0-9a-fA-F]{2}$", + "capture": { + 1: "signal", + 2: "key_vals_text"}}, + ], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + sigint = lldbutil.get_signal_number("SIGINT") + self.assertEqual(sigint, int(context.get("signal"), 16)) + kv_dict = self.parse_key_val_dict(context.get("key_vals_text")) + self.assertEqual(expected_name, kv_dict.get("name")) + + @llgs_test + def test(self): + """ Make sure lldb-server can retrieve inferior thread name""" + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + + self.run_and_check_name("hello world") + self.run_and_check_name("goodbye world") diff --git a/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/main.cpp b/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/main.cpp new file mode 100644 index 0000000000000..0403031143ba5 --- /dev/null +++ b/packages/Python/lldbsuite/test/tools/lldb-server/thread-name/main.cpp @@ -0,0 +1,22 @@ +#include <pthread.h> +#include <signal.h> + +void set_thread_name(const char *name) { +#if defined(__APPLE__) + ::pthread_setname_np(name); +#elif defined(__FreeBSD__) + ::pthread_set_name_np(::pthread_self(), name); +#elif defined(__linux__) + ::pthread_setname_np(::pthread_self(), name); +#elif defined(__NetBSD__) + ::pthread_setname_np(::pthread_self(), "%s", name); +#endif +} + +int main() { + set_thread_name("hello world"); + raise(SIGINT); + set_thread_name("goodbye world"); + raise(SIGINT); + return 0; +} |