Rev 14 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | // Based on https://gist.github.com/jvranish/4441299 |
| 2 | |||
| 8 | pmbaty | 3 | #if defined(__linux__) || defined(__FreeBSD__) // Pierre-Marie Baty -- added compile guard |
| 1 | pmbaty | 4 | |
| 5 | #define _GNU_SOURCE |
||
| 18 | pmbaty | 6 | #include "harness/config.h" |
| 1 | pmbaty | 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) { |
||
| 14 | pmbaty | 64 | static char addr2line_cmd[MAXPATHLEN]; // Pierre-Marie Baty -- use the heap instead |
| 1 | pmbaty | 65 | |
| 66 | /* have addr2line map the address to the related line in the code */ |
||
| 14 | pmbaty | 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 |
| 1 | pmbaty | 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); |
||
| 18 | pmbaty | 184 | } else { // run with relative path |
| 1 | pmbaty | 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); |
||
| 18 | pmbaty | 265 | if (harness_game_config.verbose) { |
| 266 | if (f == NULL) { |
||
| 267 | fprintf(stderr, "Failed to open \"%s\" (%s)\n", pathname, strerror(errno)); |
||
| 268 | } |
||
| 11 | pmbaty | 269 | } |
| 1 | pmbaty | 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__ |