]> git.cworth.org Git - apitrace/blob - common/os_posix.cpp
common: dump backtrace on signals
[apitrace] / common / os_posix.cpp
1 /**************************************************************************
2  *
3  * Copyright 2010-2011 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 #ifndef _WIN32
27
28 #include <assert.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include <unistd.h>
34 #include <sys/wait.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <signal.h>
38
39 #if defined(__linux__)
40 #include <linux/limits.h> // PATH_MAX
41 #endif
42
43 #ifdef __APPLE__
44 #include <sys/syslimits.h> // PATH_MAX
45 #include <mach-o/dyld.h>
46 #endif
47
48 #ifdef ANDROID
49 #include <android/log.h>
50 #endif
51
52 #ifndef PATH_MAX
53 #warning PATH_MAX undefined
54 #define PATH_MAX 4096
55 #endif
56
57 #include "os.hpp"
58 #include "os_string.hpp"
59 #include "os_backtrace.hpp"
60
61
62 namespace os {
63
64
65 String
66 getProcessName(void)
67 {
68     String path;
69     size_t size = PATH_MAX;
70     char *buf = path.buf(size);
71
72     // http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
73 #ifdef __APPLE__
74     uint32_t len = size;
75     if (_NSGetExecutablePath(buf, &len) != 0) {
76         // grow buf and retry
77         buf = path.buf(len);
78         _NSGetExecutablePath(buf, &len);
79     }
80     len = strlen(buf);
81 #else
82     ssize_t len;
83     len = readlink("/proc/self/exe", buf, size - 1);
84     if (len <= 0) {
85         // /proc/self/exe is not available on setuid processes, so fallback to
86         // /proc/self/cmdline.
87         int fd = open("/proc/self/cmdline", O_RDONLY);
88         if (fd >= 0) {
89             // buf already includes trailing zero
90             len = read(fd, buf, size);
91             close(fd);
92             if (len >= 0) {
93                 len = strlen(buf);
94             }
95         }
96     }
97     if (len <= 0) {
98         // fallback to process ID
99         len = snprintf(buf, size, "%i", (int)getpid());
100         if (len >= size) {
101             len = size - 1;
102         }
103     }
104 #endif
105     path.truncate(len);
106
107     return path;
108 }
109
110 String
111 getCurrentDir(void)
112 {
113     String path;
114     size_t size = PATH_MAX;
115     char *buf = path.buf(size);
116
117     getcwd(buf, size);
118     buf[size - 1] = 0;
119     
120     path.truncate();
121     return path;
122 }
123
124 bool
125 createDirectory(const String &path)
126 {
127     return mkdir(path, 0777) == 0;
128 }
129
130 bool
131 String::exists(void) const
132 {
133     struct stat st;
134     int err;
135
136     err = stat(str(), &st);
137     if (err) {
138         return false;
139     }
140
141     return true;
142 }
143
144 int execute(char * const * args)
145 {
146     pid_t pid = fork();
147     if (pid == 0) {
148         // child
149         execvp(args[0], args);
150         fprintf(stderr, "error: failed to execute:");
151         for (unsigned i = 0; args[i]; ++i) {
152             fprintf(stderr, " %s", args[i]);
153         }
154         fprintf(stderr, "\n");
155         exit(-1);
156     } else {
157         // parent
158         if (pid == -1) {
159             fprintf(stderr, "error: failed to fork\n");
160             return -1;
161         }
162         int status = -1;
163         int ret;
164         waitpid(pid, &status, 0);
165         if (WIFEXITED(status)) {
166             ret = WEXITSTATUS(status);
167         } else if (WIFSIGNALED(status)) {
168             // match shell return code
169             ret = WTERMSIG(status) + 128;
170         } else {
171             ret = 128;
172         }
173         return ret;
174     }
175 }
176
177 static volatile bool logging = false;
178
179 void
180 log(const char *format, ...)
181 {
182     logging = true;
183     va_list ap;
184     va_start(ap, format);
185     fflush(stdout);
186 #ifdef ANDROID
187     __android_log_vprint(ANDROID_LOG_DEBUG, "apitrace", format, ap);
188 #else
189     static FILE *log = NULL;
190     if (!log) {
191         // Duplicate stderr file descriptor, to prevent applications from
192         // redirecting our debug messages to somewhere else.
193         //
194         // Another alternative would be to log to /dev/tty when available.
195         log = fdopen(dup(STDERR_FILENO), "at");
196     }
197     vfprintf(log, format, ap);
198     fflush(log);
199 #endif
200     va_end(ap);
201     logging = false;
202 }
203
204 #if defined(__APPLE__)
205 long long timeFrequency = 0LL;
206 #endif
207
208 void
209 abort(void)
210 {
211     _exit(1);
212 }
213
214
215 static void (*gCallback)(void) = NULL;
216
217 #define NUM_SIGNALS 16
218
219 struct sigaction old_actions[NUM_SIGNALS];
220
221
222 /*
223  * See also:
224  * - http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c
225  * - http://ggi.cvs.sourceforge.net/viewvc/ggi/ggi-core/libgg/gg/cleanup.c?view=markup
226  */
227 static void
228 signalHandler(int sig, siginfo_t *info, void *context)
229 {
230     /*
231      * There are several signals that can happen when logging to stdout/stderr.
232      * For example, SIGPIPE will be emitted if stderr is a pipe with no
233      * readers.  Therefore ignore any signal while logging by returning
234      * immediately, to prevent deadlocks.
235      */
236     if (logging) {
237         return;
238     }
239
240     static int recursion_count = 0;
241
242     log("apitrace: warning: caught signal %i\n", sig);
243
244     if (recursion_count) {
245         log("apitrace: warning: recursion handling signal %i\n", sig);
246     } else {
247         ++recursion_count;
248         if (gCallback)
249             gCallback();
250         os::dump_backtrace();
251         --recursion_count;
252     }
253
254     struct sigaction *old_action;
255     if (sig >= NUM_SIGNALS) {
256         /* This should never happen */
257         log("error: unexpected signal %i\n", sig);
258         raise(SIGKILL);
259     }
260     old_action = &old_actions[sig];
261
262     if (old_action->sa_flags & SA_SIGINFO) {
263         // Handler is in sa_sigaction
264         old_action->sa_sigaction(sig, info, context);
265     } else {
266         if (old_action->sa_handler == SIG_DFL) {
267             log("apitrace: info: taking default action for signal %i\n", sig);
268
269 #if 1
270             struct sigaction dfl_action;
271             dfl_action.sa_handler = SIG_DFL;
272             sigemptyset (&dfl_action.sa_mask);
273             dfl_action.sa_flags = 0;
274             sigaction(sig, &dfl_action, NULL);
275
276             raise(sig);
277 #else
278             raise(SIGKILL);
279 #endif
280         } else if (old_action->sa_handler == SIG_IGN) {
281             /* ignore */
282         } else {
283             /* dispatch to handler */
284             old_action->sa_handler(sig);
285         }
286     }
287 }
288
289 void
290 setExceptionCallback(void (*callback)(void))
291 {
292     assert(!gCallback);
293     if (!gCallback) {
294         gCallback = callback;
295
296         struct sigaction new_action;
297         new_action.sa_sigaction = signalHandler;
298         sigemptyset(&new_action.sa_mask);
299         new_action.sa_flags = SA_SIGINFO | SA_RESTART;
300
301
302         for (int sig = 1; sig < NUM_SIGNALS; ++sig) {
303             // SIGKILL and SIGSTOP can't be handled.
304             if (sig == SIGKILL || sig == SIGSTOP) {
305                 continue;
306             }
307
308             /*
309              * SIGPIPE can be emitted when writing to stderr that is redirected
310              * to a pipe without readers.  It is also very unlikely to ocurr
311              * inside graphics APIs, and most applications where it can occur
312              * normally already ignore it.  In summary, it is unlikely that a
313              * SIGPIPE will cause abnormal termination, which it is likely that
314              * intercepting here will cause problems, so simple don't intercept
315              * it here.
316              */
317             if (sig == SIGPIPE) {
318                 continue;
319             }
320
321             if (sigaction(sig,  NULL, &old_actions[sig]) >= 0) {
322                 sigaction(sig,  &new_action, NULL);
323             }
324         }
325     }
326 }
327
328 void
329 resetExceptionCallback(void)
330 {
331     gCallback = NULL;
332 }
333
334 } /* namespace os */
335
336 #endif // !defined(_WIN32)