Update copyright to LGPL on all files
[dyninst.git] / testsuite / src / runTests-utils.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 #include <errno.h>
32 #include <iostream>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/wait.h>
37 #include <sstream>
38 #include <string>
39 #include <cstring>
40
41 #include "runTests-utils.h"
42
43 extern string pdscrdir;
44
45 int timeout = 1200; /* seconds */
46
47 void initPIDFilename(char *buffer, size_t len) {
48   snprintf(buffer, len, "pids.%d", getpid());
49 }
50
51 void cleanupMutatees(char *pidFilename) {
52    if (!pidFilename)
53       return;
54    FILE *pidFile = fopen(pidFilename, "r");
55    if (NULL == pidFile) 
56       return;
57    for (;;) {
58       int pid, count;
59       count = fscanf(pidFile, "%d\n", &pid);
60       if (count != 1) 
61          break;
62       
63       if (pid < 1) {
64          fprintf(stderr, "[%s:%u] - Read a negative PID (%d).  Something's weird.\n", __FILE__, __LINE__);
65          continue;
66       } 
67       
68       int status = kill(pid, SIGKILL);
69       if ((status != 0) && (errno != ESRCH)) {
70          // FIXME the call to strerror() is not thread-safe
71          // We don't care if the process is already dead, so we're ignoring
72          // ESRCH
73          fprintf(stderr, "[%s:%u] - INFO: %s\n", __FILE__, __LINE__, strerror(errno));
74       }
75    }
76    fclose(pidFile);
77    unlink(pidFilename);
78 }
79
80 static bool timed_out;
81 static void sigalrm_action(int sig, siginfo_t *siginfo, void *context) {
82   // Note that the child has timed out and return
83   timed_out = true;
84 }
85
86 static bool interrupted;
87 static void sigint_action(int sig, siginfo_t *siginfo, void *context) {
88   interrupted = true;
89 }
90
91 void generateTestArgs(char **exec_args[], bool resume, bool useLog,
92                       bool staticTests, string &logfile, int testLimit,
93             vector<char *> &child_argv, char *pidFilename,
94             const char *memcpu_name);
95
96 int RunTest(unsigned int iteration, bool useLog, bool staticTests,
97             string logfile, int testLimit, vector<char *> child_argv,
98             char *pidFilename, const char *memcpu_name) {
99    // TODO Fill in this function
100    int retval = -1;
101
102    char **exec_args = NULL;
103
104    generateTestArgs(&exec_args, iteration > 0, useLog, staticTests, logfile,
105                     testLimit, child_argv, pidFilename, memcpu_name);
106
107    // Fork and execute test_driver in the child process
108    int child_pid = fork();
109    if (-1 == child_pid) {
110       return -4;
111    } else if (0 == child_pid) {
112       // Child
113       execvp("test_driver", exec_args);
114       execvp("./test_driver", exec_args);
115       exit(-4);
116    } else {
117           // fprintf(stderr, "%s[%d]:  forked new process %d\n", __FILE__, __LINE__, child_pid);
118       // Parent
119       // Install signal handler for a timer
120       struct sigaction sigalrm_a;
121       struct sigaction old_sigalrm_a;
122       struct sigaction sigint_a;
123       struct sigaction old_sigint_a;
124
125       timed_out = false;
126       sigalrm_a.sa_sigaction = sigalrm_action;
127       sigemptyset(&(sigalrm_a.sa_mask));
128       sigalrm_a.sa_flags = SA_SIGINFO;
129       sigaction(SIGALRM, &sigalrm_a, &old_sigalrm_a);
130       alarm(timeout);
131
132       interrupted = false;
133       sigint_a.sa_sigaction = sigint_action;
134       sigemptyset(&(sigint_a.sa_mask));
135       sigint_a.sa_flags = SA_SIGINFO;
136       sigaction(SIGINT, &sigint_a, &old_sigint_a);
137
138       // I think I want to use wait() here instead of using a sleep loop
139       // - There are issues with using sleep() and alarm()..
140       // - I'll wait() and if the alarm goes off it will interrupt the waiting,
141       //   so the end result should be the same
142
143       // Wait for one of the signals to fire
144       do {
145          // Wait for child to exit
146          pid_t waiting_pid;
147          int child_status;
148                  //fprintf(stderr, "%s[%d]:  before waitpid(%d)\n", __FILE__, __LINE__, child_pid);
149          if (!timed_out && !interrupted) {
150             // BUG I fear there's a race condition here, where I may not catch a
151             // timeout if it occurs between the timed_out check and the call to
152             // waitpid().  I'm not sure what to do about that.
153             waiting_pid = waitpid(child_pid, &child_status, 0);
154          }
155
156 #if 0
157                  fprintf(stderr, "%s[%d]:  waitpid (%d): %s with %d\n", __FILE__, __LINE__, child_pid,
158                                  WIFEXITED(child_status) ? "exited" : 
159                                  WIFSIGNALED(child_status) ? "signaled" : "unknown",
160                                  WIFEXITED(child_status) ? WEXITSTATUS(child_status) : 
161                                  WIFSIGNALED(child_status) ? WTERMSIG(child_status) : -1);
162 #endif
163
164                  if (timed_out) {
165                          // Timed out..  timer.pl sets the return value to -1 for this case
166             fprintf(stderr,
167                     "*** Process exceeded time limit.  Reaping children.\n");
168             kill(child_pid, SIGKILL);
169             // I need to call waitpid also, so we don't leave zombie processes
170             // around
171             wait(NULL);
172             retval = -1;
173          } else if (interrupted) {
174             fprintf(stderr, "*** SIGINT received.  Reaping children.\n");
175             kill(child_pid, SIGKILL);
176             wait(NULL);
177             retval = -3;
178          } else {
179             // Do something with child_status
180             if (WIFSIGNALED(child_status)) {
181                fprintf(stderr, "*** Child terminated abnormally via signal %d.\n",
182                WTERMSIG(child_status));
183                retval = -2;
184                break;
185             } else if (WIFEXITED(child_status)) {
186                retval = WEXITSTATUS(child_status);
187                break;
188             } // I don't care about stops or continues
189          }
190       } while (!timed_out && !interrupted);
191       alarm(0); // Cancel alarm
192
193       // Uninstall handlers
194       sigaction(SIGALRM, &old_sigalrm_a, NULL);
195       sigaction(SIGINT, &old_sigint_a, NULL);
196
197       //Clean-up process group
198       if (-1 == kill(-1 * child_pid, SIGKILL))
199           {
200                   //fprintf(stderr, "%s[%d]:  ERROR:  failed to kill %d: %s\n", 
201                 //                __FILE__, __LINE__, child_pid, strerror(errno));
202           }
203    }
204
205    return retval; // FIXME Return the real return value
206 }
207
208 string ReplaceAllWith(const string &in, const string &replace, const string &with);
209
210 void generateTestArgs(char **exec_args[], bool resume, bool useLog,
211                       bool staticTests, string &logfile, int testLimit,
212             vector<char *> &child_argv, char *pidFilename,
213             const char *memcpu_name) {
214   vector<const char *> args;
215
216   if (staticTests) {
217     args.push_back("test_driver_static");
218   } else {
219     args.push_back("test_driver");
220   }
221   args.push_back("-under-runtests");
222   args.push_back("-enable-resume");
223   args.push_back("-limit");
224   char *limit_str = new char[12];
225   snprintf(limit_str, 12, "%d", testLimit);
226   args.push_back(limit_str);
227   if (pidFilename != NULL) {
228     args.push_back("-pidfile");
229     args.push_back(pidFilename);
230   }
231   if (memcpu_name)
232   {
233      args.push_back("-memcpu");
234      args.push_back(memcpu_name);
235   }
236   if (resume) {
237     args.push_back("-use-resume");
238   }
239   if (useLog) {
240     args.push_back("-log");
241     args.push_back("-logfile");
242     args.push_back(const_cast<char *>(logfile.c_str()));
243   }
244   for (unsigned int i = 0; i < child_argv.size(); i++) {
245      if ((strcmp(child_argv[i], "-memcpu") == 0) ||
246          (strcmp(child_argv[i], "-cpumem") == 0))
247      {
248         if ((child_argv[i+1][0] != '-') || 
249             (child_argv[i+1][1] == '\0'))
250         {
251            i++;
252         }
253         continue;
254      }
255      args.push_back(child_argv[i]);
256   }
257
258   // Copy the arguments from the vector to a new array
259   // BUG limit_str leaks here.  I'm not sure how to clean up this leak
260   int exec_argc = args.size();
261   *exec_args = new char *[exec_argc + 1];
262   for (unsigned int i = 0; i < args.size(); i++) {
263      (*exec_args)[i] = const_cast<char *>(args[i]);
264   }
265   (*exec_args)[exec_argc] = NULL;
266 }
267
268 void generateTestString(bool resume, bool useLog, bool staticTests,
269                         string &logfile, int testLimit,
270                         vector<char *>& child_argv, string& shellString,
271                         char *pidFilename)
272 {
273    stringstream testString;
274    if (staticTests) {
275      testString << "test_driver_static";
276    } else {
277      testString << "test_driver";
278    }
279    testString << " -under-runtests -enable-resume -limit " << testLimit;
280    testString << " -pidfile " << pidFilename;
281
282    if ( resume )
283    {
284       testString << " -use-resume";
285    }
286    if ( useLog )
287    {
288       testString << " -log";
289    }
290    if (useLog) {
291      testString << " -logfile " << logfile;
292    }
293
294    // Add child's arguments
295    for ( unsigned int i = 0; i < child_argv.size(); i++ )
296    {
297       testString << " " << child_argv[i];
298    }
299    
300    stringstream timerString;
301    timerString << pdscrdir << "/timer.pl -t " << timeout;
302    shellString = timerString.str() + " " + testString.str();
303    
304 }
305
306 char *setResumeEnv()
307 {
308    if ( getenv("RESUMELOG") != NULL )
309       return NULL;
310
311    stringstream tmp;
312    tmp << "RESUMELOG=";
313    if ( getenv("TMP") )
314    {
315       tmp << getenv("TMP") << "/";
316    }
317    else
318    {
319       tmp << "/tmp/";
320    }
321
322    tmp << "test_driver.resumelog." << getpid();
323
324    char *r_tmp = strdup(tmp.str().c_str());
325    putenv(r_tmp);
326
327    return r_tmp;
328 }
329
330 char *setLibPath()
331 {
332    stringstream tmp;
333    tmp << "LD_LIBRARY_PATH=.:";
334
335    if ( getenv("LD_LIBRARY_PATH") )
336    {
337       tmp << getenv("LD_LIBRARY_PATH");
338    }
339
340    char *l_tmp = strdup(tmp.str().c_str());
341    putenv(l_tmp);
342
343    // And add LIBPATH for AIX if necessary
344    stringstream AIXtmp;
345    AIXtmp << "LIBPATH=.:";
346    if ( getenv("LIBPATH") ) {
347        AIXtmp << getenv("LIBPATH");
348    }
349    char *a_tmp = strdup(AIXtmp.str().c_str());
350    putenv(a_tmp);
351
352    return l_tmp;
353 }
354
355 void setupVars(bool useLog, string &logfile)
356 {
357    string base_dir, tlog_dir;
358
359 #if defined(m_abi)
360    if ( getenv("DYNINSTAPI_RT_LIB") )
361    {
362       char *rtlib = getenv("DYNINSTAPI_RT_LIB");
363
364       string temp = ReplaceAllWith(rtlib, "libdyninstAPI_RT.so", "libdyninstAPI_RT_m32.so");
365       char *rtlib_mabi = strdup(temp.c_str());
366       setenv("DYNINSTAPI_RT_LIB_MABI", rtlib_mabi, 0);
367    }
368 #endif
369   
370    // Determine Base Dir
371    if ( getenv("PARADYN_BASE") == NULL )
372    {
373       if ( getenv("DYNINST_ROOT") == NULL )
374       {
375         base_dir = string("../../..");
376       }
377       else
378       {
379          base_dir = getenv("DYNINST_ROOT");
380       }
381    } else
382    {
383       base_dir = getenv("PARADYN_BASE");
384    }
385
386    pdscrdir = base_dir + "/scripts";
387    if ( ! isDir(pdscrdir) )
388    {
389       cerr << pdscrdir << " does not exist.  Paradyn scripts dir required." 
390          << endl;
391       exit(1);
392    }
393
394    // Determine Test log dir
395    char *pdtst = getenv("PDTST");
396    if ( pdtst == NULL )
397    {
398       tlog_dir = base_dir + "/log/tests";
399    } else
400    {
401       tlog_dir = pdtst;
402    }
403    tlog_dir += "2";
404
405    // Determin Test log file
406    string buildnum, build_id;
407    buildnum = pdscrdir + "/buildnum";
408    if ( getenv("PARADYN_BASE") != NULL && isRegFile(buildnum) )
409    {
410       getInput(buildnum.c_str(), build_id); 
411    } else
412    {
413       getInput("date '+%Y-%m-%d'", build_id);
414    }
415
416    if ( useLog ) 
417    {
418
419       if ( logfile == "" )
420       {
421          logfile = tlog_dir + "/" + getenv("PLATFORM") + "/" + build_id;
422       }
423
424       cout << "   ... output to " << logfile << endl;
425
426       string testslogdir, cmd;
427       cmd = "dirname " + logfile;
428       getInput(cmd.c_str(), testslogdir);
429    
430       if ( ! isDir(testslogdir) )
431       {
432          cout << testslogdir << "does not exist (yet)!" << endl;
433          cmd = "mkdir -p " + testslogdir;
434          system(cmd.c_str());
435          if ( ! isDir(testslogdir) )
436          {
437             cout << testslogdir << " creation failed - aborting!" << endl;
438             exit(1);
439          } else {
440             cout << testslogdir << " create for test logs!" << endl;
441          }
442       }
443
444       if ( isRegFile(logfile) )
445       {
446          cout << "File exists" << endl;
447       }
448       else
449       {
450          cmd = "touch " + logfile;
451          system(cmd.c_str());
452       }
453    
454       cmd = logfile + ".gz";
455       if ( isRegFile(cmd) )
456       {
457          cout << "File.gz exists" << endl;
458       }
459    }
460 }