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.

568 lines
16 KiB

  1. #include <argp.h>
  2. #include <assert.h>
  3. #include <inttypes.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/time.h>
  8. #ifdef HAVE_PROFILER
  9. #include <gperftools/profiler.h>
  10. #endif
  11. #include <getrss.h>
  12. #include <sylvan.h>
  13. #include <sylvan_table.h>
  14. /* Configuration */
  15. static int report_levels = 0; // report states at start of every level
  16. static int report_table = 0; // report table size at end of every level
  17. static int strategy = 1; // set to 1 = use PAR strategy; set to 0 = use BFS strategy
  18. static int check_deadlocks = 0; // set to 1 to check for deadlocks
  19. static int print_transition_matrix = 1; // print transition relation matrix
  20. static int workers = 0; // autodetect
  21. static char* model_filename = NULL; // filename of model
  22. static char* out_filename = NULL; // filename of output BDD
  23. #ifdef HAVE_PROFILER
  24. static char* profile_filename = NULL; // filename for profiling
  25. #endif
  26. /* argp configuration */
  27. static struct argp_option options[] =
  28. {
  29. {"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0},
  30. {"strategy", 's', "<bfs|par|sat>", 0, "Strategy for reachability (default=par)", 0},
  31. #ifdef HAVE_PROFILER
  32. {"profiler", 'p', "<filename>", 0, "Filename for profiling", 0},
  33. #endif
  34. {"deadlocks", 3, 0, 0, "Check for deadlocks", 1},
  35. {"count-states", 1, 0, 0, "Report #states at each level", 1},
  36. {"count-table", 2, 0, 0, "Report table usage at each level", 1},
  37. {0, 0, 0, 0, 0, 0}
  38. };
  39. static error_t
  40. parse_opt(int key, char *arg, struct argp_state *state)
  41. {
  42. switch (key) {
  43. case 'w':
  44. workers = atoi(arg);
  45. break;
  46. case 's':
  47. if (strcmp(arg, "bfs")==0) strategy = 0;
  48. else if (strcmp(arg, "par")==0) strategy = 1;
  49. else if (strcmp(arg, "sat")==0) strategy = 2;
  50. else argp_usage(state);
  51. break;
  52. case 3:
  53. check_deadlocks = 1;
  54. break;
  55. case 1:
  56. report_levels = 1;
  57. break;
  58. case 2:
  59. report_table = 1;
  60. break;
  61. #ifdef HAVE_PROFILER
  62. case 'p':
  63. profile_filename = arg;
  64. break;
  65. #endif
  66. case ARGP_KEY_ARG:
  67. if (state->arg_num == 0) model_filename = arg;
  68. if (state->arg_num == 1) out_filename = arg;
  69. if (state->arg_num >= 2) argp_usage(state);
  70. break;
  71. case ARGP_KEY_END:
  72. if (state->arg_num < 1) argp_usage(state);
  73. break;
  74. default:
  75. return ARGP_ERR_UNKNOWN;
  76. }
  77. return 0;
  78. }
  79. static struct argp argp = { options, parse_opt, "<model> [<output-bdd>]", 0, 0, 0, 0 };
  80. /* Globals */
  81. typedef struct set
  82. {
  83. MDD mdd;
  84. MDD proj;
  85. int size;
  86. } *set_t;
  87. typedef struct relation
  88. {
  89. MDD mdd;
  90. MDD meta;
  91. int size;
  92. } *rel_t;
  93. static size_t vector_size; // size of vector
  94. static int next_count; // number of partitions of the transition relation
  95. static rel_t *next; // each partition of the transition relation
  96. #define Abort(...) { fprintf(stderr, __VA_ARGS__); exit(-1); }
  97. /* Load a set from file */
  98. static set_t
  99. set_load(FILE* f)
  100. {
  101. lddmc_serialize_fromfile(f);
  102. size_t mdd;
  103. size_t proj;
  104. int size;
  105. if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  106. if (fread(&proj, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  107. if (fread(&size, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  108. LACE_ME;
  109. set_t set = (set_t)malloc(sizeof(struct set));
  110. set->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd));
  111. set->proj = lddmc_ref(lddmc_serialize_get_reversed(proj));
  112. set->size = size;
  113. return set;
  114. }
  115. /* Save a set to file */
  116. static void
  117. set_save(FILE* f, set_t set)
  118. {
  119. size_t mdd = lddmc_serialize_add(set->mdd);
  120. size_t proj = lddmc_serialize_add(set->proj);
  121. lddmc_serialize_tofile(f);
  122. fwrite(&mdd, sizeof(size_t), 1, f);
  123. fwrite(&proj, sizeof(size_t), 1, f);
  124. fwrite(&set->size, sizeof(int), 1, f);;
  125. }
  126. static void
  127. rel_save(FILE* f, rel_t rel)
  128. {
  129. size_t mdd = lddmc_serialize_add(rel->mdd);
  130. size_t meta = lddmc_serialize_add(rel->meta);
  131. lddmc_serialize_tofile(f);
  132. fwrite(&mdd, sizeof(size_t), 1, f);
  133. fwrite(&meta, sizeof(size_t), 1, f);
  134. }
  135. static set_t
  136. set_clone(set_t source)
  137. {
  138. set_t set = (set_t)malloc(sizeof(struct set));
  139. set->mdd = lddmc_ref(source->mdd);
  140. set->proj = lddmc_ref(source->proj);
  141. set->size = source->size;
  142. return set;
  143. }
  144. static int
  145. calculate_size(MDD meta)
  146. {
  147. int result = 0;
  148. uint32_t val = lddmc_getvalue(meta);
  149. while (val != (uint32_t)-1) {
  150. if (val != 0) result += 1;
  151. meta = lddmc_follow(meta, val);
  152. assert(meta != lddmc_true && meta != lddmc_false);
  153. val = lddmc_getvalue(meta);
  154. }
  155. return result;
  156. }
  157. /* Load a relation from file */
  158. static rel_t
  159. rel_load(FILE* f)
  160. {
  161. lddmc_serialize_fromfile(f);
  162. size_t mdd;
  163. size_t meta;
  164. if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  165. if (fread(&meta, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  166. LACE_ME;
  167. rel_t rel = (rel_t)malloc(sizeof(struct relation));
  168. rel->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd));
  169. rel->meta = lddmc_ref(lddmc_serialize_get_reversed(meta));
  170. rel->size = calculate_size(rel->meta);
  171. return rel;
  172. }
  173. static void
  174. print_example(MDD example)
  175. {
  176. if (example != lddmc_false) {
  177. LACE_ME;
  178. uint32_t vec[vector_size];
  179. lddmc_sat_one(example, vec, vector_size);
  180. size_t i;
  181. printf("[");
  182. for (i=0; i<vector_size; i++) {
  183. if (i>0) printf(",");
  184. printf("%" PRIu32, vec[i]);
  185. }
  186. printf("]");
  187. }
  188. }
  189. static void
  190. print_matrix(size_t size, MDD meta)
  191. {
  192. if (size == 0) return;
  193. uint32_t val = lddmc_getvalue(meta);
  194. if (val == 1) {
  195. printf("+");
  196. print_matrix(size-1, lddmc_follow(lddmc_follow(meta, 1), 2));
  197. } else {
  198. if (val == (uint32_t)-1) printf("-");
  199. else if (val == 0) printf("-");
  200. else if (val == 3) printf("r");
  201. else if (val == 4) printf("w");
  202. print_matrix(size-1, lddmc_follow(meta, val));
  203. }
  204. }
  205. static char*
  206. to_h(double size, char *buf)
  207. {
  208. const char* units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
  209. int i = 0;
  210. for (;size>1024;size/=1024) i++;
  211. sprintf(buf, "%.*f %s", i, size, units[i]);
  212. return buf;
  213. }
  214. static int
  215. get_first(MDD meta)
  216. {
  217. uint32_t val = lddmc_getvalue(meta);
  218. if (val != 0) return 0;
  219. return 1+get_first(lddmc_follow(meta, val));
  220. }
  221. /* Straight-forward implementation of parallel reduction */
  222. TASK_5(MDD, go_par, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks)
  223. {
  224. if (len == 1) {
  225. // Calculate NEW successors (not in visited)
  226. MDD succ = lddmc_ref(lddmc_relprod(cur, next[from]->mdd, next[from]->meta));
  227. if (deadlocks) {
  228. // check which MDDs in deadlocks do not have a successor in this relation
  229. MDD anc = lddmc_ref(lddmc_relprev(succ, next[from]->mdd, next[from]->meta, cur));
  230. *deadlocks = lddmc_ref(lddmc_minus(*deadlocks, anc));
  231. lddmc_deref(anc);
  232. }
  233. MDD result = lddmc_ref(lddmc_minus(succ, visited));
  234. lddmc_deref(succ);
  235. return result;
  236. } else {
  237. MDD deadlocks_left;
  238. MDD deadlocks_right;
  239. if (deadlocks) {
  240. deadlocks_left = *deadlocks;
  241. deadlocks_right = *deadlocks;
  242. }
  243. // Recursively calculate left+right
  244. SPAWN(go_par, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left: NULL);
  245. MDD right = CALL(go_par, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL);
  246. MDD left = SYNC(go_par);
  247. // Merge results of left+right
  248. MDD result = lddmc_ref(lddmc_union(left, right));
  249. lddmc_deref(left);
  250. lddmc_deref(right);
  251. if (deadlocks) {
  252. *deadlocks = lddmc_ref(lddmc_intersect(deadlocks_left, deadlocks_right));
  253. lddmc_deref(deadlocks_left);
  254. lddmc_deref(deadlocks_right);
  255. }
  256. return result;
  257. }
  258. }
  259. /* PAR strategy, parallel strategy (operations called in parallel *and* parallelized by Sylvan) */
  260. VOID_TASK_1(par, set_t, set)
  261. {
  262. MDD visited = set->mdd;
  263. MDD new = lddmc_ref(visited);
  264. size_t counter = 1;
  265. do {
  266. char buf[32];
  267. to_h(getCurrentRSS(), buf);
  268. printf("Memory usage: %s\n", buf);
  269. printf("Level %zu... ", counter++);
  270. if (report_levels) {
  271. printf("%zu states... ", (size_t)lddmc_satcount_cached(visited));
  272. }
  273. fflush(stdout);
  274. // calculate successors in parallel
  275. MDD cur = new;
  276. MDD deadlocks = cur;
  277. new = CALL(go_par, cur, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL);
  278. lddmc_deref(cur);
  279. if (check_deadlocks) {
  280. printf("found %zu deadlock states... ", (size_t)lddmc_satcount_cached(deadlocks));
  281. if (deadlocks != lddmc_false) {
  282. printf("example: ");
  283. print_example(deadlocks);
  284. printf("... ");
  285. check_deadlocks = 0;
  286. }
  287. }
  288. // visited = visited + new
  289. MDD old_visited = visited;
  290. visited = lddmc_ref(lddmc_union(visited, new));
  291. lddmc_deref(old_visited);
  292. if (report_table) {
  293. size_t filled, total;
  294. sylvan_table_usage(&filled, &total);
  295. printf("done, table: %0.1f%% full (%zu nodes).\n", 100.0*(double)filled/total, filled);
  296. } else {
  297. printf("done.\n");
  298. }
  299. } while (new != lddmc_false);
  300. lddmc_deref(new);
  301. set->mdd = visited;
  302. }
  303. /* Sequential version of merge-reduction */
  304. TASK_5(MDD, go_bfs, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks)
  305. {
  306. if (len == 1) {
  307. // Calculate NEW successors (not in visited)
  308. MDD succ = lddmc_ref(lddmc_relprod(cur, next[from]->mdd, next[from]->meta));
  309. if (deadlocks) {
  310. // check which MDDs in deadlocks do not have a successor in this relation
  311. MDD anc = lddmc_ref(lddmc_relprev(succ, next[from]->mdd, next[from]->meta, cur));
  312. *deadlocks = lddmc_ref(lddmc_minus(*deadlocks, anc));
  313. lddmc_deref(anc);
  314. }
  315. MDD result = lddmc_ref(lddmc_minus(succ, visited));
  316. lddmc_deref(succ);
  317. return result;
  318. } else {
  319. MDD deadlocks_left;
  320. MDD deadlocks_right;
  321. if (deadlocks) {
  322. deadlocks_left = *deadlocks;
  323. deadlocks_right = *deadlocks;
  324. }
  325. // Recursively calculate left+right
  326. MDD left = CALL(go_bfs, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left : NULL);
  327. MDD right = CALL(go_bfs, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL);
  328. // Merge results of left+right
  329. MDD result = lddmc_ref(lddmc_union(left, right));
  330. lddmc_deref(left);
  331. lddmc_deref(right);
  332. if (deadlocks) {
  333. *deadlocks = lddmc_ref(lddmc_intersect(deadlocks_left, deadlocks_right));
  334. lddmc_deref(deadlocks_left);
  335. lddmc_deref(deadlocks_right);
  336. }
  337. return result;
  338. }
  339. }
  340. /* BFS strategy, sequential strategy (but operations are parallelized by Sylvan) */
  341. VOID_TASK_1(bfs, set_t, set)
  342. {
  343. MDD visited = set->mdd;
  344. MDD new = lddmc_ref(visited);
  345. size_t counter = 1;
  346. do {
  347. char buf[32];
  348. to_h(getCurrentRSS(), buf);
  349. printf("Memory usage: %s\n", buf);
  350. printf("Level %zu... ", counter++);
  351. if (report_levels) {
  352. printf("%zu states... ", (size_t)lddmc_satcount_cached(visited));
  353. }
  354. fflush(stdout);
  355. MDD cur = new;
  356. MDD deadlocks = cur;
  357. new = CALL(go_bfs, cur, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL);
  358. lddmc_deref(cur);
  359. if (check_deadlocks) {
  360. printf("found %zu deadlock states... ", (size_t)lddmc_satcount_cached(deadlocks));
  361. if (deadlocks != lddmc_false) {
  362. printf("example: ");
  363. print_example(deadlocks);
  364. printf("... ");
  365. check_deadlocks = 0;
  366. }
  367. }
  368. // visited = visited + new
  369. MDD old_visited = visited;
  370. visited = lddmc_ref(lddmc_union(visited, new));
  371. lddmc_deref(old_visited);
  372. if (report_table) {
  373. size_t filled, total;
  374. sylvan_table_usage(&filled, &total);
  375. printf("done, table: %0.1f%% full (%zu nodes).\n", 100.0*(double)filled/total, filled);
  376. } else {
  377. printf("done.\n");
  378. }
  379. } while (new != lddmc_false);
  380. lddmc_deref(new);
  381. set->mdd = visited;
  382. }
  383. /* Obtain current wallclock time */
  384. static double
  385. wctime()
  386. {
  387. struct timeval tv;
  388. gettimeofday(&tv, NULL);
  389. return (tv.tv_sec + 1E-6 * tv.tv_usec);
  390. }
  391. int
  392. main(int argc, char **argv)
  393. {
  394. argp_parse(&argp, argc, argv, 0, 0, 0);
  395. FILE *f = fopen(model_filename, "r");
  396. if (f == NULL) {
  397. fprintf(stderr, "Cannot open file '%s'!\n", model_filename);
  398. return -1;
  399. }
  400. // Init Lace
  401. lace_init(workers, 1000000); // auto-detect number of workers, use a 1,000,000 size task queue
  402. lace_startup(0, NULL, NULL); // auto-detect program stack, do not use a callback for startup
  403. // Init Sylvan LDDmc
  404. // Nodes table size: 24 bytes * 2**N_nodes
  405. // Cache table size: 36 bytes * 2**N_cache
  406. // With: N_nodes=25, N_cache=24: 1.3 GB memory
  407. sylvan_set_sizes(1LL<<21, 1LL<<27, 1LL<<20, 1LL<<26);
  408. sylvan_init_package();
  409. sylvan_init_ldd();
  410. sylvan_init_mtbdd();
  411. // Read and report domain info (integers per vector and bits per integer)
  412. if (fread(&vector_size, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  413. printf("Vector size: %zu\n", vector_size);
  414. // Read initial state
  415. printf("Loading initial state... ");
  416. fflush(stdout);
  417. set_t initial = set_load(f);
  418. set_t states = set_clone(initial);
  419. printf("done.\n");
  420. // Read transitions
  421. if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  422. next = (rel_t*)malloc(sizeof(rel_t) * next_count);
  423. printf("Loading transition relations... ");
  424. fflush(stdout);
  425. int i;
  426. for (i=0; i<next_count; i++) {
  427. next[i] = rel_load(f);
  428. printf("%d, ", i);
  429. fflush(stdout);
  430. }
  431. fclose(f);
  432. printf("done.\n");
  433. // Report statistics
  434. printf("Read file '%s'\n", argv[1]);
  435. printf("%zu integers per state, %d transition groups\n", vector_size, next_count);
  436. printf("MDD nodes:\n");
  437. printf("Initial states: %zu MDD nodes\n", lddmc_nodecount(states->mdd));
  438. for (i=0; i<next_count; i++) {
  439. printf("Transition %d: %zu MDD nodes\n", i, lddmc_nodecount(next[i]->mdd));
  440. }
  441. if (print_transition_matrix) {
  442. for (i=0; i<next_count; i++) {
  443. print_matrix(vector_size, next[i]->meta);
  444. printf(" (%d)\n", get_first(next[i]->meta));
  445. }
  446. }
  447. LACE_ME;
  448. #ifdef HAVE_PROFILER
  449. if (profile_filename != NULL) ProfilerStart(profile_filename);
  450. #endif
  451. if (strategy == 1) {
  452. double t1 = wctime();
  453. CALL(par, states);
  454. double t2 = wctime();
  455. printf("PAR Time: %f\n", t2-t1);
  456. } else {
  457. double t1 = wctime();
  458. CALL(bfs, states);
  459. double t2 = wctime();
  460. printf("BFS Time: %f\n", t2-t1);
  461. }
  462. #ifdef HAVE_PROFILER
  463. if (profile_filename != NULL) ProfilerStop();
  464. #endif
  465. // Now we just have states
  466. printf("Final states: %zu states\n", (size_t)lddmc_satcount_cached(states->mdd));
  467. printf("Final states: %zu MDD nodes\n", lddmc_nodecount(states->mdd));
  468. if (out_filename != NULL) {
  469. printf("Writing to %s.\n", out_filename);
  470. // Create LDD file
  471. FILE *f = fopen(out_filename, "w");
  472. lddmc_serialize_reset();
  473. // Write domain...
  474. fwrite(&vector_size, sizeof(size_t), 1, f);
  475. // Write initial state...
  476. set_save(f, initial);
  477. // Write number of transitions
  478. fwrite(&next_count, sizeof(int), 1, f);
  479. // Write transitions
  480. for (int i=0; i<next_count; i++) {
  481. rel_save(f, next[i]);
  482. }
  483. // Write reachable states
  484. int has_reachable = 1;
  485. fwrite(&has_reachable, sizeof(int), 1, f);
  486. set_save(f, states);
  487. // Write action labels
  488. fclose(f);
  489. }
  490. sylvan_stats_report(stdout);
  491. return 0;
  492. }