Subversion Repositories Games.Carmageddon

Rev

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__