|
|
1.1 root 1: /*
2: * tclCkalloc.c --
3: * Interface to malloc and free that provides support for debugging problems
4: * involving overwritten, double freeing memory and loss of memory.
5: *
6: * Copyright 1991 Regents of the University of California
7: * Permission to use, copy, modify, and distribute this
8: * software and its documentation for any purpose and without
9: * fee is hereby granted, provided that the above copyright
10: * notice appear in all copies. The University of California
11: * makes no representations about the suitability of this
12: * software for any purpose. It is provided "as is" without
13: * express or implied warranty.
14: *
15: * This code contributed by Karl Lehenbauer and Mark Diekhans
16: *
17: */
18:
19: #include "tclint.h"
20:
21: #define FALSE 0
22: #define TRUE 1
23:
24: #ifdef TCL_MEM_DEBUG
25: #ifndef TCL_GENERIC_ONLY
26: #include "tclunix.h"
27: #endif
28:
29: #define GUARD_SIZE 8
30:
31: struct mem_header {
32: long length;
33: char *file;
34: int line;
35: struct mem_header *flink;
36: struct mem_header *blink;
37: unsigned char low_guard[GUARD_SIZE];
38: char body[1];
39: };
40:
41: static struct mem_header *allocHead = NULL; /* List of allocated structures */
42:
43: #define GUARD_VALUE 0341
44:
45: /* static char high_guard[] = {0x89, 0xab, 0xcd, 0xef}; */
46:
47: static int total_mallocs = 0;
48: static int total_frees = 0;
49: static int current_bytes_malloced = 0;
50: static int maximum_bytes_malloced = 0;
51: static int current_malloc_packets = 0;
52: static int maximum_malloc_packets = 0;
53: static int break_on_malloc = 0;
54: static int trace_on_at_malloc = 0;
55: static int alloc_tracing = FALSE;
56: static int init_malloced_bodies = FALSE;
57: #ifdef MEM_VALIDATE
58: static int validate_memory = TRUE;
59: #else
60: static int validate_memory = FALSE;
61: #endif
62:
63:
64: /*
65: *----------------------------------------------------------------------
66: *
67: * dump_memory_info --
68: * Display the global memory management statistics.
69: *
70: *----------------------------------------------------------------------
71: */
72: static void
73: dump_memory_info(outFile)
74: FILE *outFile;
75: {
76: fprintf(outFile,"total mallocs %10d\n",
77: total_mallocs);
78: fprintf(outFile,"total frees %10d\n",
79: total_frees);
80: fprintf(outFile,"current packets allocated %10d\n",
81: current_malloc_packets);
82: fprintf(outFile,"current bytes allocated %10d\n",
83: current_bytes_malloced);
84: fprintf(outFile,"maximum packets allocated %10d\n",
85: maximum_malloc_packets);
86: fprintf(outFile,"maximum bytes allocated %10d\n",
87: maximum_bytes_malloced);
88: }
89:
90: /*
91: *----------------------------------------------------------------------
92: *
93: * ValidateMemory --
94: * Procedure to validate allocted memory guard zones.
95: *
96: *----------------------------------------------------------------------
97: */
98: static void
99: ValidateMemory (memHeaderP, file, line, nukeGuards)
100: struct mem_header *memHeaderP;
101: char *file;
102: int line;
103: int nukeGuards;
104: {
105: unsigned char *hiPtr;
106: int idx;
107: int guard_failed = FALSE;
108:
109: for (idx = 0; idx < GUARD_SIZE; idx++)
110: if (*(memHeaderP->low_guard + idx) != GUARD_VALUE) {
111: guard_failed = TRUE;
112: fflush (stdout);
113: fprintf(stderr, "low guard byte %d is 0x%x\n", idx,
114: *(memHeaderP->low_guard + idx) & 0xff);
115: }
116:
117: if (guard_failed) {
118: dump_memory_info (stderr);
119: fprintf (stderr, "low guard failed at %lx, %s %d\n",
120: memHeaderP->body, file, line);
121: fflush (stderr); /* In case name pointer is bad. */
122: fprintf (stderr, "Allocated at (%s %d)\n", memHeaderP->file,
123: memHeaderP->line);
124: panic ("Memory validation failure");
125: }
126:
127: hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
128: for (idx = 0; idx < GUARD_SIZE; idx++)
129: if (*(hiPtr + idx) != GUARD_VALUE) {
130: guard_failed = TRUE;
131: fflush (stdout);
132: fprintf(stderr, "hi guard byte %d is 0x%x\n", idx,
133: *(hiPtr+idx) & 0xff);
134: }
135:
136: if (guard_failed) {
137: dump_memory_info (stderr);
138: fprintf (stderr, "high guard failed at %lx, %s %d\n",
139: memHeaderP->body, file, line);
140: fflush (stderr); /* In case name pointer is bad. */
141: fprintf (stderr, "Allocated at (%s %d)\n", memHeaderP->file,
142: memHeaderP->line);
143: panic ("Memory validation failure");
144: }
145:
146: if (nukeGuards) {
147: memset ((char *) memHeaderP->low_guard, 0, GUARD_SIZE);
148: memset ((char *) hiPtr, 0, GUARD_SIZE);
149: }
150:
151: }
152:
153: /*
154: *----------------------------------------------------------------------
155: *
156: * Tcl_ValidateAllMemory --
157: * Validates guard regions for all allocated memory.
158: *
159: *----------------------------------------------------------------------
160: */
161: void
162: Tcl_ValidateAllMemory (file, line)
163: char *file;
164: int line;
165: {
166: struct mem_header *memScanP;
167:
168: for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
169: ValidateMemory (memScanP, file, line, FALSE);
170:
171: }
172:
173: /*
174: *----------------------------------------------------------------------
175: *
176: * Tcl_DumpActiveMemory --
177: * Displays all allocated memory to stderr.
178: *
179: * Results:
180: * Return TCL_ERROR if an error accessing the file occures, `errno'
181: * will have the file error number left in it.
182: *----------------------------------------------------------------------
183: */
184: int
185: Tcl_DumpActiveMemory (fileName)
186: char *fileName;
187: {
188: FILE *fileP;
189: struct mem_header *memScanP;
190: char *address;
191:
192: #ifdef MSDOS
193: fileP = fopen (fileName, "wb");
194: #else
195: fileP = fopen (fileName, "w");
196: #endif
197: if (fileP == NULL)
198: return TCL_ERROR;
199:
200: for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
201: address = &memScanP->body [0];
202: fprintf (fileP, "%8lx - %8lx %7d @ %s %d\n", address,
203: address + memScanP->length - 1, memScanP->length,
204: memScanP->file, memScanP->line);
205: }
206: fclose (fileP);
207: return TCL_OK;
208: }
209:
210: /*
211: *----------------------------------------------------------------------
212: *
213: * Tcl_DbCkalloc - debugging ckalloc
214: *
215: * Allocate the requested amount of space plus some extra for
216: * guard bands at both ends of the request, plus a size, panicing
217: * if there isn't enough space, then write in the guard bands
218: * and return the address of the space in the middle that the
219: * user asked for.
220: *
221: * The second and third arguments are file and line, these contain
222: * the filename and line number corresponding to the caller.
223: * These are sent by the ckalloc macro; it uses the preprocessor
224: * autodefines __FILE__ and __LINE__.
225: *
226: *----------------------------------------------------------------------
227: */
228: char *
229: Tcl_DbCkalloc(size, file, line)
230: unsigned int size;
231: char *file;
232: int line;
233: {
234: struct mem_header *result;
235:
236: if (validate_memory)
237: Tcl_ValidateAllMemory (file, line);
238:
239: result = (struct mem_header *)malloc((unsigned)size +
240: sizeof(struct mem_header) + GUARD_SIZE);
241: if (result == NULL) {
242: fflush(stdout);
243: dump_memory_info(stderr);
244: panic("unable to alloc %d bytes, %s line %d", size, file,
245: line);
246: }
247:
248: /*
249: * Fill in guard zones and size. Link into allocated list.
250: */
251: result->length = size;
252: result->file = file;
253: result->line = line;
254: memset ((char *) result->low_guard, GUARD_VALUE, GUARD_SIZE);
255: memset (result->body + size, GUARD_VALUE, GUARD_SIZE);
256: result->flink = allocHead;
257: result->blink = NULL;
258: if (allocHead != NULL)
259: allocHead->blink = result;
260: allocHead = result;
261:
262: total_mallocs++;
263: if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
264: (void) fflush(stdout);
265: fprintf(stderr, "reached malloc trace enable point (%d)\n",
266: total_mallocs);
267: fflush(stderr);
268: alloc_tracing = TRUE;
269: trace_on_at_malloc = 0;
270: }
271:
272: if (alloc_tracing)
273: fprintf(stderr,"ckalloc %lx %d %s %d\n", result->body, size,
274: file, line);
275:
276: if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
277: break_on_malloc = 0;
278: (void) fflush(stdout);
279: fprintf(stderr,"reached malloc break limit (%d)\n",
280: total_mallocs);
281: fprintf(stderr, "program will now enter C debugger\n");
282: (void) fflush(stderr);
283: kill (getpid(), SIGINT);
284: }
285:
286: current_malloc_packets++;
287: if (current_malloc_packets > maximum_malloc_packets)
288: maximum_malloc_packets = current_malloc_packets;
289: current_bytes_malloced += size;
290: if (current_bytes_malloced > maximum_bytes_malloced)
291: maximum_bytes_malloced = current_bytes_malloced;
292:
293: if (init_malloced_bodies)
294: memset (result->body, 0xff, (int) size);
295:
296: return result->body;
297: }
298:
299: /*
300: *----------------------------------------------------------------------
301: *
302: * Tcl_DbCkfree - debugging ckfree
303: *
304: * Verify that the low and high guards are intact, and if so
305: * then free the buffer else panic.
306: *
307: * The guards are erased after being checked to catch duplicate
308: * frees.
309: *
310: * The second and third arguments are file and line, these contain
311: * the filename and line number corresponding to the caller.
312: * These are sent by the ckfree macro; it uses the preprocessor
313: * autodefines __FILE__ and __LINE__.
314: *
315: *----------------------------------------------------------------------
316: */
317:
318: int
319: Tcl_DbCkfree(ptr, file, line)
320: char * ptr;
321: char *file;
322: int line;
323: {
324: struct mem_header *memp = 0; /* Must be zero for size calc */
325:
326: /*
327: * Since header ptr is zero, body offset will be size
328: */
329: memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
330:
331: if (alloc_tracing)
332: fprintf(stderr, "ckfree %lx %ld %s %d\n", memp->body,
333: memp->length, file, line);
334:
335: if (validate_memory)
336: Tcl_ValidateAllMemory (file, line);
337:
338: ValidateMemory (memp, file, line, TRUE);
339:
340: total_frees++;
341: current_malloc_packets--;
342: current_bytes_malloced -= memp->length;
343:
344: /*
345: * Delink from allocated list
346: */
347: if (memp->flink != NULL)
348: memp->flink->blink = memp->blink;
349: if (memp->blink != NULL)
350: memp->blink->flink = memp->flink;
351: if (allocHead == memp)
352: allocHead = memp->flink;
353: free((char *) memp);
354: return 0;
355: }
356:
357: /*
358: *----------------------------------------------------------------------
359: *
360: * MemoryCmd --
361: * Implements the TCL memory command:
362: * memory info
363: * memory display
364: * break_on_malloc count
365: * trace_on_at_malloc count
366: * trace on|off
367: * validate on|off
368: *
369: * Results:
370: * Standard TCL results.
371: *
372: *----------------------------------------------------------------------
373: */
374: /* ARGSUSED */
375: static int
376: MemoryCmd (clientData, interp, argc, argv)
377: char *clientData;
378: Tcl_Interp *interp;
379: int argc;
380: char **argv;
381: {
382: char *fileName;
383:
384: if (argc < 2) {
385: Tcl_AppendResult(interp, "wrong # args: should be \"",
386: argv[0], " option [args..]\"", (char *) NULL);
387: return TCL_ERROR;
388: }
389:
390: if (strcmp(argv[1],"trace") == 0) {
391: if (argc != 3)
392: goto bad_suboption;
393: alloc_tracing = (strcmp(argv[2],"on") == 0);
394: return TCL_OK;
395: }
396: if (strcmp(argv[1],"init") == 0) {
397: if (argc != 3)
398: goto bad_suboption;
399: init_malloced_bodies = (strcmp(argv[2],"on") == 0);
400: return TCL_OK;
401: }
402: if (strcmp(argv[1],"validate") == 0) {
403: if (argc != 3)
404: goto bad_suboption;
405: validate_memory = (strcmp(argv[2],"on") == 0);
406: return TCL_OK;
407: }
408: if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
409: if (argc != 3)
410: goto argError;
411: if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK)
412: return TCL_ERROR;
413: return TCL_OK;
414: }
415: if (strcmp(argv[1],"break_on_malloc") == 0) {
416: if (argc != 3)
417: goto argError;
418: if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK)
419: return TCL_ERROR;
420: return TCL_OK;
421: }
422:
423: if (strcmp(argv[1],"info") == 0) {
424: dump_memory_info(stdout);
425: return TCL_OK;
426: }
427: if (strcmp(argv[1],"active") == 0) {
428: if (argc != 3) {
429: Tcl_AppendResult(interp, "wrong # args: should be \"",
430: argv[0], " active file", (char *) NULL);
431: return TCL_ERROR;
432: }
433: fileName = argv [2];
434: if (fileName [0] == '~')
435: if ((fileName = Tcl_TildeSubst (interp, fileName)) == NULL)
436: return TCL_ERROR;
437: if (Tcl_DumpActiveMemory (fileName) != TCL_OK) {
438: Tcl_AppendResult(interp, "error accessing ", argv[2],
439: (char *) NULL);
440: return TCL_ERROR;
441: }
442: return TCL_OK;
443: }
444: Tcl_AppendResult(interp, "bad option \"", argv[1],
445: "\": should be info, init, active, break_on_malloc, ",
446: "trace_on_at_malloc, trace, or validate", (char *) NULL);
447: return TCL_ERROR;
448:
449: argError:
450: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
451: " ", argv[1], "count\"", (char *) NULL);
452: return TCL_ERROR;
453:
454: bad_suboption:
455: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
456: " ", argv[1], " on|off\"", (char *) NULL);
457: return TCL_ERROR;
458: }
459:
460: /*
461: *----------------------------------------------------------------------
462: *
463: * Tcl_InitMemory --
464: * Initialize the memory command.
465: *
466: *----------------------------------------------------------------------
467: */
468: void
469: Tcl_InitMemory(interp)
470: Tcl_Interp *interp;
471: {
472: Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData)NULL,
473: (void (*)())NULL);
474: }
475:
476: #else
477:
478:
479: /*
480: *----------------------------------------------------------------------
481: *
482: * Tcl_Ckalloc --
483: * Interface to malloc when TCL_MEM_DEBUG is disabled. It does check
484: * that memory was actually allocated.
485: *
486: *----------------------------------------------------------------------
487: */
488: VOID *
489: Tcl_Ckalloc (size)
490: unsigned int size;
491: {
492: char *result;
493:
494: result = malloc(size);
495: if (result == NULL)
496: panic("unable to alloc %d bytes", size);
497: return result;
498: }
499:
500: /*
501: *----------------------------------------------------------------------
502: *
503: * TckCkfree --
504: * Interface to free when TCL_MEM_DEBUG is disabled. Done here rather
505: * in the macro to keep some modules from being compiled with
506: * TCL_MEM_DEBUG enabled and some with it disabled.
507: *
508: *----------------------------------------------------------------------
509: */
510: void
511: Tcl_Ckfree (ptr)
512: VOID *ptr;
513: {
514: free (ptr);
515: }
516:
517: /*
518: *----------------------------------------------------------------------
519: *
520: * Tcl_InitMemory --
521: * Dummy initialization for memory command, which is only available
522: * if TCL_MEM_DEBUG is on.
523: *
524: *----------------------------------------------------------------------
525: */
526: /* ARGSUSED */
527: void
528: Tcl_InitMemory(interp)
529: Tcl_Interp *interp;
530: {
531: }
532:
533: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.