-
-
Notifications
You must be signed in to change notification settings - Fork 154
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[benchmarks/time] Implement time-helper.c
To fix the "bash invokes dash" problem, and the "/usr/bin/time has bad precision" problem. (Although maybe that is fixed with a newer version?)
- Loading branch information
Andy Chu
committed
Nov 18, 2020
1 parent
1a07fed
commit 366097e
Showing
4 changed files
with
206 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
#define _GNU_SOURCE // for timersub() | ||
#include <assert.h> | ||
#include <getopt.h> | ||
#include <errno.h> | ||
#include <stdbool.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> // exit() | ||
#include <sys/resource.h> // getrusage() | ||
#include <sys/time.h> | ||
#include <sys/wait.h> | ||
#include <unistd.h> | ||
|
||
void die_errno(const char *message) { | ||
perror(message); | ||
exit(1); | ||
} | ||
|
||
void die(const char *message) { | ||
fprintf(stderr, "time-helper: %s\n", message); | ||
exit(1); | ||
} | ||
|
||
typedef struct Spec_t { | ||
char* out_path; | ||
bool append; | ||
|
||
char delimiter; // delimiter should be tab or , | ||
bool verbose; // whether to show verbose logging | ||
|
||
bool x; // %x status | ||
bool e; // %e elapsed | ||
bool U; // %U user time | ||
bool S; // %S system time | ||
bool M; // %M maxrss | ||
int argc; | ||
char **argv; | ||
} Spec; | ||
|
||
|
||
// Write CSV/TSV cells of different types | ||
void int_cell(FILE* f, char delimiter, int val) { | ||
if (delimiter != 0) { // NUL is invalid delimiter | ||
fprintf(f, "%c%d", delimiter, val); | ||
} else { | ||
fprintf(f, "%d", val); | ||
} | ||
} | ||
|
||
void time_cell(FILE* f, char delimiter, struct timeval *val) { | ||
fprintf(f, "%c%ld.%06ld", delimiter, val->tv_sec, val->tv_usec); | ||
} | ||
|
||
int time_helper(Spec *spec, FILE* f) { | ||
char *prog = spec->argv[0]; | ||
|
||
struct timeval start; | ||
struct timeval end; | ||
|
||
int status = 0; | ||
switch (fork()) { | ||
case -1: | ||
die_errno("fork"); | ||
break; | ||
|
||
case 0: // child exec | ||
if (execvp(prog, spec->argv) < 0) { | ||
fprintf(stderr, "time-helper: error executing '%s'\n", prog); | ||
die_errno("execvp"); | ||
} | ||
assert(0); // execvp() never returns | ||
|
||
default: // parent measures elapsed time of child | ||
if (gettimeofday(&start, NULL) < 0) { | ||
die_errno("gettimeofday"); | ||
} | ||
wait(&status); | ||
if (gettimeofday(&end, NULL) < 0) { | ||
die_errno("gettimeofday"); | ||
} | ||
break; | ||
} | ||
//fprintf(stderr, "done waiting\n"); | ||
|
||
struct timeval elapsed; | ||
timersub(&end, &start, &elapsed); | ||
|
||
struct rusage usage; | ||
getrusage(RUSAGE_CHILDREN, &usage); | ||
|
||
struct timeval *user = &usage.ru_utime; | ||
struct timeval *sys = &usage.ru_stime; | ||
|
||
char d = spec->delimiter; | ||
// NO delimiter at first! | ||
if (spec->x) { int_cell(f, 0, WEXITSTATUS(status)); } | ||
if (spec->e) { time_cell(f, d, &elapsed); } | ||
if (spec->U) { time_cell(f, d, &usage.ru_utime); } | ||
if (spec->S) { time_cell(f, d, &usage.ru_stime); } | ||
if (spec->M) { int_cell(f, d, usage.ru_maxrss); } | ||
|
||
return status; | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
Spec spec = {0}; | ||
|
||
spec.out_path = "/dev/null"; // default value | ||
|
||
// http://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html | ||
// + means to be strict about flag parsing. | ||
char c; | ||
while ((c = getopt(argc, argv, "+o:ad:vxeUSM")) != -1) { | ||
switch (c) { | ||
case 'o': | ||
spec.out_path = optarg; | ||
break; | ||
case 'a': | ||
spec.append = true; | ||
break; | ||
|
||
case 'd': | ||
spec.delimiter = optarg[0]; | ||
break; | ||
case 'v': | ||
spec.verbose = true; | ||
break; | ||
|
||
case 'x': | ||
spec.x = true; | ||
break; | ||
case 'e': | ||
spec.e = true; | ||
break; | ||
case 'U': | ||
spec.U = true; | ||
break; | ||
case 'S': | ||
spec.S = true; | ||
break; | ||
case 'M': | ||
spec.M = true; | ||
break; | ||
|
||
case '?': // getopt library will print error | ||
return 2; | ||
|
||
default: | ||
abort(); // should never happen | ||
} | ||
} | ||
|
||
int a = optind; // index into argv | ||
if (a == argc) { | ||
die("expected a command to run"); | ||
} | ||
|
||
spec.argv = argv + a; | ||
spec.argc = argc - a; | ||
|
||
char* mode = spec.append ? "a" : "w"; | ||
FILE* f = fopen(spec.out_path, mode); | ||
int status = time_helper(&spec, f); | ||
fclose(f); | ||
|
||
return status; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters