Update copyright to LGPL on all files
[dyninst.git] / testsuite / src / mutatee_util.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 #include <stdio.h>
33 #include <stdarg.h>
34 #include <assert.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39
40 #ifdef __cplusplus
41 extern "C" {
42 #endif
43
44 #include "mutatee_util.h"
45
46 #ifdef os_windows_test
47 #define WIN32_LEAN_AND_MEAN
48 #include <windows.h>
49 #include <io.h>
50 #else
51 #include <unistd.h>
52 #include <dlfcn.h>
53 #endif
54
55 #include "mutatee_call_info.h"
56    extern mutatee_call_info_t mutatee_funcs[];
57    extern int passedTest[];
58    extern unsigned int MAX_TEST;
59
60 #include "test_results.h"
61
62 #ifdef __cplusplus
63 }
64 #endif
65
66 /*
67  * Stop the process (in order to wait for the mutator to finish what it's
68  * doing and restart us).
69  */
70 #if defined(alpha_dec_osf4_0_test) && defined(__GNUC__)
71 static long long int  beginFP;
72 #endif
73
74 /* New output driver system */
75 output_t *output = NULL;
76 char *loginfofn = "-";
77 char *logerrfn = "-";
78 char *stdoutfn = "-";
79 char *stderrfn = "-";
80 char *humanfn = "-";
81 static char *od_testname = NULL;
82
83 void warningLogResult(test_results_t result) {
84    fprintf(stderr, "[%s:%u] - WARNING: output object not properly initialized\n", __FILE__, __LINE__);
85 }
86
87 void nullSetTestName(const char *testname) {
88 }
89
90 void stdLogResult(test_results_t result);
91 void stdSetTestName(const char *testname);
92
93 void initOutputDriver() {
94    output = (output_t *) malloc(sizeof (*output));
95    if (NULL == output) {
96       /* This is a nasty error, and the mutatee can't function any more */
97       fprintf(stderr, "[%s:%u] - Error allocating output driver object\n", __FILE__, __LINE__);
98       abort(); /* Is this the right thing to do? */
99    }
100    output->log = stdOutputLog;
101    output->vlog = stdOutputVLog;
102    output->redirectStream = redirectStream;
103    output->logResult = stdLogResult;
104    output->setTestName = stdSetTestName;
105 }
106
107 /* I want to store *copies* of the filenames */
108 /* I also want to maintain "-" as a constant, so I know I don't need to free it
109  * if it's what's being used.
110  */
111 void redirectStream(output_stream_t stream, const char *filename) {
112    size_t length;
113
114    if ((filename != NULL) && (strcmp(filename, "-") != 0)) {
115       length = strlen(filename) + 1;
116    }
117
118    switch (stream) {
119       case STDOUT:
120          if ((stdoutfn != NULL) && (strcmp(stdoutfn, "-") != 0)) {
121             free(stdoutfn);
122          }
123          if (NULL == filename) {
124             stdoutfn = NULL;
125          } else if (strcmp(filename, "-") == 0) {
126             stdoutfn = "-";
127          } else {
128             stdoutfn = (char *) malloc(length * sizeof (*stdoutfn));
129             if (stdoutfn != NULL) {
130                strncpy(stdoutfn, filename, length);
131             }
132          }
133          break;
134
135       case STDERR:
136          if ((stderrfn != NULL) && (strcmp(stderrfn, "-") != 0)) {
137             free(stderrfn);
138          }
139          if (NULL == filename) {
140             stderrfn = NULL;
141          } else if (strcmp(filename, "-") == 0) {
142             stderrfn = "-";
143          } else {
144             stderrfn = (char *) malloc(length * sizeof (*stderrfn));
145             if (stderrfn != NULL) {
146                strncpy(stderrfn, filename, length);
147             }
148          }
149          break;
150
151       case LOGINFO:
152          if ((loginfofn != NULL) && (strcmp(loginfofn, "-") != 0)) {
153             free(loginfofn);
154          }
155          if (NULL == filename) {
156             loginfofn = NULL;
157          } else if (strcmp(filename, "-") == 0) {
158             loginfofn = "-";
159          } else {
160             loginfofn = (char *) malloc(length * sizeof (*loginfofn));
161             if (loginfofn != NULL) {
162                strncpy(loginfofn, filename, length);
163             }
164          }
165          break;
166
167       case LOGERR:
168          if ((logerrfn != NULL) && (strcmp(logerrfn, "-") != 0)) {
169             free(logerrfn);
170          }
171          if (NULL == filename) {
172             logerrfn = NULL;
173          } else if (strcmp(filename, "-") == 0) {
174             logerrfn = "-";
175          } else {
176             logerrfn = (char *) malloc(length * sizeof (*logerrfn));
177             if (logerrfn != NULL) {
178                strncpy(logerrfn, filename, length);
179             }
180          }
181          break;
182
183       case HUMAN:
184          if ((humanfn != NULL) && (strcmp(humanfn, "-") != 0)) {
185             free(humanfn);
186          }
187          if (NULL == filename) {
188             humanfn = NULL;
189          } else if (strcmp(filename, "-") == 0) {
190             humanfn = "-";
191          } else {
192             humanfn = (char *) malloc(length * sizeof (*humanfn));
193             if (humanfn != NULL) {
194                strncpy(humanfn, filename, length);
195             }
196          }
197          break;
198    }
199 }
200
201 void stdOutputLog(output_stream_t stream, const char *fmt, ...) {
202    va_list args;
203    va_start(args, fmt);
204    stdOutputVLog(stream, fmt, args);
205    va_end(args);
206 }
207
208 void stdOutputVLog(output_stream_t stream, const char *fmt, va_list args) {
209    char *logfn = NULL;
210    switch (stream) {
211       case STDOUT:
212          logfn = stdoutfn;
213          break;
214
215       case STDERR:
216          logfn = stderrfn;
217          break;
218
219       case LOGINFO:
220          logfn = loginfofn;
221          break;
222
223       case LOGERR:
224          logfn = logerrfn;
225          break;
226
227       case HUMAN:
228          logfn = humanfn;
229          break;
230    }
231
232    if (logfn != NULL) { /* Print nothing if it is NULL */
233       FILE *out = NULL;
234       if (strcmp(logfn, "-") == 0) { /* Default output */
235          switch (stream) {
236             case STDOUT:
237             case LOGINFO:
238             case HUMAN:
239                out = stdout;
240                break;
241
242             case STDERR:
243             case LOGERR:
244                out = stderr;
245                break;
246          }
247       } else { /* Specified output */
248          out = fopen(logfn, "a");
249          if (NULL == out) {
250             fprintf(stderr, "[%s:%u] - Error opening log output file '%s'\n", __FILE__, __LINE__, logfn);
251          }
252       }
253
254       if (out != NULL) {
255          vfprintf(out, fmt, args);
256          if ((out != stdout) && (out != stderr)) {
257             fclose(out);
258          }
259       }
260    }
261 } /* stdOutputVLog() */
262
263 void stdLogResult(test_results_t result) {
264    printResultHumanLog(od_testname, result);
265 } /* stdLogResult() */
266
267 void stdSetTestName(const char *testname) {
268    if (NULL == testname) {
269       if (od_testname != NULL) {
270          free(od_testname);
271       }
272       od_testname = NULL;
273    } else {
274       size_t len = strlen(testname) + 1;
275       od_testname = (char *) malloc(len * sizeof (char));
276       strncpy(od_testname, testname, len);
277    }
278 }
279
280
281 /* Provide standard output functions that respect the specified output files */
282 FILE *outlog = NULL;
283 FILE *errlog = NULL;
284 void logstatus(const char *fmt, ...) {
285    va_list args;
286    va_start(args, fmt);
287    stdOutputVLog(LOGINFO, fmt, args);
288    va_end(args);
289 }
290 void logerror(const char *fmt, ...) {
291    va_list args;
292    va_start(args, fmt);
293    stdOutputVLog(LOGERR, fmt, args);
294    va_end(args);
295 }
296 void flushOutputLog() {
297    if (outlog != NULL) {
298       fflush(outlog);
299    }
300 }
301 void flushErrorLog() {
302    if (errlog != NULL) {
303       fflush(errlog);
304    }
305 }
306
307 /************************************************/
308 /* Support functions for database output driver */
309 /************************************************/
310
311 static char *temp_logfilename = "mutatee_dblog";
312 static char *dblog_filename = NULL;
313
314 /* Redirect all output to a file, so test_driver can pick it up when it's
315  * time to report results to the database server
316  */
317 void initDatabaseOutputDriver() {
318    output = (output_t *) malloc(sizeof (*output));
319    if (output != NULL) {
320       output->log = dbOutputLog;
321       output->vlog = dbOutputVLog;
322       output->redirectStream = dbRedirectStream;
323       output->logResult = dbLogResult;
324       output->setTestName = dbSetTestName;
325    } else {
326       fprintf(stderr, "[%s:%u] - Out of memory allocating output object\n", __FILE__, __LINE__);
327       exit(-1); /* This is very bad */
328    }
329 }
330
331 /* Sets the current test name, which controls the file we log to */
332 void dbSetTestName(const char *testname) {
333    size_t len; /* For string allocation */
334    FILE *pretestLog;
335
336    if (NULL == testname) {
337       /* TODO Handle error */
338       return;
339    }
340
341    len = strlen("dblog.") + strlen(testname) + 1;
342    dblog_filename = (char *) realloc(dblog_filename, len * sizeof (char));
343    if (NULL == dblog_filename) {
344       fprintf(stderr, "[%s:%u] - Out of memory allocating mutatee database log file name\n", __FILE__, __LINE__);
345       return;
346    }
347
348    snprintf(dblog_filename, len, "dblog.%s", testname);
349
350    /* Copy contents of temp log file into the dblog file, if there's
351     * anything there.
352     */
353    pretestLog = fopen(temp_logfilename, "r");
354    if (pretestLog != NULL) {
355       /* Copy the file */
356       size_t count;
357       char *buffer[4096]; /* FIXME Magic number */
358       FILE *out = fopen(dblog_filename, "a");
359       if (out != NULL) {
360          do {
361             count = fread(buffer, sizeof (char), 4096, pretestLog);
362             fwrite(buffer, sizeof (char), count, out);
363          } while (4096 == count); /* FIXME Magic number */
364       }
365    } else if (errno != ENOENT) {
366       /* It's not a problem is there is no pretest log file */
367       fprintf(stderr, "[%s:%u] - Error opening pretest log: '%s'\n", __FILE__, __LINE__, strerror(errno));
368    }
369 }
370
371 void warningRedirectStream(output_stream_t stream, const char *filename) {
372    fprintf(stderr, "[%s:%u] - WARNING: output object not properly initialized\n", __FILE__, __LINE__);
373 }
374
375 void warningVLog(output_stream_t stream, const char *fmt, va_list args) {
376    fprintf(stderr, "[%s:%u] - WARNING: output object not properly initialized\n", __FILE__, __LINE__);
377 }
378
379 void warningLog(output_stream_t stream, const char *fmt, ...) {
380    fprintf(stderr, "[%s:%u] - WARNING: output object not properly initialized\n", __FILE__, __LINE__);
381 }
382
383 void warningSetTestName(const char *testname) {
384    fprintf(stderr, "[%s:%u] - WARNING: output object not properly initialized\n", __FILE__, __LINE__);
385 }
386
387 void closeDatabaseOutputDriver() {
388    if (dblog_filename != NULL) {
389       free(dblog_filename);
390    }
391    output->log = warningLog;
392    output->vlog = warningVLog;
393    output->redirectStream = warningRedirectStream;
394    output->logResult = warningLogResult;
395    output->setTestName = warningSetTestName;
396 }
397
398 /* What do I do with output I receive before the output file name has been
399  * set?  I need to store it in a temp. file or something.
400  */
401 void dbOutputLog(output_stream_t stream, const char *fmt, ...) {
402    va_list args;
403    va_start(args, fmt);
404    dbOutputVLog(stream, fmt, args);
405    va_end(args);
406 }
407
408 void dbOutputVLog(output_stream_t stream,
409                   const char *fmt, va_list args) {
410    FILE *out;
411
412    if (NULL == dblog_filename) {
413       out = fopen(temp_logfilename, "a");
414    } else {
415       out = fopen(dblog_filename, "a");
416    }
417
418    if (NULL == out) {
419       fprintf(stderr, "[%s:%u] - Error opening log file '%s'\n", __FILE__, __LINE__, (dblog_filename) ? dblog_filename : temp_logfilename);
420       return;
421    }
422
423    vfprintf(out, fmt, args);
424    fclose(out);
425 }
426
427 void dbRedirectStream(output_stream_t stream, const char *filename) {
428    /* This doesn't do anything */
429 }
430
431 void dbLogResult(test_results_t result) {
432    FILE *out;
433
434    if (NULL == dblog_filename) {
435       /* TODO Handle error */
436    }
437
438    out = fopen(dblog_filename, "a");
439    if (NULL == out) {
440       /* TODO Handle error */
441    }
442
443    fprintf(out, "RESULT: %d\n\n", result);
444    fclose(out);
445 }
446
447
448 /*********************************************************/
449 /* Support for cleaning up mutatees after a test crashes */
450 /*********************************************************/
451
452 static char *pidFilename = NULL;
453 void setPIDFilename(char *pfn) {
454    pidFilename = pfn;
455 }
456 char *getPIDFilename() {
457    return pidFilename;
458 }
459 void registerPID(int pid) {
460    FILE *pidFile;
461    if (NULL == pidFilename) 
462       return;
463    pidFile = fopen(pidFilename, "a");
464    if (NULL == pidFile) {
465       logerror("[%s:%u] - Error registering mutatee PID: error opening pid file\n", __FILE__, __LINE__);
466    } else {
467       fprintf(pidFile, "%d\n", pid);
468       fclose(pidFile);
469    }
470 }
471
472 static int saved_stdout_fd = -1;
473 static int stdout_fd = 1;
474 /* setupFortranOutput() redirects stdout to point to outlog.  This seemed
475  * like the easiest way to make the Fortran mutatees output the same way that
476  * the rest of the mutatees do.
477  * Returns 0 on success, <0 on failure.
478  */
479 /* So there's a bug here, or else some behavior that I just don't understand,
480  * where unless we write something to stdout before the dup2() redirection
481  * it doesn't actually get redirected.  That's what the printf(" ") is in
482  * there for.
483  */
484 int setupFortranOutput() {
485    int outlog_fd = fileno(outlog);
486    if (-1 == outlog_fd) {
487       return -1; /* Error */
488    }
489    printf(" "); /* Workaround */
490    stdout_fd = fileno(stdout);
491    saved_stdout_fd = dup(stdout_fd); /* Duplicate stdout */
492    if (-1 == saved_stdout_fd) {
493       return -2; /* Error */
494    }
495    if (dup2(outlog_fd, stdout_fd) == -1) {
496       return -3; /* Error */
497    }
498    return 0;
499 }
500 /* cleanupFortranOutput() restores stdout so it points where it did before
501  * a call to setupFortranOutput().  If setupFortranOutput() was never called,
502  * then it just returns failure.
503  * Returns 0 on successs, <0 on failure.
504  */
505 int cleanupFortranOutput() {
506    if (-1 == saved_stdout_fd) {
507       return -1;
508    }
509    if (dup2(saved_stdout_fd, stdout_fd) == -1) {
510       return -2; /* Error */
511    }
512    return 0;
513 }
514
515 /* executable_name holds the name of the current mutatee binary */
516 char *executable_name = NULL;
517
518 void setExecutableName(const char *new_name) {
519    executable_name = (char *) new_name;
520 }
521
522 /* Flag that says whether we're running in create or attach mode */
523 int use_attach = FALSE;
524 void setUseAttach(int v) {
525    if (v) { /* normalize to {TRUE, FALSE} */
526       use_attach = TRUE;
527    } else {
528       use_attach = FALSE;
529    }
530 }
531
532 /* Filename for single line test results output */
533 char *humanlog_name = "-";
534 /* Change this to default to FALSE once test_driver is passing -humanlog
535  * parameter to the mutatee
536  */
537 int use_humanlog = TRUE;
538 void setHumanLog(const char *new_name) {
539    if (NULL == new_name) {
540       use_humanlog = FALSE;
541    } else {
542       use_humanlog = TRUE;
543    } 
544    /* humanlog_name = (char *) new_name; */
545    redirectStream(HUMAN, new_name);
546 }
547
548 /* Print out single line message reporting whether a test passed, failed, was
549  * skipped, or crashed.
550  */
551 void printResultHumanLog(const char *testname, test_results_t result)
552 {
553    FILE *human;
554
555    if ((NULL == humanlog_name) || !strcmp(humanlog_name, "-")) {
556       human = stdout;
557    } else {
558       human = fopen(humanlog_name, "a");
559       if (NULL == human) {
560          output->log(STDERR, "Error opening human log file '%s': %s\n",
561                      humanlog_name, strerror(errno));
562          human = stdout;
563       }
564    }
565
566    output->log(HUMAN, "%s: mutatee: %s create_mode: ", testname,
567                (NULL == executable_name) ? "(unknown)" : executable_name);
568    if (use_attach) {
569       output->log(HUMAN, "attach");
570    } else {
571       output->log(HUMAN, "create");
572    }
573    output->log(HUMAN, "\tresult: ");
574    switch (result) {
575       case PASSED:
576          output->log(HUMAN, "PASSED\n");
577          break;
578
579       case SKIPPED:
580          output->log(HUMAN, "SKIPPED\n");
581          break;
582
583       case FAILED:
584          output->log(HUMAN, "FAILED\n");
585          break;
586    }
587
588    if (stdout == human) {
589       fflush(human);
590    } else {
591       fclose(human);
592    }
593
594 }
595
596 void stop_process_()
597 {
598 #if defined(os_windows_test)
599    DebugBreak();
600 #else
601    kill(getpid(), SIGSTOP);
602 #endif
603 }
604
605 /* This function sets a flag noting that testname ran successfully */
606 /* FIXME Need to give this a handle to the driver's mutatee_info structure
607  * somehow
608  */
609 void test_passes(const char *testname) {
610    unsigned int i;
611    for (i = 0; i < max_tests; i++) {
612       if (!strcmp(mutatee_funcs[i].testname, testname)) {
613          /* Found it */
614          passedTest[i] = TRUE;
615          break;
616       }
617    }
618 }
619
620 /* This function sets a flag noting that testname ran unsuccessfully */
621 void test_fails(const char *testname) {
622    unsigned int i;
623    for (i = 0; i < max_tests; i++) {
624       if (!strcmp(mutatee_funcs[i].testname, testname)) {
625          /* Found it */
626          passedTest[i] = FALSE;
627          break;
628       }
629    }
630 }
631
632 /* This function returns TRUE if the test has been marked as passing, and false
633  * otherwise
634  */
635 static int test_passed(const char *testname) {
636    unsigned int i;
637    int retval;
638    for (i = 0; i < max_tests; i++) {
639       if (!strcmp(mutatee_funcs[i].testname, testname)) {
640          /* Found it */
641          retval = passedTest[i];
642          break;
643       }
644    }
645    if (i >= max_tests) {
646       retval = FALSE; /* Not found */
647    }
648    return retval;
649 }
650
651 /*
652  * Verify that a scalar value of a variable is what is expected
653  * returns TRUE on success, FALSE on failure
654  */
655 int verifyScalarValue(const char *name, int a, int value,
656                       const char *testName, const char *testDesc)
657 {
658    if (a != value) {
659       if (test_passed(testName)) {
660          logerror("**Failed** test %s (%s)\n", testName, testDesc);
661       }
662       logerror("  %s = %d, not %d\n", name, a, value);
663       return FALSE;
664    }
665    return TRUE;
666 }
667
668 char *resumelog_name = "mutatee_resumelog";
669
670 void log_testrun(char *testname)
671 {
672    FILE *f = fopen(resumelog_name, "a");
673    if (!f) {
674       output->log(STDERR, "Could not write to resume log\n");
675       exit(0);
676    }
677    
678    fprintf(f, "%s\n", testname);
679    fclose(f);
680 }
681
682 void log_testresult(int passed)
683 {
684    FILE *f = fopen(resumelog_name, "a");
685    if (!f) {
686       output->log(STDERR, "Could not write to resume log\n");
687       exit(0);
688    }
689    fprintf(f, "%d\n", passed ? 1 : 0);
690    fclose(f);
691 }