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