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