// CSE 303, Spring 2009, Marty Stepp // // This instructor-provided program lets you interactively call the various // functions of your HW6 Mallocater library to test their behavior. // // It can also be run in 'quiet mode' (-q, --quiet) and/or // echo mode (-e, --echo) to test with redirection from input files. // // If this program is compiled with the preprocessor EXTRACREDIT flag on, // it will enable tests for the extra-credit features, realloc and valgrind. // // Example: gcc -g -Wall -DEXTRACREDIT -o martytest martytest.c ... #include #include #include #include #include #include "memalloc.h" #include "memops.h" #include "memdebug.h" #ifdef EXTRACREDIT #include "extracredit.h" #endif // function forward declarations int get_int(char* prompt, int min); void* get_address(char* prompt, void* min); void* initialize(void); void parse_args(int argc, char* argv[]); bool process_choice(char choice, void* heap); // globals (gasp) bool interactive = true; bool echo = false; int main(int argc, char* argv[]) { void* heap; bool loop = true; // read command-line args and initialize mallocater system parse_args(argc, argv); heap = initialize(); // loop for user input while (loop) { // get the user's choice of function to call char choice = '\0'; printf("\n"); if (interactive) { // prompt printf("(m)alloc (c)alloc (s)izeof (f)ree (p)rint (b)locks"); #ifdef EXTRACREDIT printf(" (r)ealloc (v)algrind"); #endif printf("\n"); printf("(1)memcpy (2)memset (3)memzero (g)et (w)rite (q)uit? "); } choice = getchar(); getchar(); // \n if (echo) { printf("%c\n", choice); } // run the appropriate code to handle that choice loop = process_choice(choice, heap); } free(heap); return 0; } // Keeps re-prompting the user with the given prompt text until the user types // a memory address in hex (such as 0x8fed19ec) at least as large as the given // minimum address, then returns the address as a pointer. void* get_address(char* prompt, void* min) { void* address; while (1) { if (interactive) { printf("%s", prompt); } if (scanf("%p", &address) == 0) { return NULL; } getchar(); // \n if (echo) { printf("%p\n", address); } // disabling min so that students can test NULL and other param values // if (address < min) { // printf("invalid address (too small); please try again.\n"); // } else { break; // } } return address; } // Keeps re-prompting the user with the given prompt text until the user types // an int at least as large as the given minimum, then returns the int. int get_int(char* prompt, int min) { int result; char input[128] = {'\0'}; while (1) { if (interactive) { printf("%s", prompt); } // scanf("%d\n", &result); fgets(input, sizeof(input) - 1, stdin); result = atoi(input); if (echo) { printf("%d\n", result); } // I'm going to disable the minimum value feature for now, // so students can test passing weird values like -1 to malloc // if (result < min) { // if (interactive) { // printf("invalid integer (too small); please try again."); // } //} else { break; //} } return result; } // initializes the mallocater system void* initialize(void) { int heap_size; void* heap; if (interactive) { printf("CSE 303 mallocater test program\n"); printf("size of heap, in bytes? "); } scanf("%d", &heap_size); getchar(); // \n if (echo) { printf("%d\n", heap_size); } heap = malloc(heap_size); // must call real malloc once to make heap if (heap) { printf("created a heap of %d bytes at %p\n", heap_size, heap); } else { if (interactive) { printf("error: unable to create a heap of size %d; exiting\n", heap_size); } exit(1); } printf("init303(%p, %d)\n", heap, heap_size); init303(heap, heap_size); return heap; } // Parses command-line arguments and stores values in global booleans. void parse_args(int argc, char* argv[]) { int i; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet")) { interactive = false; } else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--echo")) { echo = true; } } } // Processes a single-character choice typed by the user. // Returns true if we should keep going, or false if the user wants to quit. bool process_choice(char choice, void* heap) { // variables used to store various arguments typed by the user int size = 0; int num = 0; void* address = NULL; void* address2 = NULL; // choose proper path based on user's choice switch (choice) { case 'm': // malloc size = get_int("malloc how many bytes? ", 1); printf("malloc303(%d)\n", size); address = malloc303(size); if (address) { printf("malloc303 returned %p (heap %p + %d)\n", address, heap, (address - heap)); } else { printf("malloc303 returned 0x0 (NULL)\n"); } break; case 'c': // calloc num = get_int("calloc how many? ", 1); size = get_int("how big is each, in bytes? ", 1); printf("calloc303(%d, %d)\n", num, size); address = calloc303(num, size); if (address) { printf("calloc303 returned %p (heap %p + %d)\n", address, heap, (address - heap)); } else { printf("calloc303 returned 0x0 (NULL)\n"); } break; #ifdef EXTRACREDIT case 'r': // realloc address = get_address("realloc what address (0 for NULL)? ", heap); size = get_int("resize to how many bytes? ", 1); printf("realloc303(%p, %d)\n", address, size); address = realloc303(address, size); if (address) { printf("realloc303 returned %p (heap %p + %d)\n", address, heap, (address - heap)); } else { printf("realloc303 returned 0x0 (NULL)\n"); } break; #endif case 'f': // free address = get_address("free what address (0 for NULL)? ", heap); printf("free303(%p)\n", address); free303(address); break; case 's': // sizeof address = get_address("sizeof what address (0 for NULL)? ", heap); printf("sizeof303(%p)\n", address); size = sizeof303(address); printf("sizeof303 returned %d\n", size); break; case 'p': // print_block address = get_address("print what address (0 for NULL)? ", heap); printf("print_block(%p)\n", address); print_block(address); break; case 'b': // print_blocks printf("print_blocks()\n"); print_blocks(); break; #ifdef EXTRACREDIT case 'v': // valgrind valgrind303(); break; #endif case '1': // memcpy address = get_address("memcpy what dst address (0 for NULL)? ", heap); address2 = get_address("memcpy what src address (0 for NULL)? ", heap); size = get_int("memcpy how many bytes? ", 1); printf("memcpy303(%p, %p, %d)\n", address, address2, size); memcpy303(address, address2, size); break; case '2': // memset address = get_address("memset what address (0 for NULL)? ", heap); num = get_int("memset set to what value? ", 0); size = get_int("memset how many bytes? ", 1); printf("memset303(%p, %d, %d)\n", address, num, size); memset303(address, num, size); break; case '3': // memzero address = get_address("memzero what address (0 for NULL)? ", heap); size = get_int("memzero how many bytes? ", 1); printf("memzero303(%p, %d)\n", address, size); memzero303(address, size); break; case 'g': // get address = get_address("get value at what address? ", heap); printf("value at %p is %d\n", address, *((int*) address)); break; case 'w': // write address = get_address("write value at what address? ", heap); num = get_int("set to what value? ", -1 << 31); *((int*) address) = num; printf("value at %p is now %d\n", address, *((int*) address)); break; case 'q': // quit case '\n': default: return false; } return true; }