Update copyright to LGPL on all files
[dyninst.git] / testsuite / src / runTests.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 #ifdef os_windows_test
32 //needed for Sleep
33 #include <windows.h>
34 #define sleep(x) Sleep(x * 1000)
35 #define unlink _unlink
36 #endif
37
38 #include <stdlib.h>
39 #include <sstream>
40 #include <string>
41 #include <iostream>
42 #include <stdio.h>
43 #include <vector>
44
45 #include <string.h>
46 #include <errno.h>
47 #include <signal.h>
48
49 #include "runTests-utils.h"
50 #include "error.h"
51 #include "help.h"
52 #include <sys/stat.h>
53 #include <sys/types.h>
54 #include <cstring>
55
56 // Default name for the resume log file
57 #define DEFAULT_RESUMELOG "resumelog"
58 // Default name for the crash log file
59 #define DEFAULT_CRASHLOG "crashlog"
60
61 #define MEMCPU_DEFAULT_LOG "memcpu_tmp.log";
62 const char *memcpu_name = NULL;
63 const char *memcpu_orig_name = NULL;
64
65 bool staticTests = false;
66 bool useLog = false;
67 string logfile;
68 string pdscrdir;
69
70 // Run No more than testLimit tests before re-exec'ing test_driver
71 int testLimit = 10;
72
73 vector<char *> child_argv;
74
75 void parseMEMCPUFile()
76 {
77    if (!memcpu_name || !memcpu_orig_name)
78       return;
79
80    signed long mem_total = 0, utime_total = 0, stime_total = 0;
81    FILE *f = fopen(memcpu_name, "r");
82    if (!f)
83       return;
84
85    for (;;)
86    {
87       signed long mem, utime, stime;
88       int res = fscanf(f, "mem=%ld\tutime=%ld\tstime=%ld\n",
89                        &mem, &utime, &stime);
90       if (res != 3)
91          break;
92       mem_total += mem;
93       utime_total += utime;
94       stime_total += stime;
95    }
96    fclose(f);
97    unlink(memcpu_name);
98
99    if (strcmp(memcpu_orig_name, "-") == 0)
100    {
101       f = stdout;
102    }
103    else {
104       f = fopen(memcpu_orig_name, "w");
105       if (!f)
106          return;
107    }
108    
109    fprintf(f, "mem=%ld\tutime=%ld\tstime=%ld\n",
110            mem_total, utime_total, stime_total);
111    if (f != stdout)
112       fclose(f);
113 }
114
115 // isRegFile:
116 // Returns true if filename is a regular file
117 bool isRegFile(const string& filename)
118 {
119    struct stat results;
120
121    if ( stat(filename.c_str(), &results) !=  0 )
122    {
123       return false;
124    }
125
126    return S_ISREG(results.st_mode);
127 }
128
129 // isDir:
130 // Returns true if filename is a directory
131 bool isDir(const string& filename)
132 {
133    struct stat results;
134
135    if ( stat(filename.c_str(), &results) !=  0 )
136    {
137       return false;
138    }
139
140    return S_ISDIR(results.st_mode);
141 }
142
143 // getInput:
144 // Run filename and store stdout in the output string.
145 void getInput(const char *filename, string& output)
146 {
147    FILE *text;
148    char *result;
149    result = (char *) calloc(1024,sizeof(char));
150    text = popen(filename, "r");
151    // Wait for program to terminate
152    fscanf(text, "%s\n", result);
153    pclose(text);
154
155    output = result;
156
157    free(result);
158 }
159
160 // parseParameters:
161 // Interpret arguments, and collect uninterpretted arguments to be passed
162 // to test_driver.
163 void parseParameters(int argc, char *argv[])
164 {
165 #if defined(STATIC_TEST_DRIVER)
166    staticTests = true;
167 #endif
168
169    for ( int i = 1; i < argc; i++ )
170    {
171       if (strncmp(argv[i], "-limit", 6) == 0) {
172          if (i == (argc-1)) {
173             fprintf(stderr, "Error: -limit requires a parameter\n");
174          }
175          else {
176             unsigned int limit = atoi(argv[i+1]);
177             testLimit = limit;
178             i++;
179          }
180       } else if ((strcmp(argv[i], "-help") == 0) ||
181                  (strcmp(argv[i], "--help") == 0)) {
182          print_help();
183          exit(0);
184       } else if (strcmp(argv[i], "-static") == 0) {
185          staticTests = true;
186       } else if (strcmp(argv[i], "-dynamic") == 0) {
187          staticTests = false;
188       }
189       else if ((strcmp(argv[i], "-memcpu") == 0) ||
190                (strcmp(argv[i], "-cpumem") == 0))
191       {
192          memcpu_name = MEMCPU_DEFAULT_LOG;
193          
194          if ((i+1 < argc) &&
195              (argv[i+1][0] != '-' || argv[i+1][1] == '\0'))
196          {
197             i++;
198             memcpu_orig_name = argv[i];
199          }
200          else
201          {
202             memcpu_orig_name = "-";
203          }
204       }
205       else
206       {
207          // Pass uninterpreted arguments to test_driver
208          child_argv.push_back(argv[i]);
209       }
210    }
211 }
212
213 // ReplaceAllWith:
214 // Replaces all occurances of 'replace' in string 'in' with the text in string
215 // 'with', the result is returned.
216 string ReplaceAllWith(const string &in, const string &replace, const string &with)
217 {
218    string cur;
219    
220    string::size_type match_pos = in.find(replace);
221    string::size_type r_len = replace.length();
222    if ( match_pos == string::npos )
223    {
224       return in;
225    } 
226    else
227    {
228       return in.substr(0,match_pos) + with + 
229          ReplaceAllWith(in.substr(match_pos + r_len, in.length()), replace, with);
230    }
231    
232    
233 }
234
235 int main(int argc, char *argv[])
236 {
237    parseParameters(argc, argv);
238    setupVars(useLog, logfile);
239
240    setLibPath();
241
242    int result = 0;
243    int invocation = 0;
244
245    // Remove a stale resumelog, if it exists
246    if ( getenv("RESUMELOG") && isRegFile(string(getenv("RESUMELOG"))) )
247    {
248            if(unlink(getenv("RESUMELOG")) == -1) {
249                    fprintf(stderr, "Couldn't delete resume log: %s\n", getenv("RESUMELOG"));
250            }
251    } else if (isRegFile(string(DEFAULT_RESUMELOG))) {
252            if(unlink(DEFAULT_RESUMELOG) == -1) {
253                    fprintf(stderr, "Couldn't delete resume log: %s\n", DEFAULT_RESUMELOG);
254            }
255    }
256
257    // Remove a stale crashlog, if it exists
258    if (getenv("CRASHLOG") && isRegFile(string(getenv("CRASHLOG")))) {
259            if(unlink(getenv("CRASHLOG")) == -1) {
260                    fprintf(stderr, "Couldn't delete crash log: %s\n", getenv("CRASHLOG"));
261            };
262       unlink(getenv("CRASHLOG"));
263    } else if (isRegFile(string(DEFAULT_CRASHLOG))) {
264            if(unlink(DEFAULT_CRASHLOG) == -1) {
265                    fprintf(stderr, "Couldn't delete crash log: %s\n", DEFAULT_CRASHLOG);
266            };
267    }
268
269    // Create a PIDs file, to track mutatee PIDs
270    char *pidFilename = new char[80];
271    initPIDFilename(pidFilename, 80);
272    // result == 2 indicates that there are no more tests to run
273    while ( result != NOTESTS )
274    {
275       result = RunTest(invocation, useLog, staticTests, logfile, testLimit,
276                        child_argv, pidFilename, memcpu_name);
277       invocation++;
278       // I want to kill any remaining mutatees now, to clean up.  I should also
279       // set a timer in RunTest in case something goes weird with test_driver.
280       // (I think we'd be better off moving away from timer.pl)
281       cleanupMutatees(pidFilename);
282       if (-3 == result) {
283          // User interrupted the test run; allow them a couple of seconds to do
284          // it again and kill runTests
285          // TODO Make sure this is portable to Windows
286          fprintf(stderr, "Press ctrl-c again with-in 2 seconds to abort runTests.\n");
287          sleep(2);
288       }
289       if (-4 == (signed char) result) {
290          fprintf(stderr, "Could not execute test_driver\n");
291          break;
292       }
293       if (-5 == (signed char) result) {
294          break;
295       }
296    }
297
298    // Remove the PID file, now that we're done with it
299    if (pidFilename && isRegFile(string(pidFilename))) {
300       unlink(pidFilename);
301    }
302    unlink(DEFAULT_RESUMELOG);
303    unlink(getenv("RESUMELOG"));
304    
305
306    parseMEMCPUFile();
307    return 0;
308 }