brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.2 KiB · 7e84552 Raw
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