Subversion Repositories Games.Carmageddon

Rev

Rev 14 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // Based on https://gist.github.com/jvranish/4441299
  2.  
  3. #if defined(__linux__) || defined(__FreeBSD__) // Pierre-Marie Baty -- added compile guard
  4.  
  5. #define _GNU_SOURCE
  6. #include "harness/config.h"
  7. #include "harness/os.h"
  8. #include <assert.h>
  9. #include <ctype.h>
  10. #include <dirent.h>
  11. #include <err.h>
  12. #include <errno.h>
  13. #include <execinfo.h>
  14. #include <fcntl.h>
  15. #include <libgen.h>
  16. #include <limits.h>
  17. #include <link.h>
  18. #include <signal.h>
  19. #include <stdbool.h>
  20. #include <stdint.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/stat.h>
  25. #include <sys/types.h>
  26. #include <termios.h>
  27. #include <time.h>
  28. #include <unistd.h>
  29.  
  30. #define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0]))
  31. #define MAX_STACK_FRAMES 64
  32. #define TRACER_PID_STRING "TracerPid:"
  33.  
  34. static int stack_nbr = 0;
  35. static char _program_name[1024];
  36.  
  37. static void* stack_traces[MAX_STACK_FRAMES];
  38. static char name_buf[4096];
  39.  
  40. struct dl_iterate_callback_data {
  41.     int initialized;
  42.     intptr_t start;
  43. } dethrace_dl_data;
  44.  
  45. static int dl_iterate_callback(struct dl_phdr_info* info, size_t size, void* data) {
  46.     struct dl_iterate_callback_data* callback_data = data;
  47.  
  48.     if (strcmp(info->dlpi_name, "") == 0) {
  49.         callback_data->start = info->dlpi_addr;
  50.     }
  51.     return 0;
  52. }
  53.  
  54. static intptr_t get_dethrace_offset(void) {
  55.     if (!dethrace_dl_data.initialized) {
  56.         dethrace_dl_data.initialized = 1;
  57.         dl_iterate_phdr(dl_iterate_callback, &dethrace_dl_data);
  58.     }
  59.     return dethrace_dl_data.start;
  60. }
  61.  
  62. // Resolve symbol name and source location given the path to the executable and an address
  63. int addr2line(char const* const program_name, void const* const addr) {
  64.     static char addr2line_cmd[MAXPATHLEN]; // Pierre-Marie Baty -- use the heap instead
  65.  
  66.     /* have addr2line map the address to the related line in the code */
  67.     sprintf(addr2line_cmd, "addr2line -f -p -e \"%s\" %p", program_name, addr - get_dethrace_offset()); // Pierre-Marie Baty -- enclosed program_name within quotes to support pathnames with spaces
  68.  
  69.     fprintf(stderr, "%d: ", stack_nbr++);
  70.     return system(addr2line_cmd);
  71. }
  72.  
  73. static void print_stack_trace(void) {
  74.     int i, trace_size = 0;
  75.     char** messages = (char**)NULL;
  76.  
  77.     fputs("\nStack trace:\n", stderr);
  78.  
  79.     trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
  80.     messages = backtrace_symbols(stack_traces, trace_size);
  81.  
  82.     /* skip the first couple stack frames (as they are this function and
  83.      our handler) and also skip the last frame as it's (always?) junk. */
  84.     for (i = 3; i < (trace_size - 1); ++i) {
  85.         if (addr2line(_program_name, stack_traces[i]) != 0) {
  86.             printf("  error determining line # for: %s\n", messages[i]);
  87.         }
  88.     }
  89.     if (messages) {
  90.         free(messages);
  91.     }
  92. }
  93.  
  94. static void signal_handler(int sig, siginfo_t* siginfo, void* context) {
  95.     (void)context;
  96.     fputs("\n******************\n", stderr);
  97.  
  98.     switch (sig) {
  99.     case SIGSEGV:
  100.         fputs("Caught SIGSEGV\n", stderr);
  101.         break;
  102.     case SIGINT:
  103.         fputs("Caught SIGINT\n", stderr);
  104.         break;
  105.     case SIGFPE:
  106.         switch (siginfo->si_code) {
  107.         case FPE_INTDIV:
  108.             fputs("Caught SIGFPE: FPE_INTDIV\n", stderr);
  109.             break;
  110.         case FPE_INTOVF:
  111.             fputs("Caught SIGFPE: FPE_INTOVF\n", stderr);
  112.             break;
  113.         case FPE_FLTDIV:
  114.             fputs("Caught SIGFPE: FPE_FLTDIV\n", stderr);
  115.             break;
  116.         case FPE_FLTOVF:
  117.             fputs("Caught SIGFPE: FPE_FLTOVF\n", stderr);
  118.             break;
  119.         case FPE_FLTUND:
  120.             fputs("Caught SIGFPE: FPE_FLTUND\n", stderr);
  121.             break;
  122.         case FPE_FLTRES:
  123.             fputs("Caught SIGFPE: FPE_FLTRES\n", stderr);
  124.             break;
  125.         case FPE_FLTINV:
  126.             fputs("Caught SIGFPE: FPE_FLTINV\n", stderr);
  127.             break;
  128.         case FPE_FLTSUB:
  129.             fputs("Caught SIGFPE: FPE_FLTSUB\n", stderr);
  130.             break;
  131.         default:
  132.             fputs("Caught SIGFPE: Arithmetic Exception\n", stderr);
  133.             break;
  134.         }
  135.         break;
  136.     case SIGILL:
  137.         switch (siginfo->si_code) {
  138.         case ILL_ILLOPC:
  139.             fputs("Caught SIGILL: ILL_ILLOPC\n", stderr);
  140.             break;
  141.         case ILL_ILLOPN:
  142.             fputs("Caught SIGILL: ILL_ILLOPN\n", stderr);
  143.             break;
  144.         case ILL_ILLADR:
  145.             fputs("Caught SIGILL: ILL_ILLADR\n", stderr);
  146.             break;
  147.         case ILL_ILLTRP:
  148.             fputs("Caught SIGILL: ILL_ILLTRP\n", stderr);
  149.             break;
  150.         case ILL_PRVOPC:
  151.             fputs("Caught SIGILL: ILL_PRVOPC\n", stderr);
  152.             break;
  153.         case ILL_PRVREG:
  154.             fputs("Caught SIGILL: ILL_PRVREG\n", stderr);
  155.             break;
  156.         case ILL_COPROC:
  157.             fputs("Caught SIGILL: ILL_COPROC\n", stderr);
  158.             break;
  159.         case ILL_BADSTK:
  160.             fputs("Caught SIGILL: ILL_BADSTK\n", stderr);
  161.             break;
  162.         default:
  163.             fputs("Caught SIGILL: Illegal Instruction\n", stderr);
  164.             break;
  165.         }
  166.         break;
  167.     case SIGTERM:
  168.         fputs("Caught SIGTERM\n", stderr);
  169.         break;
  170.     case SIGABRT:
  171.         fputs("Caught SIGABRT\n", stderr);
  172.         break;
  173.     default:
  174.         break;
  175.     }
  176.     fputs("******************\n", stderr);
  177.     print_stack_trace();
  178.     exit(1);
  179. }
  180.  
  181. void resolve_full_path(char* path, const char* argv0) {
  182.     if (argv0[0] == '/') { // run with absolute path
  183.         strcpy(path, argv0);
  184.     } else { // run with relative path
  185.         if (NULL == getcwd(path, PATH_MAX)) {
  186.             perror("getcwd error");
  187.             return;
  188.         }
  189.         strcat(path, "/");
  190.         strcat(path, argv0);
  191.     }
  192. }
  193.  
  194. void OS_InstallSignalHandler(char* program_name) {
  195.     resolve_full_path(_program_name, program_name);
  196.  
  197.     /* setup alternate stack */
  198.     {
  199.         stack_t ss = {};
  200.         ss.ss_sp = malloc(2 * MINSIGSTKSZ);
  201.         if (ss.ss_sp == NULL) {
  202.             err(1, "malloc");
  203.         }
  204.         ss.ss_size = 2 * MINSIGSTKSZ;
  205.         ss.ss_flags = 0;
  206.  
  207.         if (sigaltstack(&ss, NULL) != 0) {
  208.             err(1, "sigaltstack");
  209.         }
  210.     }
  211.  
  212.     /* register our signal handlers */
  213.     {
  214.         struct sigaction sig_action = {};
  215.         sig_action.sa_sigaction = signal_handler;
  216.         sigemptyset(&sig_action.sa_mask);
  217.  
  218.         sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
  219.  
  220.         if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
  221.             err(1, "sigaction");
  222.         }
  223.         if (sigaction(SIGFPE, &sig_action, NULL) != 0) {
  224.             err(1, "sigaction");
  225.         }
  226.         if (sigaction(SIGINT, &sig_action, NULL) != 0) {
  227.             err(1, "sigaction");
  228.         }
  229.         if (sigaction(SIGILL, &sig_action, NULL) != 0) {
  230.             err(1, "sigaction");
  231.         }
  232.         if (sigaction(SIGTERM, &sig_action, NULL) != 0) {
  233.             err(1, "sigaction");
  234.         }
  235.         if (sigaction(SIGABRT, &sig_action, NULL) != 0) {
  236.             err(1, "sigaction");
  237.         }
  238.     }
  239. }
  240.  
  241. FILE* OS_fopen(const char* pathname, const char* mode) {
  242.     FILE* f = fopen(pathname, mode);
  243.     if (f != NULL) {
  244.         return f;
  245.     }
  246.     char buffer[512];
  247.     char buffer2[512];
  248.     strcpy(buffer, pathname);
  249.     strcpy(buffer2, pathname);
  250.     char* pDirName = dirname(buffer);
  251.     char* pBaseName = basename(buffer2);
  252.     DIR* pDir = opendir(pDirName);
  253.     if (pDir == NULL) {
  254.         return NULL;
  255.     }
  256.     for (struct dirent* pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir)) {
  257.         if (strcasecmp(pBaseName, pDirent->d_name) == 0) {
  258.             strcat(pDirName, "/");
  259.             strcat(pDirName, pDirent->d_name);
  260.             f = fopen(pDirName, mode);
  261.             break;
  262.         }
  263.     }
  264.     closedir(pDir);
  265.     if (harness_game_config.verbose) {
  266.         if (f == NULL) {
  267.             fprintf(stderr, "Failed to open \"%s\" (%s)\n", pathname, strerror(errno));
  268.         }
  269.     }
  270.     return f;
  271. }
  272.  
  273. size_t OS_ConsoleReadPassword(char* pBuffer, size_t pBufferLen) {
  274.     struct termios old, new;
  275.     char c;
  276.     size_t len;
  277.  
  278.     tcgetattr(STDIN_FILENO, &old);
  279.     new = old;
  280.     new.c_lflag &= ~(ICANON | ECHO);
  281.     tcsetattr(STDIN_FILENO, TCSAFLUSH, &new);
  282.  
  283.     len = 0;
  284.     pBuffer[len] = '\0';
  285.     while (1) {
  286.         if (read(STDIN_FILENO, &c, 1) == 1) {
  287.             if (c == 0x7f) {
  288.                 if (len > 0) {
  289.                     len--;
  290.                     pBuffer[len] = '\0';
  291.                     printf("\033[1D \033[1D");
  292.                     fflush(stdout);
  293.                     continue;
  294.                 }
  295.             } else if (c == '\r' || c == '\n') {
  296.                 printf("\n");
  297.                 fflush(stdout);
  298.                 break;
  299.             } else if (len < pBufferLen - 1) {
  300.                 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
  301.                     pBuffer[len] = c;
  302.                     printf("*");
  303.                     fflush(stdout);
  304.                     len++;
  305.                     pBuffer[len] = '\0';
  306.                 }
  307.             }
  308.         }
  309.     }
  310.  
  311.     tcsetattr(STDIN_FILENO, TCSANOW, &old);
  312.     return len;
  313. }
  314.  
  315. char* OS_Dirname(const char* path) {
  316.     strcpy(name_buf, path);
  317.     return dirname(name_buf);
  318. }
  319.  
  320. char* OS_Basename(const char* path) {
  321.     strcpy(name_buf, path);
  322.     return basename(name_buf);
  323. }
  324.  
  325. #endif // __linux__
  326.