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