00001
00071 #include "examples_common.h"
00072
00074 static const size_t malloc_sizes[] = { 4, 8, 512, 4096 };
00075
00077 static const uint32_t default_malloc_cnt = 100;
00079 static uint32_t default_memory_size;
00081 static const uint32_t default_run_cnt = 1;
00083 static const uint8_t default_verbosity = 0;
00084
00086 static void *base_addr = (void *) 0x1234ABCD;
00088 static size_t max_memory_size;
00089
00093 typedef enum malloc_pattern_e_ {
00095 MALLOC_PATTERN_E_LINEAR,
00100 MALLOC_PATTERN_E_UNIFORM,
00102 MALLOC_PATTERN_E_MAX,
00103 } malloc_pattern_e;
00104
00108 typedef struct malloc_example_opts_st_ {
00110 uint32_t malloc_cnt;
00112 size_t memory_size;
00114 uint32_t run_cnt;
00116 uint32_t seed;
00118 uint8_t verbosity;
00120 malloc_pattern_e pattern;
00121 } malloc_example_opts_st;
00122
00126 typedef struct memblock_obj_st_ {
00131 void *start_addr;
00133 size_t byte_cnt;
00135 bool is_allocated;
00136 } memblock_obj_st;
00137
00141 typedef struct malloc_example_input_st_ {
00143 const malloc_example_opts_st *opts;
00145 mkavl_tree_handle tree_h;
00146 } malloc_example_input_st;
00147
00151 typedef struct memblock_ctx_st_ {
00153 uint32_t nodes_walked;
00154 } memblock_ctx_st;
00155
00162 static void
00163 print_usage (bool do_exit, int32_t exit_val)
00164 {
00165 printf("\nExample of using mkavl for an memory allocation\n\n");
00166 printf("Usage:\n");
00167 printf("-s <seed>\n"
00168 " The starting seed for the RNG (default=seeded by time()).\n");
00169 printf("-b <memory size in bytes>\n"
00170 " The number of bytes in memory (default=%u).\n",
00171 default_memory_size);
00172 printf("-n <number of allocations>\n"
00173 " The max number of allocations at any one time (default=%u).\n",
00174 default_malloc_cnt);
00175 printf("-r <runs>\n"
00176 " The number of runs to do (default=%u).\n",
00177 default_run_cnt);
00178 printf("-l\n"
00179 " Free/re-allocate linearly (default=uniform distribution).\n");
00180 printf("-v <verbosity level>\n"
00181 " A higher number gives more output (default=%u).\n",
00182 default_verbosity);
00183 printf("-h\n"
00184 " Display this help message.\n");
00185 printf("\n");
00186
00187 if (do_exit) {
00188 exit(exit_val);
00189 }
00190 }
00191
00197 static void
00198 print_opts (malloc_example_opts_st *opts)
00199 {
00200 if (NULL == opts) {
00201 return;
00202 }
00203
00204 printf("malloc_example_opts: seed=%u, malloc_cnt=%u, run_cnt=%u,\n"
00205 " verbosity=%u, memory_size=%zu\n"
00206 " pattern=%u\n",
00207 opts->seed, opts->malloc_cnt, opts->run_cnt, opts->verbosity,
00208 opts->memory_size, opts->pattern);
00209 }
00210
00218 static void
00219 parse_command_line (int argc, char **argv, malloc_example_opts_st *opts)
00220 {
00221 int c;
00222 char *end_ptr;
00223 uint32_t val;
00224
00225 if (NULL == opts) {
00226 return;
00227 }
00228
00229 opts->malloc_cnt = default_malloc_cnt;
00230 opts->memory_size = default_memory_size;
00231 opts->run_cnt = default_run_cnt;
00232 opts->verbosity = default_verbosity;
00233 opts->pattern = MALLOC_PATTERN_E_UNIFORM;
00234 opts->seed = (uint32_t) time(NULL);
00235
00236 while ((c = getopt(argc, argv, "n:r:v:s:hb:l")) != -1) {
00237 switch (c) {
00238 case 'n':
00239 val = strtol(optarg, &end_ptr, 10);
00240 if ((end_ptr != optarg) && (0 == errno)) {
00241 opts->malloc_cnt = val;
00242 }
00243 break;
00244 case 'b':
00245 val = strtol(optarg, &end_ptr, 10);
00246 if ((end_ptr != optarg) && (0 == errno)) {
00247 opts->memory_size = val;
00248 }
00249 break;
00250 case 'r':
00251 val = strtol(optarg, &end_ptr, 10);
00252 if ((end_ptr != optarg) && (0 == errno)) {
00253 opts->run_cnt = val;
00254 }
00255 break;
00256 case 'v':
00257 val = strtol(optarg, &end_ptr, 10);
00258 if ((end_ptr != optarg) && (0 == errno)) {
00259 opts->verbosity = val;
00260 }
00261 break;
00262 case 's':
00263 val = strtol(optarg, &end_ptr, 10);
00264 if ((end_ptr != optarg) && (0 == errno)) {
00265 opts->seed = val;
00266 }
00267 break;
00268 case 'l':
00269 opts->pattern = MALLOC_PATTERN_E_LINEAR;
00270 break;
00271 case 'h':
00272 case '?':
00273 default:
00274 print_usage(true, EXIT_SUCCESS);
00275 break;
00276 }
00277 }
00278
00279 if (optind < argc) {
00280 print_usage(true, EXIT_SUCCESS);
00281 }
00282
00283 if (0 == opts->malloc_cnt) {
00284 printf("Error: malloc count(%u) must be non-zero\n",
00285 opts->malloc_cnt);
00286 print_usage(true, EXIT_SUCCESS);
00287 }
00288
00289 if (opts->memory_size > max_memory_size) {
00290 printf("Error: memory size(%zu) must be no greater than %zu\n",
00291 opts->memory_size, max_memory_size);
00292 print_usage(true, EXIT_SUCCESS);
00293 }
00294
00295 if (opts->verbosity >= 3) {
00296 print_opts(opts);
00297 }
00298 }
00299
00308 static int32_t
00309 memblock_cmp_by_addr (const void *item1, const void *item2, void *context)
00310 {
00311 const memblock_obj_st *m1 = item1;
00312 const memblock_obj_st *m2 = item2;
00313
00314 if ((NULL == m1) || (NULL == m2)) {
00315 abort();
00316 }
00317
00318 if (m1->start_addr < m2->start_addr) {
00319 return (-1);
00320 } else if (m1->start_addr > m2->start_addr) {
00321 return (1);
00322 }
00323
00324 return (0);
00325 }
00326
00335 static int32_t
00336 memblock_cmp_by_size (const void *item1, const void *item2, void *context)
00337 {
00338 const memblock_obj_st *m1 = item1;
00339 const memblock_obj_st *m2 = item2;
00340
00341 if ((NULL == m1) || (NULL == m2)) {
00342 abort();
00343 }
00344
00345
00346 if (!m1->is_allocated && m2->is_allocated) {
00347 return (-1);
00348 } else if (m1->is_allocated && !m2->is_allocated) {
00349 return (1);
00350 }
00351
00352
00353 if (m1->byte_cnt < m2->byte_cnt) {
00354 return (-1);
00355 } else if (m1->byte_cnt > m2->byte_cnt) {
00356 return (1);
00357 }
00358
00359
00360 if (m1->start_addr < m2->start_addr) {
00361 return (-1);
00362 } else if (m1->start_addr > m2->start_addr) {
00363 return (1);
00364 }
00365
00366 return (0);
00367 }
00368
00372 typedef enum malloc_example_key_e_ {
00374 MALLOC_EXAMPLE_KEY_E_ADDR,
00376 MALLOC_EXAMPLE_KEY_E_SIZE,
00378 MALLOC_EXAMPLE_KEY_E_MAX,
00379 } malloc_example_key_e;
00380
00382 static mkavl_compare_fn cmp_fn_array[] = {
00383 memblock_cmp_by_addr,
00384 memblock_cmp_by_size
00385 };
00386
00388 CT_ASSERT(NELEMS(cmp_fn_array) == MALLOC_EXAMPLE_KEY_E_MAX);
00398 static mkavl_rc_e
00399 free_memblock (void *item, void *context)
00400 {
00401 if (NULL == item) {
00402 return (MKAVL_RC_E_EINVAL);
00403 }
00404
00405 free(item);
00406
00407 return (MKAVL_RC_E_SUCCESS);
00408 }
00409
00417 static void
00418 display_memory (mkavl_tree_handle tree_h, void *start_addr, size_t bytes)
00419 {
00420 memblock_obj_st *found_item, *cur_item;
00421 memblock_obj_st lookup_item = {0};
00422 uint32_t loop_cnt = 0;
00423 void *end_addr = (start_addr + bytes);
00424 mkavl_rc_e rc;
00425
00426 assert_abort(NULL != tree_h);
00427
00428 lookup_item.start_addr = start_addr;
00429 rc = mkavl_find(tree_h, MKAVL_FIND_TYPE_E_GE,
00430 MALLOC_EXAMPLE_KEY_E_ADDR, &lookup_item,
00431 (void **) &found_item);
00432 assert_abort(mkavl_rc_e_is_ok(rc));
00433 cur_item = found_item;
00434
00435 printf("\n*** Displaying memory from %p to %p (size=%zu) ***\n",
00436 start_addr, end_addr, bytes);
00437 printf("XXX = allocated, OOO = free\n\n");
00438
00439 while ((NULL != cur_item) &&
00440 (cur_item->start_addr <= end_addr)) {
00441 assert_abort(loop_cnt < EXAMPLES_RUNAWAY_SANITY);
00442
00443 printf(" %p: %s (%zu bytes)\n", cur_item->start_addr,
00444 (cur_item->is_allocated) ? "XXXXXX" : "OOOOOO",
00445 cur_item->byte_cnt);
00446
00447 rc = mkavl_find(tree_h, MKAVL_FIND_TYPE_E_GT,
00448 MALLOC_EXAMPLE_KEY_E_ADDR, cur_item,
00449 (void **) &found_item);
00450 assert_abort(mkavl_rc_e_is_ok(rc));
00451 cur_item = found_item;
00452 ++loop_cnt;
00453 }
00454
00455 printf("\n*** Finished displaying memory ***\n\n");
00456 }
00457
00470 static bool
00471 generate_memblock (memblock_obj_st **obj, void *start_addr,
00472 size_t byte_cnt)
00473 {
00474 memblock_obj_st *local_obj;
00475
00476 if (NULL == obj) {
00477 return (false);
00478 }
00479 *obj = NULL;
00480
00481 local_obj = calloc(1, sizeof(*local_obj));
00482 if (NULL == local_obj) {
00483 return (false);
00484 }
00485
00486 local_obj->start_addr = start_addr;
00487 local_obj->byte_cnt = byte_cnt;
00488 local_obj->is_allocated = false;
00489
00490 *obj = local_obj;
00491
00492 return (true);
00493 }
00494
00503 static void *
00504 my_malloc (mkavl_tree_handle tree_h, size_t size)
00505 {
00506 memblock_obj_st lookup_item = {0};
00507 memblock_obj_st *cur_item, *found_item, *new_item;
00508 size_t new_size;
00509 void *new_addr;
00510 bool bool_rc;
00511 mkavl_rc_e rc;
00512
00513 if ((NULL == tree_h) || (0 == size)) {
00514 return (NULL);
00515 }
00516
00517
00518 lookup_item.start_addr = base_addr;
00519 lookup_item.byte_cnt = size;
00520 lookup_item.is_allocated = false;
00521
00522 rc = mkavl_find(tree_h, MKAVL_FIND_TYPE_E_GE,
00523 MALLOC_EXAMPLE_KEY_E_SIZE, &lookup_item,
00524 (void **) &found_item);
00525 assert_abort(mkavl_rc_e_is_ok(rc));
00526
00527 if (NULL == found_item) {
00528
00529 return (NULL);
00530 }
00531
00532 cur_item = found_item;
00533
00534 rc = mkavl_remove_key_idx(tree_h, MALLOC_EXAMPLE_KEY_E_SIZE,
00535 cur_item, (void **) &found_item);
00536 assert_abort((NULL != found_item) &&
00537 mkavl_rc_e_is_ok(rc));
00538 cur_item = found_item;
00539
00540 cur_item->is_allocated = true;
00541
00542
00543 if (cur_item->byte_cnt > size) {
00544
00545 new_addr = (cur_item->start_addr + size);
00546 new_size = (cur_item->byte_cnt - size);
00547 cur_item->byte_cnt = size;
00548
00549 bool_rc = generate_memblock(&new_item, new_addr, new_size);
00550 assert_abort((NULL != new_item) && bool_rc);
00551
00552 rc = mkavl_add(tree_h, new_item, (void **) &found_item);
00553 assert_abort((NULL == found_item) && mkavl_rc_e_is_ok(rc));
00554 }
00555
00556 rc = mkavl_add_key_idx(tree_h, MALLOC_EXAMPLE_KEY_E_SIZE,
00557 cur_item, (void **) &found_item);
00558 assert_abort((NULL == found_item) &&
00559 mkavl_rc_e_is_ok(rc));
00560
00561 return (cur_item->start_addr);
00562 }
00563
00570 static void
00571 my_free (mkavl_tree_handle tree_h, void *ptr)
00572 {
00573 memblock_obj_st *found_item, *cur_item, *prev_item, *next_item;
00574 memblock_obj_st *update_item = NULL;
00575 memblock_obj_st lookup_item = {0};
00576 size_t new_size;
00577 mkavl_rc_e rc;
00578
00579 if ((NULL == tree_h) || (NULL == ptr)) {
00580 return;
00581 }
00582
00583 lookup_item.start_addr = ptr;
00584 rc = mkavl_find(tree_h, MKAVL_FIND_TYPE_E_EQUAL,
00585 MALLOC_EXAMPLE_KEY_E_ADDR, &lookup_item,
00586 (void **) &found_item);
00587 assert_abort(mkavl_rc_e_is_ok(rc) &&
00588 (NULL != found_item) &&
00589 (found_item->is_allocated));
00590 cur_item = found_item;
00591 update_item = cur_item;
00592 new_size = cur_item->byte_cnt;
00593
00594 rc = mkavl_find(tree_h, MKAVL_FIND_TYPE_E_GT,
00595 MALLOC_EXAMPLE_KEY_E_ADDR, &lookup_item,
00596 (void **) &found_item);
00597 assert_abort(mkavl_rc_e_is_ok(rc));
00598 next_item = found_item;
00599
00600 if ((NULL != next_item) && !next_item->is_allocated) {
00601 rc = mkavl_remove(tree_h, next_item, (void **) &found_item);
00602 assert_abort((NULL != found_item) &&
00603 mkavl_rc_e_is_ok(rc));
00604 new_size += next_item->byte_cnt;
00605 free(next_item);
00606 next_item = NULL;
00607 }
00608
00609 rc = mkavl_find(tree_h, MKAVL_FIND_TYPE_E_LT,
00610 MALLOC_EXAMPLE_KEY_E_ADDR, &lookup_item,
00611 (void **) &found_item);
00612 assert_abort(mkavl_rc_e_is_ok(rc));
00613 prev_item = found_item;
00614
00615 if ((NULL != prev_item) && !prev_item->is_allocated) {
00616 rc = mkavl_remove(tree_h, cur_item, (void **) &found_item);
00617 assert_abort((NULL != found_item) &&
00618 mkavl_rc_e_is_ok(rc));
00619 update_item = prev_item;
00620 new_size += prev_item->byte_cnt;
00621 free(cur_item);
00622 cur_item = NULL;
00623 }
00624
00625 rc = mkavl_remove_key_idx(tree_h, MALLOC_EXAMPLE_KEY_E_SIZE,
00626 update_item, (void **) &found_item);
00627 assert_abort((NULL != found_item) &&
00628 mkavl_rc_e_is_ok(rc));
00629 update_item = found_item;
00630
00631 update_item->byte_cnt = new_size;
00632 update_item->is_allocated = false;
00633
00634 rc = mkavl_add_key_idx(tree_h, MALLOC_EXAMPLE_KEY_E_SIZE,
00635 update_item, (void **) &found_item);
00636 assert_abort((NULL == found_item) &&
00637 mkavl_rc_e_is_ok(rc));
00638 }
00639
00645 static void
00646 run_malloc_example (malloc_example_input_st *input)
00647 {
00648 memblock_obj_st *cur_item, *found_item;
00649 bool bool_rc;
00650 memblock_ctx_st ctx = {0};
00651 uint32_t i, size_idx, idx, cnt;
00652 void *ptr_array[input->opts->malloc_cnt];
00653 mkavl_rc_e mkavl_rc;
00654
00655 printf("\n");
00656
00657 mkavl_rc = mkavl_new(&(input->tree_h), cmp_fn_array, NELEMS(cmp_fn_array),
00658 &ctx, NULL);
00659 assert_abort(mkavl_rc_e_is_ok(mkavl_rc));
00660
00661
00662 bool_rc = generate_memblock(&cur_item, base_addr, input->opts->memory_size);
00663 assert_abort((NULL != cur_item) && bool_rc);
00664
00665 mkavl_rc = mkavl_add(input->tree_h, cur_item, (void **) &found_item);
00666 assert_abort((NULL == found_item) && mkavl_rc_e_is_ok(mkavl_rc));
00667
00668 printf("Created memory\n");
00669 display_memory(input->tree_h, base_addr, input->opts->memory_size);
00670
00671
00672 for (i = 0; i < input->opts->malloc_cnt; ++i) {
00673 size_idx = (rand() % NELEMS(malloc_sizes));
00674 ptr_array[i] = my_malloc(input->tree_h, malloc_sizes[size_idx]);
00675 assert_abort(NULL != ptr_array[i]);
00676 }
00677
00678 printf("Allocated %u pointers\n", input->opts->malloc_cnt);
00679 display_memory(input->tree_h, base_addr, input->opts->memory_size);
00680
00681
00682 cnt = 0;
00683 for (i = 0; i < (input->opts->malloc_cnt / 2); ++i) {
00684 switch (input->opts->pattern) {
00685 case MALLOC_PATTERN_E_LINEAR:
00686 idx = i;
00687 break;
00688 case MALLOC_PATTERN_E_UNIFORM:
00689 idx = (rand() % NELEMS(ptr_array));
00690 break;
00691 default:
00692 abort();
00693 break;
00694 }
00695 if (NULL != ptr_array[idx]) {
00696 my_free(input->tree_h, ptr_array[idx]);
00697 ptr_array[idx] = NULL;
00698 ++cnt;
00699 }
00700 }
00701
00702 printf("Freed %u pointers\n", cnt);
00703 display_memory(input->tree_h, base_addr, input->opts->memory_size);
00704
00705
00706 cnt = 0;
00707 for (i = 0; i < input->opts->malloc_cnt; ++i) {
00708 if (NULL != ptr_array[i]) {
00709 continue;
00710 }
00711 size_idx = (rand() % NELEMS(malloc_sizes));
00712 ptr_array[i] = my_malloc(input->tree_h, malloc_sizes[size_idx]);
00713 assert_abort(NULL != ptr_array[i]);
00714 ++cnt;
00715 }
00716
00717 printf("Allocated %u pointers\n", cnt);
00718 display_memory(input->tree_h, base_addr, input->opts->memory_size);
00719
00720
00721 for (i = 0; i < input->opts->malloc_cnt; ++i) {
00722 my_free(input->tree_h, ptr_array[i]);
00723 ptr_array[i] = NULL;
00724 }
00725
00726 printf("Freed all memory\n");
00727 display_memory(input->tree_h, base_addr, input->opts->memory_size);
00728
00729 mkavl_rc = mkavl_delete(&(input->tree_h), free_memblock, NULL);
00730 assert_abort(mkavl_rc_e_is_ok(mkavl_rc));
00731
00732 printf("\n");
00733 }
00734
00738 int
00739 main (int argc, char *argv[])
00740 {
00741 malloc_example_opts_st opts;
00742 uint32_t cur_run, cur_seed;
00743 malloc_example_input_st input = {0};
00744
00745 default_memory_size = (4096 * default_malloc_cnt);
00746 max_memory_size = (UINTPTR_MAX - (uintptr_t) base_addr);
00747
00748 parse_command_line(argc, argv, &opts);
00749
00750 printf("\n");
00751 cur_seed = opts.seed;
00752 for (cur_run = 0; cur_run < opts.run_cnt; ++cur_run) {
00753
00754 printf("Doing run %u with seed %u\n", (cur_run + 1), cur_seed);
00755 srand(cur_seed);
00756
00757 input.opts = &opts;
00758 input.tree_h = NULL;
00759
00760 run_malloc_example(&input);
00761
00762 ++cur_seed;
00763 }
00764
00765 printf("\n");
00766
00767 return (0);
00768 }