examples/malloc_example.c
Go to the documentation of this file.
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     /* First, group by allocation status */
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     /* Then by size */
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     /* Finally by address, which is guaranteed unique */
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     /* Set to minimum possible value */
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         /* Can't satisfy request */
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     /* Split the memory block in two if larger than necessary */
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     /* Create the entire block of memory to use */
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     /* Allocate all the pointers */
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     /* Free up to half the pointers */
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     /* Re-allocate those pointers */
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     /* Free all the pointers */
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 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines