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