]> git.cworth.org Git - apitrace/blob - common/os_posix.cpp
Make os::getTime() inline and make time frequency OS-dependent variable.
[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
27 #include <assert.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include <unistd.h>
33 #include <sys/wait.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <signal.h>
37
38 #if defined(__linux__)
39 #include <linux/limits.h> // PATH_MAX
40 #endif
41
42 #ifdef __APPLE__
43 #include <sys/syslimits.h> // PATH_MAX
44 #include <mach-o/dyld.h>
45 #endif
46
47 #ifndef PATH_MAX
48 #warning PATH_MAX undefined
49 #define PATH_MAX 4096
50 #endif
51
52 #include "os.hpp"
53 #include "os_string.hpp"
54
55
56 namespace os {
57
58
59 String
60 getProcessName(void)
61 {
62     String path;
63     size_t size = PATH_MAX;
64     char *buf = path.buf(size);
65
66     // http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
67 #ifdef __APPLE__
68     uint32_t len = size;
69     if (_NSGetExecutablePath(buf, &len) != 0) {
70         *buf = 0;
71         return path;
72     }
73     len = strlen(buf);
74 #else
75     ssize_t len;
76     len = readlink("/proc/self/exe", buf, size - 1);
77     if (len == -1) {
78         // /proc/self/exe is not available on setuid processes, so fallback to
79         // /proc/self/cmdline.
80         int fd = open("/proc/self/cmdline", O_RDONLY);
81         if (fd >= 0) {
82             len = read(fd, buf, size - 1);
83             close(fd);
84         }
85     }
86     if (len <= 0) {
87         snprintf(buf, size, "%i", (int)getpid());
88         return path;
89     }
90 #endif
91     path.truncate(len);
92
93     return path;
94 }
95
96 String
97 getCurrentDir(void)
98 {
99     String path;
100     size_t size = PATH_MAX;
101     char *buf = path.buf(size);
102
103     getcwd(buf, size);
104     buf[size - 1] = 0;
105     
106     path.truncate();
107     return path;
108 }
109
110 bool
111 String::exists(void) const
112 {
113     struct stat st;
114     int err;
115
116     err = stat(str(), &st);
117     if (err) {
118         return false;
119     }
120
121     if (!S_ISREG(st.st_mode))
122         return false;
123
124     return true;
125 }
126
127 int execute(char * const * args)
128 {
129     pid_t pid = fork();
130     if (pid == 0) {
131         // child
132         execvp(args[0], args);
133         fprintf(stderr, "error: failed to execute %s\n", args[0]);
134         exit(-1);
135     } else {
136         // parent
137         if (pid == -1) {
138             fprintf(stderr, "error: failed to fork\n");
139             return -1;
140         }
141         int status = -1;
142         waitpid(pid, &status, 0);
143         return status;
144     }
145 }
146
147 static volatile bool logging = false;
148
149 void
150 log(const char *format, ...)
151 {
152     logging = true;
153     va_list ap;
154     va_start(ap, format);
155     fflush(stdout);
156     vfprintf(stderr, format, ap);
157     va_end(ap);
158     logging = false;
159 }
160
161 void
162 abort(void)
163 {
164     exit(0);
165 }
166
167
168 static void (*gCallback)(void) = NULL;
169
170 #define NUM_SIGNALS 16
171
172 struct sigaction old_actions[NUM_SIGNALS];
173
174
175 /*
176  * See also:
177  * - http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c
178  * - http://ggi.cvs.sourceforge.net/viewvc/ggi/ggi-core/libgg/gg/cleanup.c?view=markup
179  */
180 static void
181 signalHandler(int sig, siginfo_t *info, void *context)
182 {
183     /*
184      * There are several signals that can happen when logging to stdout/stderr.
185      * For example, SIGPIPE will be emitted if stderr is a pipe with no
186      * readers.  Therefore ignore any signal while logging by returning
187      * immediately, to prevent deadlocks.
188      */
189     if (logging) {
190         return;
191     }
192
193     static int recursion_count = 0;
194
195     log("apitrace: warning: caught signal %i\n", sig);
196
197     if (recursion_count) {
198         log("apitrace: warning: recursion handling signal %i\n", sig);
199     } else {
200         if (gCallback) {
201             ++recursion_count;
202             gCallback();
203             --recursion_count;
204         }
205     }
206
207     struct sigaction *old_action;
208     if (sig >= NUM_SIGNALS) {
209         /* This should never happen */
210         log("error: unexpected signal %i\n", sig);
211         raise(SIGKILL);
212     }
213     old_action = &old_actions[sig];
214
215     if (old_action->sa_flags & SA_SIGINFO) {
216         // Handler is in sa_sigaction
217         old_action->sa_sigaction(sig, info, context);
218     } else {
219         if (old_action->sa_handler == SIG_DFL) {
220             log("apitrace: info: taking default action for signal %i\n", sig);
221
222 #if 1
223             struct sigaction dfl_action;
224             dfl_action.sa_handler = SIG_DFL;
225             sigemptyset (&dfl_action.sa_mask);
226             dfl_action.sa_flags = 0;
227             sigaction(sig, &dfl_action, NULL);
228
229             raise(sig);
230 #else
231             raise(SIGKILL);
232 #endif
233         } else if (old_action->sa_handler == SIG_IGN) {
234             /* ignore */
235         } else {
236             /* dispatch to handler */
237             old_action->sa_handler(sig);
238         }
239     }
240 }
241
242 void
243 setExceptionCallback(void (*callback)(void))
244 {
245     assert(!gCallback);
246     if (!gCallback) {
247         gCallback = callback;
248
249         struct sigaction new_action;
250         new_action.sa_sigaction = signalHandler;
251         sigemptyset(&new_action.sa_mask);
252         new_action.sa_flags = SA_SIGINFO | SA_RESTART;
253
254
255         for (int sig = 1; sig < NUM_SIGNALS; ++sig) {
256             // SIGKILL and SIGSTOP can't be handled.
257             if (sig == SIGKILL || sig == SIGSTOP) {
258                 continue;
259             }
260
261             /*
262              * SIGPIPE can be emitted when writing to stderr that is redirected
263              * to a pipe without readers.  It is also very unlikely to ocurr
264              * inside graphics APIs, and most applications where it can occur
265              * normally already ignore it.  In summary, it is unlikely that a
266              * SIGPIPE will cause abnormal termination, which it is likely that
267              * intercepting here will cause problems, so simple don't intercept
268              * it here.
269              */
270             if (sig == SIGPIPE) {
271                 continue;
272             }
273
274             if (sigaction(sig,  NULL, &old_actions[sig]) >= 0) {
275                 sigaction(sig,  &new_action, NULL);
276             }
277         }
278     }
279 }
280
281 void
282 resetExceptionCallback(void)
283 {
284     gCallback = NULL;
285 }
286
287 } /* namespace os */
288