fork
Fork of cpputest by
Diff: src/CppUTest/MemoryLeakDetector.cpp
- Revision:
- 1:4769360130ed
- Parent:
- 0:0b799af9d58e
diff -r 0b799af9d58e -r 4769360130ed src/CppUTest/MemoryLeakDetector.cpp --- a/src/CppUTest/MemoryLeakDetector.cpp Tue Jan 28 09:27:41 2014 +0000 +++ b/src/CppUTest/MemoryLeakDetector.cpp Tue Jun 17 15:52:54 2014 +0100 @@ -1,621 +1,621 @@ -/* - * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the <organization> nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "CppUTest/TestHarness.h" -#include "CppUTest/MemoryLeakDetector.h" -#include "CppUTest/TestMemoryAllocator.h" -#include "CppUTest/PlatformSpecificFunctions.h" - -#define UNKNOWN ((char*)("<unknown>")) - -SimpleStringBuffer::SimpleStringBuffer() : - positions_filled_(0), write_limit_(SIMPLE_STRING_BUFFER_LEN-1) -{ -} - -void SimpleStringBuffer::clear() -{ - positions_filled_ = 0; - buffer_[0] = '\0'; -} - -void SimpleStringBuffer::add(const char* format, ...) -{ - int count = 0; - size_t positions_left = write_limit_ - positions_filled_; - if (positions_left <= 0) return; - - va_list arguments; - va_start(arguments, format); - count = PlatformSpecificVSNprintf(buffer_ + positions_filled_, positions_left+1, format, arguments); - if (count > 0) positions_filled_ += (size_t) count; - if (positions_filled_ > write_limit_) positions_filled_ = write_limit_; - va_end(arguments); -} - -char* SimpleStringBuffer::toString() -{ - return buffer_; -} - -void SimpleStringBuffer::setWriteLimit(size_t write_limit) -{ - write_limit_ = write_limit; - if (write_limit_ > SIMPLE_STRING_BUFFER_LEN-1) - write_limit_ = SIMPLE_STRING_BUFFER_LEN-1; -} -void SimpleStringBuffer::resetWriteLimit() -{ - write_limit_ = SIMPLE_STRING_BUFFER_LEN-1; -} - -bool SimpleStringBuffer::reachedItsCapacity() -{ - return positions_filled_ >= write_limit_; -} - -//////////////////////// - -#define MEM_LEAK_TOO_MUCH "\netc etc etc etc. !!!! Too much memory leaks to report. Bailing out\n" -#define MEM_LEAK_FOOTER "Total number of leaks: " -#define MEM_LEAK_ADDITION_MALLOC_WARNING "NOTE:\n" \ - "\tMemory leak reports about malloc and free can be caused by allocating using the cpputest version of malloc,\n" \ - "\tbut deallocate using the standard free.\n" \ - "\tIf this is the case, check whether your malloc/free replacements are working (#define malloc cpputest_malloc etc).\n" - -MemoryLeakOutputStringBuffer::MemoryLeakOutputStringBuffer() - : total_leaks_(0), giveWarningOnUsingMalloc_(false) -{ -} - -void MemoryLeakOutputStringBuffer::addAllocationLocation(const char* allocationFile, int allocationLineNumber, size_t allocationSize, TestMemoryAllocator* allocator) -{ - outputBuffer_.add(" allocated at file: %s line: %d size: %lu type: %s\n", allocationFile, allocationLineNumber, (unsigned long) allocationSize, allocator->alloc_name()); -} - -void MemoryLeakOutputStringBuffer::addDeallocationLocation(const char* freeFile, int freeLineNumber, TestMemoryAllocator* allocator) -{ - outputBuffer_.add(" deallocated at file: %s line: %d type: %s\n", freeFile, freeLineNumber, allocator->free_name()); -} - -void MemoryLeakOutputStringBuffer::addNoMemoryLeaksMessage() -{ - outputBuffer_.add("No memory leaks were detected."); -} - -void MemoryLeakOutputStringBuffer::startMemoryLeakReporting() -{ - giveWarningOnUsingMalloc_ = false; - total_leaks_ = 0; - - size_t memory_leak_normal_footer_size = sizeof(MEM_LEAK_FOOTER) + 10 + sizeof(MEM_LEAK_TOO_MUCH); /* the number of leaks */ - size_t memory_leak_foot_size_with_malloc_warning = memory_leak_normal_footer_size + sizeof(MEM_LEAK_ADDITION_MALLOC_WARNING); - - outputBuffer_.setWriteLimit(SimpleStringBuffer::SIMPLE_STRING_BUFFER_LEN - memory_leak_foot_size_with_malloc_warning); -} - -void MemoryLeakOutputStringBuffer::reportMemoryLeak(MemoryLeakDetectorNode* leak) -{ - if (total_leaks_ == 0) { - addMemoryLeakHeader(); - } - - total_leaks_++; - outputBuffer_.add("Alloc num (%u) Leak size: %lu Allocated at: %s and line: %d. Type: \"%s\"\n\t Memory: <%p> Content: \"%.15s\"\n", - leak->number_, (unsigned long) leak->size_, leak->file_, leak->line_, leak->allocator_->alloc_name(), leak->memory_, leak->memory_); - - if (PlatformSpecificStrCmp(leak->allocator_->alloc_name(), "malloc") == 0) - giveWarningOnUsingMalloc_ = true; -} - -void MemoryLeakOutputStringBuffer::stopMemoryLeakReporting() -{ - if (total_leaks_ == 0) { - addNoMemoryLeaksMessage(); - return; - } - - bool buffer_reached_its_capacity = outputBuffer_.reachedItsCapacity(); - outputBuffer_.resetWriteLimit(); - - if (buffer_reached_its_capacity) - addErrorMessageForTooMuchLeaks(); - - addMemoryLeakFooter(total_leaks_); - - if (giveWarningOnUsingMalloc_) - addWarningForUsingMalloc(); - -} - -void MemoryLeakOutputStringBuffer::addMemoryLeakHeader() -{ - outputBuffer_.add("Memory leak(s) found.\n"); -} - -void MemoryLeakOutputStringBuffer::addErrorMessageForTooMuchLeaks() -{ - outputBuffer_.add(MEM_LEAK_TOO_MUCH); -} - -void MemoryLeakOutputStringBuffer::addMemoryLeakFooter(int amountOfLeaks) -{ - outputBuffer_.add("%s %d\n", MEM_LEAK_FOOTER, amountOfLeaks); -} - -void MemoryLeakOutputStringBuffer::addWarningForUsingMalloc() -{ - outputBuffer_.add(MEM_LEAK_ADDITION_MALLOC_WARNING); -} - -void MemoryLeakOutputStringBuffer::reportDeallocateNonAllocatedMemoryFailure(const char* freeFile, int freeLine, TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) -{ - reportFailure("Deallocating non-allocated memory\n", "<unknown>", 0, 0, NullUnknownAllocator::defaultAllocator(), freeFile, freeLine, freeAllocator, reporter); -} - -void MemoryLeakOutputStringBuffer::reportAllocationDeallocationMismatchFailure(MemoryLeakDetectorNode* node, const char* freeFile, int freeLineNumber, TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) -{ - reportFailure("Allocation/deallocation type mismatch\n", node->file_, node->line_, node->size_, node->allocator_, freeFile, freeLineNumber, freeAllocator, reporter); -} - -void MemoryLeakOutputStringBuffer::reportMemoryCorruptionFailure(MemoryLeakDetectorNode* node, const char* freeFile, int freeLineNumber, TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) -{ - reportFailure("Memory corruption (written out of bounds?)\n", node->file_, node->line_, node->size_, node->allocator_, freeFile, freeLineNumber, freeAllocator, reporter); -} - -void MemoryLeakOutputStringBuffer::reportFailure(const char* message, const char* allocFile, int allocLine, size_t allocSize, TestMemoryAllocator* allocAllocator, const char* freeFile, int freeLine, - TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) -{ - outputBuffer_.add("%s", message); - addAllocationLocation(allocFile, allocLine, allocSize, allocAllocator); - addDeallocationLocation(freeFile, freeLine, freeAllocator); - reporter->fail(toString()); -} - - -char* MemoryLeakOutputStringBuffer::toString() -{ - return outputBuffer_.toString(); -} - -void MemoryLeakOutputStringBuffer::clear() -{ - outputBuffer_.clear(); -} - -//////////////////////// - -void MemoryLeakDetectorNode::init(char* memory, unsigned number, size_t size, TestMemoryAllocator* allocator, MemLeakPeriod period, const char* file, int line) -{ - number_ = number; - memory_ = memory; - size_ = size; - allocator_ = allocator; - period_ = period; - file_ = file; - line_ = line; -} - -/////////////////////// - -bool MemoryLeakDetectorList::isInPeriod(MemoryLeakDetectorNode* node, MemLeakPeriod period) -{ - return period == mem_leak_period_all || node->period_ == period || (node->period_ != mem_leak_period_disabled && period == mem_leak_period_enabled); -} - -void MemoryLeakDetectorList::clearAllAccounting(MemLeakPeriod period) -{ - MemoryLeakDetectorNode* cur = head_; - MemoryLeakDetectorNode* prev = 0; - - while (cur) { - if (isInPeriod(cur, period)) { - if (prev) { - prev->next_ = cur->next_; - cur = prev; - } - else { - head_ = cur->next_; - cur = head_; - continue; - } - } - prev = cur; - cur = cur->next_; - } -} - -void MemoryLeakDetectorList::addNewNode(MemoryLeakDetectorNode* node) -{ - node->next_ = head_; - head_ = node; -} - -MemoryLeakDetectorNode* MemoryLeakDetectorList::removeNode(char* memory) -{ - MemoryLeakDetectorNode* cur = head_; - MemoryLeakDetectorNode* prev = 0; - while (cur) { - if (cur->memory_ == memory) { - if (prev) { - prev->next_ = cur->next_; - return cur; - } - else { - head_ = cur->next_; - return cur; - } - } - prev = cur; - cur = cur->next_; - } - return 0; -} - -MemoryLeakDetectorNode* MemoryLeakDetectorList::retrieveNode(char* memory) -{ - MemoryLeakDetectorNode* cur = head_; - while (cur) { - if (cur->memory_ == memory) - return cur; - cur = cur->next_; - } - return NULL; -} - -MemoryLeakDetectorNode* MemoryLeakDetectorList::getLeakFrom(MemoryLeakDetectorNode* node, MemLeakPeriod period) -{ - for (MemoryLeakDetectorNode* cur = node; cur; cur = cur->next_) - if (isInPeriod(cur, period)) return cur; - return 0; -} - -MemoryLeakDetectorNode* MemoryLeakDetectorList::getFirstLeak(MemLeakPeriod period) -{ - return getLeakFrom(head_, period); -} - -MemoryLeakDetectorNode* MemoryLeakDetectorList::getNextLeak(MemoryLeakDetectorNode* node, MemLeakPeriod period) -{ - return getLeakFrom(node->next_, period); -} - -int MemoryLeakDetectorList::getTotalLeaks(MemLeakPeriod period) -{ - int total_leaks = 0; - for (MemoryLeakDetectorNode* node = head_; node; node = node->next_) { - if (isInPeriod(node, period)) total_leaks++; - } - return total_leaks; -} - -bool MemoryLeakDetectorList::hasLeaks(MemLeakPeriod period) -{ - for (MemoryLeakDetectorNode* node = head_; node; node = node->next_) - if (isInPeriod(node, period)) return true; - return false; -} - -///////////////////////////////////////////////////////////// - -unsigned long MemoryLeakDetectorTable::hash(char* memory) -{ - return (unsigned long)((size_t)memory % hash_prime); -} - -void MemoryLeakDetectorTable::clearAllAccounting(MemLeakPeriod period) -{ - for (int i = 0; i < hash_prime; i++) - table_[i].clearAllAccounting(period); -} - -void MemoryLeakDetectorTable::addNewNode(MemoryLeakDetectorNode* node) -{ - table_[hash(node->memory_)].addNewNode(node); -} - -MemoryLeakDetectorNode* MemoryLeakDetectorTable::removeNode(char* memory) -{ - return table_[hash(memory)].removeNode(memory); -} - -MemoryLeakDetectorNode* MemoryLeakDetectorTable::retrieveNode(char* memory) -{ - return table_[hash(memory)].retrieveNode(memory); -} - -bool MemoryLeakDetectorTable::hasLeaks(MemLeakPeriod period) -{ - for (int i = 0; i < hash_prime; i++) - if (table_[i].hasLeaks(period)) return true; - return false; -} - -int MemoryLeakDetectorTable::getTotalLeaks(MemLeakPeriod period) -{ - int total_leaks = 0; - for (int i = 0; i < hash_prime; i++) - total_leaks += table_[i].getTotalLeaks(period); - return total_leaks; -} - -MemoryLeakDetectorNode* MemoryLeakDetectorTable::getFirstLeak(MemLeakPeriod period) -{ - for (int i = 0; i < hash_prime; i++) { - MemoryLeakDetectorNode* node = table_[i].getFirstLeak(period); - if (node) return node; - } - return 0; -} - -MemoryLeakDetectorNode* MemoryLeakDetectorTable::getNextLeak(MemoryLeakDetectorNode* leak, MemLeakPeriod period) -{ - unsigned long i = hash(leak->memory_); - MemoryLeakDetectorNode* node = table_[i].getNextLeak(leak, period); - if (node) return node; - - for (++i; i < hash_prime; i++) { - node = table_[i].getFirstLeak(period); - if (node) return node; - } - return 0; -} - -///////////////////////////////////////////////////////////// - -MemoryLeakDetector::MemoryLeakDetector(MemoryLeakFailure* reporter) -{ - doAllocationTypeChecking_ = true; - allocationSequenceNumber_ = 1; - current_period_ = mem_leak_period_disabled; - reporter_ = reporter; - outputBuffer_ = MemoryLeakOutputStringBuffer(); - memoryTable_ = MemoryLeakDetectorTable(); -} - -void MemoryLeakDetector::clearAllAccounting(MemLeakPeriod period) -{ - memoryTable_.clearAllAccounting(period); -} - -void MemoryLeakDetector::startChecking() -{ - outputBuffer_.clear(); - current_period_ = mem_leak_period_checking; -} - -void MemoryLeakDetector::stopChecking() -{ - current_period_ = mem_leak_period_enabled; -} - -void MemoryLeakDetector::enable() -{ - current_period_ = mem_leak_period_enabled; -} - -void MemoryLeakDetector::disable() -{ - current_period_ = mem_leak_period_disabled; -} - -void MemoryLeakDetector::disableAllocationTypeChecking() -{ - doAllocationTypeChecking_ = false; -} - -void MemoryLeakDetector::enableAllocationTypeChecking() -{ - doAllocationTypeChecking_ = true; -} - -unsigned MemoryLeakDetector::getCurrentAllocationNumber() -{ - return allocationSequenceNumber_; -} - -static size_t calculateVoidPointerAlignedSize(size_t size) -{ - return (sizeof(void*) - (size % sizeof(void*))) + size; -} - -size_t MemoryLeakDetector::sizeOfMemoryWithCorruptionInfo(size_t size) -{ - return calculateVoidPointerAlignedSize(size + memory_corruption_buffer_size); -} - -MemoryLeakDetectorNode* MemoryLeakDetector::getNodeFromMemoryPointer(char* memory, size_t memory_size) -{ - return (MemoryLeakDetectorNode*) (void*) (memory + sizeOfMemoryWithCorruptionInfo(memory_size)); -} - -void MemoryLeakDetector::storeLeakInformation(MemoryLeakDetectorNode * node, char *new_memory, size_t size, TestMemoryAllocator *allocator, const char *file, int line) -{ - node->init(new_memory, allocationSequenceNumber_++, size, allocator, current_period_, file, line); - addMemoryCorruptionInformation(node->memory_ + node->size_); - memoryTable_.addNewNode(node); -} - -char* MemoryLeakDetector::reallocateMemoryAndLeakInformation(TestMemoryAllocator* allocator, char* memory, size_t size, const char* file, int line, bool allocatNodesSeperately) -{ - char* new_memory = reallocateMemoryWithAccountingInformation(allocator, memory, size, file, line, allocatNodesSeperately); - if (new_memory == NULL) return NULL; - - MemoryLeakDetectorNode *node = createMemoryLeakAccountingInformation(allocator, size, new_memory, allocatNodesSeperately); - storeLeakInformation(node, new_memory, size, allocator, file, line); - return node->memory_; -} - -void MemoryLeakDetector::invalidateMemory(char* memory) -{ - MemoryLeakDetectorNode* node = memoryTable_.retrieveNode(memory); - if (node) - PlatformSpecificMemset(memory, 0xCD, node->size_); -} - -void MemoryLeakDetector::addMemoryCorruptionInformation(char* memory) -{ - memory[0] = 'B'; - memory[1] = 'A'; - memory[2] = 'S'; -} - -bool MemoryLeakDetector::validMemoryCorruptionInformation(char* memory) -{ - return memory[0] == 'B' && memory[1] == 'A' && memory[2] == 'S'; -} - -bool MemoryLeakDetector::matchingAllocation(TestMemoryAllocator *alloc_allocator, TestMemoryAllocator *free_allocator) -{ - if (alloc_allocator == free_allocator) return true; - if (!doAllocationTypeChecking_) return true; - return free_allocator->isOfEqualType(alloc_allocator); -} - -void MemoryLeakDetector::checkForCorruption(MemoryLeakDetectorNode* node, const char* file, int line, TestMemoryAllocator* allocator, bool allocateNodesSeperately) -{ - if (!matchingAllocation(node->allocator_, allocator)) - outputBuffer_.reportAllocationDeallocationMismatchFailure(node, file, line, allocator, reporter_); - else if (!validMemoryCorruptionInformation(node->memory_ + node->size_)) - outputBuffer_.reportMemoryCorruptionFailure(node, file, line, allocator, reporter_); - else if (allocateNodesSeperately) - allocator->freeMemoryLeakNode((char*) node); -} - -char* MemoryLeakDetector::allocMemory(TestMemoryAllocator* allocator, size_t size, bool allocatNodesSeperately) -{ - return allocMemory(allocator, size, UNKNOWN, 0, allocatNodesSeperately); -} - -char* MemoryLeakDetector::allocateMemoryWithAccountingInformation(TestMemoryAllocator* allocator, size_t size, const char* file, int line, bool allocatNodesSeperately) -{ - if (allocatNodesSeperately) return allocator->alloc_memory(sizeOfMemoryWithCorruptionInfo(size), file, line); - else return allocator->alloc_memory(sizeOfMemoryWithCorruptionInfo(size) + sizeof(MemoryLeakDetectorNode), file, line); -} - -char* MemoryLeakDetector::reallocateMemoryWithAccountingInformation(TestMemoryAllocator* /*allocator*/, char* memory, size_t size, const char* /*file*/, int /*line*/, bool allocatNodesSeperately) -{ - if (allocatNodesSeperately) return (char*) PlatformSpecificRealloc(memory, sizeOfMemoryWithCorruptionInfo(size)); - else return (char*) PlatformSpecificRealloc(memory, sizeOfMemoryWithCorruptionInfo(size) + sizeof(MemoryLeakDetectorNode)); -} - -MemoryLeakDetectorNode* MemoryLeakDetector::createMemoryLeakAccountingInformation(TestMemoryAllocator* allocator, size_t size, char* memory, bool allocatNodesSeperately) -{ - if (allocatNodesSeperately) return (MemoryLeakDetectorNode*) (void*) allocator->allocMemoryLeakNode(sizeof(MemoryLeakDetectorNode)); - else return getNodeFromMemoryPointer(memory, size); -} - -char* MemoryLeakDetector::allocMemory(TestMemoryAllocator* allocator, size_t size, const char* file, int line, bool allocatNodesSeperately) -{ - /* With malloc, it is harder to guarantee that the allocator free is called. - * This is because operator new is overloaded via linker symbols, but malloc just via #defines. - * If the same allocation is used and the wrong free is called, it will deallocate the memory leak information - * without the memory leak detector ever noticing it! - * So, for malloc, we'll allocate the memory separately so we can detect this and give a proper error. - */ - - char* memory = allocateMemoryWithAccountingInformation(allocator, size, file, line, allocatNodesSeperately); - if (memory == NULL) return NULL; - MemoryLeakDetectorNode* node = createMemoryLeakAccountingInformation(allocator, size, memory, allocatNodesSeperately); - - storeLeakInformation(node, memory, size, allocator, file, line); - return node->memory_; -} - -void MemoryLeakDetector::removeMemoryLeakInformationWithoutCheckingOrDeallocatingTheMemoryButDeallocatingTheAccountInformation(TestMemoryAllocator* allocator, void* memory, bool allocatNodesSeperately) -{ - MemoryLeakDetectorNode* node = memoryTable_.removeNode((char*) memory); - if (allocatNodesSeperately) allocator->freeMemoryLeakNode( (char*) node); -} - -void MemoryLeakDetector::deallocMemory(TestMemoryAllocator* allocator, void* memory, const char* file, int line, bool allocatNodesSeperately) -{ - if (memory == 0) return; - - MemoryLeakDetectorNode* node = memoryTable_.removeNode((char*) memory); - if (node == NULL) { - outputBuffer_.reportDeallocateNonAllocatedMemoryFailure(file, line, allocator, reporter_); - return; - } - if (!allocator->hasBeenDestroyed()) { - checkForCorruption(node, file, line, allocator, allocatNodesSeperately); - allocator->free_memory((char*) memory, file, line); - } -} - -void MemoryLeakDetector::deallocMemory(TestMemoryAllocator* allocator, void* memory, bool allocatNodesSeperately) -{ - deallocMemory(allocator, (char*) memory, UNKNOWN, 0, allocatNodesSeperately); -} - -char* MemoryLeakDetector::reallocMemory(TestMemoryAllocator* allocator, char* memory, size_t size, const char* file, int line, bool allocatNodesSeperately) -{ - if (memory) { - MemoryLeakDetectorNode* node = memoryTable_.removeNode(memory); - if (node == NULL) { - outputBuffer_.reportDeallocateNonAllocatedMemoryFailure(file, line, allocator, reporter_); - return NULL; - } - checkForCorruption(node, file, line, allocator, allocatNodesSeperately); - } - return reallocateMemoryAndLeakInformation(allocator, memory, size, file, line, allocatNodesSeperately); -} - -void MemoryLeakDetector::ConstructMemoryLeakReport(MemLeakPeriod period) -{ - MemoryLeakDetectorNode* leak = memoryTable_.getFirstLeak(period); - - outputBuffer_.startMemoryLeakReporting(); - - while (leak) { - outputBuffer_.reportMemoryLeak(leak); - leak = memoryTable_.getNextLeak(leak, period); - } - - outputBuffer_.stopMemoryLeakReporting(); -} - -const char* MemoryLeakDetector::report(MemLeakPeriod period) -{ - outputBuffer_.clear(); - ConstructMemoryLeakReport(period); - - return outputBuffer_.toString(); -} - -void MemoryLeakDetector::markCheckingPeriodLeaksAsNonCheckingPeriod() -{ - MemoryLeakDetectorNode* leak = memoryTable_.getFirstLeak(mem_leak_period_checking); - while (leak) { - if (leak->period_ == mem_leak_period_checking) leak->period_ = mem_leak_period_enabled; - leak = memoryTable_.getNextLeak(leak, mem_leak_period_checking); - } -} - -int MemoryLeakDetector::totalMemoryLeaks(MemLeakPeriod period) -{ - return memoryTable_.getTotalLeaks(period); -} +/* + * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the <organization> nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "CppUTest/TestHarness.h" +#include "CppUTest/MemoryLeakDetector.h" +#include "CppUTest/TestMemoryAllocator.h" +#include "CppUTest/PlatformSpecificFunctions.h" + +#define UNKNOWN ((char*)("<unknown>")) + +SimpleStringBuffer::SimpleStringBuffer() : + positions_filled_(0), write_limit_(SIMPLE_STRING_BUFFER_LEN-1) +{ +} + +void SimpleStringBuffer::clear() +{ + positions_filled_ = 0; + buffer_[0] = '\0'; +} + +void SimpleStringBuffer::add(const char* format, ...) +{ + int count = 0; + size_t positions_left = write_limit_ - positions_filled_; + if (positions_left <= 0) return; + + va_list arguments; + va_start(arguments, format); + count = PlatformSpecificVSNprintf(buffer_ + positions_filled_, positions_left+1, format, arguments); + if (count > 0) positions_filled_ += (size_t) count; + if (positions_filled_ > write_limit_) positions_filled_ = write_limit_; + va_end(arguments); +} + +char* SimpleStringBuffer::toString() +{ + return buffer_; +} + +void SimpleStringBuffer::setWriteLimit(size_t write_limit) +{ + write_limit_ = write_limit; + if (write_limit_ > SIMPLE_STRING_BUFFER_LEN-1) + write_limit_ = SIMPLE_STRING_BUFFER_LEN-1; +} +void SimpleStringBuffer::resetWriteLimit() +{ + write_limit_ = SIMPLE_STRING_BUFFER_LEN-1; +} + +bool SimpleStringBuffer::reachedItsCapacity() +{ + return positions_filled_ >= write_limit_; +} + +//////////////////////// + +#define MEM_LEAK_TOO_MUCH "\netc etc etc etc. !!!! Too much memory leaks to report. Bailing out\n" +#define MEM_LEAK_FOOTER "Total number of leaks: " +#define MEM_LEAK_ADDITION_MALLOC_WARNING "NOTE:\n" \ + "\tMemory leak reports about malloc and free can be caused by allocating using the cpputest version of malloc,\n" \ + "\tbut deallocate using the standard free.\n" \ + "\tIf this is the case, check whether your malloc/free replacements are working (#define malloc cpputest_malloc etc).\n" + +MemoryLeakOutputStringBuffer::MemoryLeakOutputStringBuffer() + : total_leaks_(0), giveWarningOnUsingMalloc_(false) +{ +} + +void MemoryLeakOutputStringBuffer::addAllocationLocation(const char* allocationFile, int allocationLineNumber, size_t allocationSize, TestMemoryAllocator* allocator) +{ + outputBuffer_.add(" allocated at file: %s line: %d size: %lu type: %s\n", allocationFile, allocationLineNumber, (unsigned long) allocationSize, allocator->alloc_name()); +} + +void MemoryLeakOutputStringBuffer::addDeallocationLocation(const char* freeFile, int freeLineNumber, TestMemoryAllocator* allocator) +{ + outputBuffer_.add(" deallocated at file: %s line: %d type: %s\n", freeFile, freeLineNumber, allocator->free_name()); +} + +void MemoryLeakOutputStringBuffer::addNoMemoryLeaksMessage() +{ + outputBuffer_.add("No memory leaks were detected."); +} + +void MemoryLeakOutputStringBuffer::startMemoryLeakReporting() +{ + giveWarningOnUsingMalloc_ = false; + total_leaks_ = 0; + + size_t memory_leak_normal_footer_size = sizeof(MEM_LEAK_FOOTER) + 10 + sizeof(MEM_LEAK_TOO_MUCH); /* the number of leaks */ + size_t memory_leak_foot_size_with_malloc_warning = memory_leak_normal_footer_size + sizeof(MEM_LEAK_ADDITION_MALLOC_WARNING); + + outputBuffer_.setWriteLimit(SimpleStringBuffer::SIMPLE_STRING_BUFFER_LEN - memory_leak_foot_size_with_malloc_warning); +} + +void MemoryLeakOutputStringBuffer::reportMemoryLeak(MemoryLeakDetectorNode* leak) +{ + if (total_leaks_ == 0) { + addMemoryLeakHeader(); + } + + total_leaks_++; + outputBuffer_.add("Alloc num (%u) Leak size: %lu Allocated at: %s and line: %d. Type: \"%s\"\n\t Memory: <%p> Content: \"%.15s\"\n", + leak->number_, (unsigned long) leak->size_, leak->file_, leak->line_, leak->allocator_->alloc_name(), leak->memory_, leak->memory_); + + if (PlatformSpecificStrCmp(leak->allocator_->alloc_name(), "malloc") == 0) + giveWarningOnUsingMalloc_ = true; +} + +void MemoryLeakOutputStringBuffer::stopMemoryLeakReporting() +{ + if (total_leaks_ == 0) { + addNoMemoryLeaksMessage(); + return; + } + + bool buffer_reached_its_capacity = outputBuffer_.reachedItsCapacity(); + outputBuffer_.resetWriteLimit(); + + if (buffer_reached_its_capacity) + addErrorMessageForTooMuchLeaks(); + + addMemoryLeakFooter(total_leaks_); + + if (giveWarningOnUsingMalloc_) + addWarningForUsingMalloc(); + +} + +void MemoryLeakOutputStringBuffer::addMemoryLeakHeader() +{ + outputBuffer_.add("Memory leak(s) found.\n"); +} + +void MemoryLeakOutputStringBuffer::addErrorMessageForTooMuchLeaks() +{ + outputBuffer_.add(MEM_LEAK_TOO_MUCH); +} + +void MemoryLeakOutputStringBuffer::addMemoryLeakFooter(int amountOfLeaks) +{ + outputBuffer_.add("%s %d\n", MEM_LEAK_FOOTER, amountOfLeaks); +} + +void MemoryLeakOutputStringBuffer::addWarningForUsingMalloc() +{ + outputBuffer_.add(MEM_LEAK_ADDITION_MALLOC_WARNING); +} + +void MemoryLeakOutputStringBuffer::reportDeallocateNonAllocatedMemoryFailure(const char* freeFile, int freeLine, TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) +{ + reportFailure("Deallocating non-allocated memory\n", "<unknown>", 0, 0, NullUnknownAllocator::defaultAllocator(), freeFile, freeLine, freeAllocator, reporter); +} + +void MemoryLeakOutputStringBuffer::reportAllocationDeallocationMismatchFailure(MemoryLeakDetectorNode* node, const char* freeFile, int freeLineNumber, TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) +{ + reportFailure("Allocation/deallocation type mismatch\n", node->file_, node->line_, node->size_, node->allocator_, freeFile, freeLineNumber, freeAllocator, reporter); +} + +void MemoryLeakOutputStringBuffer::reportMemoryCorruptionFailure(MemoryLeakDetectorNode* node, const char* freeFile, int freeLineNumber, TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) +{ + reportFailure("Memory corruption (written out of bounds?)\n", node->file_, node->line_, node->size_, node->allocator_, freeFile, freeLineNumber, freeAllocator, reporter); +} + +void MemoryLeakOutputStringBuffer::reportFailure(const char* message, const char* allocFile, int allocLine, size_t allocSize, TestMemoryAllocator* allocAllocator, const char* freeFile, int freeLine, + TestMemoryAllocator* freeAllocator, MemoryLeakFailure* reporter) +{ + outputBuffer_.add("%s", message); + addAllocationLocation(allocFile, allocLine, allocSize, allocAllocator); + addDeallocationLocation(freeFile, freeLine, freeAllocator); + reporter->fail(toString()); +} + + +char* MemoryLeakOutputStringBuffer::toString() +{ + return outputBuffer_.toString(); +} + +void MemoryLeakOutputStringBuffer::clear() +{ + outputBuffer_.clear(); +} + +//////////////////////// + +void MemoryLeakDetectorNode::init(char* memory, unsigned number, size_t size, TestMemoryAllocator* allocator, MemLeakPeriod period, const char* file, int line) +{ + number_ = number; + memory_ = memory; + size_ = size; + allocator_ = allocator; + period_ = period; + file_ = file; + line_ = line; +} + +/////////////////////// + +bool MemoryLeakDetectorList::isInPeriod(MemoryLeakDetectorNode* node, MemLeakPeriod period) +{ + return period == mem_leak_period_all || node->period_ == period || (node->period_ != mem_leak_period_disabled && period == mem_leak_period_enabled); +} + +void MemoryLeakDetectorList::clearAllAccounting(MemLeakPeriod period) +{ + MemoryLeakDetectorNode* cur = head_; + MemoryLeakDetectorNode* prev = 0; + + while (cur) { + if (isInPeriod(cur, period)) { + if (prev) { + prev->next_ = cur->next_; + cur = prev; + } + else { + head_ = cur->next_; + cur = head_; + continue; + } + } + prev = cur; + cur = cur->next_; + } +} + +void MemoryLeakDetectorList::addNewNode(MemoryLeakDetectorNode* node) +{ + node->next_ = head_; + head_ = node; +} + +MemoryLeakDetectorNode* MemoryLeakDetectorList::removeNode(char* memory) +{ + MemoryLeakDetectorNode* cur = head_; + MemoryLeakDetectorNode* prev = 0; + while (cur) { + if (cur->memory_ == memory) { + if (prev) { + prev->next_ = cur->next_; + return cur; + } + else { + head_ = cur->next_; + return cur; + } + } + prev = cur; + cur = cur->next_; + } + return 0; +} + +MemoryLeakDetectorNode* MemoryLeakDetectorList::retrieveNode(char* memory) +{ + MemoryLeakDetectorNode* cur = head_; + while (cur) { + if (cur->memory_ == memory) + return cur; + cur = cur->next_; + } + return NULL; +} + +MemoryLeakDetectorNode* MemoryLeakDetectorList::getLeakFrom(MemoryLeakDetectorNode* node, MemLeakPeriod period) +{ + for (MemoryLeakDetectorNode* cur = node; cur; cur = cur->next_) + if (isInPeriod(cur, period)) return cur; + return 0; +} + +MemoryLeakDetectorNode* MemoryLeakDetectorList::getFirstLeak(MemLeakPeriod period) +{ + return getLeakFrom(head_, period); +} + +MemoryLeakDetectorNode* MemoryLeakDetectorList::getNextLeak(MemoryLeakDetectorNode* node, MemLeakPeriod period) +{ + return getLeakFrom(node->next_, period); +} + +int MemoryLeakDetectorList::getTotalLeaks(MemLeakPeriod period) +{ + int total_leaks = 0; + for (MemoryLeakDetectorNode* node = head_; node; node = node->next_) { + if (isInPeriod(node, period)) total_leaks++; + } + return total_leaks; +} + +bool MemoryLeakDetectorList::hasLeaks(MemLeakPeriod period) +{ + for (MemoryLeakDetectorNode* node = head_; node; node = node->next_) + if (isInPeriod(node, period)) return true; + return false; +} + +///////////////////////////////////////////////////////////// + +unsigned long MemoryLeakDetectorTable::hash(char* memory) +{ + return (unsigned long)((size_t)memory % hash_prime); +} + +void MemoryLeakDetectorTable::clearAllAccounting(MemLeakPeriod period) +{ + for (int i = 0; i < hash_prime; i++) + table_[i].clearAllAccounting(period); +} + +void MemoryLeakDetectorTable::addNewNode(MemoryLeakDetectorNode* node) +{ + table_[hash(node->memory_)].addNewNode(node); +} + +MemoryLeakDetectorNode* MemoryLeakDetectorTable::removeNode(char* memory) +{ + return table_[hash(memory)].removeNode(memory); +} + +MemoryLeakDetectorNode* MemoryLeakDetectorTable::retrieveNode(char* memory) +{ + return table_[hash(memory)].retrieveNode(memory); +} + +bool MemoryLeakDetectorTable::hasLeaks(MemLeakPeriod period) +{ + for (int i = 0; i < hash_prime; i++) + if (table_[i].hasLeaks(period)) return true; + return false; +} + +int MemoryLeakDetectorTable::getTotalLeaks(MemLeakPeriod period) +{ + int total_leaks = 0; + for (int i = 0; i < hash_prime; i++) + total_leaks += table_[i].getTotalLeaks(period); + return total_leaks; +} + +MemoryLeakDetectorNode* MemoryLeakDetectorTable::getFirstLeak(MemLeakPeriod period) +{ + for (int i = 0; i < hash_prime; i++) { + MemoryLeakDetectorNode* node = table_[i].getFirstLeak(period); + if (node) return node; + } + return 0; +} + +MemoryLeakDetectorNode* MemoryLeakDetectorTable::getNextLeak(MemoryLeakDetectorNode* leak, MemLeakPeriod period) +{ + unsigned long i = hash(leak->memory_); + MemoryLeakDetectorNode* node = table_[i].getNextLeak(leak, period); + if (node) return node; + + for (++i; i < hash_prime; i++) { + node = table_[i].getFirstLeak(period); + if (node) return node; + } + return 0; +} + +///////////////////////////////////////////////////////////// + +MemoryLeakDetector::MemoryLeakDetector(MemoryLeakFailure* reporter) +{ + doAllocationTypeChecking_ = true; + allocationSequenceNumber_ = 1; + current_period_ = mem_leak_period_disabled; + reporter_ = reporter; + outputBuffer_ = MemoryLeakOutputStringBuffer(); + memoryTable_ = MemoryLeakDetectorTable(); +} + +void MemoryLeakDetector::clearAllAccounting(MemLeakPeriod period) +{ + memoryTable_.clearAllAccounting(period); +} + +void MemoryLeakDetector::startChecking() +{ + outputBuffer_.clear(); + current_period_ = mem_leak_period_checking; +} + +void MemoryLeakDetector::stopChecking() +{ + current_period_ = mem_leak_period_enabled; +} + +void MemoryLeakDetector::enable() +{ + current_period_ = mem_leak_period_enabled; +} + +void MemoryLeakDetector::disable() +{ + current_period_ = mem_leak_period_disabled; +} + +void MemoryLeakDetector::disableAllocationTypeChecking() +{ + doAllocationTypeChecking_ = false; +} + +void MemoryLeakDetector::enableAllocationTypeChecking() +{ + doAllocationTypeChecking_ = true; +} + +unsigned MemoryLeakDetector::getCurrentAllocationNumber() +{ + return allocationSequenceNumber_; +} + +static size_t calculateVoidPointerAlignedSize(size_t size) +{ + return (sizeof(void*) - (size % sizeof(void*))) + size; +} + +size_t MemoryLeakDetector::sizeOfMemoryWithCorruptionInfo(size_t size) +{ + return calculateVoidPointerAlignedSize(size + memory_corruption_buffer_size); +} + +MemoryLeakDetectorNode* MemoryLeakDetector::getNodeFromMemoryPointer(char* memory, size_t memory_size) +{ + return (MemoryLeakDetectorNode*) (void*) (memory + sizeOfMemoryWithCorruptionInfo(memory_size)); +} + +void MemoryLeakDetector::storeLeakInformation(MemoryLeakDetectorNode * node, char *new_memory, size_t size, TestMemoryAllocator *allocator, const char *file, int line) +{ + node->init(new_memory, allocationSequenceNumber_++, size, allocator, current_period_, file, line); + addMemoryCorruptionInformation(node->memory_ + node->size_); + memoryTable_.addNewNode(node); +} + +char* MemoryLeakDetector::reallocateMemoryAndLeakInformation(TestMemoryAllocator* allocator, char* memory, size_t size, const char* file, int line, bool allocatNodesSeperately) +{ + char* new_memory = reallocateMemoryWithAccountingInformation(allocator, memory, size, file, line, allocatNodesSeperately); + if (new_memory == NULL) return NULL; + + MemoryLeakDetectorNode *node = createMemoryLeakAccountingInformation(allocator, size, new_memory, allocatNodesSeperately); + storeLeakInformation(node, new_memory, size, allocator, file, line); + return node->memory_; +} + +void MemoryLeakDetector::invalidateMemory(char* memory) +{ + MemoryLeakDetectorNode* node = memoryTable_.retrieveNode(memory); + if (node) + PlatformSpecificMemset(memory, 0xCD, node->size_); +} + +void MemoryLeakDetector::addMemoryCorruptionInformation(char* memory) +{ + memory[0] = 'B'; + memory[1] = 'A'; + memory[2] = 'S'; +} + +bool MemoryLeakDetector::validMemoryCorruptionInformation(char* memory) +{ + return memory[0] == 'B' && memory[1] == 'A' && memory[2] == 'S'; +} + +bool MemoryLeakDetector::matchingAllocation(TestMemoryAllocator *alloc_allocator, TestMemoryAllocator *free_allocator) +{ + if (alloc_allocator == free_allocator) return true; + if (!doAllocationTypeChecking_) return true; + return free_allocator->isOfEqualType(alloc_allocator); +} + +void MemoryLeakDetector::checkForCorruption(MemoryLeakDetectorNode* node, const char* file, int line, TestMemoryAllocator* allocator, bool allocateNodesSeperately) +{ + if (!matchingAllocation(node->allocator_, allocator)) + outputBuffer_.reportAllocationDeallocationMismatchFailure(node, file, line, allocator, reporter_); + else if (!validMemoryCorruptionInformation(node->memory_ + node->size_)) + outputBuffer_.reportMemoryCorruptionFailure(node, file, line, allocator, reporter_); + else if (allocateNodesSeperately) + allocator->freeMemoryLeakNode((char*) node); +} + +char* MemoryLeakDetector::allocMemory(TestMemoryAllocator* allocator, size_t size, bool allocatNodesSeperately) +{ + return allocMemory(allocator, size, UNKNOWN, 0, allocatNodesSeperately); +} + +char* MemoryLeakDetector::allocateMemoryWithAccountingInformation(TestMemoryAllocator* allocator, size_t size, const char* file, int line, bool allocatNodesSeperately) +{ + if (allocatNodesSeperately) return allocator->alloc_memory(sizeOfMemoryWithCorruptionInfo(size), file, line); + else return allocator->alloc_memory(sizeOfMemoryWithCorruptionInfo(size) + sizeof(MemoryLeakDetectorNode), file, line); +} + +char* MemoryLeakDetector::reallocateMemoryWithAccountingInformation(TestMemoryAllocator* /*allocator*/, char* memory, size_t size, const char* /*file*/, int /*line*/, bool allocatNodesSeperately) +{ + if (allocatNodesSeperately) return (char*) PlatformSpecificRealloc(memory, sizeOfMemoryWithCorruptionInfo(size)); + else return (char*) PlatformSpecificRealloc(memory, sizeOfMemoryWithCorruptionInfo(size) + sizeof(MemoryLeakDetectorNode)); +} + +MemoryLeakDetectorNode* MemoryLeakDetector::createMemoryLeakAccountingInformation(TestMemoryAllocator* allocator, size_t size, char* memory, bool allocatNodesSeperately) +{ + if (allocatNodesSeperately) return (MemoryLeakDetectorNode*) (void*) allocator->allocMemoryLeakNode(sizeof(MemoryLeakDetectorNode)); + else return getNodeFromMemoryPointer(memory, size); +} + +char* MemoryLeakDetector::allocMemory(TestMemoryAllocator* allocator, size_t size, const char* file, int line, bool allocatNodesSeperately) +{ + /* With malloc, it is harder to guarantee that the allocator free is called. + * This is because operator new is overloaded via linker symbols, but malloc just via #defines. + * If the same allocation is used and the wrong free is called, it will deallocate the memory leak information + * without the memory leak detector ever noticing it! + * So, for malloc, we'll allocate the memory separately so we can detect this and give a proper error. + */ + + char* memory = allocateMemoryWithAccountingInformation(allocator, size, file, line, allocatNodesSeperately); + if (memory == NULL) return NULL; + MemoryLeakDetectorNode* node = createMemoryLeakAccountingInformation(allocator, size, memory, allocatNodesSeperately); + + storeLeakInformation(node, memory, size, allocator, file, line); + return node->memory_; +} + +void MemoryLeakDetector::removeMemoryLeakInformationWithoutCheckingOrDeallocatingTheMemoryButDeallocatingTheAccountInformation(TestMemoryAllocator* allocator, void* memory, bool allocatNodesSeperately) +{ + MemoryLeakDetectorNode* node = memoryTable_.removeNode((char*) memory); + if (allocatNodesSeperately) allocator->freeMemoryLeakNode( (char*) node); +} + +void MemoryLeakDetector::deallocMemory(TestMemoryAllocator* allocator, void* memory, const char* file, int line, bool allocatNodesSeperately) +{ + if (memory == 0) return; + + MemoryLeakDetectorNode* node = memoryTable_.removeNode((char*) memory); + if (node == NULL) { + outputBuffer_.reportDeallocateNonAllocatedMemoryFailure(file, line, allocator, reporter_); + return; + } + if (!allocator->hasBeenDestroyed()) { + checkForCorruption(node, file, line, allocator, allocatNodesSeperately); + allocator->free_memory((char*) memory, file, line); + } +} + +void MemoryLeakDetector::deallocMemory(TestMemoryAllocator* allocator, void* memory, bool allocatNodesSeperately) +{ + deallocMemory(allocator, (char*) memory, UNKNOWN, 0, allocatNodesSeperately); +} + +char* MemoryLeakDetector::reallocMemory(TestMemoryAllocator* allocator, char* memory, size_t size, const char* file, int line, bool allocatNodesSeperately) +{ + if (memory) { + MemoryLeakDetectorNode* node = memoryTable_.removeNode(memory); + if (node == NULL) { + outputBuffer_.reportDeallocateNonAllocatedMemoryFailure(file, line, allocator, reporter_); + return NULL; + } + checkForCorruption(node, file, line, allocator, allocatNodesSeperately); + } + return reallocateMemoryAndLeakInformation(allocator, memory, size, file, line, allocatNodesSeperately); +} + +void MemoryLeakDetector::ConstructMemoryLeakReport(MemLeakPeriod period) +{ + MemoryLeakDetectorNode* leak = memoryTable_.getFirstLeak(period); + + outputBuffer_.startMemoryLeakReporting(); + + while (leak) { + outputBuffer_.reportMemoryLeak(leak); + leak = memoryTable_.getNextLeak(leak, period); + } + + outputBuffer_.stopMemoryLeakReporting(); +} + +const char* MemoryLeakDetector::report(MemLeakPeriod period) +{ + outputBuffer_.clear(); + ConstructMemoryLeakReport(period); + + return outputBuffer_.toString(); +} + +void MemoryLeakDetector::markCheckingPeriodLeaksAsNonCheckingPeriod() +{ + MemoryLeakDetectorNode* leak = memoryTable_.getFirstLeak(mem_leak_period_checking); + while (leak) { + if (leak->period_ == mem_leak_period_checking) leak->period_ = mem_leak_period_enabled; + leak = memoryTable_.getNextLeak(leak, mem_leak_period_checking); + } +} + +int MemoryLeakDetector::totalMemoryLeaks(MemLeakPeriod period) +{ + return memoryTable_.getTotalLeaks(period); +}