From e3e2609f7ea597fe923ef7029df1087bc5fcf471 Mon Sep 17 00:00:00 2001 From: Blaise Tine Date: Sun, 30 Jan 2022 05:57:18 -0500 Subject: [PATCH] adding unit test for vx_malloc --- ci/blackbox.sh | 10 + ci/regression.sh | 9 +- ci/travis_run.py | 28 ++- driver/common/vx_malloc.h | 317 +++++++++++++++--------------- tests/Makefile | 8 +- tests/unittest/Makefile | 8 + tests/unittest/vx_malloc/Makefile | 34 ++++ tests/unittest/vx_malloc/main.cpp | 52 +++++ 8 files changed, 298 insertions(+), 168 deletions(-) create mode 100644 tests/unittest/Makefile create mode 100644 tests/unittest/vx_malloc/Makefile create mode 100644 tests/unittest/vx_malloc/main.cpp diff --git a/ci/blackbox.sh b/ci/blackbox.sh index 88930faf..15257944 100755 --- a/ci/blackbox.sh +++ b/ci/blackbox.sh @@ -144,16 +144,20 @@ if [ $DEBUG -eq 1 ] then if [ $SCOPE -eq 1 ] then + echo "running: DEBUG=$DEBUG_LEVEL SCOPE=1 CONFIGS="$CONFIGS" make -C $DRIVER_PATH" DEBUG=$DEBUG_LEVEL SCOPE=1 CONFIGS="$CONFIGS" make -C $DRIVER_PATH else + echo "running: DEBUG=$DEBUG_LEVEL CONFIGS="$CONFIGS" make -C $DRIVER_PATH" DEBUG=$DEBUG_LEVEL CONFIGS="$CONFIGS" make -C $DRIVER_PATH fi if [ $HAS_ARGS -eq 1 ] then + echo "running: OPTS=$ARGS make -C $APP_PATH run-$DRIVER > run.log 2>&1" OPTS=$ARGS make -C $APP_PATH run-$DRIVER > run.log 2>&1 status=$? else + echo "running: make -C $APP_PATH run-$DRIVER > run.log 2>&1" make -C $APP_PATH run-$DRIVER > run.log 2>&1 status=$? fi @@ -163,18 +167,24 @@ then mv -f $APP_PATH/trace.vcd . fi else + echo "driver initialization..." if [ $SCOPE -eq 1 ] then + echo "running: SCOPE=1 CONFIGS="$CONFIGS" make -C $DRIVER_PATH" SCOPE=1 CONFIGS="$CONFIGS" make -C $DRIVER_PATH else + echo "running: CONFIGS="$CONFIGS" make -C $DRIVER_PATH" CONFIGS="$CONFIGS" make -C $DRIVER_PATH fi + echo "running application..." if [ $HAS_ARGS -eq 1 ] then + echo "running: OPTS=$ARGS make -C $APP_PATH run-$DRIVER" OPTS=$ARGS make -C $APP_PATH run-$DRIVER status=$? else + echo "running: make -C $APP_PATH run-$DRIVER" make -C $APP_PATH run-$DRIVER status=$? fi diff --git a/ci/regression.sh b/ci/regression.sh index b99754af..2b485a63 100755 --- a/ci/regression.sh +++ b/ci/regression.sh @@ -6,6 +6,11 @@ set -e # ensure build make -s +unittest() +{ +make -C tests/unittest run +} + coverage() { echo "begin coverage tests..." @@ -156,11 +161,13 @@ echo "stress1 tests done!" usage() { - echo "usage: regression [-coverage] [-tex] [-cluster] [-debug] [-config] [-stress[#n]] [-all] [-h|--help]" + echo "usage: regression [-unittest] [-coverage] [-tex] [-cluster] [-debug] [-config] [-stress[#n]] [-all] [-h|--help]" } while [ "$1" != "" ]; do case $1 in + -unittest ) unittest + ;; -coverage ) coverage ;; -tex ) tex diff --git a/ci/travis_run.py b/ci/travis_run.py index 5ae46755..b3a3626d 100755 --- a/ci/travis_run.py +++ b/ci/travis_run.py @@ -4,19 +4,22 @@ import time import threading import subprocess -PingInterval = 15 +# This script executes a long-running command while outputing "still running ..." periodically +# to notify Travis build system that the program has not hanged -def PingCallback(stop): +PING_INTERVAL=15 + +def monitor(stop): wait_time = 0 while True: - time.sleep(PingInterval) - wait_time += PingInterval + time.sleep(PING_INTERVAL) + wait_time += PING_INTERVAL print(" + still running (" + str(wait_time) + "s) ...") sys.stdout.flush() if stop(): break -def run_command(command): +def execute(command): process = subprocess.Popen(command, stdout=subprocess.PIPE) while True: output = process.stdout.readline() @@ -24,18 +27,23 @@ def run_command(command): break if output: print output.strip() + sys.stdout.flush() return process.returncode def main(argv): - stop_threads = False - t = threading.Thread(target = PingCallback, args =(lambda : stop_threads, )) + # start monitoring thread + stop_monitor = False + t = threading.Thread(target = monitor, args =(lambda : stop_monitor, )) t.start() - exitcode = run_command(argv) + # execute command + exitcode = execute(argv) print(" + exitcode="+str(exitcode)) - - stop_threads = True + sys.stdout.flush() + + # terminate monitoring thread + stop_monitor = True t.join() sys.exit(exitcode) diff --git a/driver/common/vx_malloc.h b/driver/common/vx_malloc.h index 4d1d1b45..650a2f80 100644 --- a/driver/common/vx_malloc.h +++ b/driver/common/vx_malloc.h @@ -21,11 +21,11 @@ public: ~MemoryAllocator() { // Free allocated pages - page_t* pCurPage = pages_; - while (pCurPage) { - auto nextPage = pCurPage->next; - this->DeletePage(pCurPage); - pCurPage = nextPage; + page_t* currPage = pages_; + while (currPage) { + auto nextPage = currPage->next; + this->DeletePage(currPage); + currPage = nextPage; } } @@ -37,143 +37,146 @@ public: size = AlignSize(size, blockAlign_); // Walk thru all pages to find a free block - block_t* pFreeBlock = nullptr; - auto pCurPage = pages_; - while (pCurPage) { - auto pCurBlock = pCurPage->pFreeSList; - if (pCurBlock) { - // The free list is already sorted with biggest block on top, - // just check if the last block has enough space. - if (pCurBlock->size >= size) { - // Find the smallest matching block - while (pCurBlock->nextFreeS - && (pCurBlock->nextFreeS->size >= size)) { - pCurBlock = pCurBlock->nextFreeS; + block_t* freeBlock = nullptr; + auto currPage = pages_; + while (currPage) { + auto currBlock = currPage->freeSList; + if (currBlock) { + // The free S-list is already sorted with the largest block first + // Quick check if the head block has enough space. + if (currBlock->size >= size) { + // Find the smallest matching block in the S-list + while (currBlock->nextFreeS + && (currBlock->nextFreeS->size >= size)) { + currBlock = currBlock->nextFreeS; } // Return the free block - pFreeBlock = pCurBlock; + freeBlock = currBlock; break; } } - pCurPage = pCurPage->next; + currPage = currPage->next; } - if (nullptr == pFreeBlock) { + if (nullptr == freeBlock) { // Allocate a new page for this request - pCurPage = this->NewPage(size); - if (nullptr == pCurPage) + currPage = this->NewPage(size); + if (nullptr == currPage) return -1; - pFreeBlock = pCurPage->pFreeSList; + freeBlock = currPage->freeSList; } // Remove the block from the free lists - assert(pFreeBlock->size >= size); - pCurPage->RemoveFreeMBlock(pFreeBlock); - pCurPage->RemoveFreeSBlock(pFreeBlock); + assert(freeBlock->size >= size); + currPage->RemoveFreeMBlock(freeBlock); + currPage->RemoveFreeSBlock(freeBlock); // If the free block we have found is larger than what we are looking for, // we may be able to split our free block in two. - uint64_t extraBytes = pFreeBlock->size - size; + uint64_t extraBytes = freeBlock->size - size; if (extraBytes >= blockAlign_) { // Reduce the free block size to the requested value - pFreeBlock->size = size; + freeBlock->size = size; // Allocate a new block to contain the extra buffer - auto nextAddr = pFreeBlock->addr + size; - auto pNewBlock = new block_t(nextAddr, extraBytes); + auto nextAddr = freeBlock->addr + size; + auto newBlock = new block_t(nextAddr, extraBytes); // Add the new block to the free lists - pCurPage->InsertFreeMBlock(pNewBlock); - pCurPage->InsertFreeSBlock(pNewBlock); + currPage->InsertFreeMBlock(newBlock); + currPage->InsertFreeSBlock(newBlock); } // Insert the free block into the used list - pCurPage->InsertUsedBlock(pFreeBlock); + currPage->InsertUsedBlock(freeBlock); // Return the free block address - *addr = pFreeBlock->addr; + *addr = freeBlock->addr; return 0; } int release(uint64_t addr) { // Walk all pages to find the pointer - block_t* pUsedBlock = nullptr; - auto pCurPage = pages_; - while (pCurPage) { - if ((pCurPage->addr < addr) - && ((pCurPage->addr + pCurPage->size) > addr)) { - auto pCurBlock = pCurPage->pUsedList; - while (pCurBlock) { - if (pCurBlock->addr == addr) { - pUsedBlock = pCurBlock; + block_t* usedBlock = nullptr; + auto currPage = pages_; + while (currPage) { + if (addr >= currPage->addr + && addr < (currPage->addr + currPage->size)) { + auto currBlock = currPage->usedList; + while (currBlock) { + if (currBlock->addr == addr) { + usedBlock = currBlock; break; } - pCurBlock = pCurBlock->nextUsed; + currBlock = currBlock->nextUsed; } - if (pUsedBlock) - break; + break; } - pCurPage = pCurPage->next; + currPage = currPage->next; } // found the corresponding block? - if (nullptr == pUsedBlock) + if (nullptr == usedBlock) return -1; // Remove the block from the used list - pCurPage->RemoveUsedBlock(pUsedBlock); + currPage->RemoveUsedBlock(usedBlock); // Insert the block into the free M-list. - pCurPage->InsertFreeMBlock(pUsedBlock); + currPage->InsertFreeMBlock(usedBlock); // Check if we can merge adjacent free blocks from the left. - if (pUsedBlock->prevFreeM) { + if (usedBlock->prevFreeM) { // Calculate the previous address - auto prevAddr = pUsedBlock->prevFreeM->addr + pUsedBlock->prevFreeM->size; - if (pUsedBlock->addr == prevAddr) { - auto pMergedBlock = pUsedBlock->prevFreeM; - - // Detach left block from the free S-list - pCurPage->RemoveFreeSBlock(pMergedBlock); + auto prevAddr = usedBlock->prevFreeM->addr + usedBlock->prevFreeM->size; + if (usedBlock->addr == prevAddr) { + auto prevBlock = usedBlock->prevFreeM; // Merge the blocks to the left - pMergedBlock->size += pUsedBlock->size; - pMergedBlock->nextFreeM = pUsedBlock->nextFreeM; - if (pMergedBlock->nextFreeM) { - pMergedBlock->nextFreeM->prevFreeM = pMergedBlock; + prevBlock->size += usedBlock->size; + prevBlock->nextFreeM = usedBlock->nextFreeM; + if (prevBlock->nextFreeM) { + prevBlock->nextFreeM->prevFreeM = prevBlock; } - pUsedBlock = pMergedBlock; + + // Detach previous block from the free S-list since size increased + currPage->RemoveFreeSBlock(prevBlock); + + // reset usedBlock + delete usedBlock; + usedBlock = prevBlock; } } // Check if we can merge adjacent free blocks from the right. - if (pUsedBlock->nextFreeM) { + if (usedBlock->nextFreeM) { // Calculate the next allocation start address - auto nextMem = pUsedBlock->addr + pUsedBlock->size; - if (pUsedBlock->nextFreeM->addr == nextMem) { - auto nextBlock = pUsedBlock->nextFreeM; - - // Detach right block from the free S-list - pCurPage->RemoveFreeSBlock(nextBlock); + auto nextAddr = usedBlock->addr + usedBlock->size; + if (usedBlock->nextFreeM->addr == nextAddr) { + auto nextBlock = usedBlock->nextFreeM; // Merge the blocks to the right - pUsedBlock->size += nextBlock->size; - pUsedBlock->nextFreeM = nextBlock->nextFreeM; - if (pUsedBlock->nextFreeM) { - pUsedBlock->nextFreeM->prevFreeM = pUsedBlock; + usedBlock->size += nextBlock->size; + usedBlock->nextFreeM = nextBlock->nextFreeM; + if (usedBlock->nextFreeM) { + usedBlock->nextFreeM->prevFreeM = usedBlock; } + + // Delete next block + currPage->RemoveFreeSBlock(nextBlock); + delete nextBlock; } } // Insert the block into the free S-list. - pCurPage->InsertFreeSBlock(pUsedBlock); + currPage->InsertFreeSBlock(usedBlock); // Check if we can free empty pages - if (nullptr == pCurPage->pUsedList) { + if (nullptr == currPage->usedList) { // Try to delete the page - while (pCurPage && this->DeletePage(pCurPage)) { - pCurPage = this->NextEmptyPage(); + while (currPage && this->DeletePage(currPage)) { + currPage = this->NextEmptyPage(); } } @@ -212,110 +215,110 @@ private: page_t* next; // List of used blocks - block_t* pUsedList; + block_t* usedList; // List with blocks sorted by descreasing sizes // Used for block lookup during memory allocation. - block_t* pFreeSList; + block_t* freeSList; // List with blocks sorted by increasing memory addresses // Used for block merging during memory release. - block_t* pFreeMList; + block_t* freeMList; uint64_t addr; uint64_t size; page_t(uint64_t addr, uint64_t size) : next(nullptr), - pUsedList(nullptr), + usedList(nullptr), addr(addr), size(size) { - pFreeSList = pFreeMList = new block_t(addr, size); + freeSList = freeMList = new block_t(addr, size); } - void InsertUsedBlock(block_t* pBlock) { - pBlock->nextUsed = pUsedList; - if (pUsedList) { - pUsedList->prevUsed = pBlock; + void InsertUsedBlock(block_t* block) { + block->nextUsed = usedList; + if (usedList) { + usedList->prevUsed = block; } - pUsedList = pBlock; + usedList = block; } - void RemoveUsedBlock(block_t* pBlock) { - if (pBlock->prevUsed) { - pBlock->prevUsed->nextUsed = pBlock->nextUsed; + void RemoveUsedBlock(block_t* block) { + if (block->prevUsed) { + block->prevUsed->nextUsed = block->nextUsed; } else { - pUsedList = pBlock->nextUsed; + usedList = block->nextUsed; } - if (pBlock->nextUsed) { - pBlock->nextUsed->prevUsed = pBlock->prevUsed; + if (block->nextUsed) { + block->nextUsed->prevUsed = block->prevUsed; } - pBlock->nextUsed = nullptr; - pBlock->prevUsed = nullptr; + block->nextUsed = nullptr; + block->prevUsed = nullptr; } - void InsertFreeMBlock(block_t* pBlock) { - block_t* pCurBlock = pFreeMList; + void InsertFreeMBlock(block_t* block) { + block_t* currBlock = freeMList; block_t* prevBlock = nullptr; - while (pCurBlock && (pCurBlock->addr < pBlock->addr)) { - prevBlock = pCurBlock; - pCurBlock = pCurBlock->nextFreeM; + while (currBlock && (currBlock->addr < block->addr)) { + prevBlock = currBlock; + currBlock = currBlock->nextFreeM; } - pBlock->nextFreeM = pCurBlock; - pBlock->prevFreeM = prevBlock; + block->nextFreeM = currBlock; + block->prevFreeM = prevBlock; if (prevBlock) { - prevBlock->nextFreeM = pBlock; + prevBlock->nextFreeM = block; } else { - pFreeMList = pBlock; + freeMList = block; } - if (pCurBlock) { - pCurBlock->prevFreeM = pBlock; + if (currBlock) { + currBlock->prevFreeM = block; } } - void RemoveFreeMBlock(block_t* pBlock) { - if (pBlock->prevFreeM) { - pBlock->prevFreeM->nextFreeM = pBlock->nextFreeM; + void RemoveFreeMBlock(block_t* block) { + if (block->prevFreeM) { + block->prevFreeM->nextFreeM = block->nextFreeM; } else { - pFreeMList = pBlock->nextFreeM; + freeMList = block->nextFreeM; } - if (pBlock->nextFreeM) { - pBlock->nextFreeM->prevFreeM = pBlock->prevFreeM; + if (block->nextFreeM) { + block->nextFreeM->prevFreeM = block->prevFreeM; } - pBlock->nextFreeM = nullptr; - pBlock->prevFreeM = nullptr; + block->nextFreeM = nullptr; + block->prevFreeM = nullptr; } - void InsertFreeSBlock(block_t* pBlock) { - block_t* pCurBlock = this->pFreeSList; + void InsertFreeSBlock(block_t* block) { + block_t* currBlock = this->freeSList; block_t* prevBlock = nullptr; - while (pCurBlock && (pCurBlock->size > pBlock->size)) { - prevBlock = pCurBlock; - pCurBlock = pCurBlock->nextFreeS; + while (currBlock && (currBlock->size > block->size)) { + prevBlock = currBlock; + currBlock = currBlock->nextFreeS; } - pBlock->nextFreeS = pCurBlock; - pBlock->prevFreeS = prevBlock; + block->nextFreeS = currBlock; + block->prevFreeS = prevBlock; if (prevBlock) { - prevBlock->nextFreeS = pBlock; + prevBlock->nextFreeS = block; } else { - this->pFreeSList = pBlock; + this->freeSList = block; } - if (pCurBlock) { - pCurBlock->prevFreeS = pBlock; + if (currBlock) { + currBlock->prevFreeS = block; } } - void RemoveFreeSBlock(block_t* pBlock) { - if (pBlock->prevFreeS) { - pBlock->prevFreeS->nextFreeS = pBlock->nextFreeS; + void RemoveFreeSBlock(block_t* block) { + if (block->prevFreeS) { + block->prevFreeS->nextFreeS = block->nextFreeS; } else { - pFreeSList = pBlock->nextFreeS; + freeSList = block->nextFreeS; } - if (pBlock->nextFreeS) { - pBlock->nextFreeS->prevFreeS = pBlock->prevFreeS; + if (block->nextFreeS) { + block->nextFreeS->prevFreeS = block->prevFreeS; } - pBlock->nextFreeS = nullptr; - pBlock->prevFreeS = nullptr; + block->nextFreeS = nullptr; + block->prevFreeS = nullptr; } }; @@ -332,54 +335,58 @@ private: if (nextAddress_ > maxAddress_) return nullptr; - // Allocate the page - auto pNewPage = new page_t(addr, size); + // Allocate object + auto newPage = new page_t(addr, size); // Insert the new page into the list - pNewPage->next = pages_; - pages_ = pNewPage; + newPage->next = pages_; + pages_ = newPage; - return pNewPage; + return newPage; } - bool DeletePage(page_t* pPage) { + bool DeletePage(page_t* page) { // The page should be empty - assert(nullptr == pPage->pUsedList); - assert(pPage->pFreeMList && (nullptr == pPage->pFreeMList->nextFreeM)); + assert(nullptr == page->usedList); + assert(page->freeMList && (nullptr == page->freeMList->nextFreeM)); // Only delete top-level pages - auto nextAddr = pPage->addr + pPage->size; + auto nextAddr = page->addr + page->size; if (nextAddr != nextAddress_) return false; // Remove the page from the list page_t* prevPage = nullptr; - auto pCurPage = pages_; - while (pCurPage) { - if (pCurPage == pPage) { + auto currPage = pages_; + while (currPage) { + if (currPage == page) { if (prevPage) { - prevPage->next = pCurPage->next; + prevPage->next = currPage->next; } else { - pages_ = pCurPage->next; + pages_ = currPage->next; } break; } - prevPage = pCurPage; - pCurPage = pCurPage->next; + prevPage = currPage; + currPage = currPage->next; } // Update next allocation address - nextAddress_ = pPage->addr; + nextAddress_ = page->addr; + + // free object + delete page->freeMList; + delete page; return true; } page_t* NextEmptyPage() { - auto pCurPage = pages_; - while (pCurPage) { - if (nullptr == pCurPage->pUsedList) - return pCurPage; - pCurPage = pCurPage->next; + auto currPage = pages_; + while (currPage) { + if (nullptr == currPage->usedList) + return currPage; + currPage = currPage->next; } return nullptr; } diff --git a/tests/Makefile b/tests/Makefile index 7c387b7b..0304f20a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,4 @@ -all: runtime regression opencl riscv +all: runtime regression opencl riscv unittest runtime: $(MAKE) -C runtime @@ -12,10 +12,14 @@ opencl: riscv: $(MAKE) -C riscv +unittest: + $(MAKE) -C unittest + clean: $(MAKE) clean -C runtime $(MAKE) clean -C regression $(MAKE) clean -C opencl $(MAKE) clean -C riscv + $(MAKE) clean -C unittest -.PHONY: all runtime regression opencl riscv \ No newline at end of file +.PHONY: all runtime regression opencl riscv unittest \ No newline at end of file diff --git a/tests/unittest/Makefile b/tests/unittest/Makefile new file mode 100644 index 00000000..ee106354 --- /dev/null +++ b/tests/unittest/Makefile @@ -0,0 +1,8 @@ +all: + $(MAKE) -C vx_malloc + +run: + $(MAKE) -C vx_malloc run + +clean: + $(MAKE) -C vx_malloc clean \ No newline at end of file diff --git a/tests/unittest/vx_malloc/Makefile b/tests/unittest/vx_malloc/Makefile new file mode 100644 index 00000000..1581ba94 --- /dev/null +++ b/tests/unittest/vx_malloc/Makefile @@ -0,0 +1,34 @@ +VORTEX_DRV_PATH ?= $(realpath ../../../driver) + +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Wfatal-errors + +CXXFLAGS += -I$(VORTEX_DRV_PATH)/common + +# Debugigng +ifdef DEBUG + CXXFLAGS += -g -O0 +else + CXXFLAGS += -O2 -DNDEBUG +endif + +PROJECT = vx_malloc + +SRCS = main.cpp + +all: $(PROJECT) + +$(PROJECT): $(SRCS) + $(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ + +run: + ./$(PROJECT) + +clean: + rm -rf $(PROJECT) *.o .depend + +clean-all: clean + rm -rf *.elf *.bin *.dump + +ifneq ($(MAKECMDGOALS),clean) + -include .depend +endif \ No newline at end of file diff --git a/tests/unittest/vx_malloc/main.cpp b/tests/unittest/vx_malloc/main.cpp new file mode 100644 index 00000000..3dd10a58 --- /dev/null +++ b/tests/unittest/vx_malloc/main.cpp @@ -0,0 +1,52 @@ +#include +#include + +#define RT_CHECK(_expr) \ + do { \ + int _ret = _expr; \ + if (0 == _ret) \ + break; \ + printf("Error: '%s' returned %d!\n", #_expr, (int)_ret); \ + return -1; \ + } while (false) + +static uint64_t minAddress = 0; +static uint64_t maxAddress = 0xffffffff; +static uint32_t pageAlign = 4096; +static uint32_t blockAlign = 64; + +int main() { + + auto allocator = new vortex::MemoryAllocator( + minAddress, maxAddress, pageAlign, blockAlign + ); + + uint64_t a0, a1, a2, a3; + + RT_CHECK(allocator->allocate(128, &a0)); + RT_CHECK(allocator->release(a0)); + + RT_CHECK(allocator->allocate(1, &a0)); + RT_CHECK(allocator->allocate(1, &a1)); + RT_CHECK(allocator->allocate(1, &a2)); + RT_CHECK(allocator->release(a1)); + RT_CHECK(allocator->allocate(1, &a3)); + RT_CHECK(allocator->release(a0)); + RT_CHECK(allocator->release(a2)); + RT_CHECK(allocator->release(a3)); + + RT_CHECK(allocator->allocate(5878, &a0)); + RT_CHECK(allocator->allocate(4095, &a1)); + RT_CHECK(allocator->allocate(1, &a2)); + RT_CHECK(allocator->allocate(1, &a3)); + RT_CHECK(allocator->release(a0)); + RT_CHECK(allocator->release(a1)); + RT_CHECK(allocator->release(a2)); + RT_CHECK(allocator->release(a3)); + + delete allocator; + + printf("PASSED!\n"); + + return 0; +} \ No newline at end of file