Update copyright to LGPL on all files
[dyninst.git] / dyninstAPI / tests / src / test_util.C
1 /*
2  * Copyright (c) 1996-2009 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as "Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * By your use of Paradyn, you understand and agree that we (or any
12  * other person or entity with proprietary rights in Paradyn) are
13  * under no obligation to provide either maintenance services,
14  * update services, notices of latent defects, or correction of
15  * defects for Paradyn.
16  * 
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2.1 of the License, or (at your option) any later version.
21  * 
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  * 
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30  */
31
32 //
33 // $Id: test_util.C,v 1.30 2008/04/11 23:30:40 legendre Exp $
34 // Utility functions for use by the dyninst API test programs.
35 //
36
37 #include <stdio.h>
38 #include <signal.h>
39 #include <sys/types.h>
40
41 #if defined(i386_unknown_nt4_0) || defined(mips_unknown_ce2_11) //ccw 10 apr 2001 
42 #ifndef mips_unknown_ce2_11 //ccw 10 apr 2001
43 #define WIN32_LEAN_AND_MEAN
44 #endif
45 #include <Windows.h>
46 #else
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <sys/stat.h>
50 #include <sys/wait.h>
51 #endif
52
53 #if defined(os_linux)
54 #include <linux/limits.h>
55 #endif
56
57 #include "BPatch.h"
58 #include "BPatch_Vector.h"
59 #include "BPatch_thread.h"
60 #include "dyninstAPI_RT/h/dyninstAPI_RT.h" // for DYNINST_BREAKPOINT_SIGNUM
61
62 //
63 // Wait for the mutatee to stop.
64 //
65 void waitUntilStopped(BPatch *bpatch, BPatch_thread *appThread, int testnum,
66                       const char *testname)
67 {
68     while (!appThread->isStopped() && !appThread->isTerminated())
69         bpatch->waitForStatusChange();
70     
71     if (!appThread->isStopped()) {
72         printf("**Failed test #%d (%s)\n", testnum, testname);
73         printf("    process did not signal mutator via stop\n");
74         printf("thread is not stopped\n");
75         if (appThread->isTerminated())
76           printf("thread is terminated\n");
77         exit(-1);
78     }
79 #if defined(i386_unknown_nt4_0)  || defined(mips_unknown_ce2_11) //ccw 10 apr 2001
80     else if (appThread->stopSignal() != EXCEPTION_BREAKPOINT && appThread->stopSignal() != -1) {
81         printf("**Failed test #%d (%s)\n", testnum, testname);
82         printf("    process stopped on signal %d, not SIGTRAP\n", 
83                 appThread->stopSignal());
84         exit(-1);
85     }
86 #else
87 #ifdef DETACH_ON_THE_FLY
88     /* FIXME: Why add SIGILL here? */
89     else if ((appThread->stopSignal() != SIGSTOP) &&
90              (appThread->stopSignal() != SIGHUP) &&
91              (appThread->stopSignal() != DYNINST_BREAKPOINT_SIGNUM) &&
92              (appThread->stopSignal() != SIGILL)) {
93 #else
94     else if ((appThread->stopSignal() != SIGSTOP) &&
95              (appThread->stopSignal() != DYNINST_BREAKPOINT_SIGNUM) &&
96 #if defined(bug_irix_broken_sigstop)
97              (appThread->stopSignal() != SIGEMT) &&
98 #endif
99              (appThread->stopSignal() != SIGHUP)) {
100 #endif /* DETACH_ON_THE_FLY */
101         printf("**Failed test #%d (%s)\n", testnum, testname);
102         printf("    process stopped on signal %d, not SIGSTOP(%d)\n", 
103                 appThread->stopSignal(), SIGSTOP);
104         exit(-1);
105     }
106 #endif
107 }
108
109
110 //
111 // Signal the child that we've attached.  The child contains a function
112 // "checkIfAttached" which simply returns the value of the global variable
113 // "isAttached."  We add instrumentation to "checkIfAttached" to set
114 // "isAttached" to 1.
115 //
116 void signalAttached(BPatch_thread* /*appThread*/, BPatch_image *appImage)
117 {
118     BPatch_variableExpr *isAttached = appImage->findVariable("isAttached");
119     if (isAttached == NULL) {
120         printf("*ERROR*: unable to start tests because variable \"isAttached\""
121                " could not be found in the child process\n");
122         exit(-1);
123     }
124
125     int yes = 1;
126     isAttached->writeValue(&yes);
127 }
128
129 #if !defined(os_windows)
130 #if !defined(os_linux)
131 pid_t fork_mutatee() {
132    return fork();
133 }
134 #else
135 pid_t fork_mutatee() {
136    /**
137     * Perform a granchild fork.  This code forks off a child, that child
138     * then forks off a granchild.  The original child then exits, making
139     * the granchild's new parent init.  Both the original process and the
140     * granchild then exit from this function.
141     *
142     * This works around a linux kernel bug in kernel version 2.6.9 to
143     * 2.6.11, see https://www.dyninst.org/emails/2006/5676.html for
144     * details.
145     **/
146    int status, result;
147    pid_t gchild_pid, child_pid;
148    int filedes[2];
149
150    pipe(filedes);
151
152    child_pid = fork();
153    if (child_pid < 0) {
154       close(filedes[0]);
155       close(filedes[1]);
156       return child_pid;
157    }
158
159    if (child_pid) {
160       //Read the grandchild pid from the child.
161       result = read(filedes[0], &gchild_pid, sizeof(pid_t));
162       if (result == -1) {
163          perror("Couldn't read from pipe");
164       }
165
166       int options = 0;
167       do {
168          result = waitpid(child_pid, &status, options);
169          if (result != child_pid) {
170             perror("Couldn't join child");
171             break;
172          }
173       } while (!WIFEXITED(status));
174       close(filedes[0]);
175       close(filedes[1]);
176       return gchild_pid;
177    }
178    //Child
179    
180    gchild_pid = fork();
181    if (gchild_pid) {
182       //Child pid, send grand child pid to parent then terminate
183       result = write(filedes[1], &gchild_pid, sizeof(pid_t));
184       if (result == -1) {
185          perror("Couldn't write to parent");
186       }         
187       close(filedes[0]);
188       close(filedes[1]);
189       exit(0);
190    }   
191
192    //Granchild
193    close(filedes[0]);
194    close(filedes[1]);
195    return 0;
196 }
197 #endif
198 #endif
199
200 //
201 // Create a new process and return its process id.  If process creation 
202 // fails, this function returns -1.
203 //
204 int startNewProcessForAttach(const char *pathname, const char *argv[])
205 {
206 #if defined(i386_unknown_nt4_0)  || defined(mips_unknown_ce2_11) //ccw 10 apr 2001
207     char child_args[1024];
208     strcpy(child_args, "");
209     if (argv[0] != NULL) {
210         strcpy(child_args, pathname);
211         for (int i = 1; argv[i] != NULL; i++) {
212             strcat(child_args, " ");
213             strcat(child_args, argv[i]);
214         }           
215         strcat(child_args, " -attach");
216     }
217
218     STARTUPINFO si;
219     memset(&si, 0, sizeof(STARTUPINFO));
220     si.cb = sizeof(STARTUPINFO);
221     PROCESS_INFORMATION pi;
222     if (!CreateProcess(pathname,        // application name
223                        child_args,      // command line
224                        NULL,            // security attributes
225                        NULL,            // thread security attributes
226                        FALSE,           // inherit handles
227                        0,               // creation flags
228                        NULL,            // environment,
229                        NULL,            // current directory
230                        &si,
231                        &pi)) {
232         return -1;
233     }
234
235     return pi.dwProcessId;
236 #else
237     /* Make a pipe that we will use to signal that the mutatee has started. */
238     int fds[2];
239     if (pipe(fds) != 0) {
240         fprintf(stderr, "*ERROR*: Unable to create pipe.\n");
241         exit(-1);
242     }
243
244     /* Create the argv string for the child process. */
245     char fdstr[32];
246     sprintf(fdstr, "%d", fds[1]);
247
248     int i;
249     for (i = 0; argv[i] != NULL; i++) ;
250     const char **attach_argv = (const char**)malloc(sizeof(char *) * (i + 3));
251
252     for (i = 0; argv[i] != NULL; i++)
253         attach_argv[i] = argv[i];
254     attach_argv[i++] = const_cast<char*>("-attach");
255     attach_argv[i++] = fdstr;
256     attach_argv[i++] = NULL;
257
258     int pid = fork_mutatee();
259     if (pid == 0) {
260         // child
261         close(fds[0]); // We don't need the read side
262         execvp(pathname, (char * const *)attach_argv);
263         exit(-1);
264     } else if (pid < 0) {
265         return -1;
266     }
267
268     // parent
269     close(fds[1]);  // We don't need the write side
270
271     // Wait for the child to write to the pipe
272     char ch;
273     if (read(fds[0], &ch, sizeof(char)) != sizeof(char)) {
274         perror("read");
275         fprintf(stderr, "*ERROR*: Error reading from pipe\n");
276         exit(-1);
277     }
278
279     if (ch != 'T') {
280         fprintf(stderr, "*ERROR*: Child didn't write expected value to pipe.\n");
281         exit(-1);
282     }
283
284     close(fds[0]);  // We're done with the pipe
285
286     return pid;
287 #endif
288 }
289
290 void updateSearchPaths(const char *filename) {
291 #if !defined(os_windows)
292    // First, find the directory we reside in
293
294    char *execpath;
295    char pathname[PATH_MAX];
296    getcwd(pathname, PATH_MAX);
297
298    if (filename[0] == '/') {
299       // If it begins with a slash, it's an absolute path
300       execpath = strdup(filename);
301       strrchr(execpath,'/')[0] = '\0';
302    } else if (strchr(filename,'/')) {
303       // If it contains slashes, it's a relative path
304       char *filename_copy = strdup(filename);
305       
306       char *loc = strrchr(filename_copy, '/');
307       if (loc)
308          *loc = '\0';
309 #if 0
310       *strrchr(filename_copy,'/') = '\0';
311 #endif
312       execpath = (char *) ::malloc(strlen(pathname) + strlen(filename_copy) + 2);
313       strcpy(execpath,pathname);
314       strcat(execpath,"/");
315       strcat(execpath,filename_copy);
316       ::free(filename_copy);
317    } else {
318       // If it's just a name, it was found in PATH
319       const char *pathenv = getenv("PATH");
320       char *pathenv_copy = strdup(pathenv);
321       char *ptrptr;
322       char *nextpath = strtok_r(pathenv_copy, ":", &ptrptr);
323       while (nextpath) {
324          struct stat statbuf;
325          
326          char *fullpath = new char[strlen(nextpath)+strlen(filename)+2];
327          strcpy(fullpath,nextpath);
328          strcat(fullpath,"/");
329          strcat(fullpath,filename);
330          
331          if (!stat(fullpath,&statbuf)) {
332             execpath = strdup(nextpath);
333             delete[] fullpath;
334             break;
335          }
336          delete[] fullpath;
337          nextpath = strtok_r(NULL,":", &ptrptr);
338       }
339       ::free(pathenv_copy);
340       
341       if (nextpath == NULL) {
342           // Not found in PATH - we'll assume its in CWD
343           return;
344       }
345    }
346
347    // Now update PATH and LD_LIBRARY_PATH/LIBPATH
348
349     char *envCopy;
350
351     char *envPath = getenv("PATH");
352     envCopy = (char *) ::malloc(((envPath && strlen(envPath)) ? strlen(envPath) + 1 : 0) + strlen(execpath) + 6);
353     strcpy(envCopy, "PATH=");
354     if (envPath && strlen(envPath)) {
355        strcat(envCopy, envPath);
356        strcat(envCopy, ":");
357     } 
358     strcat(envCopy, execpath);
359     assert(!putenv(envCopy));
360     
361     char *envLibPath;
362 #if defined(os_aix)
363     envLibPath = getenv("LIBPATH");
364 #else
365     envLibPath = getenv("LD_LIBRARY_PATH");
366 #endif
367     
368     envCopy = (char *) ::malloc(((envLibPath && strlen(envLibPath)) ? strlen(envLibPath) + 1 : 0) + strlen(execpath) + 17);
369 #if defined(os_aix)
370     strcpy(envCopy, "LIBPATH=");
371 #else
372     strcpy(envCopy, "LD_LIBRARY_PATH=");
373 #endif
374     if (envLibPath && strlen(envLibPath)) {
375        strcat(envCopy, envLibPath);
376        strcat(envCopy, ":");
377     }
378     strcat(envCopy, execpath);
379     assert(!putenv(envCopy));
380
381     ::free(execpath);
382 #endif
383 }