Update copyright to LGPL on all files
[dyninst.git] / dyninstAPI / tests / src / test4.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: test4.C,v 1.37 2008/04/11 23:30:34 legendre Exp $
33 //
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <assert.h>
38 #include <stdarg.h>
39 #ifdef i386_unknown_nt4_0
40 #define WIN32_LEAN_AND_MEAN
41 #include <windows.h>
42 #include <winbase.h>
43 #else
44 #include <unistd.h>
45 #endif
46
47 #include "BPatch.h"
48 #include "BPatch_Vector.h"
49 #include "BPatch_thread.h"
50 #include "BPatch_snippet.h"
51 #include "test_util.h"
52
53
54 const char *mutateeNameRoot = "./test4a.mutatee";
55
56 int inTest;             // current test #
57 int expectError;
58 int debugPrint = 0; // internal "mutator" tracing
59 int errorPrint = 0; // external "dyninst" tracing (via errorFunc)
60
61 bool forceRelocation = false;  // Force relocation upon instrumentation
62
63 void dprintf(const char *fmt, ...) {
64    va_list args;
65    va_start(args, fmt);
66
67    if(debugPrint)
68       vfprintf(stderr, fmt, args);
69
70    va_end(args);
71
72    fflush(stderr);
73 }
74
75 bool runAllTests = true;
76 const unsigned int MAX_TEST = 4;
77 bool runTest[MAX_TEST+1];
78 bool passedTest[MAX_TEST+1];
79 bool failedTest[MAX_TEST+1];
80 int threadCount = 0;
81 BPatch_thread *mythreads[25];
82
83 BPatch *bpatch;
84
85 /*
86  * Given a string variable name and an expected value, lookup the varaible
87  *    in the child process, and verify that the value matches.
88  *
89  */
90 bool verifyChildMemory(BPatch_thread *appThread, 
91                        const char *name, int expectedVal)
92 {
93      BPatch_image *appImage = appThread->getImage();
94
95      if (!appImage) {
96          dprintf("unable to locate image for %d\n", appThread->getPid());
97          return false;
98      }
99
100      BPatch_variableExpr *var = appImage->findVariable(name);
101      if (!var) {
102          dprintf("unable to located variable %s in child\n", name);
103          return false;
104      }
105
106      int actualVal;
107      var->readValue(&actualVal);
108
109      if (expectedVal != actualVal) {
110          printf("*** for %s, expected val = %d, but actual was %d\n",
111                 name, expectedVal, actualVal);
112          return false;
113      } else {
114          dprintf("verified %s was = %d\n", name, actualVal);
115          return true;
116      }
117 }
118
119 BPatch_thread *test2Child;
120 BPatch_thread *test2Parent;
121 BPatch_thread *test4Child;
122 BPatch_thread *test4Parent;
123
124
125 void forkFunc(BPatch_thread *parent, BPatch_thread *child)
126 {
127   dprintf("forkFunc called with parent %p, child %p\n", parent, child);
128     BPatch_image *appImage;
129     BPatch_Vector<BPatch_function *> bpfv;
130     BPatch_Vector<BPatch_snippet *> nullArgs;
131
132     if (child) mythreads[threadCount++] = child;
133
134     if (!child) {
135        dprintf("in prefork for %d\n", parent->getPid());
136     } else {
137        dprintf("in fork of %d to %d\n", parent->getPid(), child->getPid());
138     }
139
140     if (inTest == 1) {
141        // nothing to do for this case
142     } else if (inTest == 2) {
143        if (!child) return;      // skip prefork case
144
145        // Make a race condition always show up -- we don't run
146        // until the processes have had a chance.
147 #if !defined(os_windows)
148        sleep(1);
149 #endif
150        // That'll make erroneous continues break...
151
152        // insert code into parent
153        appImage = parent->getImage();
154        assert(appImage);
155
156        char *fn = "func2_3";
157        if (NULL == appImage->findFunction(fn, bpfv) || !bpfv.size()
158            || NULL == bpfv[0]){
159          fprintf(stderr, "    Unable to find function %s\n",fn);
160          exit(1);
161        }
162
163        BPatch_function *func2_3_parent = bpfv[0];
164        BPatch_funcCallExpr callExpr2(*func2_3_parent, nullArgs);
165  
166        bpfv.clear();
167        char *fn2 = "func2_2";
168        if (NULL == appImage->findFunction(fn2, bpfv) || !bpfv.size()
169            || NULL == bpfv[0]){
170          fprintf(stderr, "    Unable to find function %s\n",fn2);
171          exit(1);
172        }
173
174        BPatch_function *func2_2_parent = bpfv[0];
175        BPatch_Vector<BPatch_point *> *point2 = func2_2_parent->findPoint(BPatch_exit);
176        assert(point2);
177        
178        parent->insertSnippet(callExpr2, *point2);
179
180        dprintf("MUTATEE:  after insert in fork of %d to %d\n", parent->getPid(), child->getPid());
181        // insert different code into child
182        appImage = child->getImage();
183        assert(appImage);
184
185        bpfv.clear();
186        char *fn3 = "func2_4";
187        if (NULL == appImage->findFunction(fn3, bpfv) || !bpfv.size()
188            || NULL == bpfv[0]){
189          fprintf(stderr, "    Unable to find function %s\n",fn3);
190          exit(1);
191        }
192
193        BPatch_function *func2_4_child = bpfv[0];
194        BPatch_funcCallExpr callExpr1(*func2_4_child, nullArgs);
195
196        bpfv.clear();
197        char *fn4 = "func2_2";
198        if (NULL == appImage->findFunction(fn4, bpfv) || !bpfv.size()
199            || NULL == bpfv[0]){
200          fprintf(stderr, "    Unable to find function %s\n",fn4);
201          exit(1);
202        }
203
204        BPatch_function *func2_2_child = bpfv[0];
205        BPatch_Vector<BPatch_point *> *point1 = func2_2_child->findPoint(BPatch_exit);
206        assert(point1);
207
208        child->insertSnippet(callExpr1, *point1);
209
210        dprintf("MUTATEE:  after insert2 in fork of %d to %d\n", parent->getPid(), child->getPid());
211        test2Child = child;
212        test2Parent = parent;
213     } else if (inTest == 4) {
214        if (!child) return;      // skip prefork case
215
216        // insert code into parent
217        appImage = parent->getImage();
218        assert(appImage);
219
220        char *fn5 = "func4_3";
221        if (NULL == appImage->findFunction(fn5, bpfv) || !bpfv.size()
222            || NULL == bpfv[0]){
223          fprintf(stderr, "    Unable to find function %s\n",fn5);
224          exit(1);
225        }
226
227        BPatch_function *func4_3_parent = bpfv[0];
228        BPatch_funcCallExpr callExpr2(*func4_3_parent, nullArgs);
229
230        bpfv.clear();
231        char *fn6 = "func4_2";
232        if (NULL == appImage->findFunction(fn6, bpfv) || !bpfv.size()
233            || NULL == bpfv[0]){
234          fprintf(stderr, "    Unable to find function %s\n",fn6);
235          exit(1);
236        }
237
238        BPatch_function *func4_2_parent = bpfv[0];
239        BPatch_Vector<BPatch_point *> *point2 = func4_2_parent->findPoint(BPatch_exit);
240        assert(point2);
241        parent->insertSnippet(callExpr2, *point2);
242
243        // code goes into child after in-exec in this test.
244
245        test4Child = child;
246     }
247 }
248
249 void exitFunc(BPatch_thread *thread, BPatch_exitType exit_type)
250 {
251   dprintf("exitFunc called\n");
252     // Read out the values of the variables.
253     int exitCode = thread->getExitCode();
254
255     assert(thread->terminationStatus() == exit_type);
256     // Read out the values of the variables.
257     if (inTest == 1) {
258         if (exit_type == ExitedNormally) {
259             if(thread->getPid() == exitCode) {
260                 if (verifyChildMemory(thread, "globalVariable1_1", 1000001)) {
261                     printf("Passed test #1 (exit callback)\n");
262                     passedTest[1] = true;
263                 } else {
264                     printf("**Failed** test #1 (exit callback)\n");
265                     printf("    verifyChildMemory failed\n");
266                     passedTest[1] = false;
267                 }
268             } else {
269                 printf("**Failed** test #1 (exit callback)\n");
270                 printf("    exit code = %d, was not equal to pid\n", exitCode);
271                 passedTest[1] = false;
272             }
273         } else if (exit_type == ExitedViaSignal) {
274            printf("**Failed** test #1 (exit callback), exited via signal %d\n",
275                    thread->getExitSignal());
276             passedTest[1] = false;
277         } else assert(false);
278     } else if (inTest == 2) {
279         static int exited = 0;
280         exited++;
281         if(exit_type == ExitedViaSignal) {
282             printf("Failed test #2 (fork callback)\n");
283             printf("    a process terminated via signal %d\n",
284                    thread->getExitSignal());
285             exited = 0;            
286         } else if (thread->getPid() != exitCode) {
287             printf("Failed test #2 (fork callback)\n");
288             printf("    exit code was not equal to pid (%d != %d)\n", thread->getPid(), exitCode);            
289             exited = 0;
290         } else {
291             dprintf("test #2, pid %d exited\n", exitCode);
292             if ((test2Parent == thread) &&
293                 !verifyChildMemory(test2Parent, "globalVariable2_1", 2000002)) {
294                 failedTest[2] = true;
295             }
296             if ((test2Child == thread) &&
297                 !verifyChildMemory(test2Child, "globalVariable2_1", 2000003)) {
298                 failedTest[2] = true;
299             }
300             
301             // See if all the processes are done
302             if (exited == 2) {
303                 if (!failedTest[2]) {
304                     printf("Passed test #2 (fork callback)\n");
305                     passedTest[2] = true;
306                 } else {
307                     printf("Failed test #2 (fork callback)\n");
308                 }
309             }
310         }
311     } else if (inTest == 3) {
312         // simple exec 
313         if(exit_type == ExitedViaSignal) {
314             printf("Failed test #3 (exec callback), exited via signal %d\n",
315                    thread->getExitSignal());
316         } else if (!verifyChildMemory(thread, "globalVariable3_1", 3000002)) {
317             printf("Failed test #3 (exec callback)\n");
318         } else {
319             printf("Passed test #3 (exec callback)\n");
320             passedTest[3] = true;
321         }
322     } else if (inTest == 4) {
323         static int exited = 0;
324         exited++;
325         if (exit_type == ExitedViaSignal) {
326             printf("Failed test #4 (fork callback)\n");
327             printf("    process exited via signal %d\n",
328                    thread->getExitSignal());
329             failedTest[4] = true;            
330         } else if (thread->getPid() != exitCode) {
331             printf("Failed test #4 (fork callback)\n");
332             printf("    exit code was not equal to pid\n");
333             failedTest[4] = true;
334         } else if (test4Parent == thread) {
335             dprintf("test #4, pid %d exited\n", exitCode);
336             if (!verifyChildMemory(test4Parent,"globalVariable4_1",4000002)){
337                 failedTest[4] = true;
338             }
339         } else if (test4Child == thread) {
340             dprintf("test #4, pid %d exited\n", exitCode);
341             if (!verifyChildMemory(test4Child, "globalVariable4_1", 4000003)) {
342                 failedTest[4] = true;
343             }
344         } else {
345             // exit from unknown thread
346             printf("Failed test #4 (fork callback)\n");
347             printf("    exit from unknown pid = %d\n", exitCode);
348             failedTest[4] = true;
349         }
350         // See if all the processes are done
351         if (exited == 2) {
352             if (!failedTest[4]) {
353                 printf("Passed test #4 (fork & exec)\n");
354                 passedTest[4] = true;
355             } else {
356                 printf("Failed test #4 (fork & exec)\n");
357             }
358         }
359     } else {
360         printf("**Exit from unknown test case**\n");
361     }
362 }
363
364 void execFunc(BPatch_thread *thread)
365 {
366   BPatch_Vector<BPatch_function *> bpfv;
367
368     if (inTest == 1 || inTest == 2) {
369         printf("**Failed Test #%d\n", inTest);
370         printf("    execCallback invoked, but exec was not called!\n");
371     } else if (inTest == 3) {
372         dprintf("in exec callback for %d\n", thread->getPid());
373
374         // insert code into parent
375         BPatch_Vector<BPatch_snippet *> nullArgs;
376         BPatch_image *appImage = thread->getImage();
377         assert(appImage);
378
379         char *fn = "func3_2";
380         if (NULL == appImage->findFunction(fn, bpfv) || !bpfv.size()
381             || NULL == bpfv[0]){
382           fprintf(stderr, "    Unable to find function %s\n",fn);
383           exit(1);
384         }
385
386         BPatch_function *func3_2_parent = bpfv[0];
387         BPatch_funcCallExpr callExpr(*func3_2_parent, nullArgs);
388
389         bpfv.clear();
390         char *fn2 = "func3_1";
391         if (NULL == appImage->findFunction(fn2, bpfv) || !bpfv.size()
392             || NULL == bpfv[0]){
393           fprintf(stderr, "    Unable to find function %s\n",fn2);
394           exit(1);
395         }
396
397         BPatch_function *func3_1_parent = bpfv[0];
398         BPatch_Vector<BPatch_point *> *point = func3_1_parent->findPoint(BPatch_exit);
399
400         assert(point);
401         thread->insertSnippet(callExpr, *point);
402         dprintf("%s[%d]:  MUTATEE: exec callback for %d, done with insert snippet\n", __FILE__, __LINE__, thread->getPid());
403     } else if (inTest == 4) {
404         dprintf("in exec callback for %d\n", thread->getPid());
405
406         // insert code into child
407         BPatch_Vector<BPatch_snippet *> nullArgs;
408         BPatch_image *appImage = thread->getImage();
409         assert(appImage);
410
411         char *fn3 = "func4_4";
412         if (NULL == appImage->findFunction(fn3, bpfv) || !bpfv.size()
413             || NULL == bpfv[0]){
414           fprintf(stderr, "    Unable to find function %s\n",fn3);
415           exit(1);
416         }
417
418         BPatch_function *func4_4_child = bpfv[0];
419         BPatch_funcCallExpr callExpr1(*func4_4_child, nullArgs);
420         
421         bpfv.clear();
422         char *fn4 = "func4_2";
423         if (NULL == appImage->findFunction(fn4, bpfv) || !bpfv.size()
424             || NULL == bpfv[0]){
425           fprintf(stderr, "    Unable to find function %s\n",fn4);
426           exit(1);
427         }
428
429         BPatch_function *func4_2_child = bpfv[0];
430         BPatch_Vector<BPatch_point *> *point1 = func4_2_child->findPoint(BPatch_exit);
431
432         assert(point1);
433         thread->insertSnippet(callExpr1, *point1);
434     } else {
435         printf("in exec callback for %d\n", thread->getPid());
436     }
437 }
438
439 void errorFunc(BPatchErrorLevel level, int num, const char * const *params)
440 {
441     if (num == 0) {
442         // conditional reporting of warnings and informational messages
443         if (errorPrint) {
444             if (level == BPatchInfo)
445               { if (errorPrint > 1) printf("%s\n", params[0]); }
446             else
447                 printf("%s", params[0]);
448         }
449     } else {
450         // reporting of actual errors
451         char line[256];
452         const char *msg = bpatch->getEnglishErrorString(num);
453         bpatch->formatErrorString(line, sizeof(line), msg, params);
454         
455         if (num != expectError) {
456             printf("Error #%d (level %d): %s\n", num, level, line);
457         
458             // We consider some errors fatal.
459             if (num == 101) {
460                exit(-1);
461             }
462         }
463     }
464 }
465
466 void contAndWaitForAllThreads(BPatch_thread *appThread)
467 {
468   dprintf("Thread %d is pointer %p\n", threadCount, appThread);
469   mythreads[threadCount++] = appThread;
470    appThread->continueExecution();
471
472    while (1) {
473       int i;
474       dprintf("Checking %d threads for terminated status\n", threadCount);
475       for (i=0; i < threadCount; i++) {
476         if (!mythreads[i]->isTerminated()) {
477           dprintf("Thread %d is not terminated\n", i);
478             break;
479          }
480       }
481
482       // see if all exited
483       if (i== threadCount) {
484         dprintf("All threads terminated\n");
485         break;
486       }
487
488       bpatch->waitForStatusChange();
489
490       for (i=0; i < threadCount; i++) {
491          if (mythreads[i]->isStopped()) {
492            dprintf("Thread %d marked stopped, continuing\n", i);
493             mythreads[i]->continueExecution();
494          }
495       }
496    }
497    
498    dprintf("All threads terminated, deleting\n");
499    for (int i=0; i < threadCount; i++) {
500      delete mythreads[i];
501    }
502    threadCount = 0;
503 }
504
505 void mutatorTest1(char *pathname)
506 {
507 #if defined(i386_unknown_nt4_0) \
508  || defined(alpha_dec_osf4_0)
509
510     printf("Skipping test #1 (exit callback)\n");
511     printf("    not implemented on this platform\n");
512     passedTest[1] = true;
513
514 #else
515     int n = 0;
516     const char *child_argv[MAX_TEST+5];
517         
518     dprintf("in mutatorTest1\n");
519
520     inTest = 1;
521     child_argv[n++] = pathname;
522     if (debugPrint) child_argv[n++] = const_cast<char*>("-verbose");
523
524     child_argv[n++] = const_cast<char*>("-run");
525     child_argv[n++] = const_cast<char*>("1");
526     child_argv[n] = NULL;
527
528     // Start the mutatee
529     printf("Starting \"%s\"\n", pathname);
530
531     BPatch_thread *appThread = bpatch->createProcess(pathname, child_argv,NULL);
532     dprintf("Test 1: using thread %p\n", appThread);
533     if (appThread == NULL) {
534         fprintf(stderr, "Unable to run test program.\n");
535         exit(1);
536     }
537
538     contAndWaitForAllThreads(appThread);
539
540     inTest = 0;
541 #endif
542 }
543
544
545 void mutatorTest2(char *pathname)
546 {
547 #if defined(i386_unknown_nt4_0) \
548  || defined(alpha_dec_osf4_0)
549
550     printf("Skipping test #2 (fork callback)\n");
551     printf("    not implemented on this platform\n");
552     passedTest[2] = true;
553
554 #else
555     int n = 0;
556     const char *child_argv[MAX_TEST+5];
557         
558     dprintf("\n\n\n\nin mutatorTest2\n");
559
560     inTest = 2;
561     child_argv[n++] = pathname;
562     if (debugPrint) child_argv[n++] = const_cast<char*>("-verbose");
563
564     child_argv[n++] = const_cast<char*>("-run");
565     child_argv[n++] = const_cast<char*>("2");
566     child_argv[n] = NULL;
567
568     // Start the mutatee
569     printf("Starting \"%s\"\n", pathname);
570
571     BPatch_thread *appThread = bpatch->createProcess(pathname, child_argv,NULL);
572     dprintf("Process %p created", appThread);
573     if (appThread == NULL) {
574         fprintf(stderr, "Unable to run test program.\n");
575         exit(1);
576     }
577
578     contAndWaitForAllThreads(appThread);
579     fprintf(stderr, "Finished with test2\n");
580     inTest = 0;
581 #endif
582 }
583
584 void mutatorTest3(char *pathname)
585 {
586 #if defined(i386_unknown_nt4_0) \
587  || defined(alpha_dec_osf4_0)
588
589     printf("Skipping test #3 (exec callback)\n");
590     printf("    not implemented on this platform\n");
591     passedTest[3] = true;
592
593 #else
594     int n = 0;
595     const char *child_argv[MAX_TEST+5];
596         
597     dprintf("in mutatorTest3\n");
598
599     inTest = 3;
600     child_argv[n++] = pathname;
601     if (debugPrint) child_argv[n++] = const_cast<char*>("-verbose");
602
603     child_argv[n++] = const_cast<char*>("-run");
604     child_argv[n++] = const_cast<char*>("3");
605     child_argv[n] = NULL;
606
607     // Start the mutatee
608     printf("Starting \"%s\"\n", pathname);
609
610     BPatch_thread *appThread = bpatch->createProcess(pathname, child_argv,NULL);
611     if (appThread == NULL) {
612         fprintf(stderr, "Unable to run test program.\n");
613         exit(1);
614     }
615
616     contAndWaitForAllThreads(appThread);
617
618     inTest = 0;
619 #endif
620 }
621
622 void mutatorTest4(char *pathname)
623 {
624 #if defined(i386_unknown_nt4_0) \
625  || defined(alpha_dec_osf4_0)
626
627     printf("Skipping test #4 (fork & exec)\n");
628     printf("    not implemented on this platform\n");
629     passedTest[4] = true;
630
631 #else
632     int n = 0;
633     const char *child_argv[MAX_TEST+5];
634         
635     dprintf("in mutatorTest4\n");
636
637     inTest = 4;
638     child_argv[n++] = pathname;
639     if (debugPrint) child_argv[n++] = const_cast<char*>("-verbose");
640
641     child_argv[n++] = const_cast<char*>("-run");
642     child_argv[n++] = const_cast<char*>("4");
643     child_argv[n] = NULL;
644
645     // Start the mutatee
646     printf("Starting \"%s\"\n", pathname);
647
648     test4Parent = bpatch->createProcess(pathname, child_argv,NULL);
649     if (test4Parent == NULL) {
650         fprintf(stderr, "Unable to run test program.\n");
651         exit(1);
652     }
653
654     contAndWaitForAllThreads(test4Parent);
655
656     inTest = 0;
657 #endif
658 }
659
660 void mutatorMAIN(char *pathname)
661 {
662     // Create an instance of the BPatch library
663     bpatch = new BPatch;
664
665     // Force functions to be relocated
666     if (forceRelocation) {
667       bpatch->setForcedRelocation_NP(true);
668     }
669
670     bpatch->setTrampRecursive(true);
671     // Register a callback function that prints any error messages
672     bpatch->registerErrorCallback(errorFunc);
673     bpatch->registerPreForkCallback(forkFunc);
674     bpatch->registerPostForkCallback(forkFunc);
675     bpatch->registerExecCallback(execFunc);
676     bpatch->registerExitCallback(exitFunc);
677
678     if (runTest[1]) mutatorTest1(pathname);
679     if (runTest[2]) mutatorTest2(pathname);
680     if (runTest[3]) mutatorTest3(pathname);
681     if (runTest[4]) mutatorTest4(pathname);
682     unsigned int testsFailed = 0;
683     for (unsigned int i=1; i <= MAX_TEST; i++) {
684         if (runTest[i] && !passedTest[i]) testsFailed++;
685     }
686
687     if (!testsFailed) {
688         if (runAllTests) {
689             printf("All tests passed\n");
690         } else {
691             printf("All requested tests passed\n");
692         }
693     }
694 }
695
696 //
697 // main - decide our role and call the correct "main"
698 //
699 int
700 main(int argc, char *argv[])
701 {
702     unsigned int i;
703     bool ABI_32=false;
704     char mutateeName[128];
705     char libRTname[256];
706
707     strcpy(mutateeName,mutateeNameRoot);
708     libRTname[0]='\0';
709
710     if (!getenv("DYNINSTAPI_RT_LIB")) {
711          fprintf(stderr,"Environment variable DYNINSTAPI_RT_LIB undefined:\n"
712 #if defined(i386_unknown_nt4_0)
713                  "    using standard search strategy for libdyninstAPI_RT.dll\n");
714 #else
715                  "    set it to the full pathname of libdyninstAPI_RT\n");   
716          exit(-1);
717 #endif
718     } else
719          strcpy((char *)libRTname, (char *)getenv("DYNINSTAPI_RT_LIB"));
720
721     updateSearchPaths(argv[0]);
722
723     // by default run all tests
724     for (i=1; i <= MAX_TEST; i++) {
725         runTest[i] = true;
726         passedTest[i] = false;
727     }
728
729     for (i=1; i < argc; i++) {
730         if (strncmp(argv[i], "-v+", 3) == 0)    errorPrint++;
731         if (strncmp(argv[i], "-v++", 4) == 0)   errorPrint++;
732         if (strncmp(argv[i], "-verbose", 2) == 0) {
733             debugPrint = 1;
734         } else if (!strcmp(argv[i], "-V")) {
735             if (libRTname[0]) 
736                 fprintf (stdout, "DYNINSTAPI_RT_LIB=%s\n", libRTname);
737             fflush(stdout);
738         } else if (!strcmp(argv[i], "-skip")) {
739             unsigned int j;
740             runAllTests = false;
741             for (j=i+1; j < argc; j++) {
742                 unsigned int testId;
743                 if ((testId = atoi(argv[j]))) {
744                     if ((testId > 0) && (testId <= MAX_TEST)) {
745                         runTest[testId] = false;
746                     } else {
747                         printf("invalid test %d requested\n", testId);
748                         exit(-1);
749                     }
750                 } else {
751                     // end of test list
752                     break;
753                 }
754             }
755             i=j-1;
756         } else if (!strcmp(argv[i], "-run")) {
757             unsigned int j;
758             runAllTests = false;
759             for (j=0; j <= MAX_TEST; j++) runTest[j] = false;
760             for (j=i+1; j < argc; j++) {
761                 unsigned int testId;
762                 if ((testId = atoi(argv[j]))) {
763                     if ((testId > 0) && (testId <= MAX_TEST)) {
764                         runTest[testId] = true;
765                     } else {
766                         printf("invalid test %d requested\n", testId);
767                         exit(-1);
768                     }
769                 } else {
770                     // end of test list
771                     break;
772                 }
773             }
774             i=j-1;
775         } else if (!strcmp(argv[i], "-mutatee")) {
776             i++;
777             if (*argv[i]=='_')
778                 strcat(mutateeName,argv[i]);
779             else
780                 strcpy(mutateeName,argv[i]);
781 #if defined(i386_unknown_nt4_0) \
782  || defined(i386_unknown_linux2_0) \
783  || defined(x86_64_unknown_linux2_4) /* Blind duplication - Ray */ \
784  || defined(sparc_sun_solaris2_4) \
785  || defined(ia64_unknown_linux2_4)
786         } else if (!strcmp(argv[i], "-relocate")) {
787             forceRelocation = true;
788 #endif
789 #if defined(x86_64_unknown_linux2_4)
790         } else if (!strcmp(argv[i], "-m32")) {
791             ABI_32=true;
792 #endif
793         } else {
794             fprintf(stderr, "Usage: test4 "
795                     "[-V] [-verbose] "
796 #if defined(mips_sgi_irix6_4)
797                     "[-n32] "
798 #endif
799                     "[-mutatee <test4a.mutatee>] "
800                     "[-run <test#> <test#> ...] "
801                     "[-skip <test#> <test#> ...]\n");
802             fprintf(stderr, "%d subtests\n", MAX_TEST);
803             exit(-1);
804         }
805     }
806
807     if (!runAllTests) {
808         printf("Running Tests: ");
809         for (unsigned int j=1; j <= MAX_TEST; j++) {
810             if (runTest[j]) printf("%d ", j);
811         }
812         printf("\n");
813     }
814
815     // patch up the default compiler in mutatee name (if necessary)
816     if (!strstr(mutateeName, "_"))
817 #if defined(i386_unknown_nt4_0)
818         strcat(mutateeName,"_VC");
819 #else
820         strcat(mutateeName,"_gcc");
821 #endif
822     if (ABI_32 || strstr(mutateeName,"_m32")) {
823         // patch up file names based on alternate ABI (as necessary)
824         if (!strstr(mutateeName, "_m32")) strcat(mutateeName,"_m32");
825     }
826     // patch up the platform-specific filename extensions
827 #if defined(i386_unknown_nt4_0)
828     if (!strstr(mutateeName, ".exe")) strcat(mutateeName,".exe");
829 #endif
830
831     mutatorMAIN(mutateeName);
832
833     return 0;
834 }