]>
cvs.zerfleddert.de Git - micropolis/blob - src/tcl/tclckall.c
3 * Interface to malloc and free that provides support for debugging problems
4 * involving overwritten, double freeing memory and loss of memory.
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.
15 * This code contributed by Karl Lehenbauer and Mark Diekhans
25 #ifndef TCL_GENERIC_ONLY
35 struct mem_header
*flink
;
36 struct mem_header
*blink
;
37 unsigned char low_guard
[GUARD_SIZE
];
41 static struct mem_header
*allocHead
= NULL
; /* List of allocated structures */
43 #define GUARD_VALUE 0341
45 /* static char high_guard[] = {0x89, 0xab, 0xcd, 0xef}; */
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
;
58 static int validate_memory
= TRUE
;
60 static int validate_memory
= FALSE
;
65 *----------------------------------------------------------------------
68 * Display the global memory management statistics.
70 *----------------------------------------------------------------------
73 dump_memory_info (FILE *outFile
)
75 fprintf(outFile
,"total mallocs %10d\n",
77 fprintf(outFile
,"total frees %10d\n",
79 fprintf(outFile
,"current packets allocated %10d\n",
80 current_malloc_packets
);
81 fprintf(outFile
,"current bytes allocated %10d\n",
82 current_bytes_malloced
);
83 fprintf(outFile
,"maximum packets allocated %10d\n",
84 maximum_malloc_packets
);
85 fprintf(outFile
,"maximum bytes allocated %10d\n",
86 maximum_bytes_malloced
);
90 *----------------------------------------------------------------------
93 * Procedure to validate allocted memory guard zones.
95 *----------------------------------------------------------------------
98 ValidateMemory (struct mem_header
*memHeaderP
, char *file
, int line
, int nukeGuards
)
100 unsigned char *hiPtr
;
102 int guard_failed
= FALSE
;
104 for (idx
= 0; idx
< GUARD_SIZE
; idx
++)
105 if (*(memHeaderP
->low_guard
+ idx
) != GUARD_VALUE
) {
108 fprintf(stderr
, "low guard byte %d is 0x%x\n", idx
,
109 *(memHeaderP
->low_guard
+ idx
) & 0xff);
113 dump_memory_info (stderr
);
114 fprintf (stderr
, "low guard failed at %lx, %s %d\n",
115 memHeaderP
->body
, file
, line
);
116 fflush (stderr
); /* In case name pointer is bad. */
117 fprintf (stderr
, "Allocated at (%s %d)\n", memHeaderP
->file
,
119 panic ("Memory validation failure");
122 hiPtr
= (unsigned char *)memHeaderP
->body
+ memHeaderP
->length
;
123 for (idx
= 0; idx
< GUARD_SIZE
; idx
++)
124 if (*(hiPtr
+ idx
) != GUARD_VALUE
) {
127 fprintf(stderr
, "hi guard byte %d is 0x%x\n", idx
,
128 *(hiPtr
+idx
) & 0xff);
132 dump_memory_info (stderr
);
133 fprintf (stderr
, "high guard failed at %lx, %s %d\n",
134 memHeaderP
->body
, file
, line
);
135 fflush (stderr
); /* In case name pointer is bad. */
136 fprintf (stderr
, "Allocated at (%s %d)\n", memHeaderP
->file
,
138 panic ("Memory validation failure");
142 memset ((char *) memHeaderP
->low_guard
, 0, GUARD_SIZE
);
143 memset ((char *) hiPtr
, 0, GUARD_SIZE
);
149 *----------------------------------------------------------------------
151 * Tcl_ValidateAllMemory --
152 * Validates guard regions for all allocated memory.
154 *----------------------------------------------------------------------
157 Tcl_ValidateAllMemory (char *file
, int line
)
159 struct mem_header
*memScanP
;
161 for (memScanP
= allocHead
; memScanP
!= NULL
; memScanP
= memScanP
->flink
)
162 ValidateMemory (memScanP
, file
, line
, FALSE
);
167 *----------------------------------------------------------------------
169 * Tcl_DumpActiveMemory --
170 * Displays all allocated memory to stderr.
173 * Return TCL_ERROR if an error accessing the file occures, `errno'
174 * will have the file error number left in it.
175 *----------------------------------------------------------------------
178 Tcl_DumpActiveMemory (char *fileName
)
181 struct mem_header
*memScanP
;
185 fileP
= fopen (fileName
, "wb");
187 fileP
= fopen (fileName
, "w");
192 for (memScanP
= allocHead
; memScanP
!= NULL
; memScanP
= memScanP
->flink
) {
193 address
= &memScanP
->body
[0];
194 fprintf (fileP
, "%8lx - %8lx %7d @ %s %d\n", address
,
195 address
+ memScanP
->length
- 1, memScanP
->length
,
196 memScanP
->file
, memScanP
->line
);
203 *----------------------------------------------------------------------
205 * Tcl_DbCkalloc - debugging ckalloc
207 * Allocate the requested amount of space plus some extra for
208 * guard bands at both ends of the request, plus a size, panicing
209 * if there isn't enough space, then write in the guard bands
210 * and return the address of the space in the middle that the
213 * The second and third arguments are file and line, these contain
214 * the filename and line number corresponding to the caller.
215 * These are sent by the ckalloc macro; it uses the preprocessor
216 * autodefines __FILE__ and __LINE__.
218 *----------------------------------------------------------------------
221 Tcl_DbCkalloc (unsigned int size
, char *file
, int line
)
223 struct mem_header
*result
;
226 Tcl_ValidateAllMemory (file
, line
);
228 result
= (struct mem_header
*)malloc((unsigned)size
+
229 sizeof(struct mem_header
) + GUARD_SIZE
);
230 if (result
== NULL
) {
232 dump_memory_info(stderr
);
233 panic("unable to alloc %d bytes, %s line %d", size
, file
,
238 * Fill in guard zones and size. Link into allocated list.
240 result
->length
= size
;
243 memset ((char *) result
->low_guard
, GUARD_VALUE
, GUARD_SIZE
);
244 memset (result
->body
+ size
, GUARD_VALUE
, GUARD_SIZE
);
245 result
->flink
= allocHead
;
246 result
->blink
= NULL
;
247 if (allocHead
!= NULL
)
248 allocHead
->blink
= result
;
252 if (trace_on_at_malloc
&& (total_mallocs
>= trace_on_at_malloc
)) {
253 (void) fflush(stdout
);
254 fprintf(stderr
, "reached malloc trace enable point (%d)\n",
257 alloc_tracing
= TRUE
;
258 trace_on_at_malloc
= 0;
262 fprintf(stderr
,"ckalloc %lx %d %s %d\n", result
->body
, size
,
265 if (break_on_malloc
&& (total_mallocs
>= break_on_malloc
)) {
267 (void) fflush(stdout
);
268 fprintf(stderr
,"reached malloc break limit (%d)\n",
270 fprintf(stderr
, "program will now enter C debugger\n");
271 (void) fflush(stderr
);
272 kill (getpid(), SIGINT
);
275 current_malloc_packets
++;
276 if (current_malloc_packets
> maximum_malloc_packets
)
277 maximum_malloc_packets
= current_malloc_packets
;
278 current_bytes_malloced
+= size
;
279 if (current_bytes_malloced
> maximum_bytes_malloced
)
280 maximum_bytes_malloced
= current_bytes_malloced
;
282 if (init_malloced_bodies
)
283 memset (result
->body
, 0xff, (int) size
);
289 *----------------------------------------------------------------------
291 * Tcl_DbCkfree - debugging ckfree
293 * Verify that the low and high guards are intact, and if so
294 * then free the buffer else panic.
296 * The guards are erased after being checked to catch duplicate
299 * The second and third arguments are file and line, these contain
300 * the filename and line number corresponding to the caller.
301 * These are sent by the ckfree macro; it uses the preprocessor
302 * autodefines __FILE__ and __LINE__.
304 *----------------------------------------------------------------------
308 Tcl_DbCkfree (char *ptr
, char *file
, int line
)
310 struct mem_header
*memp
= 0; /* Must be zero for size calc */
313 * Since header ptr is zero, body offset will be size
315 memp
= (struct mem_header
*)(((char *) ptr
) - (int)memp
->body
);
318 fprintf(stderr
, "ckfree %lx %ld %s %d\n", memp
->body
,
319 memp
->length
, file
, line
);
322 Tcl_ValidateAllMemory (file
, line
);
324 ValidateMemory (memp
, file
, line
, TRUE
);
327 current_malloc_packets
--;
328 current_bytes_malloced
-= memp
->length
;
331 * Delink from allocated list
333 if (memp
->flink
!= NULL
)
334 memp
->flink
->blink
= memp
->blink
;
335 if (memp
->blink
!= NULL
)
336 memp
->blink
->flink
= memp
->flink
;
337 if (allocHead
== memp
)
338 allocHead
= memp
->flink
;
344 *----------------------------------------------------------------------
347 * Implements the TCL memory command:
350 * break_on_malloc count
351 * trace_on_at_malloc count
356 * Standard TCL results.
358 *----------------------------------------------------------------------
362 MemoryCmd (char *clientData
, Tcl_Interp
*interp
, int argc
, char **argv
)
367 Tcl_AppendResult(interp
, "wrong # args: should be \"",
368 argv
[0], " option [args..]\"", (char *) NULL
);
372 if (strcmp(argv
[1],"trace") == 0) {
375 alloc_tracing
= (strcmp(argv
[2],"on") == 0);
378 if (strcmp(argv
[1],"init") == 0) {
381 init_malloced_bodies
= (strcmp(argv
[2],"on") == 0);
384 if (strcmp(argv
[1],"validate") == 0) {
387 validate_memory
= (strcmp(argv
[2],"on") == 0);
390 if (strcmp(argv
[1],"trace_on_at_malloc") == 0) {
393 if (Tcl_GetInt(interp
, argv
[2], &trace_on_at_malloc
) != TCL_OK
)
397 if (strcmp(argv
[1],"break_on_malloc") == 0) {
400 if (Tcl_GetInt(interp
, argv
[2], &break_on_malloc
) != TCL_OK
)
405 if (strcmp(argv
[1],"info") == 0) {
406 dump_memory_info(stdout
);
409 if (strcmp(argv
[1],"active") == 0) {
411 Tcl_AppendResult(interp
, "wrong # args: should be \"",
412 argv
[0], " active file", (char *) NULL
);
416 if (fileName
[0] == '~')
417 if ((fileName
= Tcl_TildeSubst (interp
, fileName
)) == NULL
)
419 if (Tcl_DumpActiveMemory (fileName
) != TCL_OK
) {
420 Tcl_AppendResult(interp
, "error accessing ", argv
[2],
426 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
427 "\": should be info, init, active, break_on_malloc, ",
428 "trace_on_at_malloc, trace, or validate", (char *) NULL
);
432 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
433 " ", argv
[1], "count\"", (char *) NULL
);
437 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
438 " ", argv
[1], " on|off\"", (char *) NULL
);
443 *----------------------------------------------------------------------
446 * Initialize the memory command.
448 *----------------------------------------------------------------------
451 Tcl_InitMemory (Tcl_Interp
*interp
)
453 Tcl_CreateCommand (interp
, "memory", MemoryCmd
, (ClientData
)NULL
,
461 *----------------------------------------------------------------------
464 * Interface to malloc when TCL_MEM_DEBUG is disabled. It does check
465 * that memory was actually allocated.
467 *----------------------------------------------------------------------
470 Tcl_Ckalloc (unsigned int size
)
474 result
= malloc(size
);
476 panic("unable to alloc %d bytes", size
);
481 *----------------------------------------------------------------------
484 * Interface to free when TCL_MEM_DEBUG is disabled. Done here rather
485 * in the macro to keep some modules from being compiled with
486 * TCL_MEM_DEBUG enabled and some with it disabled.
488 *----------------------------------------------------------------------
491 Tcl_Ckfree (VOID
*ptr
)
497 *----------------------------------------------------------------------
500 * Dummy initialization for memory command, which is only available
501 * if TCL_MEM_DEBUG is on.
503 *----------------------------------------------------------------------
507 Tcl_InitMemory (Tcl_Interp
*interp
)