412 lines · cpp
1#include "attach.h"2#include <atomic>3#include <cassert>4#include <chrono>5#include <cstdlib>6#include <cstring>7#include <errno.h>8#include <fstream>9#include <future>10#include <inttypes.h>11#include <memory>12#include <mutex>13#if !defined(_WIN32)14#include <pthread.h>15#include <signal.h>16#include <unistd.h>17#endif18#include "thread.h"19#include <setjmp.h>20#include <stdint.h>21#include <stdio.h>22#include <string.h>23#include <string>24#include <thread>25#include <time.h>26#include <vector>27#if defined(__APPLE__)28#include <TargetConditionals.h>29#endif30 31static const char *const PRINT_PID_COMMAND = "print-pid";32 33static bool g_print_thread_ids = false;34static std::mutex g_print_mutex;35static bool g_threads_do_segfault = false;36 37static std::mutex g_jump_buffer_mutex;38static jmp_buf g_jump_buffer;39static bool g_is_segfaulting = false;40 41static char g_message[256];42 43static volatile char g_c1 = '0';44static volatile char g_c2 = '1';45 46static void print_pid() {47#if defined(_WIN32)48 fprintf(stderr, "PID: %d\n", ::GetCurrentProcessId());49#else50 fprintf(stderr, "PID: %d\n", getpid());51#endif52}53 54static void signal_handler(int signo) {55#if defined(_WIN32)56 // No signal support on Windows.57#else58 const char *signal_name = nullptr;59 switch (signo) {60 case SIGUSR1:61 signal_name = "SIGUSR1";62 break;63 case SIGSEGV:64 signal_name = "SIGSEGV";65 break;66 default:67 signal_name = nullptr;68 }69 70 // Print notice that we received the signal on a given thread.71 char buf[100];72 if (signal_name)73 snprintf(buf, sizeof(buf), "received %s on thread id: %" PRIx64 "\n", signal_name, get_thread_id());74 else75 snprintf(buf, sizeof(buf), "received signo %d (%s) on thread id: %" PRIx64 "\n", signo, strsignal(signo), get_thread_id());76 write(STDOUT_FILENO, buf, strlen(buf));77 78 // Reset the signal handler if we're one of the expected signal handlers.79 switch (signo) {80 case SIGSEGV:81 if (g_is_segfaulting) {82 // Fix up the pointer we're writing to. This needs to happen if nothing83 // intercepts the SIGSEGV (i.e. if somebody runs this from the command84 // line).85 longjmp(g_jump_buffer, 1);86 }87 break;88 case SIGUSR1:89 if (g_is_segfaulting) {90 // Fix up the pointer we're writing to. This is used to test gdb remote91 // signal delivery. A SIGSEGV will be raised when the thread is created,92 // switched out for a SIGUSR1, and then this code still needs to fix the93 // seg fault. (i.e. if somebody runs this from the command line).94 longjmp(g_jump_buffer, 1);95 }96 break;97 }98 99 // Reset the signal handler.100 sig_t sig_result = signal(signo, signal_handler);101 if (sig_result == SIG_ERR) {102 fprintf(stderr, "failed to set signal handler: errno=%d\n", errno);103 exit(1);104 }105#endif106}107 108static void swap_chars() {109#if defined(__x86_64__) || defined(__i386__)110 asm volatile("movb %1, (%2)\n\t"111 "movb %0, (%3)\n\t"112 "movb %0, (%2)\n\t"113 "movb %1, (%3)\n\t"114 :115 : "i"('0'), "i"('1'), "r"(&g_c1), "r"(&g_c2)116 : "memory");117#elif defined(__aarch64__)118 asm volatile("strb %w1, [%2]\n\t"119 "strb %w0, [%3]\n\t"120 "strb %w0, [%2]\n\t"121 "strb %w1, [%3]\n\t"122 :123 : "r"('0'), "r"('1'), "r"(&g_c1), "r"(&g_c2)124 : "memory");125#elif defined(__arm__)126 asm volatile("strb %1, [%2]\n\t"127 "strb %0, [%3]\n\t"128 "strb %0, [%2]\n\t"129 "strb %1, [%3]\n\t"130 :131 : "r"('0'), "r"('1'), "r"(&g_c1), "r"(&g_c2)132 : "memory");133#elif defined(__riscv)134 asm volatile("sb %1, (%2)\n\t"135 "sb %0, (%3)\n\t"136 "sb %0, (%2)\n\t"137 "sb %1, (%3)\n\t"138 :139 : "r"('0'), "r"('1'), "r"(&g_c1), "r"(&g_c2)140 : "memory");141 142#else143#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#endif151}152 153static void trap() {154#if defined(__x86_64__) || defined(__i386__)155 asm volatile("int3");156#elif defined(__aarch64__)157 asm volatile("brk #0xf000");158#elif defined(__arm__)159 asm volatile("udf #254");160#elif defined(__powerpc__)161 asm volatile("trap");162#elif __has_builtin(__builtin_debugtrap)163 __builtin_debugtrap();164#else165#warning Don't know how to generate a trap. Some tests may fail.166#endif167}168 169static void hello() {170 std::lock_guard<std::mutex> lock(g_print_mutex);171 printf("hello, world\n");172}173 174static void *thread_func(std::promise<void> ready) {175 ready.set_value();176 static std::atomic<int> s_thread_index(1);177 const int this_thread_index = s_thread_index++;178 if (g_print_thread_ids) {179 std::lock_guard<std::mutex> lock(g_print_mutex);180 printf("thread %d id: %" PRIx64 "\n", this_thread_index, get_thread_id());181 }182 183 if (g_threads_do_segfault) {184 // Sleep for a number of seconds based on the thread index.185 // TODO add ability to send commands to test exe so we can186 // handle timing more precisely. This is clunky. All we're187 // trying to do is add predictability as to the timing of188 // signal generation by created threads.189 int sleep_seconds = 2 * (this_thread_index - 1);190 std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds));191 192 // Test creating a SEGV.193 {194 std::lock_guard<std::mutex> lock(g_jump_buffer_mutex);195 g_is_segfaulting = true;196 int *bad_p = nullptr;197 if (setjmp(g_jump_buffer) == 0) {198 // Force a seg fault signal on this thread.199 *bad_p = 0;200 } else {201 // Tell the system we're no longer seg faulting.202 // Used by the SIGUSR1 signal handler that we inject203 // in place of the SIGSEGV so it only tries to204 // recover from the SIGSEGV if this seg fault code205 // was in play.206 g_is_segfaulting = false;207 }208 }209 210 {211 std::lock_guard<std::mutex> lock(g_print_mutex);212 printf("thread %" PRIx64 ": past SIGSEGV\n", get_thread_id());213 }214 }215 216 int sleep_seconds_remaining = 60;217 std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds_remaining));218 219 return nullptr;220}221 222static bool consume_front(std::string &str, const std::string &front) {223 if (str.find(front) != 0)224 return false;225 226 str = str.substr(front.size());227 return true;228}229 230int main(int argc, char **argv) {231 lldb_enable_attach();232 233 std::vector<std::thread> threads;234 std::unique_ptr<uint8_t[]> heap_array_up;235 int return_value = 0;236 237#if !defined(_WIN32)238 bool is_child = false;239 240 // Set the signal handler.241 sig_t sig_result = signal(SIGALRM, signal_handler);242 if (sig_result == SIG_ERR) {243 fprintf(stderr, "failed to set SIGALRM signal handler: errno=%d\n", errno);244 exit(1);245 }246 247 sig_result = signal(SIGUSR1, signal_handler);248 if (sig_result == SIG_ERR) {249 fprintf(stderr, "failed to set SIGUSR1 handler: errno=%d\n", errno);250 exit(1);251 }252 253 sig_result = signal(SIGSEGV, signal_handler);254 if (sig_result == SIG_ERR) {255 fprintf(stderr, "failed to set SIGSEGV handler: errno=%d\n", errno);256 exit(1);257 }258 259 sig_result = signal(SIGCHLD, SIG_IGN);260 if (sig_result == SIG_ERR) {261 fprintf(stderr, "failed to set SIGCHLD handler: errno=%d\n", errno);262 exit(1);263 }264#endif265 266 // Process command line args.267 for (int i = 1; i < argc; ++i) {268 std::string arg = argv[i];269 if (consume_front(arg, "syncfile:")) {270 // Write to this file to tell test framework that attaching is now271 // possible.272 std::ofstream(arg).close();273 } else if (consume_front(arg, "stderr:")) {274 // Treat remainder as text to go to stderr.275 fprintf(stderr, "%s\n", arg.c_str());276 } else if (consume_front(arg, "retval:")) {277 // Treat as the return value for the program.278 return_value = std::atoi(arg.c_str());279 } else if (consume_front(arg, "sleep:")) {280 // Treat as the amount of time to have this process sleep (in seconds).281 int sleep_seconds_remaining = std::atoi(arg.c_str());282 283 // Loop around, sleeping until all sleep time is used up. Note that284 // signals will cause sleep to end early with the number of seconds285 // remaining.286 std::this_thread::sleep_for(287 std::chrono::seconds(sleep_seconds_remaining));288 289 } else if (consume_front(arg, "set-message:")) {290 // Copy the contents after "set-message:" to the g_message buffer.291 // Used for reading inferior memory and verifying contents match292 // expectations.293 strncpy(g_message, arg.c_str(), sizeof(g_message));294 295 // Ensure we're null terminated.296 g_message[sizeof(g_message) - 1] = '\0';297 298 } else if (consume_front(arg, "print-message:")) {299 std::lock_guard<std::mutex> lock(g_print_mutex);300 printf("message: %s\n", g_message);301 } else if (consume_front(arg, "get-data-address-hex:")) {302 volatile void *data_p = nullptr;303 304 if (arg == "g_message")305 data_p = &g_message[0];306 else if (arg == "g_c1")307 data_p = &g_c1;308 else if (arg == "g_c2")309 data_p = &g_c2;310 311 std::lock_guard<std::mutex> lock(g_print_mutex);312 printf("data address: %p\n", data_p);313 } else if (consume_front(arg, "get-heap-address-hex:")) {314 // Create a byte array if not already present.315 if (!heap_array_up)316 heap_array_up.reset(new uint8_t[32]);317 318 std::lock_guard<std::mutex> lock(g_print_mutex);319 printf("heap address: %p\n", heap_array_up.get());320 321 } else if (consume_front(arg, "get-stack-address-hex:")) {322 std::lock_guard<std::mutex> lock(g_print_mutex);323 printf("stack address: %p\n", &return_value);324 } else if (consume_front(arg, "get-code-address-hex:")) {325 void (*func_p)() = nullptr;326 327 if (arg == "hello")328 func_p = hello;329 else if (arg == "swap_chars")330 func_p = swap_chars;331 332 std::lock_guard<std::mutex> lock(g_print_mutex);333 printf("code address: %p\n", func_p);334 } else if (consume_front(arg, "call-function:")) {335 void (*func_p)() = nullptr;336 337 if (arg == "hello")338 func_p = hello;339 else if (arg == "swap_chars")340 func_p = swap_chars;341 func_p();342#if !defined(_WIN32) && !defined(TARGET_OS_WATCH) && !defined(TARGET_OS_TV)343 } else if (arg == "fork") {344 pid_t fork_pid = fork();345 assert(fork_pid != -1);346 is_child = fork_pid == 0;347 } else if (arg == "vfork") {348 if (vfork() == 0)349 _exit(0);350 } else if (consume_front(arg, "process:sync:")) {351 // this is only valid after fork352 const char *filenames[] = {"parent", "child"};353 std::string my_file = arg + "." + filenames[is_child];354 std::string other_file = arg + "." + filenames[!is_child];355 356 // indicate that we're ready357 FILE *f = fopen(my_file.c_str(), "w");358 assert(f);359 fclose(f);360 361 // wait for the other process to be ready362 for (int i = 0; i < 5; ++i) {363 f = fopen(other_file.c_str(), "r");364 if (f)365 break;366 std::this_thread::sleep_for(std::chrono::milliseconds(125 * (1<<i)));367 }368 assert(f);369 fclose(f);370#endif371 } else if (consume_front(arg, "thread:new")) {372 std::promise<void> promise;373 std::future<void> ready = promise.get_future();374 threads.push_back(std::thread(thread_func, std::move(promise)));375 ready.wait();376 } else if (consume_front(arg, "thread:print-ids")) {377 // Turn on thread id announcing.378 g_print_thread_ids = true;379 380 // And announce us.381 {382 std::lock_guard<std::mutex> lock(g_print_mutex);383 printf("thread 0 id: %" PRIx64 "\n", get_thread_id());384 }385 } else if (consume_front(arg, "thread:segfault")) {386 g_threads_do_segfault = true;387 } else if (consume_front(arg, "print-pid")) {388 print_pid();389 } else if (consume_front(arg, "print-env:")) {390 // Print the value of specified envvar to stdout.391 const char *value = getenv(arg.c_str());392 printf("%s\n", value ? value : "__unset__");393 } else if (consume_front(arg, "trap")) {394 trap();395#if !defined(_WIN32)396 } else if (arg == "stop") {397 raise(SIGINT);398#endif399 } else {400 // Treat the argument as text for stdout.401 printf("%s\n", argv[i]);402 }403 }404 405 // If we launched any threads, join them406 for (std::vector<std::thread>::iterator it = threads.begin();407 it != threads.end(); ++it)408 it->join();409 410 return return_value;411}412