├── .gitignore ├── README.md ├── docs └── garbage-collection.md ├── garbageCollector.c ├── garbageCollector.h ├── gcLogger.c ├── gcLogger.h ├── heapList.c ├── heapList.h ├── list.c ├── list.h ├── main.c └── makefile /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.out 3 | *.o 4 | extended-static-java-compiler/compiler-J2C-translator/*.c 5 | extended-static-java-compiler/compiler-J2C-translator/*.h 6 | gc 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mark and Sweep Garbage Collector in C 2 | 3 | A basic garbage collection program. 4 | 5 | ## Documentation 6 | 7 | Visit [docs](/docs) for more information 8 | 9 | ## Running the tests 10 | 11 | Most of the tests are located in main. Compiling with the make file will be the easiest way to run the tests. 12 | 13 | ## Using the GC 14 | 15 | To use the GC, you simply have to add the garbageCollector.h header file to your C program. Instead of calling malloc, you will use the gcInit hooks. There are several other important garbage collection hooks that you will need to use, so make sure to read the documentation in the docs folder. 16 | 17 | ## Developers 18 | 19 | - [Brian Cain](https://github.com/briancain) 20 | - [Carlos Salazar](https://github.com/razalas) 21 | -------------------------------------------------------------------------------- /docs/garbage-collection.md: -------------------------------------------------------------------------------- 1 | # Garbage Collection 2 | 3 | This document contains the initial design phase of developing a garbage collector for a Java to C transpiler. 4 | 5 | ## Design 6 | 7 | One memory allocation up front 8 | 9 | GC will manage memory on heap for user program. 10 | 11 | ## Test CLI arguments 12 | 13 | Each test accepts two CLI arguments. 14 | 15 | - Heap Size (not required) 16 | - Logging (not required) 17 | 18 | Heap size is the size you are giving the user program to allocate all of the memory it needs to run. If no heapsize is required, it will default to 1000 bytes. 19 | 20 | Logging accepts either 1 or 0. It will default to 0 if this argument is not supplied or is anything other than 0 or 1 (say a random input from the user). 21 | 22 | ### Approach 23 | 24 | #### Mark and Sweep 25 | 26 | Algorithm 27 | 28 | 1. Mark: Starting from root, make all reachable objects using DFS pointer traversal 29 | 2. Sweep: Scan the heap from beginning to end and reclaim unmarked objects memory 30 | 31 | ### Tools used 32 | 33 | Valgrind 34 | 35 | - Memcheck 36 | 37 | ## Documentation 38 | 39 | Here is the documentation for the functions a program would need to use. 40 | 41 | ## gcInit(heapSize, logging) 42 | 43 | This function hook takes the size of the total heap the program will use in int form. It will then intialize all of the relevant garbage collector data structures along with a new heap for the program to allocate memory to. 44 | 45 | The logging param enables or disables logging/debugging output from the GC. 0 is off, 1 is on. If a random int that is neither 1 or 0 is supplied, it will default to 0. 46 | 47 | ### Usage 48 | 49 | gcInit(1000, 1); 50 | 51 | ## gcAlloc(ptrSize, address, heapParentAddress) 52 | 53 | Params: 54 | 55 | - ptrSize is how much space is required to allocate this object on the heap. 56 | - address is the program ptr address that is being allocated 57 | - heapParentAddress is the address of an objects parent (if it is a member of a struct/object (see example below) 58 | 59 | ### Usage 60 | 61 | This function hook takes the size it needs for memory. This could be an array of ints, etc. An easy way to call this might be: 62 | 63 | A* a = gcAlloc(sizeof(A)*arraySize, &a, NULL); 64 | 65 | It will then allocate space in the heap and return a pointer to that memory block. 66 | 67 | Let's say a has a member B. Here is how we would differ the allocate call 68 | 69 | A* a = gcAlloc(sizeof(A)*arraySize, &a, NULL); 70 | a->b = gcAlloc(sizeof(B)*arraySize, &(a->b), &a); 71 | 72 | ## gcDestroy() 73 | 74 | This function hooks destroys all memory generated by gcInit and gcAlloc. It should be placed at the bottom of the user programs main file. 75 | 76 | ### Usage 77 | 78 | gcDestroy(); 79 | 80 | ## gcReg(ptr, heapBlock, heapParentAddr, isAlloc) 81 | 82 | Params: 83 | 84 | - ptr is ptr address that program is attempting to register with GC 85 | - heapBlock __should remain `NULL` if called by user__. This object is passed in by allocate in GC. 86 | - heapParentAddr will be passed in if that object has a parent heap 87 | - isAlloc __should remain 0 if called by user__. This tells gcReg where it's being called from. 88 | 89 | This function hook checks to see if the pointer already exists on the Ptr List. 90 | 91 | ### Usage 92 | 93 | If you are in a random user program, you can use it like this: 94 | 95 | a = NULL; 96 | gcReg(&a, NULL, NULL, 0); 97 | 98 | If you are setting the b member of A to null, it will be used like this: 99 | 100 | a->b = NULL 101 | gcReg(&(a->b), NULL, &a, 0); 102 | 103 | # Higher Level Diagram Design Info 104 | 105 | Ptr List Heap List Heap 106 | 107 | P1[] [] [ 108 | v v 109 | P2[] [] 110 | v 111 | [] ] 112 | 113 | ![gcdesign](http://i.imgur.com/4I38SRk.png) 114 | -------------------------------------------------------------------------------- /garbageCollector.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "heapList.h" 5 | #include "list.h" 6 | #include "gcLogger.h" 7 | #include "garbageCollector.h" 8 | 9 | /* 10 | * Simple Garbage Collector 11 | * 12 | * Mark and Sweep style garbage collection 13 | * 14 | */ 15 | 16 | ProgramPtrList* programPtrList; 17 | HeapBlockList* heapBlockList; 18 | void* heap; 19 | void* freeBlockHeapPtr; 20 | size_t heapSize; 21 | size_t spaceUsed; 22 | int logging; 23 | 24 | /* 25 | * Returns a given heapBlock pointer 26 | * from the given address passed in. 27 | * 28 | * If there are two ProgramPtrs that 29 | * point to the same HeapBlock, it is 30 | * iterating through the list to retrieve 31 | * that HeapBlock 32 | * 33 | */ 34 | HeapBlock* getHeapBlockPtr(void** addr) { 35 | ProgramPtr* ptr; 36 | ProgramPtr* next; 37 | 38 | for (ptr = programPtrList->first; ptr; ptr = next) { 39 | next = ptr->next; 40 | 41 | if (addr == (ptr->varAddr)) { 42 | debug("HeapBlock Ptr found in HeapBlockList", logging); 43 | /*if (ptr->heapBlock == 0x1) { 44 | printf("heapblock is 1\n"); 45 | exit(1); 46 | }*/ 47 | return ptr->heapBlock; 48 | } 49 | } 50 | 51 | throwError("Something wrong happened. getHeapBlock", logging); 52 | return NULL;//should this just exit instead?? 53 | } 54 | 55 | /* 56 | * Find heapblock parent of given address and return 57 | * heapBlock to set in ProgramPtr 58 | * 59 | * if no parent is found, return NULL 60 | */ 61 | HeapBlock* setParent(void **addr) { 62 | HeapBlock* ptr; 63 | HeapBlock* next; 64 | 65 | for (ptr = heapBlockList->first; ptr; ptr = next) { 66 | next = ptr->next; 67 | 68 | if (ptr->heapPtr == *addr) { 69 | // this is the heapblock we are looking for 70 | debug("Found parent heap block", logging); 71 | return ptr; 72 | } 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | /* 79 | * checks to see if pointer is being tracked 80 | * by ptr list, if so update the reference 81 | * otherwise add a new ptr 82 | */ 83 | void gcReg(void** addr, HeapBlock* heapBlock, void** heapParentAddr, int isAlloc) { 84 | ProgramPtr* ptr; 85 | 86 | debug("gcReg", logging); 87 | 88 | if (programPtrList->size == 0) { 89 | debug("ProgramPtrList is empty, pushing new programPtr", logging); 90 | push(programPtrList, addr, heapBlock); 91 | } else { 92 | // check to see if addr of ptr already exists 93 | // within programPtrList 94 | for (ptr = programPtrList->first; ptr != NULL; ptr = ptr->next) { 95 | 96 | if (addr == ptr->varAddr) { 97 | debug("Found Ptr in ProgramPtrList", logging); 98 | // this is the ptr we're looking for 99 | break; 100 | } 101 | } 102 | 103 | // if it doesn't exist, push it onto programPtrList 104 | debug("ptr doesn't exist", logging); 105 | if (ptr == NULL) { 106 | push(programPtrList, addr, heapBlock); 107 | ptr = programPtrList->last; 108 | } 109 | 110 | if (heapParentAddr != NULL) { 111 | ptr->heapParent = setParent(heapParentAddr); 112 | } 113 | 114 | debug("is alloc?", logging); 115 | if (isAlloc) { 116 | ptr->heapBlock = heapBlock; 117 | if (heapBlock == NULL) { 118 | throwError("NULL heapBlock from gcAlloc! This should not happen.", logging); 119 | } 120 | } else { 121 | if (*(ptr->varAddr) == NULL) { // TODO: make sure this line is correct. 122 | logWarning("var is set to null", logging); 123 | ptr->heapBlock = NULL; 124 | } else { 125 | if (heapBlock == NULL) { 126 | ptr->heapBlock = getHeapBlockPtr(addr); 127 | } else { 128 | ptr->heapBlock = heapBlock; 129 | } 130 | } 131 | } 132 | 133 | } 134 | } 135 | 136 | /* 137 | * Traverses ptr's heapParent to see 138 | * if there is a root and still valid memory 139 | * 140 | * TODO: 141 | * does it work ( ͡° ͜ʖ ͡°) 142 | */ 143 | int isReachable(ProgramPtr* ptr) { 144 | debug("Reachable Call", logging); 145 | ProgramPtr* progPtr; 146 | ProgramPtr* next; 147 | 148 | debug("is reachable for loop", logging); 149 | for (progPtr = programPtrList->first; progPtr; progPtr = next) { 150 | next = progPtr->next; 151 | if (ptr->heapParent == progPtr->heapBlock) { 152 | debug("passed in ptr heapParent is equal to itr progptr heapblock", logging); 153 | if (progPtr->heapParent && isReachable(progPtr)) { 154 | debug("Yes reachable", logging); 155 | return 1; 156 | } 157 | else if (!progPtr->heapParent) { 158 | debug("Yes reachable heapParent null", logging); 159 | return 1; 160 | } 161 | } 162 | } 163 | 164 | debug("Not reachable", logging); 165 | return 0; 166 | } 167 | 168 | /* 169 | * Mark and Sweep 170 | * 171 | * Mark: Starting from root, mark all 172 | * reachable objects using DFS pointer traversal 173 | * 174 | * Sweep: Scan the heap from beginning to end 175 | * and reclaim unmarked objects memory 176 | * 177 | * Might be calling the defragment 178 | * function to shift over memory when GC'ing 179 | * 180 | */ 181 | void garbageCollect() { 182 | size_t a; 183 | ProgramPtr* progPtr = NULL; 184 | HeapBlock* hPtr = NULL; 185 | HeapBlock* heapNext = NULL; 186 | 187 | debug("Mark phase of GC", logging); 188 | // Mark 189 | for (progPtr = programPtrList->first; progPtr != NULL; progPtr = progPtr->next) { 190 | debug("mark begin", logging); 191 | if (progPtr->heapParent != NULL && progPtr->heapBlock != NULL) { 192 | // traverse parent HeapBlock to make sure 193 | // nothing is referencing it 194 | // reachability 195 | debug("Is reachable?", logging); 196 | if (isReachable(progPtr)) { 197 | debug("Mark heapBlock 1", logging); 198 | progPtr->heapBlock->mark = 1; 199 | } 200 | 201 | // keep it 0 202 | } else { 203 | debug("Can Mark block 1?", logging); 204 | if (progPtr->heapBlock != NULL) { 205 | debug("Mark heapBlock 1", logging); 206 | progPtr->heapBlock->mark = 1; 207 | } 208 | } 209 | debug("mark end", logging); 210 | } 211 | debug("Mark Complete", logging); 212 | 213 | debug("Sweep phase of GC", logging); 214 | // Sweep 215 | for (hPtr = heapBlockList->first; hPtr != NULL; hPtr = heapNext) { 216 | if (logging) { 217 | printf("Start of loop\n"); 218 | printf("Heap Pointer List Size: %d\n", heapBlockList->listSize); 219 | printf("HeapBlock Addr: %p\n", hPtr); 220 | 221 | /*if (hPtr == 0x1) { 222 | printf("hPtr->next is 1\n"); 223 | }*/ 224 | 225 | if (hPtr->next) { 226 | printf("HeapBlock->next Addr: %p\n", hPtr->next); 227 | } else { 228 | printf("hPtr->next doesn't exist\n"); 229 | } 230 | } 231 | heapNext = hPtr->next; 232 | 233 | if (logging) { 234 | printf("HeapNext: %p\n", heapNext); 235 | printf("hPtr->next: %p\n", hPtr->next); 236 | } 237 | 238 | if (hPtr->mark == 0) { 239 | debug("Mark is 0", logging); 240 | // unreachable memory from Ptr Program 241 | // 242 | // defrag 243 | // 244 | // TODO: does this work 245 | // 246 | // shift heap 247 | // printf("freeBlockptr: %d\n", freeBlockHeapPtr); 248 | // printf("heapPtr: %d\n", hPtr->heapPtr); 249 | 250 | if (logging) { 251 | printf("HeapNext: %p\n", heapNext); 252 | printf("hPtr->next: %p\n", hPtr->next); 253 | } 254 | debug("shift", logging); 255 | //eliminating void* arithmetic and other warnings. 256 | long t1 = (char*)freeBlockHeapPtr-(((char*)hPtr->heapPtr) + hPtr->size); 257 | size_t shiftSize = (size_t)t1; 258 | size_t zero = 0; 259 | if (shiftSize > zero) { 260 | if (logging) { 261 | printf("Shift heap by size: %zu\n", shiftSize); 262 | printf("heap pointer next: %p\n", hPtr->next->heapPtr); 263 | //printf("heap pointer next by %zu: %p\n", shiftSize, ((hPtr->next->heapPtr)+shiftSize)); 264 | //printf("end heap ptr: %p\n", (heap+heapSize)); 265 | } 266 | 267 | if (((char*)(hPtr->next->heapPtr)) >= (((char*)heap)+heapSize)) { 268 | throwError("what have you done", 1); 269 | exit(1); 270 | } 271 | 272 | if ((((char*)(hPtr->next->heapPtr))+shiftSize) > (((char*)heap)+heapSize)) { 273 | throwError("Shift will go outside allocated heap", 1); 274 | exit(1); 275 | } 276 | else if ((((char*)(hPtr->next->heapPtr))+shiftSize) == (((char*)heap)+heapSize)) { 277 | logWarning("Shift size equals end of heap. Maybe error?", logging); 278 | } 279 | 280 | memmove(hPtr->heapPtr, hPtr->next->heapPtr, shiftSize); // shift entire heap to replace unmarked block 281 | 282 | 283 | if (logging) { 284 | printf("HeapNext: %p\n", heapNext); 285 | printf("hPtr->next: %p\n", hPtr->next); 286 | } 287 | 288 | // 289 | // update all heapBlockList ptrs after heapPtr by heapPtr->size 290 | debug("update heapblocklist", logging); 291 | HeapBlock* tmp = hPtr->next; 292 | debug("About to enter while", logging); 293 | while (tmp) { 294 | if (tmp->heapPtr) { 295 | //pointer arithmetic on void* is undefined behavior. 296 | char* tmp2 = tmp->heapPtr; 297 | tmp2 -= hPtr->size; 298 | tmp->heapPtr = tmp2; 299 | } 300 | tmp = tmp->next; 301 | } 302 | debug("after heapblocklist", logging); 303 | 304 | if (logging) { 305 | printf("HeapNext: %p\n", heapNext); 306 | printf("hPtr->next: %p\n", hPtr->next); 307 | } 308 | 309 | // update all ProgramPtrs 310 | ProgramPtr* tmpPPtr; 311 | 312 | debug("update program ptrs", logging); 313 | //int index = 0; 314 | for (tmpPPtr = programPtrList->first; tmpPPtr != NULL; tmpPPtr = tmpPPtr->next) { 315 | 316 | if ((tmpPPtr->heapBlock) != NULL && (tmpPPtr->heapBlock->heapPtr) !=NULL ) { 317 | //FIXME: How do we make sure that tmpPPtr is valid so that we can access its member?? 318 | if(&(tmpPPtr->varAddr) && tmpPPtr->varAddr) { 319 | *(tmpPPtr->varAddr) = tmpPPtr->heapBlock->heapPtr;//FIXME: This line is dangerous. 320 | } 321 | else { 322 | throwError("Null varAddr!!!!", 1); 323 | } 324 | } 325 | } 326 | /* End of shifting operations */ 327 | 328 | debug("after update program ptrs", logging); 329 | 330 | if (logging) { 331 | printf("HeapNext: %p\n", heapNext); 332 | printf("hPtr->next: %p\n", hPtr->next); 333 | } 334 | } 335 | 336 | debug("update space used and freeblockheapptr", logging); 337 | // update space used and freeblockheapptr 338 | spaceUsed -= hPtr->size; 339 | //pointer arithmetic on void* is undefined behavior. 340 | char* tmp1 = freeBlockHeapPtr; 341 | tmp1 -= hPtr->size; 342 | freeBlockHeapPtr = tmp1; 343 | 344 | debug("remove heapPtr", logging); 345 | // remove heapPtr 346 | // update heapBlockList 347 | if (logging) { 348 | printf("HeapNext: %p\n", heapNext); 349 | printf("hPtr->next: %p\n", hPtr->next); 350 | } 351 | 352 | if (hPtr == heapBlockList->first) { 353 | debug("first", logging); 354 | heapBlockList->first = hPtr->next; 355 | if (heapBlockList->listSize > 1) { 356 | hPtr->next->prev = NULL; 357 | } 358 | } else if (hPtr == heapBlockList->last) { 359 | debug("last", logging); 360 | HeapBlock* t = hPtr->prev; 361 | t->next = NULL; 362 | heapBlockList->last = t; 363 | } else { 364 | debug("middle", logging); 365 | // can cause segfault 366 | HeapBlock* t = hPtr->prev; 367 | HeapBlock* p = hPtr->next; 368 | t->next = p; 369 | p->prev = t; 370 | } 371 | heapBlockList->listSize--; 372 | 373 | if (logging) { 374 | printf("HeapNext: %p\n", heapNext); 375 | printf("hPtr->next: %p\n", hPtr->next); 376 | } 377 | 378 | debug("free heapPtr", logging); 379 | if (logging) { 380 | printf("Heap Pointer List Size: %d\n", heapBlockList->listSize); 381 | 382 | printf("In loop before free\n"); 383 | printf("HeapBlock Addr: %p\n", hPtr); 384 | printf("HeapBlock->next Addr: %p\n", hPtr->next); 385 | printf("HeapNext: %p\n", heapNext); 386 | } 387 | 388 | free(hPtr); 389 | } 390 | } 391 | 392 | debug("Sweep Complete", logging); 393 | 394 | // mark all programPtr heapBlocks to 0 after 395 | debug("Resetting all marks", logging); 396 | for (hPtr = heapBlockList->first; hPtr; hPtr = heapNext) { 397 | heapNext = hPtr->next; 398 | hPtr->mark = 0; 399 | } 400 | 401 | debug("After reset", logging); 402 | } 403 | 404 | /* 405 | * Initialization of program heap space 406 | */ 407 | void initHeap(size_t size) { 408 | logMsg("Initializing Heap", logging); 409 | heap = malloc(size); 410 | memset(heap, 0, size); 411 | if (logging) { 412 | printf("Heap addr: %p\n", heap); 413 | } 414 | } 415 | 416 | /* 417 | * Checks to see if the program ptr 418 | * has already been defined in the 419 | * program ptr list. If so, the user 420 | * is reallocating the ptr. This will 421 | * set that ptr's heapblock to null 422 | */ 423 | void checkPrgPtrExist(void** addr) { 424 | logWarning("checkProgPtrExist Call", logging); 425 | ProgramPtr* ptr; 426 | //ProgramPtr* next; 427 | 428 | for (ptr = programPtrList->first; ptr != NULL; ptr = ptr->next) { 429 | 430 | if (addr == ptr->varAddr) { 431 | logWarning("Setting heap block to null", logging); 432 | ptr->heapBlock = NULL; 433 | } 434 | } 435 | } 436 | 437 | void printProgramPointers(char* msg) { 438 | logWarning(msg, 1); 439 | ProgramPtr* pp2; 440 | for (pp2 = programPtrList->first; pp2 != NULL; pp2 = pp2->next) { 441 | printf("(in test)printing programPtr->varAddr: %p\n", pp2->varAddr); 442 | printf("(in test)printing *(programPtr->varAddr): %p\n", (char*)*(pp2->varAddr)); 443 | } 444 | logWarning("end debug lines", 1); 445 | printf("\n"); 446 | } 447 | 448 | /* 449 | * Allocation hook for C Projects 450 | * 451 | * Program will ask for a size 452 | * and GC will init block in heap 453 | * and create Ptr in ProgramPtrList pointing 454 | * to that memory 455 | * 456 | * heapParent 457 | * 458 | * If ProgramPtr exists within a struct, give field pointer 459 | * the heap parent so it can figure out whether or not 460 | * it can be freed when mark & sweep happens 461 | * 462 | * link address to heaapParent (iterate through heapblock list) 463 | */ 464 | void* gcAlloc(size_t ptrSize, void** addr, void** heapParentAddr) { 465 | debug("gcAlloc call", logging); 466 | printf("addr = %p\n", addr); 467 | debug("Debug Statements Below", logging); 468 | if (logging) { 469 | printf("ptrSize: %zu\n", ptrSize); 470 | printf("heap size: %zu\n", heapSize); 471 | printf("Space Used: %zu\n", spaceUsed); 472 | printf("Space Left: %zu\n", (heapSize-spaceUsed)); 473 | } 474 | debug("Debug Statements End", logging); 475 | 476 | checkPrgPtrExist(addr); 477 | 478 | if (!heap) { 479 | throwError("Heap doesn't exist!", 1); 480 | exit(1); 481 | } else if (heapSize < ptrSize) { 482 | throwError("Allocation larger than total heap size!", 1); 483 | exit(1); 484 | } else if ((heapSize-spaceUsed) < ptrSize) { 485 | // if no space left and total space is 486 | // larger than ptrSize, call garbagecollect() 487 | // to free space 488 | 489 | logWarning("Allocation larger than free space. Running Garbage Collector", logging); 490 | garbageCollect(); 491 | 492 | debug("Post GC", logging); 493 | if (logging) { 494 | printf("heap size: %zu\n", heapSize); 495 | printf("Space Used: %zu\n", spaceUsed); 496 | printf("Space Left: %zu\n", (heapSize-spaceUsed)); 497 | } 498 | } 499 | 500 | debug("Check avail space before alloc", logging); 501 | if ((heapSize-spaceUsed) >= ptrSize) { 502 | // otherwise... 503 | // allocate memory from heap and return ptr 504 | 505 | logWarning("Add new heap block pointer to heap block list", logging); 506 | addNewHeapBlock(heapBlockList, ptrSize, freeBlockHeapPtr); 507 | 508 | logWarning("Calling gcReg to allocate ProgramPtr", logging); 509 | gcReg(addr, heapBlockList->last, heapParentAddr, 1); 510 | // 511 | // Change freeBlockHeapPtr position by ptrSize passed in 512 | void* returnPtr = freeBlockHeapPtr; 513 | debug("Increment freeblockptr", logging); 514 | // TODO: double check increment 515 | if (logging) { 516 | printf("Freeblockptr: %p\n", freeBlockHeapPtr); 517 | printf("ptrSize: %zu\n", ptrSize); 518 | } 519 | //pointer arithmetic on void* is undefined behavior. 520 | char* tmp = freeBlockHeapPtr; 521 | tmp += ptrSize; 522 | freeBlockHeapPtr = tmp; 523 | if (logging) { 524 | printf("Freeblockptr after inc: %p\n", freeBlockHeapPtr); 525 | } 526 | spaceUsed += ptrSize; 527 | debug("Done", logging); 528 | if (logging) { 529 | printf("Return ptr: %p\n", returnPtr); 530 | logWarning("returning from gcAlloc()!", logging); 531 | } 532 | 533 | //DEBUG: testing memory 534 | // write 0s to allocated memory, 535 | // read from allocated memory. 536 | /*logWarning("Testing memory.", logging); 537 | int* derp; 538 | for (derp = returnPtr; derp < freeBlockHeapPtr; derp++) { 539 | *derp = 0; 540 | printf("memtest read *derp = %d\n", *derp); 541 | } 542 | logWarning("Memory test complete.", logging); 543 | */ 544 | return returnPtr; 545 | } else { 546 | if (logging) { 547 | printf("heap size: %zu\n", heapSize); 548 | printf("avail space: %zu\n", (heapSize-spaceUsed)); 549 | printf("Ptr size: %zu\n", ptrSize); 550 | } 551 | throwError("ERROR: No space left to allocate memory", 1); 552 | exit(1); 553 | } 554 | } 555 | 556 | /* 557 | * Initialization Hook for C Projects. 558 | * 559 | * size: total space required for 560 | * the entire project 561 | * 562 | */ 563 | void gcInit(size_t size, int log) { 564 | logMsg("Initializing Garbage Collector", logging); 565 | logMsg("Allocate a memory block", logging); 566 | initHeap(size); 567 | logging = (log ? 1 : 0); 568 | heapSize = size; 569 | spaceUsed = 0; 570 | freeBlockHeapPtr = heap; 571 | 572 | if (logging) { 573 | printf("Memory Address of Block: %p\n", heap); 574 | } 575 | 576 | programPtrList = init(); 577 | 578 | heapBlockList = initHeapList(); 579 | } 580 | 581 | /* 582 | * Frees all memory used by GC 583 | */ 584 | void gcDestroy() { 585 | logWarning("Garbage Collector Destroy Invoke!", logging); 586 | freeList(programPtrList); 587 | debug("free list done", logging); 588 | freeHeapBlockList(heapBlockList); 589 | debug("free heap", logging); 590 | free(heap); 591 | logWarning("Garbage Collector Destroyed", logging); 592 | } 593 | -------------------------------------------------------------------------------- /garbageCollector.h: -------------------------------------------------------------------------------- 1 | #ifndef GARBAGECOLLECTOR_H 2 | #define GARBAGECOLLECTOR_H 3 | #include "heapList.h" 4 | #include "list.h" 5 | #include "gcLogger.h" 6 | 7 | void gcReg(void** addr, HeapBlock* heapBlock, void** heapParentAddr, int isAlloc); 8 | void* gcAlloc(size_t ptrSize, void** addr, void** heapParent); 9 | void gcInit(size_t size, int log); 10 | void gcDestroy(void); 11 | HeapBlock* getHeapBlockPtr(void** addr); 12 | HeapBlock* setParent(void **addr); 13 | int isReachable(ProgramPtr* ptr); 14 | void garbageCollect(void); 15 | void initHeap(size_t size); 16 | void checkPrgPtrExist(void** addr); 17 | void printProgramPointers(char* msg); 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /gcLogger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Garbage Collector Logger 3 | * 4 | * - A logging system for the GC. 5 | * 6 | * Brian Cain 7 | */ 8 | 9 | #include 10 | #include "gcLogger.h" 11 | 12 | void throwError(char* err, int log) { 13 | if (log) { 14 | fprintf(stderr, "\x1B[31m[GCErr] - %s\n", err); 15 | printf("\x1b[0m"); 16 | } 17 | } 18 | 19 | void logWarning(char* msg, int log) { 20 | if (log) { 21 | printf("\x1B[33m[GCWrn] - %s\n", msg); 22 | printf("\x1b[0m"); 23 | } 24 | } 25 | 26 | void logMsg(char* msg, int log) { 27 | if (log) { 28 | printf("\x1B[0m[GCMsg] - %s\n", msg); 29 | } 30 | } 31 | 32 | void runTest(char* msg, int log) { 33 | if (log) { 34 | printf("\x1B[32m[GCTst] - %s\n", msg); 35 | printf("\x1b[0m"); 36 | } 37 | } 38 | 39 | void debug(char* msg, int log) { 40 | if (log) { 41 | printf("\x1B[34m[GCDbg] - %s\n", msg); 42 | printf("\x1b[0m"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /gcLogger.h: -------------------------------------------------------------------------------- 1 | #ifndef GCLOGGER_H 2 | #define GCLOGGER_H 3 | 4 | void throwError(char* err, int log); 5 | void logWarning(char* msg, int log); 6 | void logMsg(char* msg, int log); 7 | void runTest(char* msg, int log); 8 | void debug(char* msg, int log); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /heapList.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "heapList.h" 5 | #include "list.h" 6 | #include "gcLogger.h" 7 | 8 | /* 9 | * HeapBlock and HeapBlockList 10 | * 11 | * - A doubly linked list implementation 12 | * for keeping track of all the pointers 13 | * on the heap. 14 | */ 15 | 16 | /* 17 | * Initialize the HeapBlockList list 18 | */ 19 | HeapBlockList* initHeapList() { 20 | HeapBlockList* list = malloc(sizeof(HeapBlockList)); 21 | 22 | list->listSize = 0; 23 | list->first = list->last = NULL; 24 | return list; 25 | } 26 | 27 | /* 28 | * Add a new heap block onto the list 29 | */ 30 | void addNewHeapBlock(HeapBlockList* list, size_t size, void* heapPtr) { 31 | HeapBlock* block = malloc(sizeof(HeapBlock)); 32 | 33 | block->size = size; 34 | block->mark = 0; 35 | block->heapPtr = heapPtr; 36 | block->next = block->prev = NULL; 37 | 38 | if (list->listSize == 0) { 39 | list->first = block; 40 | list->last = block; 41 | } else { 42 | list->last->next = block; 43 | block->prev = list->last; 44 | list->last = block; 45 | } 46 | 47 | list->listSize++; 48 | } 49 | 50 | /* 51 | * Free all memory in HeapBlockList 52 | */ 53 | void freeHeapBlockList(HeapBlockList* list) { 54 | HeapBlock* ptr; 55 | HeapBlock* next; 56 | if (list->listSize == 0) { 57 | //logWarning("There is no HeapBlock's in list to free"); 58 | free(list); 59 | } else { 60 | //logMsg("Heap BlockList Children Memory"); 61 | for (ptr = list->first; ptr; ptr = next) { 62 | next = ptr->next; 63 | free(ptr); 64 | } 65 | free(list); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /heapList.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAPLIST_H 2 | #define HEAPLIST_H 3 | 4 | typedef struct HeapBlock { 5 | size_t size; 6 | int mark; 7 | void* heapPtr; 8 | struct HeapBlock* next; 9 | struct HeapBlock* prev; 10 | } HeapBlock; 11 | 12 | typedef struct HeapBlockList { 13 | int listSize; 14 | HeapBlock* first; 15 | HeapBlock* last; 16 | } HeapBlockList; 17 | 18 | HeapBlockList* initHeapList(void); 19 | void addNewHeapBlock(HeapBlockList* list, size_t size, void* heapPtr); 20 | void freeHeapBlockList(HeapBlockList* list); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "heapList.h" 5 | #include "list.h" 6 | #include "gcLogger.h" 7 | 8 | /* 9 | * Program Pointer List file 10 | * 11 | * Keeps track of all program pointers 12 | */ 13 | 14 | /* 15 | * Initialize a Ptr list 16 | */ 17 | ProgramPtrList* init() { 18 | ProgramPtrList* list = malloc(sizeof(ProgramPtrList)); 19 | list->first = list->last = NULL; 20 | list->size = 0; 21 | 22 | return list; 23 | } 24 | 25 | /* 26 | * Free the memory list and 27 | * all of its neighbors 28 | */ 29 | void freeList(ProgramPtrList* list) { 30 | //logMsg("Free Memory"); 31 | ProgramPtr* ptr; 32 | ProgramPtr* next; 33 | if (list->size == 0) { 34 | //logWarning("There is no ProgramPtr's in list to free"); 35 | free(list); 36 | } else { 37 | //logMsg("Free Children Memory"); 38 | for (ptr = list->first; ptr; ptr = next) { 39 | next = ptr->next; 40 | free(ptr); 41 | } 42 | free(list); 43 | } 44 | } 45 | 46 | /* 47 | * [] => [] => + [] 48 | * 49 | * Push: 50 | * [] => [] => => [] 51 | */ 52 | void push(ProgramPtrList* list, void** addr, HeapBlock* heapBlock) { 53 | ProgramPtr* ptr = malloc(sizeof(ProgramPtr)); 54 | 55 | ptr->varAddr = addr; 56 | // TODO: make sure heapblock is real 57 | ptr->heapBlock = heapBlock; 58 | ptr->heapParent = NULL; 59 | ptr->next = NULL; 60 | ptr->prev = NULL; 61 | 62 | if (list->size == 0) { 63 | list->first = ptr; 64 | list->last = ptr; 65 | } else { 66 | list->last->next = ptr; 67 | ptr->prev = list->last; 68 | list->last = ptr; 69 | } 70 | list->size++; 71 | } 72 | 73 | /* 74 | * [] => [] 75 | * 76 | * Pop: 77 | * [] 78 | */ 79 | void pop(ProgramPtrList* list) { 80 | if (list->size == 0) { 81 | //throwError("List is empty"); 82 | } else { 83 | //ProgramPtr* tmp = list->last; 84 | list->last = list->last->prev; 85 | list->size--; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H 2 | #define LIST_H 3 | 4 | typedef struct ProgramPtr { 5 | void** varAddr; 6 | HeapBlock* heapBlock; 7 | HeapBlock* heapParent; 8 | struct ProgramPtr* next; 9 | struct ProgramPtr* prev; 10 | } ProgramPtr; 11 | 12 | typedef struct ProgramPtrList { 13 | int size; 14 | ProgramPtr* first; 15 | ProgramPtr* last; 16 | } ProgramPtrList; 17 | 18 | ProgramPtrList* init(void); 19 | void freeList(ProgramPtrList* list); 20 | void push(ProgramPtrList* list, void** value, HeapBlock* heapBlock); 21 | void pop(ProgramPtrList* list); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "garbageCollector.h" 5 | // #include "heapList.h" 6 | // #include "list.h" 7 | // #include "gcLogger.h" 8 | 9 | /* 10 | * Unit Test file 11 | */ 12 | 13 | // void pop_empty_list() { 14 | // runTest("Run Test pop_empty_list"); 15 | // ProgramPtrList* list = init(); 16 | // pop(list); 17 | // // free(list); 18 | // freeList(list); 19 | // } 20 | // 21 | // void pop_single() { 22 | // runTest("Run Test pop_single"); 23 | // ProgramPtrList* list = init(); 24 | // push(list, "Data", NULL); 25 | // push(list, "Data Pop", NULL); 26 | // 27 | // printf("List Size Before Pop: %d\n", list->size); 28 | // printf("List Last Val: %s\n", list->last->varAddr); 29 | // pop(list); 30 | // printf("List Size After Pop: %d\n", list->size); 31 | // printf("List Last Val: %s\n", list->last->varAddr); 32 | // 33 | // freeList(list); 34 | // } 35 | // 36 | // void push_single() { 37 | // runTest("Run Test push_single"); 38 | // ProgramPtrList* list = init(); 39 | // push(list, "Data", NULL); 40 | // printf("Count: %d\n", list->size); 41 | // printf("Value: %s\n", list->first->varAddr); 42 | // freeList(list); 43 | // } 44 | // 45 | // void push_double() { 46 | // runTest("Run Test push_double"); 47 | // ProgramPtrList* list = init(); 48 | // push(list, "Data", NULL); 49 | // printf("Count: %d\n", list->size); 50 | // printf("Value: %s\n", list->first->varAddr); 51 | // push(list, "Data 2", NULL); 52 | // printf("Count: %d\n", list->size); 53 | // printf("Value: %s\n", list->first->next->varAddr); 54 | // freeList(list); 55 | // } 56 | 57 | void init_hook(int heapSize) { 58 | runTest("Run test init_hook", 1); 59 | gcInit(heapSize, 1); 60 | gcDestroy(); 61 | } 62 | 63 | void alloc_hook(int heapSize, int ptrSize) { 64 | runTest("Run test alloc_hook", 1); 65 | gcInit(heapSize, 1); 66 | //gcAlloc(sizeof(int)*ptrSize); 67 | gcDestroy(); 68 | } 69 | 70 | void ptr_no_heap() { 71 | runTest("Run test ptr_no_heap", 1); 72 | 73 | void* mem = NULL; 74 | if (mem == NULL) { 75 | throwError("Memory is NULL. Allocation Failed", 1); 76 | } 77 | } 78 | 79 | void no_space() { 80 | runTest("Running test no_space", 1); 81 | gcInit(5, 1); 82 | //gcAlloc(10); 83 | gcDestroy(); 84 | } 85 | 86 | void herp_derp_heap() { 87 | runTest("Running test herp_derp_heap", 1); 88 | 89 | void* heap = malloc(100); 90 | 91 | void* ptr = heap; 92 | 93 | printf("ProgramPtr mem: %p\n", ptr); 94 | ptr++; 95 | printf("ProgramPtr mem+1: %p\n", ptr); 96 | free(heap); 97 | } 98 | 99 | void heap_chunk() { 100 | runTest("Running test heap_chunk", 1); 101 | gcInit(50, 1); 102 | int* mem = gcAlloc(sizeof(int)*10, &mem, NULL); 103 | printf("Mem space is: %p\n", mem); 104 | 105 | int i; 106 | for (i = 0; i < 10; i++) { 107 | mem[i] = i; 108 | printf("mem[%d]: %d\n", i, mem[i]); 109 | } 110 | gcDestroy(); 111 | } 112 | 113 | void heap_test_equality() { 114 | runTest("Running test heap_test_equality", 1); 115 | // b = a 116 | gcInit(50, 1); 117 | int* a = gcAlloc(sizeof(int)*10, &a, NULL); 118 | int* b; 119 | printf("a space is: %p\n", a); 120 | 121 | int i; 122 | for (i = 0; i < 10; i++) { 123 | a[i] = i; 124 | printf("a[%d]: %d\n", i, a[i]); 125 | } 126 | 127 | b = a; 128 | gcReg(&b, NULL, NULL, 0); 129 | 130 | printf("b space is: %p\n", a); 131 | 132 | for (i = 0; i < 10; i++) { 133 | printf("b[%d]: %d\n", i, b[i]); 134 | } 135 | gcDestroy(); 136 | } 137 | 138 | void force_gc() { 139 | runTest("Running test force_gc", 1); 140 | gcInit(20, 1); 141 | void* mem = gcAlloc(15, &mem, NULL); 142 | void* omg = gcAlloc(6, &omg, NULL); 143 | 144 | gcDestroy(); 145 | } 146 | 147 | void test_gc_functionality() { 148 | runTest("Running test test_gc_functionality", 1); 149 | size_t heapSize = ((sizeof(int)*5)*2); 150 | gcInit(heapSize, 1); 151 | int* a = gcAlloc(sizeof(int)*5, &a, NULL); 152 | int* b = gcAlloc(sizeof(int)*5, &b, NULL); 153 | 154 | int i; 155 | for (i = 0; i < 5; i++) { 156 | b[i] = i; 157 | printf("b[%d] = %d\n", i, b[i]); 158 | } 159 | a = NULL; 160 | gcReg(&a, NULL, NULL, 0); 161 | 162 | int* c = gcAlloc(sizeof(int)*5, &c, NULL); 163 | 164 | for (i = 0; i < 5; i++) { 165 | printf("b[%d] = %d\n", i, b[i]); 166 | } 167 | 168 | gcDestroy(); 169 | } 170 | 171 | void test_gc_multiple_arr() { 172 | runTest("Running test test_gc_multiple_arr", 1); 173 | int heapSize = ((sizeof(int)*5)*4); 174 | gcInit(heapSize, 1); 175 | 176 | int* a = gcAlloc(sizeof(int)*5, &a, NULL); 177 | int* b = gcAlloc(sizeof(int)*5, &b, NULL); 178 | int* c = gcAlloc(sizeof(int)*5, &c, NULL); 179 | int* d = gcAlloc(sizeof(int)*5, &d, NULL); 180 | 181 | int i; 182 | for (i = 0; i < 5; i++) { 183 | a[i] = 1; 184 | b[i] = 2; 185 | c[i] = 3; 186 | d[i] = 4; 187 | } 188 | 189 | printf("Address: %p\n", a); 190 | for (i = 0; i < 5; i++) { 191 | printf("a[%d] = %d\n", i, a[i]); 192 | } 193 | printf("\n"); 194 | 195 | 196 | printf("Address: %p\n", b); 197 | for (i = 0; i < 5; i++) { 198 | printf("b[%d] = %d\n", i, b[i]); 199 | } 200 | printf("\n"); 201 | 202 | 203 | printf("Address: %p\n", c); 204 | for (i = 0; i < 5; i++) { 205 | printf("c[%d] = %d\n", i, c[i]); 206 | } 207 | printf("\n"); 208 | 209 | printf("Address: %p\n", d); 210 | for (i = 0; i < 5; i++) { 211 | printf("d[%d] = %d\n", i, d[i]); 212 | } 213 | printf("\n"); 214 | 215 | b = NULL; 216 | gcReg(&b, NULL, NULL, 0); 217 | 218 | int* e = gcAlloc(sizeof(int)*5, &e, NULL); 219 | 220 | printf("Address: %p\n", a); 221 | for (i = 0; i < 5; i++) { 222 | printf("a[%d] = %d\n", i, a[i]); 223 | } 224 | printf("\n"); 225 | 226 | printf("Address: %p\n", c); 227 | for (i = 0; i < 5; i++) { 228 | printf("c[%d] = %d\n", i, c[i]); 229 | } 230 | printf("\n"); 231 | 232 | printf("Address: %p\n", d); 233 | for (i = 0; i < 5; i++) { 234 | printf("d[%d] = %d\n", i, d[i]); 235 | } 236 | printf("\n"); 237 | 238 | gcDestroy(); 239 | } 240 | 241 | void reachability_gc(size_t heapS) { 242 | runTest("Running test reachability", 1); 243 | 244 | typedef struct B { 245 | int x; 246 | } B; 247 | 248 | typedef struct A { 249 | B* b; 250 | } A; 251 | 252 | size_t heapSize = (sizeof(A)+sizeof(B)); 253 | size_t derpSize = 131049; 254 | gcInit(1000000, 0); 255 | 256 | printf("Allocation\n"); 257 | A* a = gcAlloc(sizeof(A), &a, NULL); 258 | printf("Allocation of a: %p\n", a); 259 | printf("About to set a member in struct a\n"); 260 | a->b = gcAlloc(sizeof(B), &(a->b), &a); 261 | printf("About to set a var\n"); 262 | a->b->x = 0; 263 | printf("a->b->x: %d\n", a->b->x); 264 | a = gcAlloc(sizeof(A), &a, NULL); 265 | a->b = gcAlloc(sizeof(B), &(a->b), &a); 266 | a->b->x = 100; 267 | 268 | printf("a->b->x: %d\n", a->b->x); 269 | 270 | gcDestroy(); 271 | } 272 | 273 | /** 274 | * Main tests 275 | */ 276 | int main(int argc, const char *argv[]) 277 | { 278 | size_t heapSize; 279 | int logging; 280 | 281 | if (argc <= 1) { 282 | logWarning("No CLI arguments given, using default heap size 100.", logging); 283 | heapSize = 1000; 284 | } else { 285 | heapSize = atol(argv[1]); 286 | int log = atoi(argv[2]); 287 | printf("Log: %d\n", log); 288 | logging = (log ? 1 : 0); 289 | printf("Heap size from CLI: %d\n", heapSize); 290 | printf("Logging: %d\n", logging); 291 | } 292 | 293 | logMsg("Running Tests", logging); 294 | printf("------------------\n"); 295 | 296 | // Doubly Linked List Unit Tests 297 | // pop_empty_list(); 298 | // push_single(); 299 | // push_double(); 300 | // pop_single(); 301 | 302 | // Garbage Collector Unit Tests 303 | // init_hook(heapSize); 304 | // alloc_hook(heapSize, 5); 305 | // ptr_no_heap(); // breaking test 306 | // no_space(); // breaking test 307 | // herp_derp_heap(); 308 | heap_chunk(); 309 | heap_test_equality(); 310 | // force_gc(); 311 | test_gc_functionality(); 312 | test_gc_multiple_arr(); 313 | reachability_gc(heapSize); 314 | return 0; 315 | } 316 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | OBJECTS = garbageCollector.o gcLogger.o main.o list.o heapList.o 3 | 4 | gc: $(OBJECTS) 5 | $(CC) $(OBJECTS) -o gc 6 | 7 | clean: 8 | rm *.o gc 9 | --------------------------------------------------------------------------------