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