这是一个本人学习 csapp 的 learning 库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

583 regels
15 KiB

2 jaren geleden
  1. /*
  2. * CS:APP Data Lab
  3. *
  4. * btest.c - A test harness that checks a student's solution in bits.c
  5. * for correctness.
  6. *
  7. * Copyright (c) 2001-2011, R. Bryant and D. O'Hallaron, All rights
  8. * reserved. May not be used, modified, or copied without permission.
  9. *
  10. * This is an improved version of btest that tests large windows
  11. * around zero and tmin and tmax for integer puzzles, and zero, norm,
  12. * and denorm boundaries for floating point puzzles.
  13. *
  14. * Note: not 64-bit safe. Always compile with gcc -m32 option.
  15. */
  16. #include <stdio.h>
  17. #include <unistd.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <limits.h>
  21. #include <signal.h>
  22. #include <setjmp.h>
  23. #include <math.h>
  24. #include "btest.h"
  25. /* Not declared in some stdlib.h files, so define here */
  26. float strtof(const char *nptr, char **endptr);
  27. /*************************
  28. * Configuration Constants
  29. *************************/
  30. /* Handle infinite loops by setting upper limit on execution time, in
  31. seconds */
  32. #define TIMEOUT_LIMIT 10
  33. /* For functions with a single argument, generate TEST_RANGE values
  34. above and below the min and max test values, and above and below
  35. zero. Functions with two or three args will use square and cube
  36. roots of this value, respectively, to avoid combinatorial
  37. explosion */
  38. #define TEST_RANGE 500000
  39. /* This defines the maximum size of any test value array. The
  40. gen_vals() routine creates k test values for each value of
  41. TEST_RANGE, thus MAX_TEST_VALS must be at least k*TEST_RANGE */
  42. #define MAX_TEST_VALS 13*TEST_RANGE
  43. /**********************************
  44. * Globals defined in other modules
  45. **********************************/
  46. /* This characterizes the set of puzzles to test.
  47. Defined in decl.c and generated from templates in ./puzzles dir */
  48. extern test_rec test_set[];
  49. /************************************************
  50. * Write-once globals defined by command line args
  51. ************************************************/
  52. /* Emit results in a format for autograding, without showing
  53. and counter-examples */
  54. static int grade = 0;
  55. /* Time out after this number of seconds */
  56. static int timeout_limit = TIMEOUT_LIMIT; /* -T */
  57. /* If non-NULL, test only one function (-f) */
  58. static char* test_fname = NULL;
  59. /* Special case when only use fixed argument(s) (-1, -2, or -3) */
  60. static int has_arg[3] = {0,0,0};
  61. static unsigned argval[3] = {0,0,0};
  62. /* Use fixed weight for rating, and if so, what should it be? (-r) */
  63. static int global_rating = 0;
  64. /******************
  65. * Helper functions
  66. ******************/
  67. /*
  68. * Signal - installs a signal handler
  69. */
  70. typedef void handler_t(int);
  71. handler_t *Signal(int signum, handler_t *handler)
  72. {
  73. struct sigaction action, old_action;
  74. action.sa_handler = handler;
  75. sigemptyset(&action.sa_mask); /* block sigs of type being handled */
  76. action.sa_flags = SA_RESTART; /* restart syscalls if possible */
  77. if (sigaction(signum, &action, &old_action) < 0)
  78. perror("Signal error");
  79. return (old_action.sa_handler);
  80. }
  81. /*
  82. * timeout_handler - SIGALARM hander
  83. */
  84. sigjmp_buf envbuf;
  85. void timeout_handler(int sig) {
  86. siglongjmp(envbuf, 1);
  87. }
  88. /*
  89. * random_val - Return random integer value between min and max
  90. */
  91. static int random_val(int min, int max)
  92. {
  93. double weight = rand()/(double) RAND_MAX;
  94. int result = min * (1-weight) + max * weight;
  95. return result;
  96. }
  97. /*
  98. * gen_vals - Generate the integer values we'll use to test a function
  99. */
  100. static int gen_vals(int test_vals[], int min, int max, int test_range, int arg)
  101. {
  102. int i;
  103. int test_count = 0;
  104. /* Special case: If the user has specified a specific function
  105. argument using the -1, -2, or -3 flags, then simply use this
  106. argument and return */
  107. if (has_arg[arg]) {
  108. test_vals[0] = argval[arg];
  109. return 1;
  110. }
  111. /*
  112. * Special case: Generate test vals for floating point functions
  113. * where the input argument is an unsigned bit-level
  114. * representation of a float. For this case we want to test the
  115. * regions around zero, the smallest normalized and largest
  116. * denormalized numbers, one, and the largest normalized number,
  117. * as well as inf and nan.
  118. */
  119. if ((min == 1 && max == 1)) {
  120. unsigned smallest_norm = 0x00800000;
  121. unsigned one = 0x3f800000;
  122. unsigned largest_norm = 0x7f000000;
  123. unsigned inf = 0x7f800000;
  124. unsigned nan = 0x7fc00000;
  125. unsigned sign = 0x80000000;
  126. /* Test range should be at most 1/2 the range of one exponent
  127. value */
  128. if (test_range > (1 << 23)) {
  129. test_range = 1 << 23;
  130. }
  131. /* Functions where the input argument is an unsigned bit-level
  132. representation of a float. The number of tests generated
  133. inside this loop body is the value k referenced in the
  134. comment for the global variable MAX_TEST_VALS. */
  135. for (i = 0; i < test_range; i++) {
  136. /* Denorms around zero */
  137. test_vals[test_count++] = i;
  138. test_vals[test_count++] = sign | i;
  139. /* Region around norm to denorm transition */
  140. test_vals[test_count++] = smallest_norm + i;
  141. test_vals[test_count++] = smallest_norm - i;
  142. test_vals[test_count++] = sign | (smallest_norm + i);
  143. test_vals[test_count++] = sign | (smallest_norm - i);
  144. /* Region around one */
  145. test_vals[test_count++] = one + i;
  146. test_vals[test_count++] = one - i;
  147. test_vals[test_count++] = sign | (one + i);
  148. test_vals[test_count++] = sign | (one - i);
  149. /* Region below largest norm */
  150. test_vals[test_count++] = largest_norm - i;
  151. test_vals[test_count++] = sign | (largest_norm - i);
  152. }
  153. /* special vals */
  154. test_vals[test_count++] = inf; /* inf */
  155. test_vals[test_count++] = sign | inf; /* -inf */
  156. test_vals[test_count++] = nan; /* nan */
  157. test_vals[test_count++] = sign | nan; /* -nan */
  158. return test_count;
  159. }
  160. /*
  161. * Normal case: Generate test vals for integer functions
  162. */
  163. /* If the range is small enough, then do exhaustively */
  164. if (max - MAX_TEST_VALS <= min) {
  165. for (i = min; i <= max; i++)
  166. test_vals[test_count++] = i;
  167. return test_count;
  168. }
  169. /* Otherwise, need to sample. Do so near the boundaries, around
  170. zero, and for some random cases. */
  171. for (i = 0; i < test_range; i++) {
  172. /* Test around the boundaries */
  173. test_vals[test_count++] = min + i;
  174. test_vals[test_count++] = max - i;
  175. /* If zero falls between min and max, then also test around zero */
  176. if (i >= min && i <= max)
  177. test_vals[test_count++] = i;
  178. if (-i >= min && -i <= max)
  179. test_vals[test_count++] = -i;
  180. /* Random case between min and max */
  181. test_vals[test_count++] = random_val(min, max);
  182. }
  183. return test_count;
  184. }
  185. /*
  186. * test_0_arg - Test a function with zero arguments
  187. */
  188. static int test_0_arg(funct_t f, funct_t ft, char *name)
  189. {
  190. int r = f();
  191. int rt = ft();
  192. int error = (r != rt);
  193. if (error && !grade)
  194. printf("ERROR: Test %s() failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, r, r, rt, rt);
  195. return error;
  196. }
  197. /*
  198. * test_1_arg - Test a function with one argument
  199. */
  200. static int test_1_arg(funct_t f, funct_t ft, int arg1, char *name)
  201. {
  202. funct1_t f1 = (funct1_t) f;
  203. funct1_t f1t = (funct1_t) ft;
  204. int r, rt, error;
  205. r = f1(arg1);
  206. rt = f1t(arg1);
  207. error = (r != rt);
  208. if (error && !grade)
  209. printf("ERROR: Test %s(%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, r, r, rt, rt);
  210. return error;
  211. }
  212. /*
  213. * test_2_arg - Test a function with two arguments
  214. */
  215. static int test_2_arg(funct_t f, funct_t ft, int arg1, int arg2, char *name)
  216. {
  217. funct2_t f2 = (funct2_t) f;
  218. funct2_t f2t = (funct2_t) ft;
  219. int r = f2(arg1, arg2);
  220. int rt = f2t(arg1, arg2);
  221. int error = (r != rt);
  222. if (error && !grade)
  223. printf("ERROR: Test %s(%d[0x%x],%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, arg2, arg2, r, r, rt, rt);
  224. return error;
  225. }
  226. /*
  227. * test_3_arg - Test a function with three arguments
  228. */
  229. static int test_3_arg(funct_t f, funct_t ft,
  230. int arg1, int arg2, int arg3, char *name)
  231. {
  232. funct3_t f3 = (funct3_t) f;
  233. funct3_t f3t = (funct3_t) ft;
  234. int r = f3(arg1, arg2, arg3);
  235. int rt = f3t(arg1, arg2, arg3);
  236. int error = (r != rt);
  237. if (error && !grade)
  238. printf("ERROR: Test %s(%d[0x%x],%d[0x%x],%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, arg2, arg2, arg3, arg3, r, r, rt, rt);
  239. return error;
  240. }
  241. /*
  242. * test_function - Test a function. Return number of errors
  243. */
  244. static int test_function(test_ptr t) {
  245. int test_counts[3]; /* number of test values for each arg */
  246. int args = t->args; /* number of function arguments */
  247. int arg_test_range[3]; /* test range for each argument */
  248. int i, a1, a2, a3;
  249. int errors = 0;
  250. /* These are the test values for each arg. Declared with the
  251. static attribute so that the array will be allocated in bss
  252. rather than the stack */
  253. static int arg_test_vals[3][MAX_TEST_VALS];
  254. /* Sanity check on the number of args */
  255. if (args < 0 || args > 3) {
  256. printf("Configuration error: invalid number of args (%d) for function %s\n", args, t->name);
  257. exit(1);
  258. }
  259. /* Assign range of argument test vals so as to conserve the total
  260. number of tests, independent of the number of arguments */
  261. if (args == 1) {
  262. arg_test_range[0] = TEST_RANGE;
  263. }
  264. else if (args == 2) {
  265. arg_test_range[0] = pow((double)TEST_RANGE, 0.5); /* sqrt */
  266. arg_test_range[1] = arg_test_range[0];
  267. }
  268. else {
  269. arg_test_range[0] = pow((double)TEST_RANGE, 0.333); /* cbrt */
  270. arg_test_range[1] = arg_test_range[0];
  271. arg_test_range[2] = arg_test_range[0];
  272. }
  273. /* Sanity check on the ranges */
  274. if (arg_test_range[0] < 1)
  275. arg_test_range[0] = 1;
  276. if (arg_test_range[1] < 1)
  277. arg_test_range[1] = 1;
  278. if (arg_test_range[2] < 1)
  279. arg_test_range[2] = 1;
  280. /* Create a test set for each argument */
  281. for (i = 0; i < args; i++) {
  282. test_counts[i] = gen_vals(arg_test_vals[i],
  283. t->arg_ranges[i][0], /* min */
  284. t->arg_ranges[i][1], /* max */
  285. arg_test_range[i],
  286. i);
  287. }
  288. /* Handle timeouts in the test code */
  289. if (timeout_limit > 0) {
  290. int rc;
  291. rc = sigsetjmp(envbuf, 1);
  292. if (rc) {
  293. /* control will reach here if there is a timeout */
  294. errors = 1;
  295. printf("ERROR: Test %s failed.\n Timed out after %d secs (probably infinite loop)\n", t->name, timeout_limit);
  296. return errors;
  297. }
  298. alarm(timeout_limit);
  299. }
  300. /* Test function has no arguments */
  301. if (args == 0) {
  302. errors += test_0_arg(t->solution_funct, t->test_funct, t->name);
  303. return errors;
  304. }
  305. /*
  306. * Test function has at least one argument
  307. */
  308. /* Iterate over the values for first argument */
  309. for (a1 = 0; a1 < test_counts[0]; a1++) {
  310. if (args == 1) {
  311. errors += test_1_arg(t->solution_funct,
  312. t->test_funct,
  313. arg_test_vals[0][a1],
  314. t->name);
  315. /* Stop testing if there is an error */
  316. if (errors)
  317. return errors;
  318. }
  319. else {
  320. /* if necessary, iterate over values for second argument */
  321. for (a2 = 0; a2 < test_counts[1]; a2++) {
  322. if (args == 2) {
  323. errors += test_2_arg(t->solution_funct,
  324. t->test_funct,
  325. arg_test_vals[0][a1],
  326. arg_test_vals[1][a2],
  327. t->name);
  328. /* Stop testing if there is an error */
  329. if (errors)
  330. return errors;
  331. }
  332. else {
  333. /* if necessary, iterate over vals for third arg */
  334. for (a3 = 0; a3 < test_counts[2]; a3++) {
  335. errors += test_3_arg(t->solution_funct,
  336. t->test_funct,
  337. arg_test_vals[0][a1],
  338. arg_test_vals[1][a2],
  339. arg_test_vals[2][a3],
  340. t->name);
  341. /* Stop testing if there is an error */
  342. if (errors)
  343. return errors;
  344. } /* a3 */
  345. }
  346. } /* a2 */
  347. }
  348. } /* a1 */
  349. return errors;
  350. }
  351. /*
  352. * run_tests - Run series of tests. Return number of errors
  353. */
  354. static int run_tests()
  355. {
  356. int i;
  357. int errors = 0;
  358. double points = 0.0;
  359. double max_points = 0.0;
  360. printf("Score\tRating\tErrors\tFunction\n");
  361. for (i = 0; test_set[i].solution_funct; i++) {
  362. int terrors;
  363. double tscore;
  364. double tpoints;
  365. if (!test_fname || strcmp(test_set[i].name,test_fname) == 0) {
  366. int rating = global_rating ? global_rating : test_set[i].rating;
  367. terrors = test_function(&test_set[i]);
  368. errors += terrors;
  369. tscore = terrors == 0 ? 1.0 : 0.0;
  370. tpoints = rating * tscore;
  371. points += tpoints;
  372. max_points += rating;
  373. if (grade || terrors < 1)
  374. printf(" %.0f\t%d\t%d\t%s\n",
  375. tpoints, rating, terrors, test_set[i].name);
  376. }
  377. }
  378. printf("Total points: %.0f/%.0f\n", points, max_points);
  379. return errors;
  380. }
  381. /*
  382. * get_num_val - Extract hex/decimal/or float value from string
  383. */
  384. static int get_num_val(char *sval, unsigned *valp) {
  385. char *endp;
  386. /* See if it's an integer or floating point */
  387. int ishex = 0;
  388. int isfloat = 0;
  389. int i;
  390. for (i = 0; sval[i]; i++) {
  391. switch (sval[i]) {
  392. case 'x':
  393. case 'X':
  394. ishex = 1;
  395. break;
  396. case 'e':
  397. case 'E':
  398. if (!ishex)
  399. isfloat = 1;
  400. break;
  401. case '.':
  402. isfloat = 1;
  403. break;
  404. default:
  405. break;
  406. }
  407. }
  408. if (isfloat) {
  409. float fval = strtof(sval, &endp);
  410. if (!*endp) {
  411. *valp = *(unsigned *) &fval;
  412. return 1;
  413. }
  414. return 0;
  415. } else {
  416. long long int llval = strtoll(sval, &endp, 0);
  417. long long int upperbits = llval >> 31;
  418. /* will give -1 for negative, 0 or 1 for positive */
  419. if (!*valp && (upperbits == 0 || upperbits == -1 || upperbits == 1)) {
  420. *valp = (unsigned) llval;
  421. return 1;
  422. }
  423. return 0;
  424. }
  425. }
  426. /*
  427. * usage - Display usage info
  428. */
  429. static void usage(char *cmd) {
  430. printf("Usage: %s [-hg] [-r <n>] [-f <name> [-1|-2|-3 <val>]*] [-T <time limit>]\n", cmd);
  431. printf(" -1 <val> Specify first function argument\n");
  432. printf(" -2 <val> Specify second function argument\n");
  433. printf(" -3 <val> Specify third function argument\n");
  434. printf(" -f <name> Test only the named function\n");
  435. printf(" -g Compact output for grading (with no error msgs)\n");
  436. printf(" -h Print this message\n");
  437. printf(" -r <n> Give uniform weight of n for all problems\n");
  438. printf(" -T <lim> Set timeout limit to lim\n");
  439. exit(1);
  440. }
  441. /**************
  442. * Main routine
  443. **************/
  444. int main(int argc, char *argv[])
  445. {
  446. char c;
  447. /* parse command line args */
  448. while ((c = getopt(argc, argv, "hgf:r:T:1:2:3:")) != -1)
  449. switch (c) {
  450. case 'h': /* help */
  451. usage(argv[0]);
  452. break;
  453. case 'g': /* grading option for autograder */
  454. grade = 1;
  455. break;
  456. case 'f': /* test only one function */
  457. test_fname = strdup(optarg);
  458. break;
  459. case 'r': /* set global rating for each problem */
  460. global_rating = atoi(optarg);
  461. if (global_rating < 0)
  462. usage(argv[0]);
  463. break;
  464. case '1': /* Get first argument */
  465. has_arg[0] = get_num_val(optarg, &argval[0]);
  466. if (!has_arg[0]) {
  467. printf("Bad argument '%s'\n", optarg);
  468. exit(0);
  469. }
  470. break;
  471. case '2': /* Get first argument */
  472. has_arg[1] = get_num_val(optarg, &argval[1]);
  473. if (!has_arg[1]) {
  474. printf("Bad argument '%s'\n", optarg);
  475. exit(0);
  476. }
  477. break;
  478. case '3': /* Get first argument */
  479. has_arg[2] = get_num_val(optarg, &argval[2]);
  480. if (!has_arg[2]) {
  481. printf("Bad argument '%s'\n", optarg);
  482. exit(0);
  483. }
  484. break;
  485. case 'T': /* Set timeout limit */
  486. timeout_limit = atoi(optarg);
  487. break;
  488. default:
  489. usage(argv[0]);
  490. }
  491. if (timeout_limit > 0) {
  492. Signal(SIGALRM, timeout_handler);
  493. }
  494. /* test each function */
  495. run_tests();
  496. return 0;
  497. }