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. #ifdef __APPLE__ // Pierre-Marie Baty -- added compile guard
  4.  
  5. #include "harness/config.h"
  6. #include "harness/os.h"
  7. #include <assert.h>
  8. #include <dirent.h>
  9. #include <err.h>
  10. #include <errno.h>
  11. #include <execinfo.h>
  12. #include <inttypes.h>
  13. #include <libgen.h>
  14. #include <limits.h>
  15. #include <mach-o/dyld.h>
  16. #include <signal.h>
  17. #include <stdbool.h>
  18. #include <stdint.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <sys/sysctl.h>
  23. #include <sys/types.h>
  24. #include <time.h>
  25. #include <unistd.h>
  26.  
  27. #define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0]))
  28.  
  29. static int stack_nbr = 0;
  30. static char _program_name[1024];
  31. #define MAX_STACK_FRAMES 64
  32. static void* stack_traces[MAX_STACK_FRAMES];
  33. static char name_buf[4096];
  34.  
  35. // Resolve symbol name and source location given the path to the executable and an address
  36. int addr2line(char const* const program_name, intptr_t slide, void const* const addr) {
  37.     static char addr2line_cmd[MAXPATHLEN]; // Pierre-Marie Baty -- use the heap instead
  38.  
  39.     /* have addr2line map the address to the related line in the code */
  40.     sprintf(addr2line_cmd, "atos -s %" PRIxPTR " -o \"%s\" %p", slide, program_name, addr); // Pierre-Marie Baty -- enclosed program_name within quotes to support pathnames with spaces
  41.  
  42.     // printf("addr2line command: %s\n", addr2line_cmd);
  43.  
  44.     fprintf(stderr, "%d: ", stack_nbr++);
  45.     return system(addr2line_cmd);
  46. }
  47.  
  48. static void print_stack_trace(void) {
  49.     int i, trace_size = 0;
  50.     char** messages = (char**)NULL;
  51.     intptr_t slide;
  52.  
  53.     trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
  54.     messages = backtrace_symbols(stack_traces, trace_size);
  55.     slide = _dyld_get_image_vmaddr_slide(0);
  56.  
  57.     fputs("\nStack trace:\n", stderr);
  58.  
  59.     /* skip the first couple stack frames (as they are this function and
  60.      our handler) and also skip the last frame as it's (always?) junk. */
  61.     for (i = 3; i < (trace_size - 1); i++) {
  62.  
  63.         // seem to get a duplicate frame right where the error happens
  64.         if (stack_traces[i] == stack_traces[i - 1]) {
  65.             continue;
  66.         }
  67.         if (addr2line(_program_name, slide, stack_traces[i]) != 0) {
  68.             printf("  error determining line # for: %s\n", messages[i]);
  69.         }
  70.     }
  71.     if (messages) {
  72.         free(messages);
  73.     }
  74. }
  75.  
  76. void signal_handler(int sig, siginfo_t* siginfo, void* context) {
  77.     (void)context;
  78.     fputs("\n******************\n", stderr);
  79.  
  80.     switch (sig) {
  81.     case SIGSEGV:
  82.         fputs("Caught SIGSEGV\n", stderr);
  83.         break;
  84.     case SIGINT:
  85.         fputs("Caught SIGINT\n", stderr);
  86.         break;
  87.     case SIGFPE:
  88.         switch (siginfo->si_code) {
  89.         case FPE_INTDIV:
  90.             fputs("Caught SIGFPE: FPE_INTDIV\n", stderr);
  91.             break;
  92.         case FPE_INTOVF:
  93.             fputs("Caught SIGFPE: FPE_INTOVF\n", stderr);
  94.             break;
  95.         case FPE_FLTDIV:
  96.             fputs("Caught SIGFPE: FPE_FLTDIV\n", stderr);
  97.             break;
  98.         case FPE_FLTOVF:
  99.             fputs("Caught SIGFPE: FPE_FLTOVF\n", stderr);
  100.             break;
  101.         case FPE_FLTUND:
  102.             fputs("Caught SIGFPE: FPE_FLTUND\n", stderr);
  103.             break;
  104.         case FPE_FLTRES:
  105.             fputs("Caught SIGFPE: FPE_FLTRES\n", stderr);
  106.             break;
  107.         case FPE_FLTINV:
  108.             fputs("Caught SIGFPE: FPE_FLTINV\n", stderr);
  109.             break;
  110.         case FPE_FLTSUB:
  111.             fputs("Caught SIGFPE: FPE_FLTSUB\n", stderr);
  112.             break;
  113.         default:
  114.             fputs("Caught SIGFPE: Arithmetic Exception\n", stderr);
  115.             break;
  116.         }
  117.         break;
  118.     case SIGILL:
  119.         switch (siginfo->si_code) {
  120.         case ILL_ILLOPC:
  121.             fputs("Caught SIGILL: ILL_ILLOPC\n", stderr);
  122.             break;
  123.         case ILL_ILLOPN:
  124.             fputs("Caught SIGILL: ILL_ILLOPN\n", stderr);
  125.             break;
  126.         case ILL_ILLADR:
  127.             fputs("Caught SIGILL: ILL_ILLADR\n", stderr);
  128.             break;
  129.         case ILL_ILLTRP:
  130.             fputs("Caught SIGILL: ILL_ILLTRP\n", stderr);
  131.             break;
  132.         case ILL_PRVOPC:
  133.             fputs("Caught SIGILL: ILL_PRVOPC\n", stderr);
  134.             break;
  135.         case ILL_PRVREG:
  136.             fputs("Caught SIGILL: ILL_PRVREG\n", stderr);
  137.             break;
  138.         case ILL_COPROC:
  139.             fputs("Caught SIGILL: ILL_COPROC\n", stderr);
  140.             break;
  141.         case ILL_BADSTK:
  142.             fputs("Caught SIGILL: ILL_BADSTK\n", stderr);
  143.             break;
  144.         default:
  145.             fputs("Caught SIGILL: Illegal Instruction\n", stderr);
  146.             break;
  147.         }
  148.         break;
  149.     case SIGTERM:
  150.         fputs("Caught SIGTERM\n", stderr);
  151.         break;
  152.     case SIGABRT:
  153.         fputs("Caught SIGABRT\n", stderr);
  154.         break;
  155.     default:
  156.         break;
  157.     }
  158.     fputs("******************\n", stderr);
  159.     print_stack_trace();
  160.     exit(1);
  161. }
  162.  
  163. static uint8_t alternate_stack[SIGSTKSZ];
  164.  
  165. void resolve_full_path(char* path, const char* argv0) {
  166.     if (argv0[0] == '/') { // run with absolute path
  167.         strcpy(path, argv0);
  168.     } else { // run with relative path
  169.         if (NULL == getcwd(path, PATH_MAX)) {
  170.             perror("getcwd error");
  171.             return;
  172.         }
  173.         strcat(path, "/");
  174.         strcat(path, argv0);
  175.     }
  176. }
  177.  
  178. void OS_InstallSignalHandler(char* program_name) {
  179.  
  180.     resolve_full_path(_program_name, program_name);
  181.  
  182.     /* setup alternate stack */
  183.     {
  184.         stack_t ss = {};
  185.         /* malloc is usually used here, I'm not 100% sure my static allocation
  186.          is valid but it seems to work just fine. */
  187.         ss.ss_sp = (void*)alternate_stack;
  188.         ss.ss_size = SIGSTKSZ;
  189.         ss.ss_flags = 0;
  190.  
  191.         if (sigaltstack(&ss, NULL) != 0) {
  192.             err(1, "sigaltstack");
  193.         }
  194.     }
  195.  
  196.     /* register our signal handlers */
  197.     {
  198.         struct sigaction sig_action = {};
  199.         sig_action.sa_sigaction = signal_handler;
  200.         sigemptyset(&sig_action.sa_mask);
  201.  
  202.         sig_action.sa_flags = SA_SIGINFO;
  203.  
  204.         if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
  205.             err(1, "sigaction");
  206.         }
  207.         if (sigaction(SIGFPE, &sig_action, NULL) != 0) {
  208.             err(1, "sigaction");
  209.         }
  210.         if (sigaction(SIGINT, &sig_action, NULL) != 0) {
  211.             err(1, "sigaction");
  212.         }
  213.         if (sigaction(SIGILL, &sig_action, NULL) != 0) {
  214.             err(1, "sigaction");
  215.         }
  216.         if (sigaction(SIGTERM, &sig_action, NULL) != 0) {
  217.             err(1, "sigaction");
  218.         }
  219.         if (sigaction(SIGABRT, &sig_action, NULL) != 0) {
  220.             err(1, "sigaction");
  221.         }
  222.     }
  223. }
  224.  
  225. FILE* OS_fopen(const char* pathname, const char* mode) {
  226.     FILE* f;
  227.  
  228.     f = fopen(pathname, mode);
  229.     if (harness_game_config.verbose) {
  230.         if (f == NULL) {
  231.             fprintf(stderr, "Failed to open \"%s\" (%s)\n", pathname, strerror(errno));
  232.         }
  233.     }
  234.  
  235.     return f;
  236. }
  237.  
  238. size_t OS_ConsoleReadPassword(char* pBuffer, size_t pBufferLen) {
  239.     // FIXME: unsafe implementation (echos the password)
  240.     pBuffer[0] = '\0';
  241.     fgets(pBuffer, pBufferLen, stdin);
  242.     return strlen(pBuffer);
  243. }
  244.  
  245. char* OS_Dirname(const char* path) {
  246.     strcpy(name_buf, path);
  247.     return dirname(name_buf);
  248. }
  249.  
  250. char* OS_Basename(const char* path) {
  251.     strcpy(name_buf, path);
  252.     return basename(name_buf);
  253. }
  254.  
  255. #endif // __APPLE__
  256.