Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[6model/c] begin performance testing OpenMP threads

  • Loading branch information...
commit d50b8cbd295b8a33d3071fbfb2ab08b3ba5de53b 1 parent 24a54a4
@mberends mberends authored
View
14 c/src/threads.c
@@ -31,7 +31,7 @@ thread_join(struct thread_info * info)
{
int status;
#if defined( __APPLE__ ) || defined( __linux__ )
- status = pthread_join(&info->thread_id, NULL);
+ status = pthread_join(info->thread_id, NULL);
#elif defined( _WIN32 )
status = WaitForSingleObject(threadhandle[i], INFINITE);
#endif
@@ -39,11 +39,11 @@ thread_join(struct thread_info * info)
}
-/* Caution - do not underestimate the overheads of creating and */
-/* synchronizing threads. See for example the HPL-2004-209 report */
-/* below, stating that some of the machine code instructions took */
-/* over 100 machine cycles on a Pentium 4. More detailed and recent */
-/* performance figures would be very welcome. */
+/* Caution - bear in mind the time required to create and synchronize */
+/* threads. See for example the HPL-2004-209 report below, stating */
+/* that some of the machine code instructions took over 100 machine */
+/* cycles on a Pentium 4. More detailed and recent measurements are */
+/* very welcome. */
/* See also: */
/* Lawrence Livermore National Laboratory tutorials: */
@@ -52,5 +52,7 @@ thread_join(struct thread_info * info)
/* Threads Cannot be Implemented as a Library (may now be outdated): */
/* http://www.hpl.hp.com/techreports/2004/HPL-2004-209.html */
/* http://en.wikipedia.org/wiki/Lock-free_and_wait-free_algorithms */
+/* Ross Bencina - Some notes on lock-free and wait-free algorithms */
+/* http://www.rossbencina.com/code/lockfree */
/* end of threads.c */
View
22 c/src/timing.c
@@ -4,10 +4,10 @@
/* systems give the current time and do delays. */
#ifdef _WIN32
- #include <windows.h> /* FILETIME GetSystemTimeAsFileTime */
+ #include <windows.h> /* FILETIME GetSystemTimeAsFileTime Sleep */
#else
#include <stdlib.h> /* NULL */
- #include <sys/time.h> /* gettimeofday */
+ #include <sys/time.h> /* gettimeofday nanosleep */
#endif
#include "timing.h"
@@ -21,17 +21,17 @@ gettime()
int microseconds;
long long seconds, result;
#ifdef _WIN32
- FILETIME time1;
- GetSystemTimeAsFileTime(&time1);
- seconds = (((long long)time1.dwHighDateTime) << 32)
- | time1.dwLowDateTime;
- seconds -= 11644473600; /* from 1601-01-01 to 1970-01-01 */
+ FILETIME now;
+ GetSystemTimeAsFileTime(&now);
+ seconds = (((long long)now.dwHighDateTime) << 32)
+ | now.dwLowDateTime;
+ seconds -= 11644473600LL; /* from 1601-01-01 to 1970-01-01 */
microseconds = (seconds % 10000000)/10; seconds /= 10000000;
#else
- struct timeval time1;
- gettimeofday(&time1, NULL);
- seconds = time1.tv_sec;
- microseconds = time1.tv_usec;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ seconds = now.tv_sec;
+ microseconds = now.tv_usec;
#endif
return (seconds << 20) | microseconds;
}
View
45 c/t/01-toolchain/01b-timing.c
@@ -14,34 +14,49 @@ void
seconds_microseconds_sleep()
{
/* long long for 64 bits because on Win32, long is only 32 bits */
- long long seconds1, seconds2, seconds3, timediff;
- int microseconds1, microseconds2, microseconds3;
+ long long seconds1, seconds2, seconds3, seconds4, seconds5, timediff;
+ int microseconds1, microseconds2, microseconds3, microseconds4, microseconds5;
char message[80];
/* Read the clock twice in quick succession, sleep for 1 second, */
/* then read the clock a third time. Report the differences */
/* between the times and verify that the sleep was about 1 sec. */
#ifdef _WIN32
- FILETIME time1, time2, time3;
+ FILETIME time1, time2, time3, time4, time5;
GetSystemTimeAsFileTime(&time1); /* * 100ns since 1601-01-01 */
GetSystemTimeAsFileTime(&time2);
- Sleep(1000); /* milliseconds */
+ Sleep(0); /* 0 milliseconds */
GetSystemTimeAsFileTime(&time3);
+ Sleep(1); /* 1 millisecond */
+ GetSystemTimeAsFileTime(&time4);
+ Sleep(1000); /* 1 second */
+ GetSystemTimeAsFileTime(&time5);
seconds1 = (((long long)time1.dwHighDateTime) << 32) | time1.dwLowDateTime;
microseconds1 = (seconds1 % 10000000)/10; seconds1 /= 10000000;
seconds2 = (((long long)time2.dwHighDateTime) << 32) | time2.dwLowDateTime;
microseconds2 = (seconds2 % 10000000)/10; seconds2 /= 10000000;
seconds3 = (((long long)time3.dwHighDateTime) << 32) | time3.dwLowDateTime;
microseconds3 = (seconds3 % 10000000)/10; seconds3 /= 10000000;
+ seconds4 = (((long long)time4.dwHighDateTime) << 32) | time4.dwLowDateTime;
+ microseconds4 = (seconds4 % 10000000)/10; seconds4 /= 10000000;
+ seconds5 = (((long long)time5.dwHighDateTime) << 32) | time5.dwLowDateTime;
+ microseconds5 = (seconds5 % 10000000)/10; seconds5 /= 10000000;
#else
- struct timeval time1, time2, time3;
+ struct timeval time1, time2, time3, time4, time5;
+ struct timespec ns = {0, 0}, ns_remain;
gettimeofday(&time1, NULL);
gettimeofday(&time2, NULL);
- sleep(1); /* seconds */
+ nanosleep(&ns, &ns_remain); /* 0 nanoseconds */
gettimeofday(&time3, NULL);
+ ns.tv_nsec = 1000000; nanosleep(&ns, &ns_remain); /* 1 millisecond */
+ gettimeofday(&time4, NULL);
+ sleep(1); /* 1 second */
+ gettimeofday(&time5, NULL);
seconds1 = time1.tv_sec; microseconds1 = time1.tv_usec;
seconds2 = time2.tv_sec; microseconds2 = time2.tv_usec;
seconds3 = time3.tv_sec; microseconds3 = time3.tv_usec;
+ seconds4 = time4.tv_sec; microseconds4 = time4.tv_usec;
+ seconds5 = time5.tv_sec; microseconds5 = time5.tv_usec;
#endif
/* test 1 - time1 is nonzero */
sprintf(message, "clock returned %lld seconds and %d microseconds",
@@ -53,9 +68,23 @@ seconds_microseconds_sleep()
seconds2, microseconds2);
ok(seconds2 > seconds1 || microseconds2 >= microseconds1, message);
- /* test 3 - time3 is within 2ms of time2 + 1 second */
+ /* test 3 - time3 is within 1ms of time2 */
timediff = (seconds3 - seconds2) * 1000000
+ microseconds3 - microseconds2;
+ sprintf(message, "zero sleep measured %lld microseconds",
+ timediff);
+ ok(0 <= timediff && timediff < 1000, message);
+
+ /* test 4 - time4 is within 0.15ms of time3 + 1 millisecond */
+ timediff = (seconds4 - seconds3) * 1000000
+ + microseconds4 - microseconds3;
+ sprintf(message, "one millisecond sleep measured %lld microseconds",
+ timediff);
+ ok(1000 <= timediff && timediff < 1150, message);
+
+ /* test 5 - time5 is within 2ms of time4 + 1 second */
+ timediff = (seconds5 - seconds4) * 1000000
+ + microseconds5 - microseconds4;
sprintf(message, "one second sleep measured %lld microseconds",
timediff);
ok(998000 < timediff && timediff < 1002000, message);
@@ -67,7 +96,7 @@ int
main(int arg, char * argv[])
{
diag("01b-timing");
- plan(3);
+ plan(5);
seconds_microseconds_sleep();
return 0;
}
View
126 c/t/01-toolchain/01c-osthreads.c
@@ -6,10 +6,12 @@
#include <stdlib.h> /* system */
#include <string.h> /* strlen */
#include "../Test.h" /* diag is_ii ok plan */
+#if defined( _OPENMP )
+ #include <omp.h>
+#endif
#ifdef _WIN32
- #include <windows.h>
- #define sleep(seconds) Sleep(seconds*1000)
+ #include <windows.h> /* FILETIME GetSystemTimeAsFileTime Sleep */
#else
#include <pthread.h> /* pthread_create pthread_join */
#endif
@@ -57,9 +59,9 @@ tests1_4sleeps()
struct test1_threadargs thread1arguments, thread2arguments;
/* Create the first thread */
- thread1arguments.testnumber = 3;
- thread1arguments.seconds = 2;
- thread1arguments.description = "first";
+ thread1arguments.testnumber = 3;
+ thread1arguments.seconds = 2;
+ thread1arguments.description = "first";
threadstacksize = 16384; /* Minimum allowed by Posix threads */
#ifdef _WIN32
threadhandle1 = CreateThread(NULL, threadstacksize,
@@ -76,9 +78,9 @@ tests1_4sleeps()
sleep(1); /* Let the first thread run immediately */
/* Create the second thread */
- thread2arguments.testnumber = 4;
- thread2arguments.seconds = 2;
- thread2arguments.description = "second";
+ thread2arguments.testnumber = 4;
+ thread2arguments.seconds = 2;
+ thread2arguments.description = "second";
#ifdef _WIN32
threadhandle2 = CreateThread(NULL, threadstacksize,
(LPTHREAD_START_ROUTINE) test1_thread, &thread2arguments, 0,
@@ -205,14 +207,118 @@ tests5_6charcount()
}
+/* tests6_8vectordotproduct */
+void
+tests6_8vectordotproduct()
+{
+ /* Calculate a vector dot product three ways, first in a single */
+ /* thread for comparison, then with native OS threads, and then */
+ /* with OpenMP threads. Test with 10, 1000 and 1000000 elements. */
+ int i, j, vectorsize[3] = {10,1000,1000000}, elements, chunk = 3;
+ long long time1, time2, * v1, * v2, dp1, dp2;
+// double * u, * v, dotproduct1, dotproduct2;
+ char message[80];
+ #if defined( _WIN32 )
+ FILETIME now;
+ #else
+ struct timeval now;
+ #endif
+
+ /* Do the tests three times, with 10, 100 and 100000 elements */
+ for (i=0; i<3; ++i) {
+ elements = vectorsize[i];
+// u = (double *) malloc(elements * sizeof(double)); assert(u!=NULL);
+// v = (double *) malloc(elements * sizeof(double)); assert(v!=NULL);
+ v1 = (long long *) malloc(elements * sizeof(long long)); assert(v1!=NULL);
+ v2 = (long long *) malloc(elements * sizeof(long long)); assert(v2!=NULL);
+ /* Fill the two vectors with data */
+ for (j=0; j<elements; ++j) {
+// u[j] = (double)(j);
+// v[j] = (double)(elements - j);
+ v1[j] = (long long)(j);
+ v2[j] = (long long)(elements - j);
+ }
+ /* Calculate the dot product with just the main thread */
+ /* Get the system time in microseconds into time1 */
+ #if defined( _WIN32 )
+ GetSystemTimeAsFileTime(&now);
+ time1 = ((((long long)now.dwHighDateTime) << 32) | now.dwLowDateTime)/10;
+ #else
+ gettimeofday(&now, NULL);
+ time1 = now.tv_sec * 1000000LL + now.tv_usec;
+ #endif
+// dotproduct1 = 0.0;
+ dp1 = 0;
+ for (j=0; j<elements; ++j) {
+// dotproduct1 += u[j] * v[j];
+ dp1 += v1[j] * v2[j];
+ }
+ /* Get the system time in microseconds into time2 */
+ #if defined( _WIN32 )
+ GetSystemTimeAsFileTime(&now);
+ time2 = ((((long long)now.dwHighDateTime) << 32) | now.dwLowDateTime)/10;
+ #else
+ gettimeofday(&now, NULL);
+ time2 = now.tv_sec * 1000000LL + now.tv_usec;
+ #endif
+ sprintf(message, "single threaded%9d elements%7d microseconds", elements, (int)(time2-time1));
+ diag(message);
+ /* Calculate the dot product with OpenMP threads, if available */
+ #if defined( _OPENMP )
+ /* Get the system time in microseconds into time1 */
+ #if defined( _WIN32 )
+ GetSystemTimeAsFileTime(&now);
+ time1 = ((((long long)now.dwHighDateTime) << 32) | now.dwLowDateTime)/10;
+ #else
+ gettimeofday(&now, NULL);
+ time1 = now.tv_sec * 1000000LL + now.tv_usec;
+ #endif
+// dotproduct2 = 0.0;
+ dp2 = 0;
+// #pragma omp parallel for default(shared) private(j) schedule(static) reduction(+:dotproduct2) reduction(+:dp2)
+ #pragma omp parallel for default(shared) private(j) schedule(static) reduction(+:dp2)
+ for (j=0; j < elements; j++) {
+// dotproduct2 = dotproduct2 + (u[j] * v[j]);
+ dp2 = dp2 + (v1[j] * v2[j]);
+ }
+
+ /* Get the system time in microseconds into time2 */
+ #if defined( _WIN32 )
+ GetSystemTimeAsFileTime(&now);
+ time2 = ((((long long)now.dwHighDateTime) << 32) | now.dwLowDateTime)/10;
+ #else
+ gettimeofday(&now, NULL);
+ time2 = now.tv_sec * 1000000LL + now.tv_usec;
+ #endif
+ sprintf(message, "OpenMP threaded%9d elements%7d microseconds", elements, (int)(time2-time1));
+ diag(message);
+// sprintf(message, "OpenMP dot products double %lf %lf", dotproduct1, dotproduct2);
+// diag(message);
+// sprintf(message, "OpenMP dot products long long %lld %lld", dp1, dp2);
+// diag(message);
+// ok(dotproduct2 == dotproduct1, "dot products match double");
+ ok(dp2 == dp1, "dot products match");
+ #else
+ diag("OpenMP not available");
+ #endif
+// free(u); free(v);
+ free(v1); free(v2);
+ }
+}
+
/* main */
int
main(int arg, char * argv[])
{
+ int testplan = 5;
diag("01c-osthreads");
- plan(5);
- tests1_4sleeps(); /* two threads that sleep and print */
+ #if defined( _OPENMP )
+ testplan += 3;
+ #endif
+ plan(testplan);
+ tests1_4sleeps(); /* two threads that sleep and print */
tests5_6charcount(); /* four threads returning integers */
+ tests6_8vectordotproduct();
return 0;
}
View
26 c/tools/build/Configure.c
@@ -14,7 +14,7 @@
/* Systems usually predefine some variables and macros. Users might */
/* make the same definitions. */
-/* Currently works on:
+/* Currently works with:
* MinGW
* http://mingw.org/
* Currently based on GCC 4.5.2, 85MB disk.
@@ -49,7 +49,7 @@ enum { OS_VAR,
DETECTED_END /* This one must always be last */};
char * detected[DETECTED_END] = {""};
/* Subscript names for configuration strings. Almost like a hash ;) */
-enum { CC, EXE, LDL, MAKE_COMMAND, OUTFILE, RM_RF, THREADS,
+enum { CC, EXE, LDL, MAKE_COMMAND, OPENMP, OUTFILE, RM_RF, THREADS,
CONFIG_END /* this one must always be last */ };
/* note the words and OUT clash with MinGW */
char * config[CONFIG_END] = {"", "", "", "", "", "", ""};
@@ -81,6 +81,9 @@ detection(void)
#endif
#if defined( _WIN32 )
printf("_WIN32");
+ config[EXE] = ".exe";
+ #else
+ config[EXE] = "";
#endif
#if !(defined(__APPLE__) || defined(__linux__) || defined(_WIN32))
printf("unknown\n (not __APPLE__ __linux__ or _WIN32)\n");
@@ -103,8 +106,14 @@ detection(void)
printf(" OpenMP: ");
#if defined( _OPENMP )
processors = omp_get_num_procs();
- printf("_OPENMP is %d with max %d threads on %d processors\n",
- _OPENMP, omp_get_max_threads(), processors);
+ printf("v%d max processors/threads %d/%d\n", _OPENMP,
+ processors, omp_get_max_threads());
+ config[OPENMP] =
+ #if defined( __GNUC__ )
+ "-fopenmp";
+ #else
+ "-openmp";
+ #endif
#else
printf("not enabled in the C compiler\n");
@@ -141,13 +150,6 @@ detection(void)
void
config_set(void)
{
- /* Operating system */
- #if defined( _WIN32 )
- config[EXE] = ".exe";
- #else
- config[EXE] = "";
- #endif
-
/* C compiler */
#if defined( _MSC_VER )
/* See http://msdn.microsoft.com/en-US/library/b0084kay%28v=VS.100%29.aspx */
@@ -193,6 +195,7 @@ config_set(void)
#else
"make";
#endif
+ /* TODO: verify that the chosen make command actually works */
}
@@ -208,6 +211,7 @@ makefile_convert(char * programfilename, char * templatefilename,
trans(&makefiletext, "This is the file", "This is NOT the file");
trans(&makefiletext, "@cc@", config[CC]);
trans(&makefiletext, "@exe@", config[EXE]);
+ trans(&makefiletext, "@openmp@", config[OPENMP]);
trans(&makefiletext, "@outfile@", config[OUTFILE]);
trans(&makefiletext, "@rm_rf@", config[RM_RF]);
trans(&makefiletext, "@threads@", config[THREADS]);
View
17 c/tools/build/Makefile.in
@@ -11,43 +11,44 @@
# Configure.c replaces words it recognizes between the @ signs.
CC = @cc@
EXE = @exe@
-O = @outfile@
+OUTFILE = @outfile@
RM_RF = @rm_rf@
THREADS = @threads@
+OPENMP = @openmp@
# The first target is default, will be used by a plain 'make' command.
all: test
# Recipes to build executables
t/01-toolchain/01a-cc.exe: t/01-toolchain/01a-cc.c t/Test.h
- $(CC) $(O) t/01-toolchain/01a-cc.exe t/01-toolchain/01a-cc.c
+ $(CC) $(OUTFILE) t/01-toolchain/01a-cc.exe t/01-toolchain/01a-cc.c
-$(RM_RF) 01a-cc.obj
t/01-toolchain/01b-timing.exe: t/01-toolchain/01b-timing.c t/Test.h
- $(CC) $(O) t/01-toolchain/01b-timing.exe t/01-toolchain/01b-timing.c
+ $(CC) $(OUTFILE) t/01-toolchain/01b-timing.exe t/01-toolchain/01b-timing.c
-$(RM_RF) 01b-timing.obj
t/01-toolchain/01c-osthreads.exe: t/01-toolchain/01c-osthreads.c t/Test.h
- $(CC) $(THREADS) $(O) t/01-toolchain/01c-osthreads.exe t/01-toolchain/01c-osthreads.c
+ $(CC) $(THREADS) $(OPENMP) $(OUTFILE) t/01-toolchain/01c-osthreads.exe t/01-toolchain/01c-osthreads.c
-$(RM_RF) 01c-osthreads.obj
t/02-components/02a-threads.exe: t/02-components/02a-threads.c \
src/threads.h src/threads.c src/timing.c src/timing.h t/Test.h
- $(CC) $(THREADS) $(O) t/02-components/02a-threads.exe src/threads.c src/timing.c t/02-components/02a-threads.c
+ $(CC) $(THREADS) $(OUTFILE) t/02-components/02a-threads.exe src/threads.c src/timing.c t/02-components/02a-threads.c
-$(RM_RF) threads.obj 02a-threads.obj
t/02-components/02b-hashtable.exe: t/02-components/02b-hashtable.c \
src/hashtable.h src/hashtable.c t/Test.h
- $(CC) $(O) t/02-components/02b-hashtable.exe src/hashtable.c t/02-components/02b-hashtable.c
+ $(CC) $(OUTFILE) t/02-components/02b-hashtable.exe src/hashtable.c t/02-components/02b-hashtable.c
-$(RM_RF) hashtable.obj 02b-hashtable.obj
t/02-components/02c-heapmanager.exe: t/02-components/02c-heapmanager.c \
src/heapmanager.h src/heapmanager.c t/Test.h
- $(CC) $(O) t/02-components/02c-heapmanager.exe src/heapmanager.c t/02-components/02c-heapmanager.c
+ $(CC) $(OUTFILE) t/02-components/02c-heapmanager.exe src/heapmanager.c t/02-components/02c-heapmanager.c
-$(RM_RF) heapmanager.obj 02b-heapmanager.obj
tools/build/prove$(EXE): tools/build/prove.c
- $(CC) $(O) tools/build/prove$(EXE) tools/build/prove.c
+ $(CC) $(OUTFILE) tools/build/prove$(EXE) tools/build/prove.c
-$(RM_RF) prove.obj
# Note: test executables are named *.exe on all operating systems so
View
320 c/tools/build/prove.c
@@ -4,25 +4,23 @@
/* TODO: parse the test script output looking for 'ok', 'not ok' etc */
/* and output a summary instead of every test result. */
-/* TODO: handle multiple directory arguments instead of just one */
-
-/* TODO: replace glob code with opendir() etc / FindFirstFile() etc */
-
-#ifdef _WIN32
+#include <assert.h> /* assert */
+#include <stdio.h> /* FILE fprintf printf stderr */
+#include <stdlib.h> /* exit free malloc qsort realloc */
+#include <string.h> /* strcat strcpy strlen */
+#if defined( _WIN32 )
#include <windows.h>
#define pclose _pclose
#define popen _popen
#else
- #include <glob.h> /* glob globfree */
+ #include <dirent.h> /* opendir readdir */
#endif
-#include <stdio.h> /* FILE fprintf printf stderr */
-#include <stdlib.h> /* exit free malloc realloc */
-#include <string.h> /* strcat strcpy strlen */
#define LINEBUFFERSIZE 128
-char * executable_program = NULL;
-char * filename_extension = NULL;
+char * program_name;
+char * executable_program;
+char * filename_extension;
/* options */
@@ -31,10 +29,11 @@ char * filename_extension = NULL;
int
options(int argc, char * argv[])
{
- int argindex = 0; /* will skip argv[0], it is the program name */
+ int argindex = 0;
int scanning_args = 0;
- executable_program = NULL;
- filename_extension = NULL;
+ program_name = argv[0];
+ executable_program = NULL; /* should be "perl6" ;-) */
+ filename_extension = NULL; /* should be ".t" */
if (argc < 2) {
fprintf(stderr, "Usage: %s test_directory\n", argv[0]);
exit(1);
@@ -45,8 +44,16 @@ options(int argc, char * argv[])
if (strcmp(argv[argindex], "-e")==0) {
executable_program = argv[++argindex];
}
- if (strcmp(argv[argindex], "--ext")==0) {
- filename_extension = argv[++argindex];
+ else {
+ if (strcmp(argv[argindex], "--ext")==0) {
+ filename_extension = argv[++argindex];
+ }
+ else {
+ fprintf(stderr, "%s: invalid option '%s'\n"
+ " (the only usable options are -e and --ext)\n",
+ program_name, argv[argindex]);
+ exit(1);
+ }
}
}
return argindex;
@@ -78,58 +85,251 @@ qx(char * command)
}
-/* main */
+#if ! defined( _WIN32 )
+/* scandirectory_comparenames */
int
-main(int argc, char * argv[])
+scandirectory_comparenames(const void * a, const void * b)
{
- char * glob_pattern, * tap_output, * errormessage;
- int argi, patternlength, glob_flags;
- int (* glob_errfunc) (const char * epath, int eerrno);
- int status, pathindex;
- #ifdef _WIN32
+ /* Hard to understand this casting, but it works. See 'man qsort' */
+ return strcmp(* (char * const *) a, * (char * const *) b);
+}
+#endif
+
+
+/* scandirectory */
+/* Almost an os-independent re-invention of scandir() */
+int
+scandirectory(char * dirname, char *** filenamelist) /* yes, triple pointer */
+{
+ int found_a_file, filename_extension_len = 0, filenamecount = 0;
+ char * filename;
+ #if defined( _WIN32 )
+ WIN32_FIND_DATA dir;
+ HANDLE hFind;
#else
- glob_t globbuf;
+ DIR * dir;
+ struct dirent * direntry;
+ char * nametail;
+ #endif
+ if (filename_extension != NULL) {
+ filename_extension_len = strlen(filename_extension);
+ }
- /* Get command line options and process them */
- argi = options(argc, argv);
+ #if defined( _WIN32 )
+ filename = (char *) malloc(strlen(dirname) + 2
+ + filename_extension_len + 1);
+ strcpy(filename, dirname);
+ strcat(filename, "\\*");
+ if (filename_extension != NULL)
+ strcat(filename, filename_extension);
+ printf("%s\n", filename);
+ hFind = FindFirstFile(filename, &dir);
+ found_a_file = (hFind != INVALID_HANDLE_VALUE);
+ #else
+ dir = opendir(dirname);
+ if (dir == NULL) {
+ fprintf(stderr, "%s: cannot open directory '%s'\n",
+ program_name, dirname);
+ exit(1);
+ }
+ direntry = readdir(dir);
+ found_a_file = (direntry != NULL);
+ printf("scandirectory %s\n", dirname);
+ #endif
+ while (found_a_file) {
+ #if defined( _WIN32 )
+ filename = dir.cFileName;
+ #else
+ filename = direntry->d_name;
+ if (filename_extension != NULL) {
+ nametail = filename+strlen(filename)-strlen(filename_extension);
+ if (strcmp(nametail, filename_extension)!=0)
+ filename = NULL;
+ }
+ #endif
+ /* Exclude filenames we don't want */
+ if (filename && * filename == '.')
+ filename = NULL;
+ /* Keep the names we do want */
+ if (filename) {
+ if (++filenamecount == 1) {
+ * filenamelist = malloc(sizeof(char *));
+ }
+ else {
+ * filenamelist = realloc(* filenamelist,
+ filenamecount * sizeof(char *));
+ }
+ (* filenamelist)[filenamecount-1] = (char *)
+ malloc(strlen(filename)+1);
+ strcpy((* filenamelist)[filenamecount-1], filename);
+ }
+ #if defined( _WIN32 )
+ found_a_file = FindNextFile(hFind, &dir);
+ #else
+ direntry = readdir(dir);
+ found_a_file = (direntry != NULL);
+ #endif
+ }
+ #if defined( _WIN32 )
+ FindClose(hFind);
+ #else
+ closedir(dir);
+ /* sort the found names */
+ if (filenamecount > 1) {
+ qsort(* filenamelist, filenamecount, sizeof(char *),
+ scandirectory_comparenames);
+ }
+ #endif
+ return filenamecount;
+}
+
+
+/* filecollection - a list of directory names containing file names */
+struct filecollection {
+ int dircount;
+ struct filecollection_dir {
+ char * dirname;
+ int filecount;
+ char ** filenames;
+ } * dirs;
+};
- /* Scan the specified directory for test files */
- patternlength = strlen(argv[argi]) + strlen(filename_extension) + 3;
- glob_pattern = (char *) malloc(patternlength);
- strcpy(glob_pattern,argv[argi]);
- strcat(glob_pattern, "/*");
- strcat(glob_pattern, filename_extension);
- glob_flags = 0;
- glob_errfunc = NULL;
- printf("pattern=%s exe=%s ext=%s arg=%s\n", glob_pattern,
- executable_program, filename_extension, argv[argi]);
- status = glob(glob_pattern, glob_flags, glob_errfunc, &globbuf);
- free(glob_pattern);
- if (status) {
- switch (status) {
- case GLOB_NOSPACE:
- errormessage = "out of memory";
- break;
- case GLOB_ABORTED:
- errormessage = "read error";
- break;
- case GLOB_NOMATCH:
- errormessage = "no files found";
- break;
+
+/* filecollection_free */
+void
+filecollection_free(struct filecollection * coll)
+{
+ int i, j;
+ for (i=0; i<coll->dircount; ++i) {
+// printf("Freeing %s has %d files\n", coll->dirs[i].dirname, coll->dirs[i].filecount);
+ free(coll->dirs[i].dirname);
+ for (j=0; j<coll->dirs[i].filecount; ++j) {
+// printf(" Freeing %s\n", coll->dirs[i].filenames[j]);
+ free(coll->dirs[i].filenames[j]);
}
- fprintf(stderr,
- "%s: scanning directory '%s' ended unexpectedly with %s\n",
- argv[0], argv[argi], errormessage);
- exit(1);
+ free(coll->dirs[i].filenames);
}
+ free(coll->dirs);
+ free(coll);
+}
- /* Run each test file found in the directory and scan the output */
- for (pathindex=0; pathindex<globbuf.gl_pathc; pathindex++) {
- // printf("found path %s\n", globbuf.gl_pathv[pathindex]);
- tap_output = qx(globbuf.gl_pathv[pathindex]);
- printf("%s\n", tap_output);
- free(tap_output);
+/* scandirectories */
+struct filecollection *
+scandirectories(int argc, char * argv[])
+{
+ int i, j, filenamecount;
+ char ** filenamelist, * dirname, * s1, * s2;
+ struct filecollection * filecoll = NULL;
+ struct filecollection_dir * filedir;
+
+ /* The argument list is a series of directory names */
+ for (i=0; i<argc; ++i) {
+ dirname = argv[i];
+ filenamelist = NULL;
+ filenamecount = scandirectory(dirname, & filenamelist);
+ if (filenamecount) {
+ /* Create a new filecollection if it does not yet exist */
+ if (filecoll == NULL) {
+ filecoll = (struct filecollection *) malloc(sizeof(struct filecollection));
+ filecoll->dircount = 0;
+ filecoll->dirs = NULL;
+ }
+ /* Add this directory to the filecollection */
+ if (filecoll->dircount++ == 0) {
+ filecoll->dirs = (struct filecollection_dir *)
+ malloc(sizeof(struct filecollection_dir));
+ }
+ else {
+ filecoll->dirs = (struct filecollection_dir *)
+ realloc(filecoll->dirs, filecoll->dircount *
+ sizeof(struct filecollection_dir));
+ }
+ filedir = & filecoll->dirs[filecoll->dircount-1];
+ filedir->filecount = 0;
+ filedir->filenames = NULL;
+ /* Make a copy of the directory name without any trailing */
+ /* slashes. */
+ s1 = dirname; s2 = dirname + (strlen(dirname) - 1);
+ while (* s2 == '/' && s2 > s1+1)
+ --s2;
+ filedir->dirname = (char *) malloc((s2-s1)+2);
+ strncpy( filedir->dirname, dirname, (s2-s1)+2 ); /* remember strncpy does not always copy a '\0' */
+
+ /* Add the found file names to the list of tests to be run */
+ for (j=0; j<filenamecount; ++j ) {
+ if (filedir->filecount++ == 0) {
+ filedir->filenames = (char **) malloc(sizeof(char *));
+ }
+ else {
+ filedir->filenames = (char **) realloc(filedir->filenames, filedir->filecount * sizeof(char *));
+ }
+ filedir->filenames[filedir->filecount-1] =
+ (char *) malloc(strlen(filenamelist[j])+1);
+ strcpy(filedir->filenames[filedir->filecount-1], filenamelist[j]);
+// printf(" %s\n", filenamelist[j]);
+ free(filenamelist[j]);
+ }
+ }
+ free(filenamelist);
}
- #endif
+ return filecoll;
+}
+
+
+/* runtests */
+void
+runtests(struct filecollection * coll)
+{
+ int i, j, commandlen;
+ char * command, * tap_output;
+ for (i=0; i<coll->dircount; ++i) {
+ for (j=0; j<coll->dirs[i].filecount; ++j) {
+ printf("runtest %s / %s\n", coll->dirs[i].dirname, coll->dirs[i].filenames[j]);
+ commandlen = (executable_program ? strlen(executable_program) + 1 : 0)
+ + strlen(coll->dirs[i].dirname) + 1
+ + strlen(coll->dirs[i].filenames[j]) + 1;
+ command = (char *) malloc(commandlen);
+ * command = '\0';
+ if (executable_program) {
+ strcpy(command, executable_program);
+ strcat(command, " ");
+ }
+ strcat(command, coll->dirs[i].dirname);
+ #if defined( _WIN32 )
+ strcat(command, "\\");
+ #else
+ strcat(command, "/");
+ #endif
+ strcat(command, coll->dirs[i].filenames[j]);
+
+ tap_output = qx(command);
+ printf("%s\n", tap_output);
+ free(command);
+ free(tap_output);
+ }
+ }
+}
+
+/* main */
+int
+main(int argc, char * argv[])
+{
+ int argi;
+ struct filecollection * files;
+
+ /* Get command line options and process them */
+ argi = options(argc, argv);
+
+ /* Scan the remaining non-option arguments as directory names */
+ files = scandirectories(argc-argi, argv+argi); /* argi hides what options() saw */
+
+ /* Perform each test and parse its TAP output */
+ runtests(files);
+
+ /* Clean up when finished */
+ filecollection_free(files);
return 0;
}
+
+
+/* end of prove.c */
Please sign in to comment.
Something went wrong with that request. Please try again.