Subversion Repositories Games.Carmageddon

Rev

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