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