Frobby 0.9.5
DebugAllocator.cpp
Go to the documentation of this file.
1/* Frobby: Software for monomial ideal computations.
2 Copyright (C) 2007 Bjarke Hammersholt Roune (www.broune.com)
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see http://www.gnu.org/licenses/.
16*/
17#include "stdinc.h"
18#include "DebugAllocator.h"
19
20#include "main.h"
21#include "error.h"
22#include "Ideal.h"
23#include "test/TestCase.h"
24#include "IOHandler.h"
25#include <limits>
26
27// Must not include code below if not debugging. For one, then Frobby
28// cannot be built as a library as this code calls main.
29// The code only applies to debug builds anyway.
30#ifdef DEBUG
31#undef new
32
33static const size_t AllocationLimitsTryFirst = 500;
34static const size_t AllocationLimitsTryLast = 200;
35static const size_t AllocationLimitsStepRatio = 1000;
36
37DebugAllocator& DebugAllocator::getSingleton() {
38 static DebugAllocator singleton;
39 return singleton;
40}
41
42void DebugAllocator::rewindInput() {
43 if (_inputFile != "")
44 if (!freopen(_inputFile.c_str(), "r", stdin))
45 reportError("Could not open file \"" + _inputFile + "\" for input.");
46}
47
55int DebugAllocator::runDebugMain(int argc, const char** argv) {
56 processDebugOptions(argc, argv);
57
58 // Run all the way through once to count the number of allocations
59 // and to produce the correct output. Note that the number of
60 // allocations on subsequent runs can be a bit less due to static
61 // caches.
62 rewindInput();
63 _allocationCount = 0;
64 int exitCode = frobbyMain(argc, argv);
65 size_t maxAllocations = _allocationCount;
66 if (_actionIsTest || !_debugAllocation)
67 return exitCode;
68
69 fclose(stdout); // Output has already been produced.
70
71 fputs("DEBUG: Now debugging out-of-memory conditions.\n", stderr);
72 fprintf(stderr, "DEBUG: There are %i allocations in total on this input.\n",
73 (int)maxAllocations);
74
75 // If maxAllocations is small then just try every possible limit.
76 if (maxAllocations < AllocationLimitsTryFirst + AllocationLimitsTryLast) {
77 fputs("DEBUG: This is few, so am now running through "
78 "every possible condition.\n", stderr);
79 for (size_t limit = 0; limit < maxAllocations; ++limit)
80 runWithLimit(argc, argv, limit);
81 return ExitCodeSuccess;
82 }
83
84 fprintf(stderr, "DEBUG: Trying the first %i allocations.\n",
85 (int)AllocationLimitsTryFirst);
86 for (size_t limit = 0; limit < AllocationLimitsTryFirst; ++limit)
87 runWithLimit(argc, argv, limit);
88
89 fprintf(stderr, "DEBUG: Trying the last %i allocations.\n",
90 (int)AllocationLimitsTryLast);
91 for (size_t limit = 0; limit < AllocationLimitsTryLast; ++limit)
92 runWithLimit(argc, argv, maxAllocations - limit);
93
94 size_t limitsLeft =
95 maxAllocations - AllocationLimitsTryFirst - AllocationLimitsTryLast;
96 size_t stepSize = limitsLeft / AllocationLimitsStepRatio + 1;
97 fprintf(stderr,
98 "DEBUG: Going through the %i remaining allocations "
99 "with steps of size %i.\n",
100 (int)limitsLeft, (int)stepSize);
101
102 for (size_t limit = AllocationLimitsTryFirst;
103 limit < maxAllocations - AllocationLimitsTryLast; limit += stepSize)
104 runWithLimit(argc, argv, limit);
105
106 return ExitCodeSuccess;
107}
108
109void DebugAllocator::runWithLimit(int argc, const char** argv, size_t limit) {
110 if (_detailAllocation)
111 fprintf(stderr, "DEBUG: Trying allocation limit of %i\n", (int)limit);
112
113 // To make each run more similar.
115
116 _limitAllocation = true;
117 _allocationLimit = limit;
118 _allocationCount = 0;
119
120 // We use this to distinguish genuinely running out of memory from
121 // our artificial limit-induced throwing of bad_alloc.
122 _expectBadAllocException = false;
123
124 rewindInput();
125
126 try {
127 frobbyMain(argc, argv);
128 } catch (const bad_alloc&) {
129 if (!_expectBadAllocException)
130 throw;
131 }
132
133 _limitAllocation = false;
134}
135
136void DebugAllocator::processDebugOptions(int& argc, const char**& argv) {
137 const char* originalArgvZero = argv[0];
138
139 while (true) {
140 if (argc <= 1 || argv[1][0] != '_')
141 break;
142
143 string option(argv[1] + 1);
144 ++argv;
145 --argc;
146
147 if (option == "debugAlloc")
148 _debugAllocation = true;
149 else if (option == "detailAlloc")
150 _detailAllocation = true;
151 else if (option == "input") {
152 if (argc <= 1)
153 reportError("Debug option _input requries an argument.");
154
155 _inputFile = argv[1];
156 ++argv;
157 --argc;
158 } else
159 reportError("Unknown debug option \"" + option + "\".");
160 }
161
162 if (argc >= 2 && string(argv[1]) == "test")
163 _actionIsTest = true;
164
165 argv[0] = originalArgvZero;
166}
167
168void DebugAllocator::runTest(TestCase& test, const string& name) {
169 test.run(name.c_str(), true);
170
171 if (!_debugAllocation)
172 return;
173
174 _limitAllocation = true;
175
176 for (_allocationLimit = 0; _allocationLimit < numeric_limits<size_t>::max();
177 ++_allocationLimit) {
178 if (_detailAllocation)
179 fprintf(stderr, "DEBUG: Trying test allocation limit of %i\n",
180 (int)_allocationLimit);
181
182 // To make each run more similar.
184
185 _allocationCount = 0;
186 _expectBadAllocException = false;
187
188 rewindInput();
189
190 try {
191 test.run(name.c_str(), false);
192
193 // At this point test has completed within allocation limit.
194 break;
195 } catch (const bad_alloc&) {
196 if (!_expectBadAllocException)
197 throw;
198 // Continue and try next limit.
199 }
200 }
201
202 _limitAllocation = false;
203}
204
205void* DebugAllocator::allocate(size_t size) {
206 return allocate(size, 0, 0);
207}
208
209void* DebugAllocator::allocate(size_t size,
210 const char* file,
211 size_t lineNumber) {
212 if (_detailAllocation) {
213 if (file != 0)
214 fprintf(stderr, "DEBUG: Allocating %i bytes at %s:%i.\n",
215 (int)size, file, (int)lineNumber);
216 if (file == 0)
217 fprintf(stderr, "DEBUG: Allocating %i bytes at an unknown point.\n",
218 (int)size);
219 }
220
221 if (_limitAllocation && _allocationCount >= _allocationLimit) {
222 if (_detailAllocation)
223 fputs("DEBUG: Throwing bad_alloc due to artifically imposed limit.\n",
224 stderr);
225 _expectBadAllocException = true;
226 throw bad_alloc();
227 }
228
229 ++_allocationCount;
230 void* p = malloc(size);
231 if (p == 0)
232 throw bad_alloc();
233 return p;
234}
235
236DebugAllocator::DebugAllocator():
237 _debugAllocation(false),
238 _detailAllocation(false),
239 _limitAllocation(false),
240 _expectBadAllocException(false),
241 _actionIsTest(false),
242 _allocationCount(0),
243 _allocationLimit(0) {
244}
245
246void* operator new(size_t size, const char* file, size_t lineNumber)
247 throw(bad_alloc) {
248 return DebugAllocator::getSingleton().allocate(size, file, lineNumber);
249}
250
251void* operator new[](size_t size, const char* file, size_t lineNumber)
252 throw(bad_alloc) {
253 return DebugAllocator::getSingleton().allocate(size, file, lineNumber);
254}
255
256void* operator new(size_t size) throw (bad_alloc) {
257 return DebugAllocator::getSingleton().allocate(size);
258}
259
260void* operator new[](size_t size) throw (bad_alloc) {
261 return DebugAllocator::getSingleton().allocate(size);
262}
263
264void operator delete(void* buffer) throw() {
265 free(buffer);
266}
267
268void operator delete[](void* buffer) throw() {
269 free(buffer);
270}
271
290void operator delete(void* buffer, const char* file, size_t line) {
291 free(buffer);
292}
293
294void operator delete[](void* buffer, const char* file, size_t line) {
295 free(buffer);
296}
297
298#endif
static void clearStaticCache()
Ideal caches memory allocated with new internally and reuses it to avoid calling new all the time.
Definition Ideal.cpp:810
Represents a test case, which is usually created through a macro that defines a subclass.
Definition TestCase.h:29
virtual void run(const char *nameOfTest, bool printDots)=0
Run the test and record the name of the test as __nameOfTest.
void reportError(const string &errorMsg)
Definition error.cpp:23
int frobbyMain(int argc, const char **argv)
This function runs the Frobby console interface.
Definition main.cpp:34
static const int ExitCodeSuccess
Definition main.h:33
This header file includes common definitions and is included as the first line of code in every imple...