1 // CT - simple-minded unit testing for C
22 static int rjobfd
= -1, wjobfd
= -1;
23 static int64 bstart
, bdur
;
24 static int btiming
; // bool
26 static const int64 Second
= 1000 * 1000 * 1000;
27 static const int64 BenchTime
= Second
;
28 static const int MaxN
= 1000 * 1000 * 1000;
33 # include <mach/mach_time.h>
38 return (int64
)mach_absolute_time();
47 clock_gettime(CLOCK_MONOTONIC
, &t
);
48 return (int64
)(t
.tv_sec
)*Second
+ t
.tv_nsec
;
54 ctlogpn(char *p
, int n
, char *fmt
, ...)
58 printf("%s:%d: ", p
, n
);
105 bdur
+= nstime() - bstart
;
119 die(int code
, int err
, char *msg
)
128 fputs(strerror(err
), stderr
);
139 die(1, errno
, "tmpfile");
148 return WIFSIGNALED(s
) && (WTERMSIG(s
) == SIGABRT
);
158 pid
= wait3(&stat
, 0, 0);
160 die(3, errno
, "wait");
164 for (t
=ts
; t
->f
; t
++) {
169 } else if (failed(t
->status
)) {
184 strcpy(t
->dir
, TmpDirPat
);
188 die(1, errno
, "fork");
189 } else if (!t
->pid
) {
191 if (dup2(t
->fd
, 1) == -1) {
192 die(3, errno
, "dup2");
194 if (close(t
->fd
) == -1) {
195 die(3, errno
, "fclose");
197 if (dup2(1, 2) == -1) {
198 die(3, errno
, "dup2");
204 setpgid(t
->pid
, t
->pid
);
209 runalltest(Test
*ts
, int limit
)
213 for (t
=ts
; t
->f
; t
++) {
221 for (; nrun
; nrun
--) {
228 copyfd(FILE *out
, int in
)
231 char buf
[1024]; // arbitrary size
233 while ((n
= read(in
, buf
, sizeof(buf
))) != 0) {
234 if (fwrite(buf
, 1, n
, out
) != (size_t)n
) {
235 die(3, errno
, "fwrite");
241 // Removes path and all of its children.
242 // Writes errors to stderr and keeps going.
243 // If path doesn't exist, rmtree returns silently.
247 int r
= unlink(path
);
248 if (r
== 0 || errno
== ENOENT
) {
251 int unlinkerr
= errno
;
253 DIR *d
= opendir(path
);
255 if (errno
== ENOTDIR
) {
256 fprintf(stderr
, "ct: unlink: %s\n", strerror(unlinkerr
));
258 perror("ct: opendir");
260 fprintf(stderr
, "ct: path %s\n", path
);
264 while ((ent
= readdir(d
))) {
265 if (strcmp(ent
->d_name
, ".") == 0 || strcmp(ent
->d_name
, "..") == 0) {
268 int n
= strlen(path
) + 1 + strlen(ent
->d_name
);
270 sprintf(s
, "%s/%s", path
, ent
->d_name
);
277 fprintf(stderr
, "ct: path %s\n", path
);
283 runbenchn(Benchmark
*b
, int n
)
287 strcpy(b
->dir
, TmpDirPat
);
291 die(1, errno
, "fork");
294 if (dup2(outfd
, 1) == -1) {
295 die(3, errno
, "dup2");
297 if (close(outfd
) == -1) {
298 die(3, errno
, "fclose");
300 if (dup2(1, 2) == -1) {
301 die(3, errno
, "dup2");
307 write(durfd
, &bdur
, sizeof bdur
);
308 write(durfd
, &bbytes
, sizeof bbytes
);
313 pid
= waitpid(pid
, &b
->status
, 0);
315 die(3, errno
, "wait");
319 if (b
->status
!= 0) {
321 lseek(outfd
, 0, SEEK_SET
);
322 copyfd(stdout
, outfd
);
326 lseek(durfd
, 0, SEEK_SET
);
327 int r
= read(durfd
, &b
->dur
, sizeof b
->dur
);
328 if (r
!= sizeof b
->dur
) {
332 r
= read(durfd
, &b
->bytes
, sizeof b
->bytes
);
333 if (r
!= sizeof b
->bytes
) {
340 // rounddown10 rounds a number down to the nearest power of 10.
345 // tens = floor(log_10(n))
352 for (i
= 0; i
< tens
; i
++) {
359 // roundup rounds n up to a number of the form [1eX, 2eX, 5eX].
363 int base
= rounddown10(n
);
395 runbench(Benchmark
*b
)
397 printf("%s\t", b
->name
);
401 while (b
->status
== 0 && b
->dur
< BenchTime
&& n
< MaxN
) {
403 // Predict iterations/sec.
404 int nsop
= b
->dur
/ n
;
408 n
= BenchTime
/ nsop
;
410 // Run more iterations than we think we'll need for a second (1.5x).
411 // Don't grow too fast in case we had timing errors previously.
412 // Be sure to run at least one more than last time.
413 n
= max(min(n
+n
/2, 100*last
), last
+1);
414 // Round up to something easy to read.
418 if (b
->status
== 0) {
419 printf("%8d\t%10lld ns/op", n
, b
->dur
/n
);
423 int64 sec
= b
->dur
/ 1000L / 1000L / 1000L;
424 int64 nsec
= b
->dur
% 1000000000L;
425 double dur
= (double)sec
+ (double)nsec
*.0000000001;
426 mbs
= ((double)b
->bytes
* (double)n
/ 1000000) / dur
;
428 printf("\t%7.2f MB/s", mbs
);
432 if (failed(b
->status
)) {
436 if (WIFEXITED(b
->status
)) {
437 printf(" (exit status %d)", WEXITSTATUS(b
->status
));
439 if (WIFSIGNALED(b
->status
)) {
440 printf(" (signal %d)", WTERMSIG(b
->status
));
449 runallbench(Benchmark
*b
)
460 int nfail
= 0, nerr
= 0;
469 printf("\n%s: ", t
->name
);
470 if (failed(t
->status
)) {
476 if (WIFEXITED(t
->status
)) {
477 printf(" (exit status %d)", WEXITSTATUS(t
->status
));
479 if (WIFSIGNALED(t
->status
)) {
480 printf(" (signal %d)", WTERMSIG(t
->status
));
485 lseek(t
->fd
, 0, SEEK_SET
);
486 copyfd(stdout
, t
->fd
);
490 printf("\n%d failures; %d errors.\n", nfail
, nerr
);
494 return nfail
|| nerr
;
503 if ((s
= strstr(getenv("MAKEFLAGS"), " --jobserver-fds="))) {
504 rjobfd
= (int)strtol(s
+17, &s
, 10); // skip " --jobserver-fds="
505 wjobfd
= (int)strtol(s
+1, NULL
, 10); // skip comma
508 fcntl(rjobfd
, F_SETFL
, fcntl(rjobfd
, F_GETFL
)|O_NONBLOCK
);
509 while (read(rjobfd
, &c
, 1) > 0) {
522 fcntl(wjobfd
, F_SETFL
, fcntl(wjobfd
, F_GETFL
)|O_NONBLOCK
);
524 write(wjobfd
, &c
, 1); // ignore error; nothing we can do anyway
531 main(int argc
, char **argv
)
533 int n
= readtokens();
534 runalltest(ctmaintest
, n
);
536 int code
= report(ctmaintest
);
540 if (argc
== 2 && strcmp(argv
[1], "-b") == 0) {
541 runallbench(ctmainbench
);