1 /* CT - simple-minded unit testing for C */
23 static int rjobfd
= -1, wjobfd
= -1;
24 int fail
= 0; /* bool */
25 static int64 bstart
, bdur
;
26 static int btiming
; /* bool */
28 enum { Second
= 1000 * 1000 * 1000 };
29 enum { BenchTime
= Second
};
30 enum { MaxN
= 1000 * 1000 * 1000 };
35 # include <mach/mach_time.h>
40 return (int64
)mach_absolute_time();
50 clock_gettime(CLOCK_MONOTONIC
, &t
);
51 return (int64
)(t
.tv_sec
)*Second
+ t
.tv_nsec
;
57 ctlogpn(const char *p
, int n
, const char *fmt
, ...)
61 printf("%s:%d: ", p
, n
);
113 bdur
+= nstime() - bstart
;
127 die(int code
, int err
, const char *msg
)
136 fputs(strerror(err
), stderr
);
147 die(1, errno
, "tmpfile");
156 return WIFSIGNALED(s
) && (WTERMSIG(s
) == SIGABRT
);
166 pid
= wait3(&stat
, 0, 0);
168 die(3, errno
, "wait");
172 for (t
=ts
; t
->f
; t
++) {
177 } else if (failed(t
->status
)) {
192 strcpy(t
->dir
, TmpDirPat
);
193 if (mkdtemp(t
->dir
) == NULL
) {
194 die(1, errno
, "mkdtemp");
199 die(1, errno
, "fork");
200 } else if (!t
->pid
) {
202 if (dup2(t
->fd
, 1) == -1) {
203 die(3, errno
, "dup2");
205 if (close(t
->fd
) == -1) {
206 die(3, errno
, "fclose");
208 if (dup2(1, 2) == -1) {
209 die(3, errno
, "dup2");
218 setpgid(t
->pid
, t
->pid
);
223 runalltest(Test
*ts
, int limit
)
227 for (t
=ts
; t
->f
; t
++) {
235 for (; nrun
; nrun
--) {
242 copyfd(FILE *out
, int in
)
245 char buf
[1024]; /* arbitrary size */
247 while ((n
= read(in
, buf
, sizeof(buf
))) != 0) {
248 if (fwrite(buf
, 1, n
, out
) != (size_t)n
) {
249 die(3, errno
, "fwrite");
256 Removes path and all of its children.
257 Writes errors to stderr and keeps going.
258 If path doesn't exist, rmtree returns silently.
263 int r
= unlink(path
);
264 if (r
== 0 || errno
== ENOENT
) {
265 return; /* success */
267 int unlinkerr
= errno
;
269 DIR *d
= opendir(path
);
271 if (errno
== ENOTDIR
) {
272 fprintf(stderr
, "ct: unlink: %s\n", strerror(unlinkerr
));
274 perror("ct: opendir");
276 fprintf(stderr
, "ct: path %s\n", path
);
280 while ((ent
= readdir(d
))) {
281 if (strcmp(ent
->d_name
, ".") == 0 || strcmp(ent
->d_name
, "..") == 0) {
284 int n
= strlen(path
) + 1 + strlen(ent
->d_name
);
286 sprintf(s
, "%s/%s", path
, ent
->d_name
);
293 fprintf(stderr
, "ct: path %s\n", path
);
299 runbenchn(Benchmark
*b
, int n
)
303 strcpy(b
->dir
, TmpDirPat
);
304 if (mkdtemp(b
->dir
) == NULL
) {
305 die(1, errno
, "mkdtemp");
310 die(1, errno
, "fork");
313 if (dup2(outfd
, 1) == -1) {
314 die(3, errno
, "dup2");
316 if (close(outfd
) == -1) {
317 die(3, errno
, "fclose");
319 if (dup2(1, 2) == -1) {
320 die(3, errno
, "dup2");
326 write(durfd
, &bdur
, sizeof bdur
);
327 write(durfd
, &bbytes
, sizeof bbytes
);
332 pid
= waitpid(pid
, &b
->status
, 0);
334 die(3, errno
, "wait");
338 if (b
->status
!= 0) {
340 lseek(outfd
, 0, SEEK_SET
);
341 copyfd(stdout
, outfd
);
345 lseek(durfd
, 0, SEEK_SET
);
346 int r
= read(durfd
, &b
->dur
, sizeof b
->dur
);
347 if (r
!= sizeof b
->dur
) {
351 r
= read(durfd
, &b
->bytes
, sizeof b
->bytes
);
352 if (r
!= sizeof b
->bytes
) {
359 /* rounddown10 rounds a number down to the nearest power of 10. */
364 /* tens = floor(log_10(n)) */
369 /* result = 10**tens */
371 for (i
= 0; i
< tens
; i
++) {
378 /* roundup rounds n up to a number of the form [1eX, 2eX, 5eX]. */
382 int base
= rounddown10(n
);
414 runbench(Benchmark
*b
)
416 printf("%s\t", b
->name
);
420 while (b
->status
== 0 && b
->dur
< BenchTime
&& n
< MaxN
) {
422 /* Predict iterations/sec. */
423 int nsop
= b
->dur
/ n
;
427 n
= BenchTime
/ nsop
;
429 /* Run more iterations than we think we'll need for a second (1.5x).
430 Don't grow too fast in case we had timing errors previously.
431 Be sure to run at least one more than last time. */
432 n
= max(min(n
+n
/2, 100*last
), last
+1);
433 /* Round up to something easy to read. */
437 if (b
->status
== 0) {
438 printf("%8d\t%10" PRId64
" ns/op", n
, b
->dur
/n
);
442 int64 sec
= b
->dur
/ 1000L / 1000L / 1000L;
443 int64 nsec
= b
->dur
% 1000000000L;
444 double dur
= (double)sec
+ (double)nsec
*.0000000001;
445 mbs
= ((double)b
->bytes
* (double)n
/ 1000000) / dur
;
447 printf("\t%7.2f MB/s", mbs
);
451 if (failed(b
->status
)) {
455 if (WIFEXITED(b
->status
)) {
456 printf(" (exit status %d)", WEXITSTATUS(b
->status
));
458 if (WIFSIGNALED(b
->status
)) {
459 printf(" (signal %d)", WTERMSIG(b
->status
));
468 runallbench(Benchmark
*b
)
479 int nfail
= 0, nerr
= 0;
488 printf("\n%s: ", t
->name
);
489 if (failed(t
->status
)) {
495 if (WIFEXITED(t
->status
)) {
496 printf(" (exit status %d)", WEXITSTATUS(t
->status
));
498 if (WIFSIGNALED(t
->status
)) {
499 printf(" (signal %d)", WTERMSIG(t
->status
));
504 lseek(t
->fd
, 0, SEEK_SET
);
505 copyfd(stdout
, t
->fd
);
509 printf("\n%d failures; %d errors.\n", nfail
, nerr
);
513 return nfail
|| nerr
;
522 if ((s
= strstr(getenv("MAKEFLAGS"), " --jobserver-fds="))) {
523 rjobfd
= (int)strtol(s
+17, &s
, 10); /* skip " --jobserver-fds=" */
524 wjobfd
= (int)strtol(s
+1, NULL
, 10); /* skip comma */
527 fcntl(rjobfd
, F_SETFL
, fcntl(rjobfd
, F_GETFL
)|O_NONBLOCK
);
528 while (read(rjobfd
, &c
, 1) > 0) {
541 fcntl(wjobfd
, F_SETFL
, fcntl(wjobfd
, F_GETFL
)|O_NONBLOCK
);
543 write(wjobfd
, &c
, 1); /* ignore error; nothing we can do anyway */
550 main(int argc
, char **argv
)
552 int n
= readtokens();
553 runalltest(ctmaintest
, n
);
555 int code
= report(ctmaintest
);
559 if (argc
== 2 && strcmp(argv
[1], "-b") == 0) {
560 runallbench(ctmainbench
);