1 #include <atomic> 2 #include <chrono> 3 #include <cstdlib> 4 #include <cstring> 5 #include <errno.h> 6 #include <inttypes.h> 7 #include <memory> 8 #include <mutex> 9 #if !defined(_WIN32) 10 #include <pthread.h> 11 #include <signal.h> 12 #include <unistd.h> 13 #endif 14 #include "thread.h" 15 #include <setjmp.h> 16 #include <stdint.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <thread> 20 #include <time.h> 21 #include <vector> 22 23 static const char *const RETVAL_PREFIX = "retval:"; 24 static const char *const SLEEP_PREFIX = "sleep:"; 25 static const char *const STDERR_PREFIX = "stderr:"; 26 static const char *const SET_MESSAGE_PREFIX = "set-message:"; 27 static const char *const PRINT_MESSAGE_COMMAND = "print-message:"; 28 static const char *const GET_DATA_ADDRESS_PREFIX = "get-data-address-hex:"; 29 static const char *const GET_STACK_ADDRESS_COMMAND = "get-stack-address-hex:"; 30 static const char *const GET_HEAP_ADDRESS_COMMAND = "get-heap-address-hex:"; 31 32 static const char *const GET_CODE_ADDRESS_PREFIX = "get-code-address-hex:"; 33 static const char *const CALL_FUNCTION_PREFIX = "call-function:"; 34 35 static const char *const THREAD_PREFIX = "thread:"; 36 static const char *const THREAD_COMMAND_NEW = "new"; 37 static const char *const THREAD_COMMAND_PRINT_IDS = "print-ids"; 38 static const char *const THREAD_COMMAND_SEGFAULT = "segfault"; 39 40 static const char *const PRINT_PID_COMMAND = "print-pid"; 41 42 static bool g_print_thread_ids = false; 43 static std::mutex g_print_mutex; 44 static bool g_threads_do_segfault = false; 45 46 static std::mutex g_jump_buffer_mutex; 47 static jmp_buf g_jump_buffer; 48 static bool g_is_segfaulting = false; 49 50 static char g_message[256]; 51 52 static volatile char g_c1 = '0'; 53 static volatile char g_c2 = '1'; 54 55 static void print_pid() { 56 #if defined(_WIN32) 57 fprintf(stderr, "PID: %d\n", ::GetCurrentProcessId()); 58 #else 59 fprintf(stderr, "PID: %d\n", getpid()); 60 #endif 61 } 62 63 static void signal_handler(int signo) { 64 #if defined(_WIN32) 65 // No signal support on Windows. 66 #else 67 const char *signal_name = nullptr; 68 switch (signo) { 69 case SIGUSR1: 70 signal_name = "SIGUSR1"; 71 break; 72 case SIGSEGV: 73 signal_name = "SIGSEGV"; 74 break; 75 default: 76 signal_name = nullptr; 77 } 78 79 // Print notice that we received the signal on a given thread. 80 char buf[100]; 81 if (signal_name) 82 snprintf(buf, sizeof(buf), "received %s on thread id: %" PRIx64 "\n", signal_name, get_thread_id()); 83 else 84 snprintf(buf, sizeof(buf), "received signo %d (%s) on thread id: %" PRIx64 "\n", signo, strsignal(signo), get_thread_id()); 85 write(STDOUT_FILENO, buf, strlen(buf)); 86 87 // Reset the signal handler if we're one of the expected signal handlers. 88 switch (signo) { 89 case SIGSEGV: 90 if (g_is_segfaulting) { 91 // Fix up the pointer we're writing to. This needs to happen if nothing 92 // intercepts the SIGSEGV (i.e. if somebody runs this from the command 93 // line). 94 longjmp(g_jump_buffer, 1); 95 } 96 break; 97 case SIGUSR1: 98 if (g_is_segfaulting) { 99 // Fix up the pointer we're writing to. This is used to test gdb remote 100 // signal delivery. A SIGSEGV will be raised when the thread is created, 101 // switched out for a SIGUSR1, and then this code still needs to fix the 102 // seg fault. (i.e. if somebody runs this from the command line). 103 longjmp(g_jump_buffer, 1); 104 } 105 break; 106 } 107 108 // Reset the signal handler. 109 sig_t sig_result = signal(signo, signal_handler); 110 if (sig_result == SIG_ERR) { 111 fprintf(stderr, "failed to set signal handler: errno=%d\n", errno); 112 exit(1); 113 } 114 #endif 115 } 116 117 static void swap_chars() { 118 #if defined(__x86_64__) || defined(__i386__) 119 asm volatile("movb %1, (%2)\n\t" 120 "movb %0, (%3)\n\t" 121 "movb %0, (%2)\n\t" 122 "movb %1, (%3)\n\t" 123 : 124 : "i"('0'), "i"('1'), "r"(&g_c1), "r"(&g_c2) 125 : "memory"); 126 #elif defined(__aarch64__) 127 asm volatile("strb %w1, [%2]\n\t" 128 "strb %w0, [%3]\n\t" 129 "strb %w0, [%2]\n\t" 130 "strb %w1, [%3]\n\t" 131 : 132 : "r"('0'), "r"('1'), "r"(&g_c1), "r"(&g_c2) 133 : "memory"); 134 #elif defined(__arm__) 135 asm volatile("strb %1, [%2]\n\t" 136 "strb %0, [%3]\n\t" 137 "strb %0, [%2]\n\t" 138 "strb %1, [%3]\n\t" 139 : 140 : "r"('0'), "r"('1'), "r"(&g_c1), "r"(&g_c2) 141 : "memory"); 142 #else 143 #warning This may generate unpredictible assembly and cause the single-stepping test to fail. 144 #warning Please add appropriate assembly for your target. 145 g_c1 = '1'; 146 g_c2 = '0'; 147 148 g_c1 = '0'; 149 g_c2 = '1'; 150 #endif 151 } 152 153 static void hello() { 154 std::lock_guard<std::mutex> lock(g_print_mutex); 155 printf("hello, world\n"); 156 } 157 158 static void *thread_func(void *arg) { 159 static std::atomic<int> s_thread_index(1); 160 const int this_thread_index = s_thread_index++; 161 if (g_print_thread_ids) { 162 std::lock_guard<std::mutex> lock(g_print_mutex); 163 printf("thread %d id: %" PRIx64 "\n", this_thread_index, get_thread_id()); 164 } 165 166 if (g_threads_do_segfault) { 167 // Sleep for a number of seconds based on the thread index. 168 // TODO add ability to send commands to test exe so we can 169 // handle timing more precisely. This is clunky. All we're 170 // trying to do is add predictability as to the timing of 171 // signal generation by created threads. 172 int sleep_seconds = 2 * (this_thread_index - 1); 173 std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds)); 174 175 // Test creating a SEGV. 176 { 177 std::lock_guard<std::mutex> lock(g_jump_buffer_mutex); 178 g_is_segfaulting = true; 179 int *bad_p = nullptr; 180 if (setjmp(g_jump_buffer) == 0) { 181 // Force a seg fault signal on this thread. 182 *bad_p = 0; 183 } else { 184 // Tell the system we're no longer seg faulting. 185 // Used by the SIGUSR1 signal handler that we inject 186 // in place of the SIGSEGV so it only tries to 187 // recover from the SIGSEGV if this seg fault code 188 // was in play. 189 g_is_segfaulting = false; 190 } 191 } 192 193 { 194 std::lock_guard<std::mutex> lock(g_print_mutex); 195 printf("thread %" PRIx64 ": past SIGSEGV\n", get_thread_id()); 196 } 197 } 198 199 int sleep_seconds_remaining = 60; 200 std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds_remaining)); 201 202 return nullptr; 203 } 204 205 int main(int argc, char **argv) { 206 lldb_enable_attach(); 207 208 std::vector<std::thread> threads; 209 std::unique_ptr<uint8_t[]> heap_array_up; 210 int return_value = 0; 211 212 #if !defined(_WIN32) 213 // Set the signal handler. 214 sig_t sig_result = signal(SIGALRM, signal_handler); 215 if (sig_result == SIG_ERR) { 216 fprintf(stderr, "failed to set SIGALRM signal handler: errno=%d\n", errno); 217 exit(1); 218 } 219 220 sig_result = signal(SIGUSR1, signal_handler); 221 if (sig_result == SIG_ERR) { 222 fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno); 223 exit(1); 224 } 225 226 sig_result = signal(SIGSEGV, signal_handler); 227 if (sig_result == SIG_ERR) { 228 fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno); 229 exit(1); 230 } 231 #endif 232 233 // Process command line args. 234 for (int i = 1; i < argc; ++i) { 235 if (std::strstr(argv[i], STDERR_PREFIX)) { 236 // Treat remainder as text to go to stderr. 237 fprintf(stderr, "%s\n", (argv[i] + strlen(STDERR_PREFIX))); 238 } else if (std::strstr(argv[i], RETVAL_PREFIX)) { 239 // Treat as the return value for the program. 240 return_value = std::atoi(argv[i] + strlen(RETVAL_PREFIX)); 241 } else if (std::strstr(argv[i], SLEEP_PREFIX)) { 242 // Treat as the amount of time to have this process sleep (in seconds). 243 int sleep_seconds_remaining = std::atoi(argv[i] + strlen(SLEEP_PREFIX)); 244 245 // Loop around, sleeping until all sleep time is used up. Note that 246 // signals will cause sleep to end early with the number of seconds 247 // remaining. 248 std::this_thread::sleep_for( 249 std::chrono::seconds(sleep_seconds_remaining)); 250 251 } else if (std::strstr(argv[i], SET_MESSAGE_PREFIX)) { 252 // Copy the contents after "set-message:" to the g_message buffer. 253 // Used for reading inferior memory and verifying contents match 254 // expectations. 255 strncpy(g_message, argv[i] + strlen(SET_MESSAGE_PREFIX), 256 sizeof(g_message)); 257 258 // Ensure we're null terminated. 259 g_message[sizeof(g_message) - 1] = '\0'; 260 261 } else if (std::strstr(argv[i], PRINT_MESSAGE_COMMAND)) { 262 std::lock_guard<std::mutex> lock(g_print_mutex); 263 printf("message: %s\n", g_message); 264 } else if (std::strstr(argv[i], GET_DATA_ADDRESS_PREFIX)) { 265 volatile void *data_p = nullptr; 266 267 if (std::strstr(argv[i] + strlen(GET_DATA_ADDRESS_PREFIX), "g_message")) 268 data_p = &g_message[0]; 269 else if (std::strstr(argv[i] + strlen(GET_DATA_ADDRESS_PREFIX), "g_c1")) 270 data_p = &g_c1; 271 else if (std::strstr(argv[i] + strlen(GET_DATA_ADDRESS_PREFIX), "g_c2")) 272 data_p = &g_c2; 273 274 std::lock_guard<std::mutex> lock(g_print_mutex); 275 printf("data address: %p\n", data_p); 276 } else if (std::strstr(argv[i], GET_HEAP_ADDRESS_COMMAND)) { 277 // Create a byte array if not already present. 278 if (!heap_array_up) 279 heap_array_up.reset(new uint8_t[32]); 280 281 std::lock_guard<std::mutex> lock(g_print_mutex); 282 printf("heap address: %p\n", heap_array_up.get()); 283 284 } else if (std::strstr(argv[i], GET_STACK_ADDRESS_COMMAND)) { 285 std::lock_guard<std::mutex> lock(g_print_mutex); 286 printf("stack address: %p\n", &return_value); 287 } else if (std::strstr(argv[i], GET_CODE_ADDRESS_PREFIX)) { 288 void (*func_p)() = nullptr; 289 290 if (std::strstr(argv[i] + strlen(GET_CODE_ADDRESS_PREFIX), "hello")) 291 func_p = hello; 292 else if (std::strstr(argv[i] + strlen(GET_CODE_ADDRESS_PREFIX), 293 "swap_chars")) 294 func_p = swap_chars; 295 296 std::lock_guard<std::mutex> lock(g_print_mutex); 297 printf("code address: %p\n", func_p); 298 } else if (std::strstr(argv[i], CALL_FUNCTION_PREFIX)) { 299 void (*func_p)() = nullptr; 300 301 // Default to providing the address of main. 302 if (std::strcmp(argv[i] + strlen(CALL_FUNCTION_PREFIX), "hello") == 0) 303 func_p = hello; 304 else if (std::strcmp(argv[i] + strlen(CALL_FUNCTION_PREFIX), 305 "swap_chars") == 0) 306 func_p = swap_chars; 307 else { 308 std::lock_guard<std::mutex> lock(g_print_mutex); 309 printf("unknown function: %s\n", 310 argv[i] + strlen(CALL_FUNCTION_PREFIX)); 311 } 312 if (func_p) 313 func_p(); 314 } else if (std::strstr(argv[i], THREAD_PREFIX)) { 315 // Check if we're creating a new thread. 316 if (std::strstr(argv[i] + strlen(THREAD_PREFIX), THREAD_COMMAND_NEW)) { 317 threads.push_back(std::thread(thread_func, nullptr)); 318 } else if (std::strstr(argv[i] + strlen(THREAD_PREFIX), 319 THREAD_COMMAND_PRINT_IDS)) { 320 // Turn on thread id announcing. 321 g_print_thread_ids = true; 322 323 // And announce us. 324 { 325 std::lock_guard<std::mutex> lock(g_print_mutex); 326 printf("thread 0 id: %" PRIx64 "\n", get_thread_id()); 327 } 328 } else if (std::strstr(argv[i] + strlen(THREAD_PREFIX), 329 THREAD_COMMAND_SEGFAULT)) { 330 g_threads_do_segfault = true; 331 } else { 332 // At this point we don't do anything else with threads. 333 // Later use thread index and send command to thread. 334 } 335 } else if (std::strstr(argv[i], PRINT_PID_COMMAND)) { 336 print_pid(); 337 } else { 338 // Treat the argument as text for stdout. 339 printf("%s\n", argv[i]); 340 } 341 } 342 343 // If we launched any threads, join them 344 for (std::vector<std::thread>::iterator it = threads.begin(); 345 it != threads.end(); ++it) 346 it->join(); 347 348 return return_value; 349 } 350