fork
Fork of cpputest by
Diff: src/CppUTest/MemoryLeakDetector.cpp
- Revision:
- 0:0b799af9d58e
- Child:
- 1:4769360130ed
diff -r 000000000000 -r 0b799af9d58e src/CppUTest/MemoryLeakDetector.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CppUTest/MemoryLeakDetector.cpp Tue Jan 28 09:27:41 2014 +0000 @@ -0,0 +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); +}