Update copyright to LGPL on all files
[dyninst.git] / testsuite / src / dyninst / test_lib_mutateeStart.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 // $Id: test_lib_mutateeStart.C,v 1.1 2008/10/30 19:21:46 legendre Exp $
33 // Functions Dealing with mutatee Startup
34
35 #define COMPLIB_DLL_BUILD
36
37 #include "dyninst_comp.h"
38 #include "test_lib.h"
39 #include "util.h"
40 #include "ResumeLog.h"
41 #include <stdlib.h>
42
43
44 BPatch_thread *startMutateeTestGeneric(BPatch *bpatch, const char *pathname, const char **child_argv, bool useAttach)
45 {
46    BPatch_thread *appThread;
47    if (useAttach) {
48       // I should be able to remove the outlog and errlog parameters from
49       // startNewProcessForAttach without harming anything.
50       int pid = startNewProcessForAttach(pathname, child_argv,
51                                          NULL, NULL, true);
52       if (pid < 0) {
53          fprintf(stderr, "*ERROR*: unable to start tests due to error creating mutatee process\n");
54          return NULL;
55       } else {
56          dprintf("New mutatee process pid %d started; attaching...\n", pid);
57          registerPID(pid); // Register PID for cleanup
58       }
59       dprintf("Attaching to process: %s, %d\n", pathname, pid);
60       appThread = bpatch->attachProcess(pathname, pid);
61       dprintf("Attached to process\n");
62       dprintf("appThread == %lu\n", (unsigned long) appThread);
63    } else {
64       appThread = bpatch->createProcess(pathname, child_argv, NULL);
65       if (appThread != NULL) {
66          int pid = appThread->getProcess()->getPid();
67          registerPID(pid); // Register PID for cleanup
68       }
69    }
70
71    return appThread;
72 }
73
74 BPatch_thread *startMutateeTest(BPatch *bpatch, const char *mutatee, const char *testname,
75                                 bool useAttach, char *logfilename,
76                                 char *humanlogname)
77 {
78    std::vector<std::string> mutateeArgs;
79    getOutput()->getMutateeArgs(*&mutateeArgs); // mutateeArgs is an output parameter
80    const char **child_argv = new const char *[8 + mutateeArgs.size()];
81    if (NULL == child_argv) {
82       return NULL;
83    }
84
85    // Start the mutatee
86    dprintf("Starting \"%s\"\n", mutatee);
87
88    int n = 0;
89    child_argv[n++] = mutatee;
90    if (logfilename != NULL) {
91       child_argv[n++] = const_cast<char *>("-log");
92       child_argv[n++] = logfilename;
93    }
94    if (humanlogname != NULL) {
95       child_argv[n++] = const_cast<char *>("-humanlog");
96       child_argv[n++] = humanlogname;
97    }
98    child_argv[n++] = const_cast<char *>("-run");
99    child_argv[n++] = testname;
100    for (int i = 0; i < mutateeArgs.size(); i++) {
101       child_argv[n++] = mutateeArgs[i].c_str();
102    }
103    child_argv[n] = NULL;
104
105    BPatch_thread *retval = startMutateeTestGeneric(bpatch, mutatee, child_argv,
106                                                    useAttach);
107    delete [] child_argv;
108    return retval;
109 }
110
111 static const char** parseArgsIndividual(RunGroup *group, TestInfo *test,
112                               char *logfilename, char *humanlogname,
113                               bool verboseFormat, bool printLabels,
114                               int debugPrint, const char *pidfilename)
115 {
116    std::vector<std::string> mutateeArgs;
117    getOutput()->getMutateeArgs(mutateeArgs); // mutateeArgs is an output parameter
118    const char **child_argv = new const char *[12 + (4 * group->tests.size()) +
119                                               mutateeArgs.size()];
120    assert(child_argv);
121    int n = 0;
122    child_argv[n++] = group->mutatee;
123    if (logfilename != NULL) {
124       child_argv[n++] = const_cast<char *>("-log");
125       child_argv[n++] = logfilename;
126    }
127    if (humanlogname != NULL) {
128       child_argv[n++] = const_cast<char *>("-humanlog");
129       child_argv[n++] = humanlogname;
130    }
131    if (false == verboseFormat) {
132       child_argv[n++] = const_cast<char *>("-q");
133       // TODO I'll also want to pass a parameter specifying a file to write
134       // postponed messages to
135    }
136    if (debugPrint != 0) {
137       child_argv[n++] = const_cast<char *>("-verbose");
138    }
139    if (pidfilename != NULL) {
140       child_argv[n++] = const_cast<char *>("-pidfile");
141       child_argv[n++] = pidfilename;
142    }
143    if (shouldRunTest(group, test)) {
144          child_argv[n++] = const_cast<char*>("-run");
145          child_argv[n++] = test->name;
146       }
147    if (printLabels) {
148          child_argv[n++] = const_cast<char *>("-label");
149          child_argv[n++] = test->label;
150       child_argv[n++] = const_cast<char *>("-print-labels");
151    }
152    for (int i = 0; i < mutateeArgs.size(); i++) {
153       child_argv[n++] = strdup(mutateeArgs[i].c_str());
154    }
155    child_argv[n] = NULL;   
156
157    return child_argv;
158 }
159
160 static const char** parseArgs(RunGroup *group,
161                               char *logfilename, char *humanlogname,
162                               bool verboseFormat, bool printLabels,
163                               int debugPrint, char *pidfilename)
164 {
165    std::vector<std::string> mutateeArgs;
166    getOutput()->getMutateeArgs(mutateeArgs); // mutateeArgs is an output parameter
167    const char **child_argv = new const char *[12 + (4 * group->tests.size()) +
168                                               mutateeArgs.size()];
169    assert(child_argv);
170    int n = 0;
171    child_argv[n++] = group->mutatee;
172    if (logfilename != NULL) {
173       child_argv[n++] = const_cast<char *>("-log");
174       child_argv[n++] = logfilename;
175    }
176    if (humanlogname != NULL) {
177       child_argv[n++] = const_cast<char *>("-humanlog");
178       child_argv[n++] = humanlogname;
179  }
180    if (false == verboseFormat) {
181       child_argv[n++] = const_cast<char *>("-q");
182       // TODO I'll also want to pass a parameter specifying a file to write
183       // postponed messages to
184    }
185    if (debugPrint != 0) {
186       child_argv[n++] = const_cast<char *>("-verbose");
187    }
188    if (pidfilename != NULL) {
189       child_argv[n++] = const_cast<char *>("-pidfile");
190       child_argv[n++] = pidfilename;
191    }
192    for (int i = 0; i < group->tests.size(); i++) {
193       if (shouldRunTest(group, group->tests[i])) {
194          child_argv[n++] = const_cast<char*>("-run");
195          child_argv[n++] = group->tests[i]->name;
196       }
197    }
198    if (printLabels) {
199       for (int i = 0; i < group->tests.size(); i++) {
200          child_argv[n++] = const_cast<char *>("-label");
201          child_argv[n++] = group->tests[i]->label;
202       }
203       child_argv[n++] = const_cast<char *>("-print-labels");
204    }
205    for (int i = 0; i < mutateeArgs.size(); i++) {
206       child_argv[n++] = strdup(mutateeArgs[i].c_str());
207    }
208    child_argv[n] = NULL;   
209
210    return child_argv;
211 }
212
213 BPatch_thread *startMutateeTest(BPatch *bpatch, RunGroup *group,
214                                 char *logfilename, char *humanlogname,
215                                 bool verboseFormat, bool printLabels,
216                                 int debugPrint, char *pidfilename)
217 {
218    const char **child_argv = parseArgs(group, logfilename, humanlogname,
219                                        verboseFormat, printLabels, 
220                                        debugPrint, pidfilename);
221    
222    // Start the mutatee
223    dprintf("Starting \"%s\"\n", group->mutatee);
224    BPatch_thread *retval = startMutateeTestGeneric(bpatch, group->mutatee,
225                                                    child_argv,
226                                                    group->useAttach);
227    delete [] child_argv;
228    return retval;
229 }
230
231 BPatch_binaryEdit *startBinaryTest(BPatch *bpatch, RunGroup *group)
232 {
233    BPatch_binaryEdit *binEdit = bpatch->openBinary(group->mutatee, true);
234    return binEdit;
235 }
236
237
238 #if defined(os_linux_test)
239 #include <dirent.h>
240 #include <unistd.h>
241 #include <sys/stat.h>
242 #include <sys/types.h>
243 #include <sys/wait.h>
244 #include <signal.h>
245
246 static void clearBinEditFiles()
247 {
248    struct dirent **files;
249    int result = scandir(BINEDIT_DIR, &files, NULL, NULL);
250    if (result == -1) {
251       return;
252    }
253
254    int num_files = result;
255    for (unsigned i=0; i<num_files; i++) {
256       if ((strcmp(files[i]->d_name, ".") == 0) || 
257           (strcmp(files[i]->d_name, "..") == 0)) 
258       {
259          free(files[i]);
260          continue;
261       }
262       std::string full_file = std::string(BINEDIT_DIR) + std::string("/") +
263          std::string(files[i]->d_name);
264           if (!getenv("DYNINST_REWRITER_NO_UNLINK"))
265           {
266                   dprintf("%s[%d]:  unlinking %s\n", FILE__, __LINE__, full_file.c_str());
267                   unlink(full_file.c_str());
268           }
269       free(files[i]);
270    }
271    free(files);
272 }
273
274 static bool cdBinDir()
275 {
276    int result = chdir(BINEDIT_DIR);
277    if (result != -1) {
278       return true;
279    }
280
281    result = mkdir(BINEDIT_DIR, 0700);
282    if (result == -1) {
283       perror("Could not mkdir " BINEDIT_DIR);
284       return false;
285    }
286    result = chdir(BINEDIT_DIR);
287    if (result == -1) {
288       perror("Could not chdir " BINEDIT_DIR);
289       return false;
290    }
291    return true;
292 }
293
294 static bool cdBack()
295 {
296    int result = chdir("..");
297    if (result == -1) {
298       perror("Could not chdir ..");
299       return false;
300    }
301    return true;
302 }
303
304 static bool waitForCompletion(int pid, bool &app_crash, int &app_return)
305 {
306    int result, status;
307    int options = __WALL;
308    do {
309       result = waitpid(pid, &status, options);
310    } while (result == -1 && errno == EINTR);
311
312    if (result == -1) {
313       perror("Could not collect child result");
314       return false;
315    }
316
317    assert(!WIFSTOPPED(status));
318
319    if (WIFSIGNALED(status)) {
320       app_crash = true;
321       app_return = WTERMSIG(status);
322    }
323    else if (WIFEXITED(status)) {
324       app_crash = false;
325       app_return = WEXITSTATUS(status);
326    }
327    else {
328       assert(0);
329    }
330
331    return true;
332 }
333
334 static void killWaywardChild(int pid)
335 {
336    int result = kill(pid, SIGKILL);
337    if (result == -1) {
338       return;
339    }
340
341    bool dont_care1;
342    int dont_care2;
343    waitForCompletion(pid, dont_care1, dont_care2);
344 }
345 #else
346 void clearBinEditFiles()
347 {
348    assert(0); //IMPLEMENT ME
349 }
350
351 static bool cdBinDir()
352 {
353    assert(0); //IMPLEMENT ME
354    return false;
355 }
356
357 static bool cdBack()
358 {
359    assert(0); //IMPLEMENT ME
360    return false;
361 }
362
363 static void killWaywardChild(int)
364 {
365    assert(0); //IMPLEMENT ME
366 }
367
368 static bool waitForCompletion(int, bool &, int &)
369 {
370    assert(0); //IMPLEMENT ME
371    return false;
372 }
373 #endif
374
375 bool runBinaryTest(BPatch *bpatch, RunGroup *group,
376                    BPatch_binaryEdit *binEdit,
377                    char *logfilename, char *humanlogname,
378                    bool verboseFormat, bool printLabels,
379                    int debugPrint, char *pidfilename,
380                    bool noClean, test_results_t &test_result)
381 {
382    bool cd_done = false;
383    bool file_written = false;
384    bool file_running = false;
385    bool error = true;
386    bool result;
387    int app_return;
388    bool app_crash;
389    const char **child_argv = NULL;
390    int pid;
391    std::string outfile;
392
393    test_result = UNKNOWN;
394
395    clearBinEditFiles();
396
397    //  runTests is clobbering (perhaps intentionally?) the user's LD_LIBRARY_PATH
398    //  which means that if we cd to the bin dir, and any rewritten binary depends
399    //  on a library in the test directory (one level up), the library will no
400    //  be found.  So rather than cd to the directory, keep the CWD in the test 
401    //  directory and generate and execute the rewritten binary via relative path
402    result = cdBinDir();
403    if (!result) {
404       goto done;
405    }
406    cd_done = true;
407
408    outfile = /*std::string(BINEDIT_DIR) +*/ std::string("rewritten_") + std::string(group->mutatee);
409
410 #if !defined(os_windows_test)
411    if (getenv("DYNINST_REWRITER_NO_UNLINK"))
412    {
413       //  we may in fact generate several binaries  (one for each rungroup)
414       //  sequentially rewriting over them.  If DYNINST_REWRITER_NO_UNLINK is
415       //  set, add a uniqification parameter to the filename, as well as a 
416       //  report file that indicates which tests are represented in the
417       //  binary
418       outfile += std::string("_") + Dyninst::utos((unsigned)clock());
419       std::string reportfile = outfile + std::string(".report");
420       FILE *myrep = fopen(reportfile.c_str(), "w");
421       fprintf(myrep, "Test group contains:\n");
422       for (unsigned int i = 0; i < group->tests.size(); ++i)
423          if (shouldRunTest(group, group->tests[i])) 
424             fprintf(myrep, "%s\n", group->tests[i]->name);
425       fclose(myrep);
426    }
427 #endif
428
429    result = binEdit->writeFile(outfile.c_str());
430    if (!result) {
431       goto done;
432    }
433    file_written = true;
434
435    if (cd_done) {
436       cdBack();
437       cd_done = false;
438    }
439
440    child_argv = parseArgs(group, logfilename, humanlogname,
441                           verboseFormat, printLabels, 
442                           debugPrint, pidfilename);
443    
444    dprintf("%s[%d]:  starting rewritten process '%s ", FILE__, __LINE__, outfile.c_str());
445    if (child_argv)
446    {
447      const char **argv_iter = child_argv;
448      while(argv_iter)
449      {
450        const char *arg = *argv_iter;
451        if (arg) dprintf("%s ", arg);
452        else break;
453        argv_iter++;
454      }
455    }
456    dprintf("'...\n\n\n");
457    outfile = "./binaries/" + outfile;
458    pid = startNewProcessForAttach(outfile.c_str(), child_argv,
459                                   NULL, NULL, false);
460    if (pid == -1) {
461       goto done;
462    }
463    file_running = false;
464
465    result = waitForCompletion(pid, app_crash, app_return);
466    if (!result)
467       goto done;
468    file_running = false;
469
470    
471    dprintf("%s[%d]:  after waitForCompletion: %s, result = %d\n", FILE__, __LINE__, app_crash ? "crashed" : "no crash", app_return);
472
473    if ((app_crash)  || (app_return != 0))
474    {
475      parse_mutateelog(group);
476      test_result = UNKNOWN;
477    }
478    else {
479      test_result = PASSED;
480    }
481    
482    error = false;
483  done:
484
485    if (error)
486       test_result = FAILED;
487    if (cd_done)
488       cdBack();
489    if (file_written && !noClean)
490       clearBinEditFiles();
491    if (file_running)
492       killWaywardChild(pid);
493    if (child_argv)
494       delete [] child_argv;
495       
496    return !error;  
497 }