C/C++ determination/reporting.
[dyninst.git] / dyninstAPI / tests / src / test4.C
1 // $Id: test4.C,v 1.4 2000/06/20 21:45:58 wylie Exp $
2 //
3
4 #include <stdio.h>
5 #include <string.h>
6 #include <assert.h>
7 #ifdef i386_unknown_nt4_0
8 #include <windows.h>
9 #include <winbase.h>
10 #else
11 #include <unistd.h>
12 #endif
13
14 #include "BPatch.h"
15 #include "BPatch_Vector.h"
16 #include "BPatch_thread.h"
17 #include "BPatch_snippet.h"
18 #include "test_util.h"
19
20 extern "C" const char V_libdyninstAPI[];
21
22 char *mutateeNameRoot = "test4a.mutatee";
23
24 int inTest;             // current test #
25 int expectError;
26 int debugPrint = 0; // internal "mutator" tracing
27 int errorPrint = 0; // external "dyninst" tracing (via errorFunc)
28
29 #define dprintf if (debugPrint) printf
30
31 bool runAllTests = true;
32 const unsigned int MAX_TEST = 4;
33 bool runTest[MAX_TEST+1];
34 bool passedTest[MAX_TEST+1];
35 bool failedTest[MAX_TEST+1];
36 int threadCount = 0;
37 BPatch_thread *mythreads[25];
38
39 BPatch *bpatch;
40
41 /*
42  * Given a string variable name and an expected value, lookup the varaible
43  *    in the child process, and verify that the value matches.
44  *
45  */
46 bool verifyChildMemory(BPatch_thread *appThread, 
47                               char *name, int expectedVal)
48 {
49      BPatch_image *appImage = appThread->getImage();
50
51      if (!appImage) {
52          dprintf("unable to locate image for %d\n", appThread->getPid());
53          return false;
54      }
55
56      BPatch_variableExpr *var = appImage->findVariable(name);
57      if (!var) {
58          dprintf("unable to located variable %s in child\n", name);
59          return false;
60      }
61
62      int actualVal;
63      var->readValue(&actualVal);
64
65      if (expectedVal != actualVal) {
66          printf("*** for %s, expected val = %d, but actual was %d\n",
67                 name, expectedVal, actualVal);
68          return false;
69      } else {
70          dprintf("verified %s was = %d\n", name, actualVal);
71          return true;
72      }
73 }
74
75 BPatch_thread *test2Child;
76 BPatch_thread *test2Parent;
77 BPatch_thread *test4Child;
78 BPatch_thread *test4Parent;
79
80
81 void forkFunc(BPatch_thread *parent, BPatch_thread *child)
82 {
83     BPatch_image *appImage;
84     BPatch_Vector<BPatch_snippet *> nullArgs;
85
86     if (child) mythreads[threadCount++] = child;
87
88     if (!child) {
89         dprintf("in prefork for %d\n", parent->getPid());
90     } else {
91         dprintf("in fork of %d to %d\n", parent->getPid(), child->getPid());
92     }
93
94     if (inTest == 1) {
95        // nothing to do for this case
96     } else if (inTest == 2) {
97        if (!child) return;      // skip prefork case
98
99        // insert code into parent
100        appImage = parent->getImage();
101        assert(appImage);
102        BPatch_function *func2_3 = appImage->findFunction("func2_3");
103        assert(func2_3);
104        BPatch_funcCallExpr callExpr2(*func2_3, nullArgs);
105        BPatch_Vector<BPatch_point *> *point2;
106        point2 = appImage->findProcedurePoint("func2_2", BPatch_exit);
107        assert(point2);
108        parent->insertSnippet(callExpr2, *point2);
109
110        // insert different code into child
111        appImage = child->getImage();
112        assert(appImage);
113        BPatch_function *func2_4 = appImage->findFunction("func2_4");
114        BPatch_funcCallExpr callExpr1(*func2_4, nullArgs);
115        BPatch_Vector<BPatch_point *> *point1;
116        point1 = appImage->findProcedurePoint("func2_2", BPatch_exit);
117        child->insertSnippet(callExpr1, *point1);
118
119        test2Child = child;
120        test2Parent = parent;
121     } else if (inTest == 4) {
122        if (!child) return;      // skip prefork case
123
124        // insert code into parent
125        appImage = parent->getImage();
126        assert(appImage);
127        BPatch_function *func4_3 = appImage->findFunction("func4_3");
128        assert(func4_3);
129        BPatch_funcCallExpr callExpr2(*func4_3, nullArgs);
130        BPatch_Vector<BPatch_point *> *point2;
131        point2 = appImage->findProcedurePoint("func4_2", BPatch_exit);
132        assert(point2);
133        parent->insertSnippet(callExpr2, *point2);
134
135        // code goes into child after in-exec in this test.
136
137        test4Child = child;
138     }
139 }
140
141 void exitFunc(BPatch_thread *thread, int code)
142 {
143     // Read out the values of the variables.
144     if (inTest == 1) {
145         if (thread->getPid() == code) {
146             if (verifyChildMemory(thread, "globalVariable1_1", 1000001)) {
147                 printf("Passed test #1 (exit callback)\n");
148                 passedTest[1] = true;
149             } else {
150                 passedTest[1] = false;
151             }
152         } else {
153             printf("**Failed** test 1\n");
154             printf("    exit code = %d, was not equal to pid\n", code);
155         }
156     } else if (inTest == 2) {
157         static int exited = 0;
158         exited++;
159         if (thread->getPid() != code) {
160             printf("Failed test #2 (fork callback)\n");
161             printf("    exit code was not equal to pid\n");
162             exited = 0;
163         } else {
164             dprintf("test #2, pid %d exited\n", code);
165             if ((test2Parent == thread) &&
166                 !verifyChildMemory(test2Parent, "globalVariable2_1", 2000002)) {
167                 failedTest[2] = true;
168             }
169             if ((test2Child == thread) &&
170                 !verifyChildMemory(test2Child, "globalVariable2_1", 2000003)) {
171                 failedTest[2] = true;
172             }
173
174             // See if all the processes are done
175             if (exited == 2) {
176                 if (!failedTest[2]) {
177                     printf("Passed test #2 (fork callback)\n");
178                     passedTest[2] = true;
179                 } else {
180                     printf("Failed test #2 (fork callback)\n");
181                 }
182             }
183         }
184     } else if (inTest == 3) {
185         // simple exec 
186         if (!verifyChildMemory(thread, "globalVariable3_1", 3000002)) {
187             printf("Failed test #3 (exec callback)\n");
188         } else {
189             printf("Passed test #3 (exec callback)\n");
190             passedTest[3] = true;
191         }
192     } else if (inTest == 4) {
193         static int exited = 0;
194         exited++;
195         if (thread->getPid() != code) {
196             printf("Failed test #4 (fork callback)\n");
197             printf("    exit code was not equal to pid\n");
198             failedTest[4] = true;
199         } else if (test4Parent == thread) {
200             dprintf("test #4, pid %d exited\n", code);
201             if (!verifyChildMemory(test4Parent,"globalVariable4_1",4000002)){
202                 failedTest[4] = true;
203             }
204         } else if (test4Child == thread) {
205             dprintf("test #4, pid %d exited\n", code);
206             if (!verifyChildMemory(test4Child, "globalVariable4_1", 4000003)) {
207                 failedTest[4] = true;
208             }
209         } else {
210             // exit from un-known thread
211             printf("Failed test #4 (fork callback)\n");
212             printf("    exit from unknown pid = %d\n", code);
213             failedTest[4] = true;
214         }
215         // See if all the processes are done
216         if (exited == 2) {
217             if (!failedTest[4]) {
218                 printf("Passed test #4 (fork & exec)\n");
219                 passedTest[4] = true;
220             } else {
221                 printf("Failed test #4 (fork & exec)\n");
222             }
223         }
224     } else {
225         printf("**Exit from unknown test case**\n");
226     }
227 }
228
229 void execFunc(BPatch_thread *thread)
230 {
231     if (inTest == 1 || inTest == 2) {
232         printf("**Failed Test #%d\n", inTest);
233         printf("    execCallback invoked, but exec was not called!\n");
234     } else if (inTest == 3) {
235         dprintf("in exec callback for %d\n", thread->getPid());
236
237         // insert code into parent
238         BPatch_Vector<BPatch_snippet *> nullArgs;
239         BPatch_image *appImage = thread->getImage();
240         assert(appImage);
241         BPatch_function *func3_2 = appImage->findFunction("func3_2");
242         assert(func3_2);
243         BPatch_funcCallExpr callExpr(*func3_2, nullArgs);
244         BPatch_Vector<BPatch_point *> *point;
245         point = appImage->findProcedurePoint("func3_1", BPatch_exit);
246         assert(point);
247         thread->insertSnippet(callExpr, *point);
248     } else if (inTest == 4) {
249         dprintf("in exec callback for %d\n", thread->getPid());
250
251         // insert code into child
252         BPatch_Vector<BPatch_snippet *> nullArgs;
253         BPatch_image *appImage = thread->getImage();
254         assert(appImage);
255         BPatch_function *func4_4 = appImage->findFunction("func4_4");
256         assert(func4_4);
257         BPatch_funcCallExpr callExpr1(*func4_4, nullArgs);
258         BPatch_Vector<BPatch_point *> *point1;
259         point1 = appImage->findProcedurePoint("func4_2", BPatch_exit);
260         assert(point1);
261         thread->insertSnippet(callExpr1, *point1);
262     } else {
263         printf("in exec callback for %d\n", thread->getPid());
264     }
265 }
266
267 void errorFunc(BPatchErrorLevel level, int num, const char **params)
268 {
269     if (num == 0) {
270         // conditional reporting of warnings and informational messages
271         if (errorPrint) {
272             if (level == BPatchInfo)
273               { if (errorPrint > 1) printf("%s\n", params[0]); }
274             else
275                 printf("%s", params[0]);
276         }
277     } else {
278         // reporting of actual errors
279         char line[256];
280         const char *msg = bpatch->getEnglishErrorString(num);
281         bpatch->formatErrorString(line, sizeof(line), msg, params);
282         
283         if (num != expectError) {
284             printf("Error #%d (level %d): %s\n", num, level, line);
285         
286             // We consider some errors fatal.
287             if (num == 101) {
288                exit(-1);
289             }
290         }
291     }
292 }
293
294 void contAndWaitForAllThreads(BPatch_thread *appThread)
295 {
296     mythreads[threadCount++] = appThread;
297
298     appThread->continueExecution();
299     while (1) {
300         int i;
301         for (i=0; i < threadCount; i++) {
302             if (!mythreads[i]->isTerminated()) {
303                 break;
304             }
305         }
306
307         // see if all exited
308         if (i== threadCount) break;
309
310         bpatch->waitForStatusChange();
311         for (i=0; i < threadCount; i++) {
312             if (mythreads[i]->isStopped()) {
313                 mythreads[i]->continueExecution();
314             }
315         }
316     }
317
318     for (int i=0; i < threadCount; i++) {
319         delete mythreads[i];
320     }
321     threadCount = 0;
322 }
323
324 void mutatorTest1(char *pathname)
325 {
326 #if !defined(sparc_sun_solaris2_4) && !defined(i386_unknown_solaris2_5)
327     printf("Skipping test #1 (exit callback)\n");
328     printf("    not implemented on this platform\n");
329     passedTest[1] = true;
330 #else
331     int n = 0;
332     char *child_argv[MAX_TEST+5];
333         
334     dprintf("in mutatorTest1\n");
335
336     inTest = 1;
337     child_argv[n++] = pathname;
338     if (debugPrint) child_argv[n++] = "-verbose";
339
340     child_argv[n++] = "-run";
341     child_argv[n++] = "1";
342     child_argv[n] = NULL;
343
344     BPatch_thread *appThread = bpatch->createProcess(pathname, child_argv,NULL);
345     if (appThread == NULL) {
346         fprintf(stderr, "Unable to run test program.\n");
347         exit(1);
348     }
349
350     contAndWaitForAllThreads(appThread);
351
352     inTest = 0;
353 #endif
354 }
355
356
357 void mutatorTest2(char *pathname)
358 {
359 #if !defined(sparc_sun_solaris2_4) && !defined(i386_unknown_solaris2_5)
360     printf("Skipping test #2 (fork callback)\n");
361     printf("    not implemented on this platform\n");
362     passedTest[2] = true;
363 #else
364     int n = 0;
365     char *child_argv[MAX_TEST+5];
366         
367     dprintf("in mutatorTest2\n");
368
369     inTest = 2;
370     child_argv[n++] = pathname;
371     if (debugPrint) child_argv[n++] = "-verbose";
372
373     child_argv[n++] = "-run";
374     child_argv[n++] = "2";
375     child_argv[n] = NULL;
376
377     BPatch_thread *appThread = bpatch->createProcess(pathname, child_argv,NULL);
378     if (appThread == NULL) {
379         fprintf(stderr, "Unable to run test program.\n");
380         exit(1);
381     }
382
383     contAndWaitForAllThreads(appThread);
384
385     inTest = 0;
386 #endif
387 }
388
389 void mutatorTest3(char *pathname)
390 {
391 #if !defined(sparc_sun_solaris2_4) && !defined(i386_unknown_solaris2_5)
392     printf("Skipping test #3 (exec callback)\n");
393     printf("    not implemented on this platform\n");
394     passedTest[3] = true;
395 #else
396     int n = 0;
397     char *child_argv[MAX_TEST+5];
398         
399     dprintf("in mutatorTest3\n");
400
401     inTest = 3;
402     child_argv[n++] = pathname;
403     if (debugPrint) child_argv[n++] = "-verbose";
404
405     child_argv[n++] = "-run";
406     child_argv[n++] = "3";
407     child_argv[n] = NULL;
408
409     BPatch_thread *appThread = bpatch->createProcess(pathname, child_argv,NULL);
410     if (appThread == NULL) {
411         fprintf(stderr, "Unable to run test program.\n");
412         exit(1);
413     }
414
415     contAndWaitForAllThreads(appThread);
416
417     inTest = 0;
418 #endif
419 }
420
421 void mutatorTest4(char *pathname)
422 {
423 #if !defined(sparc_sun_solaris2_4) && !defined(i386_unknown_solaris2_5)
424     printf("Skipping test #4 (fork callback)\n");
425     printf("    not implemented on this platform\n");
426     passedTest[4] = true;
427 #else
428     int n = 0;
429     char *child_argv[MAX_TEST+5];
430         
431     dprintf("in mutatorTest4\n");
432
433     inTest = 4;
434     child_argv[n++] = pathname;
435     if (debugPrint) child_argv[n++] = "-verbose";
436
437     child_argv[n++] = "-run";
438     child_argv[n++] = "4";
439     child_argv[n] = NULL;
440
441     test4Parent = bpatch->createProcess(pathname, child_argv,NULL);
442     if (test4Parent == NULL) {
443         fprintf(stderr, "Unable to run test program.\n");
444         exit(1);
445     }
446
447     contAndWaitForAllThreads(test4Parent);
448
449     inTest = 0;
450 #endif
451 }
452
453 void mutatorMAIN(char *pathname)
454 {
455     // Create an instance of the BPatch library
456     bpatch = new BPatch;
457
458     // Register a callback function that prints any error messages
459     bpatch->registerErrorCallback(errorFunc);
460     bpatch->registerPreForkCallback(forkFunc);
461     bpatch->registerPostForkCallback(forkFunc);
462     bpatch->registerExecCallback(execFunc);
463     bpatch->registerExitCallback(exitFunc);
464
465     // Start the mutatee
466     printf("Starting \"%s\"\n", pathname);
467
468     if (runTest[1]) mutatorTest1(pathname);
469     if (runTest[2]) mutatorTest2(pathname);
470     if (runTest[3]) mutatorTest3(pathname);
471     if (runTest[4]) mutatorTest4(pathname);
472
473     unsigned int testsFailed = 0;
474     for (unsigned int i=1; i <= MAX_TEST; i++) {
475         if (runTest[i] && !passedTest[i]) testsFailed++;
476     }
477
478     if (!testsFailed) {
479         if (runAllTests) {
480             printf("All tests passed\n");
481         } else {
482             printf("All requested tests passed\n");
483         }
484     }
485 }
486
487 //
488 // main - decide our role and call the correct "main"
489 //
490 int
491 main(unsigned int argc, char *argv[])
492 {
493     unsigned int i;
494     bool N32ABI=false;
495     char mutateeName[128];
496     char libRTname[256];
497
498     strcpy(mutateeName,mutateeNameRoot);
499     libRTname[0]='\0';
500
501 #if !defined(USES_LIBDYNINSTRT_SO)
502     fprintf(stderr,"(Expecting subject application to be statically linked"
503                         " with libdyninstAPI_RT.)\n");
504 #else
505     if (!getenv("DYNINSTAPI_RT_LIB")) {
506          fprintf(stderr,"Environment variable DYNINSTAPI_RT_LIB undefined:\n"
507 #if defined(i386_unknown_nt4_0)
508                  "    using standard search strategy for libdyninstAPI_RT.dll\n");
509 #else
510                  "    set it to the full pathname of libdyninstAPI_RT\n");   
511          exit(-1);
512 #endif
513     } else
514          strcpy((char *)libRTname, (char *)getenv("DYNINSTAPI_RT_LIB"));
515 #endif
516
517     // by default run all tests
518     for (i=1; i <= MAX_TEST; i++) {
519         runTest[i] = true;
520         passedTest[i] = false;
521     }
522
523     for (i=1; i < argc; i++) {
524         if (strncmp(argv[i], "-v+", 3) == 0)    errorPrint++;
525         if (strncmp(argv[i], "-v++", 4) == 0)   errorPrint++;
526         if (strncmp(argv[i], "-verbose", 2) == 0) {
527             debugPrint = 1;
528         } else if (!strcmp(argv[i], "-V")) {
529             fprintf (stdout, "%s\n", V_libdyninstAPI);
530             if (libRTname[0]) 
531                 fprintf (stdout, "DYNINSTAPI_RT_LIB=%s\n", libRTname);
532             fflush(stdout);
533         } else if (!strcmp(argv[i], "-skip")) {
534             unsigned int j;
535             runAllTests = false;
536             for (j=i+1; j < argc; j++) {
537                 unsigned int testId;
538                 if ((testId = atoi(argv[j]))) {
539                     if ((testId > 0) && (testId <= MAX_TEST)) {
540                         runTest[testId] = false;
541                     } else {
542                         printf("invalid test %d requested\n", testId);
543                         exit(-1);
544                     }
545                 } else {
546                     // end of test list
547                     break;
548                 }
549             }
550             i=j-1;
551         } else if (!strcmp(argv[i], "-run")) {
552             unsigned int j;
553             runAllTests = false;
554             for (j=0; j <= MAX_TEST; j++) runTest[j] = false;
555             for (j=i+1; j < argc; j++) {
556                 unsigned int testId;
557                 if ((testId = atoi(argv[j]))) {
558                     if ((testId > 0) && (testId <= MAX_TEST)) {
559                         runTest[testId] = true;
560                     } else {
561                         printf("invalid test %d requested\n", testId);
562                         exit(-1);
563                     }
564                 } else {
565                     // end of test list
566                     break;
567                 }
568             }
569             i=j-1;
570         } else if (!strcmp(argv[i], "-mutatee")) {
571             i++;
572             if (*argv[i]=='_')
573                 strcat(mutateeName,argv[i]);
574             else
575                 strcpy(mutateeName,argv[i]);
576 #if defined(mips_sgi_irix6_4)
577         } else if (!strcmp(argv[i], "-n32")) {
578             N32ABI=true;
579 #endif
580         } else {
581             fprintf(stderr, "Usage: test4 "
582                     "[-V] [-verbose] "
583 #if defined(mips_sgi_irix6_4)
584                     "[-n32] "
585 #endif
586                     "[-mutatee <test4a.mutatee>] "
587                     "[-run <test#> <test#> ...] "
588                     "[-skip <test#> <test#> ...]\n");
589             exit(-1);
590         }
591     }
592
593     if (!runAllTests) {
594         printf("Running Tests: ");
595         for (unsigned int j=1; j <= MAX_TEST; j++) {
596             if (runTest[j]) printf("%d ", j);
597         }
598         printf("\n");
599     }
600
601     // patch up the default compiler in mutatee name (if necessary)
602     if (!strstr(mutateeName, "_"))
603 #if defined(i386_unknown_nt4_0)
604         strcat(mutateeName,"_VC");
605 #else
606         strcat(mutateeName,"_gcc");
607 #endif
608     if (N32ABI || strstr(mutateeName,"_n32")) {
609         // patch up file names based on alternate ABI (as necessary)
610         if (!strstr(mutateeName, "_n32")) strcat(mutateeName,"_n32");
611     }
612     // patch up the platform-specific filename extensions
613 #if defined(i386_unknown_nt4_0)
614     if (!strstr(mutateeName, ".exe")) strcat(mutateeName,".exe");
615 #endif
616
617     mutatorMAIN(mutateeName);
618
619     return 0;
620 }