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 
33 static const size_t AllocationLimitsTryFirst = 500;
34 static const size_t AllocationLimitsTryLast = 200;
35 static const size_t AllocationLimitsStepRatio = 1000;
36 
37 DebugAllocator& DebugAllocator::getSingleton() {
38  static DebugAllocator singleton;
39  return singleton;
40 }
41 
42 void DebugAllocator::rewindInput() {
43  if (_inputFile != "")
44  if (!freopen(_inputFile.c_str(), "r", stdin))
45  reportError("Could not open file \"" + _inputFile + "\" for input.");
46 }
47 
55 int 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 
109 void 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 
136 void 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 
168 void 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 
205 void* DebugAllocator::allocate(size_t size) {
206  return allocate(size, 0, 0);
207 }
208 
209 void* 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 
236 DebugAllocator::DebugAllocator():
237  _debugAllocation(false),
238  _detailAllocation(false),
239  _limitAllocation(false),
240  _expectBadAllocException(false),
241  _actionIsTest(false),
242  _allocationCount(0),
243  _allocationLimit(0) {
244 }
245 
246 void* 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 
251 void* 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 
256 void* operator new(size_t size) throw (bad_alloc) {
257  return DebugAllocator::getSingleton().allocate(size);
258 }
259 
260 void* operator new[](size_t size) throw (bad_alloc) {
261  return DebugAllocator::getSingleton().allocate(size);
262 }
263 
264 void operator delete(void* buffer) throw() {
265  free(buffer);
266 }
267 
268 void operator delete[](void* buffer) throw() {
269  free(buffer);
270 }
271 
290 void operator delete(void* buffer, const char* file, size_t line) {
291  free(buffer);
292 }
293 
294 void 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