1 /*
2 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the <organization> nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "CppUTest/TestHarness.h"
29 #include "CppUTestExt/CodeMemoryReportFormatter.h"
30 #include "CppUTestExt/MemoryReportAllocator.h"
31 #include "CppUTest/PlatformSpecificFunctions.h"
32
33
34 #define MAX_VARIABLE_NAME_LINE_PART 10
35 #define MAX_VARIABLE_NAME_FILE_PART 53
36 #define MAX_VARIABLE_NAME_SEPERATOR_PART 1
37 #define MAX_VARIABLE_NAME_LENGTH MAX_VARIABLE_NAME_FILE_PART + MAX_VARIABLE_NAME_SEPERATOR_PART + MAX_VARIABLE_NAME_LINE_PART
38
39 struct CodeReportingAllocationNode
40 {
41 char variableName_[MAX_VARIABLE_NAME_LENGTH + 1];
42 void* memory_;
43 CodeReportingAllocationNode* next_;
44 };
45
CodeMemoryReportFormatter(TestMemoryAllocator * internalAllocator)46 CodeMemoryReportFormatter::CodeMemoryReportFormatter(TestMemoryAllocator* internalAllocator)
47 : codeReportingList_(NULLPTR), internalAllocator_(internalAllocator)
48 {
49 }
50
~CodeMemoryReportFormatter()51 CodeMemoryReportFormatter::~CodeMemoryReportFormatter()
52 {
53 clearReporting();
54 }
55
clearReporting()56 void CodeMemoryReportFormatter::clearReporting()
57 {
58 while (codeReportingList_) {
59 CodeReportingAllocationNode* oldNode = codeReportingList_;
60 codeReportingList_ = codeReportingList_->next_;
61 internalAllocator_->free_memory((char*) oldNode, 0, __FILE__, __LINE__);
62 }
63 }
64
addNodeToList(const char * variableName,void * memory,CodeReportingAllocationNode * next)65 void CodeMemoryReportFormatter::addNodeToList(const char* variableName, void* memory, CodeReportingAllocationNode* next)
66 {
67 CodeReportingAllocationNode* newNode = (CodeReportingAllocationNode*) (void*) internalAllocator_->alloc_memory(sizeof(CodeReportingAllocationNode), __FILE__, __LINE__);
68 newNode->memory_ = memory;
69 newNode->next_ = next;
70 SimpleString::StrNCpy(newNode->variableName_, variableName, MAX_VARIABLE_NAME_LENGTH);
71 codeReportingList_ = newNode;
72 }
73
findNode(void * memory)74 CodeReportingAllocationNode* CodeMemoryReportFormatter::findNode(void* memory)
75 {
76
77 CodeReportingAllocationNode* current = codeReportingList_;
78 while (current && current->memory_ != memory) {
79 current = current->next_;
80 }
81 return current;
82 }
83
extractFileNameFromPath(const char * file)84 static SimpleString extractFileNameFromPath(const char* file)
85 {
86 const char* fileNameOnly = file + SimpleString::StrLen(file);
87 while (fileNameOnly != file && *fileNameOnly != '/')
88 fileNameOnly--;
89 if (*fileNameOnly == '/') fileNameOnly++;
90 return fileNameOnly;
91 }
92
createVariableNameFromFileLineInfo(const char * file,size_t line)93 SimpleString CodeMemoryReportFormatter::createVariableNameFromFileLineInfo(const char *file, size_t line)
94 {
95 SimpleString fileNameOnly = extractFileNameFromPath(file);
96 fileNameOnly.replace(".", "_");
97
98 for (int i = 1; i < 100; i++) {
99 SimpleString variableName = StringFromFormat("%s_%d_%d", fileNameOnly.asCharString(), (int) line, i);
100 if (!variableExists(variableName))
101 return variableName;
102 }
103 return "";
104 }
105
isNewAllocator(TestMemoryAllocator * allocator)106 bool CodeMemoryReportFormatter::isNewAllocator(TestMemoryAllocator* allocator)
107 {
108 return SimpleString::StrCmp(allocator->alloc_name(), defaultNewAllocator()->alloc_name()) == 0 || SimpleString::StrCmp(allocator->alloc_name(), defaultNewArrayAllocator()->alloc_name()) == 0;
109 }
110
variableExists(const SimpleString & variableName)111 bool CodeMemoryReportFormatter::variableExists(const SimpleString& variableName)
112 {
113 CodeReportingAllocationNode* current = codeReportingList_;
114 while (current) {
115 if (variableName == current->variableName_)
116 return true;
117 current = current->next_;
118 }
119 return false;
120 }
121
getAllocationString(TestMemoryAllocator * allocator,const SimpleString & variableName,size_t size)122 SimpleString CodeMemoryReportFormatter::getAllocationString(TestMemoryAllocator* allocator, const SimpleString& variableName, size_t size)
123 {
124 if (isNewAllocator(allocator))
125 return StringFromFormat("char* %s = new char[%lu]; /* using %s */", variableName.asCharString(), (unsigned long) size, allocator->alloc_name());
126 else
127 return StringFromFormat("void* %s = malloc(%lu);", variableName.asCharString(), (unsigned long) size);
128 }
129
getDeallocationString(TestMemoryAllocator * allocator,const SimpleString & variableName,const char * file,size_t line)130 SimpleString CodeMemoryReportFormatter::getDeallocationString(TestMemoryAllocator* allocator, const SimpleString& variableName, const char* file, size_t line)
131 {
132 if (isNewAllocator(allocator))
133 return StringFromFormat("delete [] %s; /* using %s at %s:%d */", variableName.asCharString(), allocator->free_name(), file, (int) line);
134 else
135 return StringFromFormat("free(%s); /* at %s:%d */", variableName.asCharString(), file, (int) line);
136 }
137
report_test_start(TestResult * result,UtestShell & test)138 void CodeMemoryReportFormatter::report_test_start(TestResult* result, UtestShell& test)
139 {
140 clearReporting();
141 result->print(StringFromFormat("*/\nTEST(%s_memoryReport, %s)\n{ /* at %s:%d */\n",
142 test.getGroup().asCharString(), test.getName().asCharString(), test.getFile().asCharString(), (int) test.getLineNumber()).asCharString());
143 }
144
report_test_end(TestResult * result,UtestShell &)145 void CodeMemoryReportFormatter::report_test_end(TestResult* result, UtestShell&)
146 {
147 result->print("}/*");
148 }
149
report_testgroup_start(TestResult * result,UtestShell & test)150 void CodeMemoryReportFormatter::report_testgroup_start(TestResult* result, UtestShell& test)
151 {
152 result->print(StringFromFormat("*/TEST_GROUP(%s_memoryReport)\n{\n};\n/*",
153 test.getGroup().asCharString()).asCharString());
154 }
155
report_alloc_memory(TestResult * result,TestMemoryAllocator * allocator,size_t size,char * memory,const char * file,size_t line)156 void CodeMemoryReportFormatter::report_alloc_memory(TestResult* result, TestMemoryAllocator* allocator, size_t size, char* memory, const char* file, size_t line)
157 {
158 SimpleString variableName = createVariableNameFromFileLineInfo(file, line);
159 result->print(StringFromFormat("\t%s\n", getAllocationString(allocator, variableName, size).asCharString()).asCharString());
160 addNodeToList(variableName.asCharString(), memory, codeReportingList_);
161 }
162
report_free_memory(TestResult * result,TestMemoryAllocator * allocator,char * memory,const char * file,size_t line)163 void CodeMemoryReportFormatter::report_free_memory(TestResult* result, TestMemoryAllocator* allocator, char* memory, const char* file, size_t line)
164 {
165 SimpleString variableName;
166 CodeReportingAllocationNode* node = findNode(memory);
167
168 if (memory == NULLPTR) variableName = "NULL";
169 else variableName = node->variableName_;
170
171 result->print(StringFromFormat("\t%s\n", getDeallocationString(allocator, variableName, file, line).asCharString()).asCharString());
172 }
173