Subversion Repositories Games.Carmageddon

Rev

Rev 18 | 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) {
14 pmbaty 63
    static char addr2line_cmd[MAXPATHLEN]; // Pierre-Marie Baty -- use the heap instead
1 pmbaty 64
 
65
    /* have addr2line map the address to the related line in the code */
14 pmbaty 66
    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 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);
20 pmbaty 183
    } else {               // run with relative path
1 pmbaty 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);
20 pmbaty 264
    if (f == NULL) {
265
        fprintf(stderr, "Failed to open \"%s\" (%s)\n", pathname, strerror(errno));
11 pmbaty 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__