fork

Fork of cpputest by Rohit Grover

src/CppUTest/MemoryLeakDetector.cpp

Committer:
Rohit Grover
Date:
2014-06-17
Revision:
1:4769360130ed
Parent:
0:0b799af9d58e

File content as of revision 1:4769360130ed:

/*
 * 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);
}