Update copyright to LGPL on all files
[dyninst.git] / dyninstAPI / tests / src / test8.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: test8.C,v 1.22 2008/04/11 23:30:38 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 const char *mutateeNameRoot = "test8.mutatee";
54
55 int expectError = 112;
56 int debugPrint = 1; // internal "mutator" tracing
57 int errorPrint = 0; // external "dyninst" tracing (via errorFunc)
58
59 bool forceRelocation = false;  // Force relocation upon instrumentation
60
61 void dprintf(const char *fmt, ...) {
62    va_list args;
63    va_start(args, fmt);
64
65    if(debugPrint)
66       vfprintf(stderr, fmt, args);
67
68    va_end(args);
69
70    fflush(stderr);
71 }
72
73 bool runAllTests = true;
74 const unsigned int MAX_TEST = 3;
75 bool runTest[MAX_TEST+1];
76 bool passedTest[MAX_TEST+1];
77 bool failedTest[MAX_TEST+1];
78
79 BPatch *bpatch;
80
81 typedef struct {
82     bool             valid;
83     bool             optional;
84     BPatch_frameType type;
85     const char      *function_name;
86 } frameInfo_t;
87
88 void errorFunc(BPatchErrorLevel level, int num, const char * const *params)
89 {
90     if (num == 0) {
91         // conditional reporting of warnings and informational messages
92         if (errorPrint) {
93             if (level == BPatchInfo)
94               { if (errorPrint > 1) fprintf( stderr, "%s\n", params[0]); }
95             else
96                 fprintf( stderr, "%s", params[0]);
97         }
98     } else {
99         // reporting of actual errors
100         char line[256];
101         const char *msg = bpatch->getEnglishErrorString(num);
102         bpatch->formatErrorString(line, sizeof(line), msg, params);
103         
104         if (num != expectError) {
105             fprintf(stderr, "Error #%d (level %d): %s\n", num, level, line);
106         
107             // We consider some errors fatal.
108             if (num == 101) {
109                exit(-1);
110             }
111         }
112     }
113 }
114
115 const char *frameTypeString(BPatch_frameType frameType)
116 {
117     switch (frameType) {
118       case BPatch_frameNormal:
119         return "BPatch_frameNormal";
120       case BPatch_frameSignal:
121         return "BPatch_frameSignal";
122       case BPatch_frameTrampoline:
123         return "BPatch_frameTrampoline";
124       default:
125         break;
126     };
127
128     return "UNKNOWN";
129 }
130
131 bool hasExtraUnderscores(const char *str)
132 {
133     assert( str );
134     int len = strlen(str) - 1;
135     return (str[0] == '_' || str[len] == '_');
136 }
137
138 /* WARNING: This function is not thread safe. */
139 const char *fixUnderscores(const char *str)
140 {
141     static char buf[256];
142
143     assert( str );
144     assert( strlen(str) < sizeof(buf) );
145
146     while (*str == '_') ++str;
147     strncpy(buf, str, 256);
148
149     char *ptr = buf + strlen(buf) - 1;
150     while (ptr > buf && *ptr == '_') *(ptr--) = 0;
151
152     return buf;
153 }
154
155 bool checkStack(BPatch_thread *appThread,
156                 const frameInfo_t correct_frame_info[],
157                 unsigned num_correct_names,
158                 int test_num, const char *test_name)
159 {
160     unsigned i, j;
161
162     const int name_max = 256;
163     bool failed = false;
164
165     BPatch_Vector<BPatch_frame> stack;
166     appThread->getCallStack(stack);
167
168     if (debugPrint) {
169         printf("Stack in test %d (%s):\n", test_num, test_name);
170         for( unsigned i = 0; i < stack.size(); i++) {
171             char name[name_max];
172             BPatch_function *func = stack[i].findFunction();
173             if (func == NULL)
174                 strcpy(name, "[UNKNOWN]");
175             else
176                 func->getName(name, name_max);
177             printf("  %10p: %s, fp = %p, type %s\n",
178                stack[i].getPC(),
179                name,
180                stack[i].getFP(),
181                frameTypeString(stack[i].getFrameType()));
182         
183         }
184         printf("End of stack dump.\n");
185     }
186
187     if (stack.size() < num_correct_names) {
188         fprintf(stderr, "**Failed** test %d (%s)\n", test_num, test_name);
189         fprintf(stderr, "    Stack trace should contain more frames.\n");
190         failed = true;
191     }
192
193     for (i = 0, j = 0; i < num_correct_names; i++, j++) {
194 #if !defined(i386_unknown_nt4_0)
195         if (j < stack.size()-1 && stack[j].getFP() == 0) {
196             fprintf(stderr, "**Failed** test %d (%s)\n", test_num, test_name);
197             fprintf(stderr, "    A stack frame other than the lowest has a null FP.\n");
198             failed = true;
199             break;
200         }
201 #endif
202
203         if (correct_frame_info[i].valid) {
204             char name[name_max], name2[name_max];
205
206             BPatch_function *func = stack[j].findFunction();
207             if (func != NULL)
208                 func->getName(name, name_max);
209
210             BPatch_function *func2 =
211                 appThread->findFunctionByAddr(stack[j].getPC());
212             if (func2 != NULL)
213                 func2->getName(name2, name_max);
214
215             if ((func == NULL && func2 != NULL) ||
216                 (func != NULL && func2 == NULL)) {
217                 fprintf(stderr, "**Failed** test %d (%s)\n", test_num, test_name);
218                 fprintf(stderr, "    frame->findFunction() disagrees with thread->findFunctionByAddr()\n");
219                 fprintf(stderr, "    frame->findFunction() returns %s\n",
220                         name);
221                 fprintf(stderr, "    thread->findFunctionByAddr() return %s\n",
222                         name2);
223                 failed = true;
224                 break;
225             } else if (func!=NULL && func2!=NULL && strcmp(name, name2)!=0) {
226                 fprintf(stderr, "**Failed** test %d (%s)\n", test_num, test_name);
227                 fprintf(stderr, "    BPatch_frame::findFunction disagrees with BPatch_thread::findFunctionByAddr\n");
228                 failed = true;
229                 break;
230             }
231
232             if (correct_frame_info[i].type != stack[j].getFrameType()) {
233                 fprintf(stderr, "**Failed** test %d (%s)\n", test_num, test_name);
234                 fprintf(stderr, "    Stack frame #%d has wrong type, is %s, should be %s\n", i+1, frameTypeString(stack[i].getFrameType()), frameTypeString(correct_frame_info[i].type));
235                 fprintf(stderr, "    Stack frame 0x%p, 0x%p\n", stack[i].getPC(), stack[i].getFP() );
236                 failed = true;
237                 break;
238             }
239
240             if (stack[j].getFrameType() == BPatch_frameSignal ||
241                 stack[j].getFrameType() == BPatch_frameTrampoline) {
242                 // No further checking for these types right now
243             } else {
244                 if (func == NULL) {
245                     fprintf(stderr, "**Failed** test %d (%s)\n",
246                             test_num, test_name);
247                     fprintf(stderr, "    Stack frame #%d refers to an unknown function, should refer to %s\n", j+1, correct_frame_info[i].function_name);
248                     failed = true;
249                     break;
250                 } else { /* func != NULL */
251                     if (!hasExtraUnderscores(correct_frame_info[i].function_name))
252                         strncpy(name, fixUnderscores(name), name_max);
253
254                     if (strcmp(name, correct_frame_info[i].function_name) != 0) {
255                         if (correct_frame_info[i].optional) {
256                             j--;
257                             continue;
258                         }
259                         fprintf(stderr, "**Failed** test %d (%s)\n", test_num, test_name);
260                         fprintf(stderr, "    Stack frame #%d refers to function %s, should be %s\n", j+1, name, correct_frame_info[i].function_name);
261                         failed = true;
262                         break;
263                     }
264                 }
265             }
266         }
267     }
268
269     return !failed;
270 }
271
272 void mutatorTest1(BPatch_thread *appThread, BPatch_image * /* appImage */)
273 {
274 #if !defined(alpha_dec_osf4_0)
275     static const frameInfo_t correct_frame_info[] = {
276 #if defined( os_linux ) && (defined( arch_x86 ) || defined( arch_x86_64 ))
277         { true, true, BPatch_frameNormal, "_dl_sysinfo_int80" },
278 #endif
279 #if !defined(rs6000_ibm_aix4_1)
280         { false, false, BPatch_frameNormal, NULL },
281 #endif
282 #if !defined(i386_unknown_nt4_0)
283         { true,  false, BPatch_frameNormal, "stop_process_" },
284 #endif
285         { true,  false, BPatch_frameNormal, "func1_3" },
286         { true,  false, BPatch_frameNormal, "func1_2" },
287         { true,  false, BPatch_frameNormal, "func1_1" },
288         { true,  false, BPatch_frameNormal, "main" },
289     };
290
291     waitUntilStopped(bpatch, appThread, 1, "getCallStack");
292
293     if (checkStack(appThread,
294                    correct_frame_info,
295                    sizeof(correct_frame_info)/sizeof(frameInfo_t),
296                    1, "getCallStack")) {
297         passedTest[1] = true;
298         printf("Passed test #1 (getCallStack)\n");
299     }
300
301     appThread->continueExecution();
302 #else
303     printf("Skipping test #1 (getCallStack)\n");
304     printf("    feature not implemented on this platform\n");
305      passedTest[1] = true;
306 #endif
307 }
308
309 void mutatorTest2(BPatch_thread *appThread, BPatch_image * /* appImage */)
310 {
311 #if defined(i386_unknown_linux2_0) \
312  || defined(x86_64_unknown_linux2_4) /* Blind duplication - Ray */ \
313  || defined(sparc_sun_solaris2_4) \
314  || defined(ia64_unknown_linux2_4)
315
316     static const frameInfo_t correct_frame_info[] = {
317
318 #if defined( os_linux ) && (defined( arch_x86 ) || defined( arch_x86_64 ))
319         { true, true, BPatch_frameNormal, "_dl_sysinfo_int80" },
320 #endif
321 #if !defined(rs6000_ibm_aix4_1)
322         { false, false, BPatch_frameNormal, NULL },
323 #endif  
324         { true,  false, BPatch_frameNormal, "stop_process_" },
325         { true,  false, BPatch_frameNormal, "func2_4" },
326         { true,  false, BPatch_frameNormal, "sigalrm_handler" },
327         { true,  false, BPatch_frameSignal, NULL },
328         { true,  false, BPatch_frameNormal, "func2_3" },
329         { true,  false, BPatch_frameNormal, "func2_2" },
330         { true,  false, BPatch_frameNormal, "func2_1" },
331         { true,  false, BPatch_frameNormal, "main" }
332     };
333
334     waitUntilStopped(bpatch, appThread, 2, "getCallStack in signal handler");
335
336     if (checkStack(appThread,
337                    correct_frame_info,
338                    sizeof(correct_frame_info)/sizeof(frameInfo_t),
339                    2, "getCallStack in signal handler")) {
340         passedTest[2] = true;
341         printf("Passed test #2 (getCallStack in signal handler)\n");
342     }
343
344     appThread->continueExecution();
345 #else
346     printf("Skipping test #2 (getCallStack in signal handler)\n");
347     printf("    feature not implemented on this platform\n");
348      passedTest[2] = true;
349 #endif
350 }
351
352 #if defined( DEBUG )
353 #include <sys/ptrace.h>
354 #endif
355 void mutatorTest3( BPatch_thread * appThread, BPatch_image * appImage ) {
356
357 #if (defined( arch_alpha ) && defined( os_osf )) 
358         printf("Skipping test #3 (getCallStack through instrumentation)\n");
359         printf("    unwinding through base & minitramps not implemented on this platform\n");
360         passedTest[3] = true;
361 #else
362         static const frameInfo_t correct_frame_info[] = {
363         
364 #if defined( os_linux ) && (defined( arch_x86 ) || defined( arch_x86_64 ))
365           { true, true, BPatch_frameNormal, "_dl_sysinfo_int80" },
366 #endif
367 #if defined( os_aix ) && defined( arch_power )
368                 /* AIX uses kill(), but the PC of a process in a syscall can
369                    not be correctly determined, and appears to be the address
370                    to which the syscall function will return. */
371 #elif defined( os_windows ) && (defined( arch_x86 ) || defined( arch_x86_64 ))
372                 /* Windows/x86 does not use kill(), so its lowermost frame will be 
373                    something unidentifiable in a system DLL. */
374                 { false, false, BPatch_frameNormal, NULL },
375 #else
376                 { true, false, BPatch_frameNormal, "kill" },    
377 #endif
378 #if ! defined( os_windows )
379                 /* Windows/x86's stop_process_() calls DebugBreak(); it's 
380                    apparently normal to lose this frame. */
381                 { true, false, BPatch_frameNormal, "stop_process_" },
382 #endif
383                 { true, false, BPatch_frameNormal, "func3_3" },
384                 { true, false, BPatch_frameTrampoline, NULL },
385                 /* On AIX and x86 (and others), if our instrumentation fires
386                    before frame construction or after frame destruction, it's 
387                    acceptable to not report the function (since, after all, it
388                    doesn't have a frame on the stack. */
389                 { true, true, BPatch_frameNormal, "func3_2" },
390                 { true, false, BPatch_frameNormal, "func3_1" },
391                 { true, false, BPatch_frameNormal, "main" }
392                 };
393         
394         /* Wait for the mutatee to stop in func3_1(). */
395         waitUntilStopped( bpatch, appThread, 1, "getCallStack through instrumentation" );
396         
397         /* Instrument func3_2() to call func3_3(), which will trip another breakpoint. */
398         BPatch_Vector<BPatch_function *> instrumentedFunctions; 
399         appImage->findFunction( "func3_2", instrumentedFunctions );
400         assert( instrumentedFunctions.size() == 1 );
401         
402         BPatch_Vector<BPatch_point *> * functionEntryPoints = instrumentedFunctions[0]->findPoint( BPatch_entry );
403         assert( functionEntryPoints->size() == 1 );
404         
405         BPatch_Vector<BPatch_function *> calledFunctions;
406         appImage->findFunction( "func3_3", calledFunctions );
407         assert( calledFunctions.size() == 1 );
408         
409         BPatch_Vector<BPatch_snippet *> functionArguments;
410         BPatch_funcCallExpr functionCall( * calledFunctions[0], functionArguments );
411         
412         appThread->insertSnippet( functionCall, functionEntryPoints[0] );
413
414         /* Repeat for all three types of instpoints. */
415         BPatch_Vector<BPatch_point *> * functionCallPoints = instrumentedFunctions[0]->findPoint( BPatch_subroutine );
416         assert( functionCallPoints->size() == 1 );
417         appThread->insertSnippet( functionCall, functionCallPoints[0] );
418         
419         BPatch_Vector<BPatch_point *> * functionExitPoints = instrumentedFunctions[0]->findPoint( BPatch_exit );
420         assert( functionExitPoints->size() == 1 );
421         appThread->insertSnippet( functionCall, functionExitPoints[0] );
422
423 #if defined( DEBUG )
424         for( int i = 0; i < 80; i++ ) { ptrace( PTRACE_SINGLESTEP, appThread->getPid(), NULL, NULL ); }
425         
426         for( int i = 80; i < 120; i++ ) {
427                 ptrace( PTRACE_SINGLESTEP, appThread->getPid(), NULL, NULL );
428                 
429             BPatch_Vector<BPatch_frame> stack;
430             appThread->getCallStack( stack );
431                 
432                 fprintf( stderr, "single-step stack walk, %d instructions after stop for instrumentation.\n", i );
433                 for( unsigned i = 0; i < stack.size(); i++ ) {
434                         char name[ 40 ];
435                         BPatch_function * func = stack[i].findFunction();
436                 
437                         if( func == NULL ) { strcpy( name, "[UNKNOWN]" ); }
438                         else { func->getName( name, 40 ); }
439                         
440                         fprintf( stderr, "  %10p: %s, fp = %p\n", stack[i].getPC(), name, stack[i].getFP() );
441                         } /* end stack walk dumper */
442                 fprintf( stderr, "end of stack walk.\n" );
443                 } /* end single-step iterator */
444 #endif /* defined( DEBUG ) */           
445
446         /* After inserting the instrumentation, let it be called. */
447         appThread->continueExecution();
448           
449         /* Wait for the mutatee to stop because of the instrumentation we just inserted. */
450         waitUntilStopped( bpatch, appThread, 1, "getCallStack through instrumentation (entry)" );
451
452         passedTest[3] = true;
453         if( !checkStack( appThread, correct_frame_info,
454                          sizeof(correct_frame_info)/sizeof(frameInfo_t),
455                          3, "getCallStack through instrumentation (entry)" ) ) {
456             passedTest[3] = false;
457         }
458
459         /* Repeat for other two types of instpoints. */
460         appThread->continueExecution(); 
461
462         /* Wait for the mutatee to stop because of the instrumentation we just inserted. */
463         waitUntilStopped( bpatch, appThread, 1, "getCallStack through instrumentation (call)" );
464
465         if( !checkStack( appThread, correct_frame_info,
466                          sizeof(correct_frame_info)/sizeof(frameInfo_t),
467                          3, "getCallStack through instrumentation (call)" ) ) {
468             passedTest[3] = false;
469         }
470         
471         appThread->continueExecution(); 
472
473         /* Wait for the mutatee to stop because of the instrumentation we just inserted. */
474         waitUntilStopped( bpatch, appThread, 1, "getCallStack through instrumentation (exit)" );
475
476         if( !checkStack( appThread, correct_frame_info,
477                          sizeof(correct_frame_info)/sizeof(frameInfo_t),
478                          3, "getCallStack through instrumentation (exit)" ) ) {
479             passedTest[3] = false;
480         }
481
482         if (passedTest[3])
483             printf("Passed test #3 (unwind through base and mini tramps)\n");
484
485         /* Return the mutatee to its normal state. */
486         appThread->continueExecution(); 
487
488 #endif
489         } /* end mutatorTest3() */
490
491 int mutatorMAIN(char *pathname)
492 {
493     BPatch_thread *appThread;
494
495     // Create an instance of the BPatch library
496     bpatch = new BPatch;
497
498     // Force functions to be relocated
499     if (forceRelocation) {
500       bpatch->setForcedRelocation_NP(true);
501     }
502
503     // Register a callback function that prints any error messages
504     bpatch->registerErrorCallback(errorFunc);
505
506     // Start the mutatee
507     printf("Starting \"%s\"\n", pathname);
508
509     const char *child_argv[MAX_TEST+5];
510
511     int n = 0;
512     child_argv[n++] = pathname;
513     if (debugPrint) child_argv[n++] = const_cast<char*>("-verbose");
514
515     if (runAllTests) {
516         child_argv[n++] = const_cast<char*>("-runall"); // signifies all tests
517     } else {
518         child_argv[n++] = const_cast<char*>("-run");
519         for (unsigned int j=1; j <= MAX_TEST; j++) {
520             if (runTest[j]) {
521                 char str[5];
522                 sprintf(str, "%d", j);
523                 child_argv[n++] = strdup(str);
524             }
525         }
526     }
527
528     child_argv[n] = NULL;
529
530     appThread = bpatch->createProcess(pathname, child_argv,NULL);
531
532     if (appThread == NULL) {
533        fprintf(stderr, "Unable to run test program.\n");
534        exit(1);
535     }
536
537     // Read the program's image and get an associated image object
538     BPatch_image *appImage = appThread->getImage();
539
540     dprintf("starting program execution.\n");
541     appThread->continueExecution();
542
543     if (runTest[1]) mutatorTest1(appThread, appImage);
544     if (runTest[2]) mutatorTest2(appThread, appImage);
545     if (runTest[3]) mutatorTest3(appThread, appImage);
546
547     // Start of code to continue the process.  All mutations made
548     // above will be in place before the mutatee begins its tests.
549
550     while (!appThread->isTerminated()) {
551        if (appThread->isStopped()) {
552           appThread->continueExecution();
553           }
554        bpatch->waitForStatusChange();
555     }
556
557     int retval;
558     if(appThread->terminationStatus() == ExitedNormally) {
559        int exitCode = appThread->getExitCode();
560        if (exitCode || debugPrint)
561           printf("Mutatee exited with exit code 0x%x\n", exitCode);
562        retval = exitCode;
563     } else if(appThread->terminationStatus() == ExitedViaSignal) {
564        int signalNum = appThread->getExitSignal();
565        if (signalNum || debugPrint)
566           printf("Mutatee exited from signal 0x%d\n", signalNum);
567        retval = signalNum;
568     }
569
570     unsigned int testsFailed = 0;
571     for (unsigned int i=1; i <= MAX_TEST; i++) {
572         if (runTest[i] && !passedTest[i]) testsFailed++;
573     }
574
575     if (!testsFailed) {
576         if (runAllTests) {
577             printf("All tests passed\n");
578         } else {
579             printf("All requested tests passed\n");
580         }
581     } else {
582        printf("**Failed** %d test%c\n",testsFailed,(testsFailed>1)?'s':' ');
583     }
584
585     dprintf("Done.\n");
586
587     return retval;
588 }
589
590 //
591 // main
592 //
593 int
594 main(int argc, char *argv[])
595 {
596     unsigned int i;
597     bool ABI_32=false;
598     char mutateeName[128];
599     char libRTname[256];
600
601     strcpy(mutateeName,mutateeNameRoot);
602     libRTname[0]='\0';
603
604     if (!getenv("DYNINSTAPI_RT_LIB")) {
605          fprintf(stderr,"Environment variable DYNINSTAPI_RT_LIB undefined:\n"
606 #if defined(i386_unknown_nt4_0)
607                  "    using standard search strategy for libdyninstAPI_RT.dll\n");
608 #else
609                  "    set it to the full pathname of libdyninstAPI_RT\n");   
610          exit(-1);
611 #endif
612     } else
613          strcpy((char *)libRTname, (char *)getenv("DYNINSTAPI_RT_LIB"));
614
615     updateSearchPaths(argv[0]);
616
617     // by default run all tests
618     for (i=1; i <= MAX_TEST; i++) {
619         runTest[i] = true;
620         passedTest[i] = false;
621     }
622
623     for (i=1; i < argc; i++) {
624         if (strncmp(argv[i], "-v+", 3) == 0)    errorPrint++;
625         if (strncmp(argv[i], "-v++", 4) == 0)   errorPrint++;
626         if (strncmp(argv[i], "-verbose", 2) == 0) {
627             debugPrint = 1;
628         } else if (!strcmp(argv[i], "-V")) {
629             if (libRTname[0]) 
630                 fprintf (stdout, "DYNINSTAPI_RT_LIB=%s\n", libRTname);
631             fflush(stdout);
632         } else if (!strcmp(argv[i], "-skip")) {
633             unsigned int j;
634             runAllTests = false;
635             for (j=i+1; j < argc; j++) {
636                 unsigned int testId;
637                 if ((testId = atoi(argv[j]))) {
638                     if ((testId > 0) && (testId <= MAX_TEST)) {
639                         runTest[testId] = false;
640                     } else {
641                         printf("invalid test %d requested\n", testId);
642                         exit(-1);
643                     }
644                 } else {
645                     // end of test list
646                     break;
647                 }
648             }
649             i=j-1;
650         } else if (!strcmp(argv[i], "-run")) {
651             unsigned int j;
652             runAllTests = false;
653             for (j=0; j <= MAX_TEST; j++) runTest[j] = false;
654             for (j=i+1; j < argc; j++) {
655                 unsigned int testId;
656                 if ((testId = atoi(argv[j]))) {
657                     if ((testId > 0) && (testId <= MAX_TEST)) {
658                         runTest[testId] = true;
659                     } else {
660                         printf("invalid test %d requested\n", testId);
661                         exit(-1);
662                     }
663                 } else {
664                     // end of test list
665                     break;
666                 }
667             }
668             i=j-1;
669         } else if (!strcmp(argv[i], "-mutatee")) {
670             i++;
671             if (*argv[i]=='_')
672                 strcat(mutateeName,argv[i]);
673             else
674                 strcpy(mutateeName,argv[i]);
675 #if defined(i386_unknown_nt4_0) \
676  || defined(i386_unknown_linux2_0) \
677  || defined(x86_64_unknown_linux2_4) /* Blind duplication - Ray */ \
678  || defined(sparc_sun_solaris2_4) \
679  || defined(ia64_unknown_linux2_4)
680         } else if (!strcmp(argv[i], "-relocate")) {
681             forceRelocation = true;
682 #endif
683 #if defined(x86_64_unknown_linux2_4)
684         } else if (!strcmp(argv[i], "-m32")) {
685             ABI_32=true;
686 #endif
687         } else {
688             fprintf(stderr, "Usage: test8 "
689                     "[-V] [-verbose] "
690 #if defined(mips_sgi_irix6_4)
691                     "[-n32] "
692 #endif
693                     "[-mutatee <test8.mutatee>] "
694                     "[-run <test#> <test#> ...] "
695                     "[-skip <test#> <test#> ...]\n");
696             fprintf(stderr, "%d subtests\n", MAX_TEST);
697             exit(-1);
698         }
699     }
700
701     if (!runAllTests) {
702         printf("Running Tests: ");
703         for (unsigned int j=1; j <= MAX_TEST; j++) {
704             if (runTest[j]) printf("%d ", j);
705         }
706         printf("\n");
707     }
708
709     // patch up the default compiler in mutatee name (if necessary)
710     if (!strstr(mutateeName, "_"))
711 #if defined(i386_unknown_nt4_0)
712         strcat(mutateeName,"_VC");
713 #else
714         strcat(mutateeName,"_gcc");
715 #endif
716     if (ABI_32 || strstr(mutateeName,"_m32")) {
717         // patch up file names based on alternate ABI (as necessary)
718         if (!strstr(mutateeName, "_m32")) strcat(mutateeName,"_m32");
719     }
720     // patch up the platform-specific filename extensions
721 #if defined(i386_unknown_nt4_0)
722     if (!strstr(mutateeName, ".exe")) strcat(mutateeName,".exe");
723 #endif
724
725     mutatorMAIN(mutateeName);
726
727     return 0;
728 }