1 /*****************************************************************************/
2 /* */
3 /* MEMCHECK.CC */
4 /* */
5 /* (C) 1995 Ullrich von Bassewitz */
6 /* Zwehrenbuehlstrasse 33 */
7 /* D-72070 Tuebingen */
8 /* EMail: uz@ibb.schwaben.com */
9 /* */
10 /*****************************************************************************/
11
12
13
14 // $Id$
15 //
16 // $Log$
17 //
18 //
19
20
21
22 // Poor man's memory checker. Overloads the global operators new and delete
23 // and does some additional checks if the variable MemCheck is set to true:
24 //
25 // * Check if an allocated block is already allocated (heap corrupt)
26 // * Check if a block that should be freed is allocated
27 // * Check if there have been writes outside the blocks bounds (by
28 // adding a signature to the end)
29 // * Check if new does not provide a NULL pointer.
30
31
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include "machine.h"
38 #include "check.h"
39
40
41
42 /*****************************************************************************/
43 /* Data */
44 /*****************************************************************************/
45
46
47
48 // Signature of a memory block
49 static u32 MemSig = 0x12785634;
50
51 // Switch memory checking on or off
52 int MemCheck = 0;
53
54 // Switch memory filling on or off
55 int MemFill = 0;
56
57 // Statistics
58 u32 MemNewCount = 0;
59 u32 MemDelCount = 0;
60 u32 MemDelNULLCount = 0;
61 u32 MemNewCheckCount = 0;
62 u32 MemDelCheckCount = 0;
63 u32 MemLargestBlock = 0;
64 u32 MemUsage = 0;
65 u32 MemMaxUsage = 0;
66
67 // This is the fill value for memory blocks if MemFill is true. On intel
68 // architectures, this is the code for "INT 3", an instruction that is
69 // often used by debuggers as a breakpoint.
70 const unsigned char FillVal = 0xCC;
71
72
73
74 /*****************************************************************************/
75 /* struct BlockInfo */
76 /*****************************************************************************/
77
78
79
80 struct BlockInfo {
81
82 unsigned char* Ptr;
83 u32 Size;
84
85 };
86
87 //
88 const int FirstChunk = 2000;
89 const int Delta = 1000;
90
91 // Variables needed
92 static int IsInitialized = 0;
93 static int BlockCount = 0;
94 static int BlockLimit = 0;
95 static BlockInfo* Blocks = NULL;
96
97
98
99 /*****************************************************************************/
100 /* class BlockInfoColl */
101 /*****************************************************************************/
102
103
104
MemSetCount(int NewCount)105 static void MemSetCount (int NewCount)
106 // Make shure, there is space for NewSize blocks in Blocks
107 {
108 if (NewCount > BlockLimit) {
109 // OOPS, need realloc
110 if (BlockLimit == 0 && NewCount <= FirstChunk) {
111 BlockLimit = FirstChunk;
112 } else {
113 BlockLimit = ((NewCount / Delta) + 1) * Delta;
114 }
115 Blocks = (BlockInfo*) realloc (Blocks, BlockLimit * sizeof (BlockInfo));
116 }
117 BlockCount = NewCount;
118 }
119
120
121
MemSearch(const unsigned char * Ptr,int & Index)122 static int MemSearch (const unsigned char* Ptr, int& Index)
123 // Search for the block. Return 1 if the block is found (Index holds the
124 // block index in this case). Return 0 if the block is not found and return
125 // in Index the index where the block should be inserted.
126 {
127 // do a binary search
128 int First = 0;
129 int Last = BlockCount - 1;
130 int Current;
131 int S = 0;
132
133 while (First <= Last) {
134
135 // Set current to mid of range
136 Current = (Last + First) / 2;
137
138 // Do a compare
139 if (Blocks [Current].Ptr < Ptr) {
140 First = Current + 1;
141 } else {
142 Last = Current - 1;
143 if (Blocks [Current].Ptr == Ptr) {
144 // Found.
145 S = 1; // function result
146 // Set condition to terminate loop
147 First = Current;
148 }
149 }
150
151 }
152
153 Index = First;
154 return S;
155 }
156
157
158
MemDelBlock(int Index)159 static void MemDelBlock (int Index)
160 // Delete the block with the given index
161 {
162 BlockCount--;
163 memmove (Blocks+Index, Blocks+Index+1, (BlockCount-Index) * sizeof (BlockInfo));
164 }
165
166
167
MemInsBlock(int Index,unsigned char * Ptr,u32 Size)168 static void MemInsBlock (int Index, unsigned char* Ptr, u32 Size)
169 {
170 // Set the new size
171 MemSetCount (BlockCount + 1);
172
173 // We can insert the element. If the item is not inserted at the end
174 // of the collection, we must create a "hole"
175 if (Index != BlockCount - 1) {
176 memmove (Blocks + Index + 1,
177 Blocks + Index,
178 (BlockCount - 1 - Index) * sizeof (BlockInfo));
179 }
180
181 // store the new data
182 Blocks [Index].Ptr = Ptr;
183 Blocks [Index].Size = Size;
184 }
185
186
187
MemBlocksInUse()188 u32 MemBlocksInUse ()
189 {
190 return (u32) BlockCount;
191 }
192
193
194
MemLogBlocksInUse(const char * Name)195 void MemLogBlocksInUse (const char* Name)
196 {
197 FILE* F = fopen (Name, "w+t");
198 if (F == NULL) {
199 // This is a debug function, so ignore the error
200 return;
201 }
202
203 // Get the block count and log some statistics
204 fprintf (F, "Blocks currently in use: %8lu\n\n"
205 "Calls to operator new: %8lu\n"
206 "Calls to operator delete: %8lu\n"
207 "Checked calls to new: %8lu\n"
208 "Checked calls to delete: %8lu\n"
209 "Calls to delete with a NULL arg: %8lu\n\n"
210 "Largest block allocated: %8lu\n"
211 "Maximum memory usage: %8lu\n\n",
212 (unsigned long) BlockCount,
213 (unsigned long) MemNewCount,
214 (unsigned long) MemDelCount,
215 (unsigned long) MemNewCheckCount,
216 (unsigned long) MemDelCheckCount,
217 (unsigned long) MemDelNULLCount,
218 (unsigned long) MemLargestBlock,
219 (unsigned long) MemMaxUsage);
220
221 // Log the blocks
222 BlockInfo* Block = Blocks;
223 for (int I = 0; I < BlockCount; I++, Block++) {
224
225 // Print a line describing the block (convert pointers to hex values)
226 fprintf (F, "Block %5u: Loc = 0x%08lX, Size = %5lu\n",
227 I, (unsigned long) Block->Ptr, (unsigned long) Block->Size);
228
229 }
230
231 // Close the file
232 fclose (F);
233 }
234
235
236
MemDone()237 static void MemDone ()
238 // Log the memory blocks if requested. Does *not* delete the block array
239 // since the startup code may release memory after calling the exit functions
240 // and in this case we will work with a freed block, if we free the block
241 // array here
242 {
243 // If the environment variable SPUNK_MEMLOGBLOCKS is set to something, use
244 // this "something" as a filename to log a list of still allocated blocks
245 const char* Name = getenv ("SPUNK_MEMLOGBLOCKS");
246 if (Name) {
247 MemLogBlocksInUse (Name);
248 }
249 }
250
251
252
MemInit()253 static void MemInit ()
254 // Initialize the memory checker
255 {
256 // Get the defaults for the memory checker
257 MemCheck = getenv ("SPUNK_MEMCHECK") != NULL;
258 MemFill = getenv ("SPUNK_MEMFILL") != NULL;
259
260 // Register the exit function
261 atexit (MemDone);
262
263 // Initialized now
264 IsInitialized = 1;
265 }
266
267
268
269 #ifdef __WATCOMC__
270 /*****************************************************************************/
271 /* class MemCheckInit */
272 /*****************************************************************************/
273
274
275
276 // This section is needed to initialize the memory checker *before* any other
277 // library modules. It does work with Watcom-C++ only, but probably other
278 // compilers have similar ways to ensure library initialization.
279 //
280 // Create a class with a static instance. The constructor of the class will
281 // call the memory initialization, a #pragma assures that the module
282 // initialization code is executed very early.
283 #pragma initialize library;
284
285
286 class MemCheckInit {
287 public:
288 MemCheckInit ();
289 };
290
291
292
MemCheckInit()293 inline MemCheckInit::MemCheckInit ()
294 {
295 if (IsInitialized == 0) {
296 MemInit ();
297 }
298 }
299
300
301
302 static MemCheckInit Initializer;
303
304
305
306 // End of WATCOM specific code
307 #endif
308
309
310
311 /*****************************************************************************/
312 /* Code */
313 /*****************************************************************************/
314
315
316
operator new(size_t Size)317 void* operator new (size_t Size)
318 {
319 // Initialize the memory checker on the first call
320 if (IsInitialized == 0) {
321 MemInit ();
322 }
323
324 // Count the calls to new
325 MemNewCount++;
326
327 // Update largest block info
328 if (Size > MemLargestBlock) {
329 MemLargestBlock = Size;
330 }
331 unsigned char* Ptr;
332 if (MemCheck) {
333
334 // Count the checked calls
335 MemNewCheckCount++;
336
337 // Update memory usage
338 MemUsage += Size;
339 if (MemUsage > MemMaxUsage) {
340 MemMaxUsage = MemUsage;
341 }
342
343 // Get a memory block
344 Ptr = (unsigned char*) malloc (Size + sizeof (MemSig));
345
346 // Make a signature at the end of the block
347 memcpy (Ptr + Size, &MemSig, sizeof (MemSig));
348
349 // Search for the block
350 int Index;
351 if (MemSearch (Ptr, Index) != 0) {
352 // An item with this key exists. This means that the heap is
353 // corrupted
354 FAIL ("MemCheck: Duplicate block!");
355 } else {
356 // The returned pointer is not in the collection of already
357 // allocated blocks, but it may point inside of an already
358 // allocated block. Check this.
359 // Note: Index is the index of the item _before the given
360 // pointer, so simply check the range of the entry with index
361 // Index.
362 if (Index > 0) {
363 // There is a block that's memory address is less than the
364 // one returned by malloc
365 const BlockInfo* BB = Blocks + Index - 1;
366 if (Ptr < BB->Ptr + BB->Size) {
367 // Pointer points inside the block below - heap corrupted
368 FAIL ("MemCheck: Heap corrupt!");
369 }
370 }
371
372 // Heap ok, insert the new block
373 MemInsBlock (Index, Ptr, Size);
374 }
375
376 } else {
377
378 // No memory checking. Allocate a memory block, but beware: New is
379 // defined so that "new char [0]" points to a distinct object every
380 // time it is called, so one cannot return NULL for a size of 0!
381 Ptr = (unsigned char*) malloc (Size ? Size : 1);
382
383 }
384
385 // Check if we got memory, fail otherwise
386 if (Ptr == NULL) {
387 FAIL ("MemCheck: Out of memory");
388 }
389
390 // Fill the memory block if requested
391 if (MemFill) {
392 memset (Ptr, FillVal, Size);
393 }
394
395 // Return a pointer to the memory block
396 return Ptr;
397 }
398
399
400
operator delete(void * P)401 void operator delete (void* P)
402 {
403 // We cannot call delete if the memory system is not initialized
404 if (IsInitialized == 0) {
405 FAIL ("MemCheck: Trying to delete a block before the first call to new!");
406 }
407
408 // Count the calls to delete
409 MemDelCount++;
410
411 // Deleting NULL pointers is always ok, nothing has to be done
412 if (P == 0) {
413 MemDelNULLCount++;
414 return;
415 }
416
417 if (MemCheck) {
418
419 // Count the calls
420 MemDelCheckCount++;
421
422 // Cast the pointer
423 unsigned char* Ptr = (unsigned char*) P;
424
425 // Search for the block
426 int Index;
427 if (MemSearch (Ptr, Index) != 0) {
428
429 // The block exists. Check the signature, then delete it
430 BlockInfo* BI = Blocks + Index;
431 if (memcmp (Ptr + BI->Size, &MemSig, sizeof (MemSig)) != 0) {
432 // Signature overwritten
433 FAIL ("MemCheck: Block signature overwritten");
434 }
435
436 // Fill the memory block if requested
437 if (MemFill) {
438 memset (Ptr, FillVal, BI->Size);
439 }
440
441 // Update memory usage
442 MemUsage -= BI->Size;
443
444 // Delete the entry
445 MemDelBlock (Index);
446
447 // Delete the memory block
448 free (P);
449
450 } else {
451 // Trying to free a block that is not allocated
452 FAIL ("MemCheck: Trying to free a block that is not allocated");
453 }
454 } else {
455
456 // Free the block without checks
457 free (P);
458
459 }
460 }
461
462
463
464