1 | /*
|
---|
2 | * tclCkalloc.c --
|
---|
3 | *
|
---|
4 | * Interface to malloc and free that provides support for debugging problems
|
---|
5 | * involving overwritten, double freeing memory and loss of memory.
|
---|
6 | *
|
---|
7 | * Copyright (c) 1991-1994 The Regents of the University of California.
|
---|
8 | * Copyright (c) 1994-1996 Sun Microsystems, Inc.
|
---|
9 | *
|
---|
10 | * See the file "license.terms" for information on usage and redistribution
|
---|
11 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
---|
12 | *
|
---|
13 | * This code contributed by Karl Lehenbauer and Mark Diekhans
|
---|
14 | *
|
---|
15 | * RCS: @(#) $Id: tclCkalloc.c,v 1.1 2008-06-04 13:58:04 demin Exp $
|
---|
16 | */
|
---|
17 |
|
---|
18 | #include "tclInt.h"
|
---|
19 | #include "tclPort.h"
|
---|
20 |
|
---|
21 | #define FALSE 0
|
---|
22 | #define TRUE 1
|
---|
23 |
|
---|
24 | #ifdef TCL_MEM_DEBUG
|
---|
25 |
|
---|
26 | /*
|
---|
27 | * One of the following structures is allocated each time the
|
---|
28 | * "memory tag" command is invoked, to hold the current tag.
|
---|
29 | */
|
---|
30 |
|
---|
31 | typedef struct MemTag {
|
---|
32 | int refCount; /* Number of mem_headers referencing
|
---|
33 | * this tag. */
|
---|
34 | char string[4]; /* Actual size of string will be as
|
---|
35 | * large as needed for actual tag. This
|
---|
36 | * must be the last field in the structure. */
|
---|
37 | } MemTag;
|
---|
38 |
|
---|
39 | #define TAG_SIZE(bytesInString) ((unsigned) sizeof(MemTag) + bytesInString - 3)
|
---|
40 |
|
---|
41 | static MemTag *curTagPtr = NULL;/* Tag to use in all future mem_headers
|
---|
42 | * (set by "memory tag" command). */
|
---|
43 |
|
---|
44 | /*
|
---|
45 | * One of the following structures is allocated just before each
|
---|
46 | * dynamically allocated chunk of memory, both to record information
|
---|
47 | * about the chunk and to help detect chunk under-runs.
|
---|
48 | */
|
---|
49 |
|
---|
50 | #define LOW_GUARD_SIZE (8 + (32 - (sizeof(long) + sizeof(int)))%8)
|
---|
51 | struct mem_header {
|
---|
52 | struct mem_header *flink;
|
---|
53 | struct mem_header *blink;
|
---|
54 | MemTag *tagPtr; /* Tag from "memory tag" command; may be
|
---|
55 | * NULL. */
|
---|
56 | char *file;
|
---|
57 | long length;
|
---|
58 | int line;
|
---|
59 | unsigned char low_guard[LOW_GUARD_SIZE];
|
---|
60 | /* Aligns body on 8-byte boundary, plus
|
---|
61 | * provides at least 8 additional guard bytes
|
---|
62 | * to detect underruns. */
|
---|
63 | char body[1]; /* First byte of client's space. Actual
|
---|
64 | * size of this field will be larger than
|
---|
65 | * one. */
|
---|
66 | };
|
---|
67 |
|
---|
68 | static struct mem_header *allocHead = NULL; /* List of allocated structures */
|
---|
69 |
|
---|
70 | #define GUARD_VALUE 0141
|
---|
71 |
|
---|
72 | /*
|
---|
73 | * The following macro determines the amount of guard space *above* each
|
---|
74 | * chunk of memory.
|
---|
75 | */
|
---|
76 |
|
---|
77 | #define HIGH_GUARD_SIZE 8
|
---|
78 |
|
---|
79 | /*
|
---|
80 | * The following macro computes the offset of the "body" field within
|
---|
81 | * mem_header. It is used to get back to the header pointer from the
|
---|
82 | * body pointer that's used by clients.
|
---|
83 | */
|
---|
84 |
|
---|
85 | #define BODY_OFFSET \
|
---|
86 | ((unsigned long) (&((struct mem_header *) 0)->body))
|
---|
87 |
|
---|
88 | static int total_mallocs = 0;
|
---|
89 | static int total_frees = 0;
|
---|
90 | static int current_bytes_malloced = 0;
|
---|
91 | static int maximum_bytes_malloced = 0;
|
---|
92 | static int current_malloc_packets = 0;
|
---|
93 | static int maximum_malloc_packets = 0;
|
---|
94 | static int break_on_malloc = 0;
|
---|
95 | static int trace_on_at_malloc = 0;
|
---|
96 | static int alloc_tracing = FALSE;
|
---|
97 | static int init_malloced_bodies = TRUE;
|
---|
98 | #ifdef MEM_VALIDATE
|
---|
99 | static int validate_memory = TRUE;
|
---|
100 | #else
|
---|
101 | static int validate_memory = FALSE;
|
---|
102 | #endif
|
---|
103 |
|
---|
104 | /*
|
---|
105 | * Prototypes for procedures defined in this file:
|
---|
106 | */
|
---|
107 |
|
---|
108 | static int MemoryCmd _ANSI_ARGS_((ClientData clientData,
|
---|
109 | Tcl_Interp *interp, int argc, char **argv));
|
---|
110 | static void ValidateMemory _ANSI_ARGS_((
|
---|
111 | struct mem_header *memHeaderP, char *file,
|
---|
112 | int line, int nukeGuards));
|
---|
113 | |
---|
114 |
|
---|
115 | /*
|
---|
116 | *----------------------------------------------------------------------
|
---|
117 | *
|
---|
118 | * TclDumpMemoryInfo --
|
---|
119 | * Display the global memory management statistics.
|
---|
120 | *
|
---|
121 | *----------------------------------------------------------------------
|
---|
122 | */
|
---|
123 | void
|
---|
124 | TclDumpMemoryInfo(outFile)
|
---|
125 | FILE *outFile;
|
---|
126 | {
|
---|
127 | fprintf(outFile,"total mallocs %10d\n",
|
---|
128 | total_mallocs);
|
---|
129 | fprintf(outFile,"total frees %10d\n",
|
---|
130 | total_frees);
|
---|
131 | fprintf(outFile,"current packets allocated %10d\n",
|
---|
132 | current_malloc_packets);
|
---|
133 | fprintf(outFile,"current bytes allocated %10d\n",
|
---|
134 | current_bytes_malloced);
|
---|
135 | fprintf(outFile,"maximum packets allocated %10d\n",
|
---|
136 | maximum_malloc_packets);
|
---|
137 | fprintf(outFile,"maximum bytes allocated %10d\n",
|
---|
138 | maximum_bytes_malloced);
|
---|
139 | }
|
---|
140 | |
---|
141 |
|
---|
142 | /*
|
---|
143 | *----------------------------------------------------------------------
|
---|
144 | *
|
---|
145 | * ValidateMemory --
|
---|
146 | * Procedure to validate allocted memory guard zones.
|
---|
147 | *
|
---|
148 | *----------------------------------------------------------------------
|
---|
149 | */
|
---|
150 | static void
|
---|
151 | ValidateMemory(memHeaderP, file, line, nukeGuards)
|
---|
152 | struct mem_header *memHeaderP;
|
---|
153 | char *file;
|
---|
154 | int line;
|
---|
155 | int nukeGuards;
|
---|
156 | {
|
---|
157 | unsigned char *hiPtr;
|
---|
158 | int idx;
|
---|
159 | int guard_failed = FALSE;
|
---|
160 | int byte;
|
---|
161 |
|
---|
162 | for (idx = 0; idx < LOW_GUARD_SIZE; idx++) {
|
---|
163 | byte = *(memHeaderP->low_guard + idx);
|
---|
164 | if (byte != GUARD_VALUE) {
|
---|
165 | guard_failed = TRUE;
|
---|
166 | fflush(stdout);
|
---|
167 | byte &= 0xff;
|
---|
168 | fprintf(stderr, "low guard byte %d is 0x%x \t%c\n", idx, byte,
|
---|
169 | (isprint(UCHAR(byte)) ? byte : ' '));
|
---|
170 | }
|
---|
171 | }
|
---|
172 | if (guard_failed) {
|
---|
173 | TclDumpMemoryInfo (stderr);
|
---|
174 | fprintf(stderr, "low guard failed at %lx, %s %d\n",
|
---|
175 | (long unsigned int) memHeaderP->body, file, line);
|
---|
176 | fflush(stderr); /* In case name pointer is bad. */
|
---|
177 | fprintf(stderr, "%ld bytes allocated at (%s %d)\n", memHeaderP->length,
|
---|
178 | memHeaderP->file, memHeaderP->line);
|
---|
179 | panic ("Memory validation failure");
|
---|
180 | }
|
---|
181 |
|
---|
182 | hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
|
---|
183 | for (idx = 0; idx < HIGH_GUARD_SIZE; idx++) {
|
---|
184 | byte = *(hiPtr + idx);
|
---|
185 | if (byte != GUARD_VALUE) {
|
---|
186 | guard_failed = TRUE;
|
---|
187 | fflush (stdout);
|
---|
188 | byte &= 0xff;
|
---|
189 | fprintf(stderr, "hi guard byte %d is 0x%x \t%c\n", idx, byte,
|
---|
190 | (isprint(UCHAR(byte)) ? byte : ' '));
|
---|
191 | }
|
---|
192 | }
|
---|
193 |
|
---|
194 | if (guard_failed) {
|
---|
195 | TclDumpMemoryInfo (stderr);
|
---|
196 | fprintf(stderr, "high guard failed at %lx, %s %d\n",
|
---|
197 | (long unsigned int) memHeaderP->body, file, line);
|
---|
198 | fflush(stderr); /* In case name pointer is bad. */
|
---|
199 | fprintf(stderr, "%ld bytes allocated at (%s %d)\n",
|
---|
200 | memHeaderP->length, memHeaderP->file,
|
---|
201 | memHeaderP->line);
|
---|
202 | panic("Memory validation failure");
|
---|
203 | }
|
---|
204 |
|
---|
205 | if (nukeGuards) {
|
---|
206 | memset ((char *) memHeaderP->low_guard, 0, LOW_GUARD_SIZE);
|
---|
207 | memset ((char *) hiPtr, 0, HIGH_GUARD_SIZE);
|
---|
208 | }
|
---|
209 |
|
---|
210 | }
|
---|
211 | |
---|
212 |
|
---|
213 | /*
|
---|
214 | *----------------------------------------------------------------------
|
---|
215 | *
|
---|
216 | * Tcl_ValidateAllMemory --
|
---|
217 | * Validates guard regions for all allocated memory.
|
---|
218 | *
|
---|
219 | *----------------------------------------------------------------------
|
---|
220 | */
|
---|
221 | void
|
---|
222 | Tcl_ValidateAllMemory (file, line)
|
---|
223 | char *file;
|
---|
224 | int line;
|
---|
225 | {
|
---|
226 | struct mem_header *memScanP;
|
---|
227 |
|
---|
228 | for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
|
---|
229 | ValidateMemory(memScanP, file, line, FALSE);
|
---|
230 |
|
---|
231 | }
|
---|
232 | |
---|
233 |
|
---|
234 | /*
|
---|
235 | *----------------------------------------------------------------------
|
---|
236 | *
|
---|
237 | * Tcl_DumpActiveMemory --
|
---|
238 | * Displays all allocated memory to stderr.
|
---|
239 | *
|
---|
240 | * Results:
|
---|
241 | * Return TCL_ERROR if an error accessing the file occures, `errno'
|
---|
242 | * will have the file error number left in it.
|
---|
243 | *----------------------------------------------------------------------
|
---|
244 | */
|
---|
245 | int
|
---|
246 | Tcl_DumpActiveMemory (fileName)
|
---|
247 | char *fileName;
|
---|
248 | {
|
---|
249 | FILE *fileP;
|
---|
250 | struct mem_header *memScanP;
|
---|
251 | char *address;
|
---|
252 |
|
---|
253 | fileP = fopen(fileName, "w");
|
---|
254 | if (fileP == NULL)
|
---|
255 | return TCL_ERROR;
|
---|
256 |
|
---|
257 | for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
|
---|
258 | address = &memScanP->body [0];
|
---|
259 | fprintf(fileP, "%8lx - %8lx %7ld @ %s %d %s",
|
---|
260 | (long unsigned int) address,
|
---|
261 | (long unsigned int) address + memScanP->length - 1,
|
---|
262 | memScanP->length, memScanP->file, memScanP->line,
|
---|
263 | (memScanP->tagPtr == NULL) ? "" : memScanP->tagPtr->string);
|
---|
264 | (void) fputc('\n', fileP);
|
---|
265 | }
|
---|
266 | fclose (fileP);
|
---|
267 | return TCL_OK;
|
---|
268 | }
|
---|
269 | |
---|
270 |
|
---|
271 | /*
|
---|
272 | *----------------------------------------------------------------------
|
---|
273 | *
|
---|
274 | * Tcl_DbCkalloc - debugging ckalloc
|
---|
275 | *
|
---|
276 | * Allocate the requested amount of space plus some extra for
|
---|
277 | * guard bands at both ends of the request, plus a size, panicing
|
---|
278 | * if there isn't enough space, then write in the guard bands
|
---|
279 | * and return the address of the space in the middle that the
|
---|
280 | * user asked for.
|
---|
281 | *
|
---|
282 | * The second and third arguments are file and line, these contain
|
---|
283 | * the filename and line number corresponding to the caller.
|
---|
284 | * These are sent by the ckalloc macro; it uses the preprocessor
|
---|
285 | * autodefines __FILE__ and __LINE__.
|
---|
286 | *
|
---|
287 | *----------------------------------------------------------------------
|
---|
288 | */
|
---|
289 | char *
|
---|
290 | Tcl_DbCkalloc(size, file, line)
|
---|
291 | unsigned int size;
|
---|
292 | char *file;
|
---|
293 | int line;
|
---|
294 | {
|
---|
295 | struct mem_header *result;
|
---|
296 |
|
---|
297 | if (validate_memory)
|
---|
298 | Tcl_ValidateAllMemory (file, line);
|
---|
299 |
|
---|
300 | result = (struct mem_header *) TclpAlloc((unsigned)size +
|
---|
301 | sizeof(struct mem_header) + HIGH_GUARD_SIZE);
|
---|
302 | if (result == NULL) {
|
---|
303 | fflush(stdout);
|
---|
304 | TclDumpMemoryInfo(stderr);
|
---|
305 | panic("unable to alloc %d bytes, %s line %d", size, file,
|
---|
306 | line);
|
---|
307 | }
|
---|
308 |
|
---|
309 | /*
|
---|
310 | * Fill in guard zones and size. Also initialize the contents of
|
---|
311 | * the block with bogus bytes to detect uses of initialized data.
|
---|
312 | * Link into allocated list.
|
---|
313 | */
|
---|
314 | if (init_malloced_bodies) {
|
---|
315 | memset ((VOID *) result, GUARD_VALUE,
|
---|
316 | size + sizeof(struct mem_header) + HIGH_GUARD_SIZE);
|
---|
317 | } else {
|
---|
318 | memset ((char *) result->low_guard, GUARD_VALUE, LOW_GUARD_SIZE);
|
---|
319 | memset (result->body + size, GUARD_VALUE, HIGH_GUARD_SIZE);
|
---|
320 | }
|
---|
321 | result->length = size;
|
---|
322 | result->tagPtr = curTagPtr;
|
---|
323 | if (curTagPtr != NULL) {
|
---|
324 | curTagPtr->refCount++;
|
---|
325 | }
|
---|
326 | result->file = file;
|
---|
327 | result->line = line;
|
---|
328 | result->flink = allocHead;
|
---|
329 | result->blink = NULL;
|
---|
330 | if (allocHead != NULL)
|
---|
331 | allocHead->blink = result;
|
---|
332 | allocHead = result;
|
---|
333 |
|
---|
334 | total_mallocs++;
|
---|
335 | if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
|
---|
336 | (void) fflush(stdout);
|
---|
337 | fprintf(stderr, "reached malloc trace enable point (%d)\n",
|
---|
338 | total_mallocs);
|
---|
339 | fflush(stderr);
|
---|
340 | alloc_tracing = TRUE;
|
---|
341 | trace_on_at_malloc = 0;
|
---|
342 | }
|
---|
343 |
|
---|
344 | if (alloc_tracing)
|
---|
345 | fprintf(stderr,"ckalloc %lx %d %s %d\n",
|
---|
346 | (long unsigned int) result->body, size, file, line);
|
---|
347 |
|
---|
348 | if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
|
---|
349 | break_on_malloc = 0;
|
---|
350 | (void) fflush(stdout);
|
---|
351 | fprintf(stderr,"reached malloc break limit (%d)\n",
|
---|
352 | total_mallocs);
|
---|
353 | fprintf(stderr, "program will now enter C debugger\n");
|
---|
354 | (void) fflush(stderr);
|
---|
355 | abort();
|
---|
356 | }
|
---|
357 |
|
---|
358 | current_malloc_packets++;
|
---|
359 | if (current_malloc_packets > maximum_malloc_packets)
|
---|
360 | maximum_malloc_packets = current_malloc_packets;
|
---|
361 | current_bytes_malloced += size;
|
---|
362 | if (current_bytes_malloced > maximum_bytes_malloced)
|
---|
363 | maximum_bytes_malloced = current_bytes_malloced;
|
---|
364 |
|
---|
365 | return result->body;
|
---|
366 | }
|
---|
367 | |
---|
368 |
|
---|
369 | /*
|
---|
370 | *----------------------------------------------------------------------
|
---|
371 | *
|
---|
372 | * Tcl_DbCkfree - debugging ckfree
|
---|
373 | *
|
---|
374 | * Verify that the low and high guards are intact, and if so
|
---|
375 | * then free the buffer else panic.
|
---|
376 | *
|
---|
377 | * The guards are erased after being checked to catch duplicate
|
---|
378 | * frees.
|
---|
379 | *
|
---|
380 | * The second and third arguments are file and line, these contain
|
---|
381 | * the filename and line number corresponding to the caller.
|
---|
382 | * These are sent by the ckfree macro; it uses the preprocessor
|
---|
383 | * autodefines __FILE__ and __LINE__.
|
---|
384 | *
|
---|
385 | *----------------------------------------------------------------------
|
---|
386 | */
|
---|
387 |
|
---|
388 | int
|
---|
389 | Tcl_DbCkfree(ptr, file, line)
|
---|
390 | char * ptr;
|
---|
391 | char *file;
|
---|
392 | int line;
|
---|
393 | {
|
---|
394 | /*
|
---|
395 | * The following cast is *very* tricky. Must convert the pointer
|
---|
396 | * to an integer before doing arithmetic on it, because otherwise
|
---|
397 | * the arithmetic will be done differently (and incorrectly) on
|
---|
398 | * word-addressed machines such as Crays (will subtract only bytes,
|
---|
399 | * even though BODY_OFFSET is in words on these machines).
|
---|
400 | */
|
---|
401 |
|
---|
402 | struct mem_header *memp = (struct mem_header *)
|
---|
403 | (((unsigned long) ptr) - BODY_OFFSET);
|
---|
404 |
|
---|
405 | if (alloc_tracing)
|
---|
406 | fprintf(stderr, "ckfree %lx %ld %s %d\n",
|
---|
407 | (long unsigned int) memp->body, memp->length, file, line);
|
---|
408 |
|
---|
409 | if (validate_memory)
|
---|
410 | Tcl_ValidateAllMemory(file, line);
|
---|
411 |
|
---|
412 | ValidateMemory(memp, file, line, TRUE);
|
---|
413 | if (init_malloced_bodies) {
|
---|
414 | memset((VOID *) ptr, GUARD_VALUE, (size_t) memp->length);
|
---|
415 | }
|
---|
416 |
|
---|
417 | total_frees++;
|
---|
418 | current_malloc_packets--;
|
---|
419 | current_bytes_malloced -= memp->length;
|
---|
420 |
|
---|
421 | if (memp->tagPtr != NULL) {
|
---|
422 | memp->tagPtr->refCount--;
|
---|
423 | if ((memp->tagPtr->refCount == 0) && (curTagPtr != memp->tagPtr)) {
|
---|
424 | TclpFree((char *) memp->tagPtr);
|
---|
425 | }
|
---|
426 | }
|
---|
427 |
|
---|
428 | /*
|
---|
429 | * Delink from allocated list
|
---|
430 | */
|
---|
431 | if (memp->flink != NULL)
|
---|
432 | memp->flink->blink = memp->blink;
|
---|
433 | if (memp->blink != NULL)
|
---|
434 | memp->blink->flink = memp->flink;
|
---|
435 | if (allocHead == memp)
|
---|
436 | allocHead = memp->flink;
|
---|
437 | TclpFree((char *) memp);
|
---|
438 | return 0;
|
---|
439 | }
|
---|
440 | |
---|
441 |
|
---|
442 | /*
|
---|
443 | *--------------------------------------------------------------------
|
---|
444 | *
|
---|
445 | * Tcl_DbCkrealloc - debugging ckrealloc
|
---|
446 | *
|
---|
447 | * Reallocate a chunk of memory by allocating a new one of the
|
---|
448 | * right size, copying the old data to the new location, and then
|
---|
449 | * freeing the old memory space, using all the memory checking
|
---|
450 | * features of this package.
|
---|
451 | *
|
---|
452 | *--------------------------------------------------------------------
|
---|
453 | */
|
---|
454 | char *
|
---|
455 | Tcl_DbCkrealloc(ptr, size, file, line)
|
---|
456 | char *ptr;
|
---|
457 | unsigned int size;
|
---|
458 | char *file;
|
---|
459 | int line;
|
---|
460 | {
|
---|
461 | char *new;
|
---|
462 | unsigned int copySize;
|
---|
463 |
|
---|
464 | /*
|
---|
465 | * See comment from Tcl_DbCkfree before you change the following
|
---|
466 | * line.
|
---|
467 | */
|
---|
468 |
|
---|
469 | struct mem_header *memp = (struct mem_header *)
|
---|
470 | (((unsigned long) ptr) - BODY_OFFSET);
|
---|
471 |
|
---|
472 | copySize = size;
|
---|
473 | if (copySize > (unsigned int) memp->length) {
|
---|
474 | copySize = memp->length;
|
---|
475 | }
|
---|
476 | new = Tcl_DbCkalloc(size, file, line);
|
---|
477 | memcpy((VOID *) new, (VOID *) ptr, (size_t) copySize);
|
---|
478 | Tcl_DbCkfree(ptr, file, line);
|
---|
479 | return(new);
|
---|
480 | }
|
---|
481 |
|
---|
482 | |
---|
483 |
|
---|
484 | /*
|
---|
485 | *----------------------------------------------------------------------
|
---|
486 | *
|
---|
487 | * Tcl_Alloc, et al. --
|
---|
488 | *
|
---|
489 | * These functions are defined in terms of the debugging versions
|
---|
490 | * when TCL_MEM_DEBUG is set.
|
---|
491 | *
|
---|
492 | * Results:
|
---|
493 | * Same as the debug versions.
|
---|
494 | *
|
---|
495 | * Side effects:
|
---|
496 | * Same as the debug versions.
|
---|
497 | *
|
---|
498 | *----------------------------------------------------------------------
|
---|
499 | */
|
---|
500 |
|
---|
501 | #undef Tcl_Alloc
|
---|
502 | #undef Tcl_Free
|
---|
503 | #undef Tcl_Realloc
|
---|
504 |
|
---|
505 | char *
|
---|
506 | Tcl_Alloc(size)
|
---|
507 | unsigned int size;
|
---|
508 | {
|
---|
509 | return Tcl_DbCkalloc(size, "unknown", 0);
|
---|
510 | }
|
---|
511 |
|
---|
512 | void
|
---|
513 | Tcl_Free(ptr)
|
---|
514 | char *ptr;
|
---|
515 | {
|
---|
516 | Tcl_DbCkfree(ptr, "unknown", 0);
|
---|
517 | }
|
---|
518 |
|
---|
519 | char *
|
---|
520 | Tcl_Realloc(ptr, size)
|
---|
521 | char *ptr;
|
---|
522 | unsigned int size;
|
---|
523 | {
|
---|
524 | return Tcl_DbCkrealloc(ptr, size, "unknown", 0);
|
---|
525 | }
|
---|
526 | |
---|
527 |
|
---|
528 | /*
|
---|
529 | *----------------------------------------------------------------------
|
---|
530 | *
|
---|
531 | * MemoryCmd --
|
---|
532 | * Implements the TCL memory command:
|
---|
533 | * memory info
|
---|
534 | * memory display
|
---|
535 | * break_on_malloc count
|
---|
536 | * trace_on_at_malloc count
|
---|
537 | * trace on|off
|
---|
538 | * validate on|off
|
---|
539 | *
|
---|
540 | * Results:
|
---|
541 | * Standard TCL results.
|
---|
542 | *
|
---|
543 | *----------------------------------------------------------------------
|
---|
544 | */
|
---|
545 | /* ARGSUSED */
|
---|
546 | static int
|
---|
547 | MemoryCmd (clientData, interp, argc, argv)
|
---|
548 | ClientData clientData;
|
---|
549 | Tcl_Interp *interp;
|
---|
550 | int argc;
|
---|
551 | char **argv;
|
---|
552 | {
|
---|
553 | char *fileName;
|
---|
554 | Tcl_DString buffer;
|
---|
555 | int result;
|
---|
556 |
|
---|
557 | if (argc < 2) {
|
---|
558 | Tcl_AppendResult(interp, "wrong # args: should be \"",
|
---|
559 | argv[0], " option [args..]\"", (char *) NULL);
|
---|
560 | return TCL_ERROR;
|
---|
561 | }
|
---|
562 |
|
---|
563 | if (strcmp(argv[1],"active") == 0) {
|
---|
564 | if (argc != 3) {
|
---|
565 | Tcl_AppendResult(interp, "wrong # args: should be \"",
|
---|
566 | argv[0], " active file\"", (char *) NULL);
|
---|
567 | return TCL_ERROR;
|
---|
568 | }
|
---|
569 | fileName = Tcl_TranslateFileName(interp, argv[2], &buffer);
|
---|
570 | if (fileName == NULL) {
|
---|
571 | return TCL_ERROR;
|
---|
572 | }
|
---|
573 | result = Tcl_DumpActiveMemory (fileName);
|
---|
574 | Tcl_DStringFree(&buffer);
|
---|
575 | if (result != TCL_OK) {
|
---|
576 | Tcl_AppendResult(interp, "error accessing ", argv[2],
|
---|
577 | (char *) NULL);
|
---|
578 | return TCL_ERROR;
|
---|
579 | }
|
---|
580 | return TCL_OK;
|
---|
581 | }
|
---|
582 | if (strcmp(argv[1],"break_on_malloc") == 0) {
|
---|
583 | if (argc != 3) {
|
---|
584 | goto argError;
|
---|
585 | }
|
---|
586 | if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK) {
|
---|
587 | return TCL_ERROR;
|
---|
588 | }
|
---|
589 | return TCL_OK;
|
---|
590 | }
|
---|
591 | if (strcmp(argv[1],"info") == 0) {
|
---|
592 | TclDumpMemoryInfo(stdout);
|
---|
593 | return TCL_OK;
|
---|
594 | }
|
---|
595 | if (strcmp(argv[1],"init") == 0) {
|
---|
596 | if (argc != 3) {
|
---|
597 | goto bad_suboption;
|
---|
598 | }
|
---|
599 | init_malloced_bodies = (strcmp(argv[2],"on") == 0);
|
---|
600 | return TCL_OK;
|
---|
601 | }
|
---|
602 | if (strcmp(argv[1],"tag") == 0) {
|
---|
603 | if (argc != 3) {
|
---|
604 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
---|
605 | " tag string\"", (char *) NULL);
|
---|
606 | return TCL_ERROR;
|
---|
607 | }
|
---|
608 | if ((curTagPtr != NULL) && (curTagPtr->refCount == 0)) {
|
---|
609 | TclpFree((char *) curTagPtr);
|
---|
610 | }
|
---|
611 | curTagPtr = (MemTag *) TclpAlloc(TAG_SIZE(strlen(argv[2])));
|
---|
612 | curTagPtr->refCount = 0;
|
---|
613 | strcpy(curTagPtr->string, argv[2]);
|
---|
614 | return TCL_OK;
|
---|
615 | }
|
---|
616 | if (strcmp(argv[1],"trace") == 0) {
|
---|
617 | if (argc != 3) {
|
---|
618 | goto bad_suboption;
|
---|
619 | }
|
---|
620 | alloc_tracing = (strcmp(argv[2],"on") == 0);
|
---|
621 | return TCL_OK;
|
---|
622 | }
|
---|
623 |
|
---|
624 | if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
|
---|
625 | if (argc != 3) {
|
---|
626 | goto argError;
|
---|
627 | }
|
---|
628 | if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK) {
|
---|
629 | return TCL_ERROR;
|
---|
630 | }
|
---|
631 | return TCL_OK;
|
---|
632 | }
|
---|
633 | if (strcmp(argv[1],"validate") == 0) {
|
---|
634 | if (argc != 3) {
|
---|
635 | goto bad_suboption;
|
---|
636 | }
|
---|
637 | validate_memory = (strcmp(argv[2],"on") == 0);
|
---|
638 | return TCL_OK;
|
---|
639 | }
|
---|
640 |
|
---|
641 | Tcl_AppendResult(interp, "bad option \"", argv[1],
|
---|
642 | "\": should be active, break_on_malloc, info, init, ",
|
---|
643 | "tag, trace, trace_on_at_malloc, or validate", (char *) NULL);
|
---|
644 | return TCL_ERROR;
|
---|
645 |
|
---|
646 | argError:
|
---|
647 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
---|
648 | " ", argv[1], " count\"", (char *) NULL);
|
---|
649 | return TCL_ERROR;
|
---|
650 |
|
---|
651 | bad_suboption:
|
---|
652 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
---|
653 | " ", argv[1], " on|off\"", (char *) NULL);
|
---|
654 | return TCL_ERROR;
|
---|
655 | }
|
---|
656 | |
---|
657 |
|
---|
658 | /*
|
---|
659 | *----------------------------------------------------------------------
|
---|
660 | *
|
---|
661 | * Tcl_InitMemory --
|
---|
662 | * Initialize the memory command.
|
---|
663 | *
|
---|
664 | *----------------------------------------------------------------------
|
---|
665 | */
|
---|
666 | void
|
---|
667 | Tcl_InitMemory(interp)
|
---|
668 | Tcl_Interp *interp;
|
---|
669 | {
|
---|
670 | Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData) NULL,
|
---|
671 | (Tcl_CmdDeleteProc *) NULL);
|
---|
672 | }
|
---|
673 |
|
---|
674 | #else
|
---|
675 |
|
---|
676 | |
---|
677 |
|
---|
678 | /*
|
---|
679 | *----------------------------------------------------------------------
|
---|
680 | *
|
---|
681 | * Tcl_Alloc --
|
---|
682 | * Interface to TclpAlloc when TCL_MEM_DEBUG is disabled. It does check
|
---|
683 | * that memory was actually allocated.
|
---|
684 | *
|
---|
685 | *----------------------------------------------------------------------
|
---|
686 | */
|
---|
687 |
|
---|
688 | char *
|
---|
689 | Tcl_Alloc (size)
|
---|
690 | unsigned int size;
|
---|
691 | {
|
---|
692 | char *result;
|
---|
693 |
|
---|
694 | result = TclpAlloc(size);
|
---|
695 | if (result == NULL)
|
---|
696 | panic("unable to alloc %d bytes", size);
|
---|
697 | return result;
|
---|
698 | }
|
---|
699 |
|
---|
700 | char *
|
---|
701 | Tcl_DbCkalloc(size, file, line)
|
---|
702 | unsigned int size;
|
---|
703 | char *file;
|
---|
704 | int line;
|
---|
705 | {
|
---|
706 | char *result;
|
---|
707 |
|
---|
708 | result = (char *) TclpAlloc(size);
|
---|
709 |
|
---|
710 | if (result == NULL) {
|
---|
711 | fflush(stdout);
|
---|
712 | panic("unable to alloc %d bytes, %s line %d", size, file,
|
---|
713 | line);
|
---|
714 | }
|
---|
715 | return result;
|
---|
716 | }
|
---|
717 |
|
---|
718 | |
---|
719 |
|
---|
720 | /*
|
---|
721 | *----------------------------------------------------------------------
|
---|
722 | *
|
---|
723 | * Tcl_Realloc --
|
---|
724 | * Interface to TclpRealloc when TCL_MEM_DEBUG is disabled. It does
|
---|
725 | * check that memory was actually allocated.
|
---|
726 | *
|
---|
727 | *----------------------------------------------------------------------
|
---|
728 | */
|
---|
729 |
|
---|
730 | char *
|
---|
731 | Tcl_Realloc(ptr, size)
|
---|
732 | char *ptr;
|
---|
733 | unsigned int size;
|
---|
734 | {
|
---|
735 | char *result;
|
---|
736 |
|
---|
737 | result = TclpRealloc(ptr, size);
|
---|
738 | if (result == NULL)
|
---|
739 | panic("unable to realloc %d bytes", size);
|
---|
740 | return result;
|
---|
741 | }
|
---|
742 |
|
---|
743 | char *
|
---|
744 | Tcl_DbCkrealloc(ptr, size, file, line)
|
---|
745 | char *ptr;
|
---|
746 | unsigned int size;
|
---|
747 | char *file;
|
---|
748 | int line;
|
---|
749 | {
|
---|
750 | char *result;
|
---|
751 |
|
---|
752 | result = (char *) TclpRealloc(ptr, size);
|
---|
753 |
|
---|
754 | if (result == NULL) {
|
---|
755 | fflush(stdout);
|
---|
756 | panic("unable to realloc %d bytes, %s line %d", size, file,
|
---|
757 | line);
|
---|
758 | }
|
---|
759 | return result;
|
---|
760 | }
|
---|
761 | |
---|
762 |
|
---|
763 | /*
|
---|
764 | *----------------------------------------------------------------------
|
---|
765 | *
|
---|
766 | * Tcl_Free --
|
---|
767 | * Interface to TclpFree when TCL_MEM_DEBUG is disabled. Done here
|
---|
768 | * rather in the macro to keep some modules from being compiled with
|
---|
769 | * TCL_MEM_DEBUG enabled and some with it disabled.
|
---|
770 | *
|
---|
771 | *----------------------------------------------------------------------
|
---|
772 | */
|
---|
773 |
|
---|
774 | void
|
---|
775 | Tcl_Free (ptr)
|
---|
776 | char *ptr;
|
---|
777 | {
|
---|
778 | TclpFree(ptr);
|
---|
779 | }
|
---|
780 |
|
---|
781 | int
|
---|
782 | Tcl_DbCkfree(ptr, file, line)
|
---|
783 | char * ptr;
|
---|
784 | char *file;
|
---|
785 | int line;
|
---|
786 | {
|
---|
787 | TclpFree(ptr);
|
---|
788 | return 0;
|
---|
789 | }
|
---|
790 | |
---|
791 |
|
---|
792 | /*
|
---|
793 | *----------------------------------------------------------------------
|
---|
794 | *
|
---|
795 | * Tcl_InitMemory --
|
---|
796 | * Dummy initialization for memory command, which is only available
|
---|
797 | * if TCL_MEM_DEBUG is on.
|
---|
798 | *
|
---|
799 | *----------------------------------------------------------------------
|
---|
800 | */
|
---|
801 | /* ARGSUSED */
|
---|
802 | void
|
---|
803 | Tcl_InitMemory(interp)
|
---|
804 | Tcl_Interp *interp;
|
---|
805 | {
|
---|
806 | }
|
---|
807 |
|
---|
808 | #undef Tcl_DumpActiveMemory
|
---|
809 | #undef Tcl_ValidateAllMemory
|
---|
810 |
|
---|
811 | extern int Tcl_DumpActiveMemory _ANSI_ARGS_((char *fileName));
|
---|
812 | extern void Tcl_ValidateAllMemory _ANSI_ARGS_((char *file,
|
---|
813 | int line));
|
---|
814 |
|
---|
815 | int
|
---|
816 | Tcl_DumpActiveMemory(fileName)
|
---|
817 | char *fileName;
|
---|
818 | {
|
---|
819 | return TCL_OK;
|
---|
820 | }
|
---|
821 |
|
---|
822 | void
|
---|
823 | Tcl_ValidateAllMemory(file, line)
|
---|
824 | char *file;
|
---|
825 | int line;
|
---|
826 | {
|
---|
827 | }
|
---|
828 |
|
---|
829 | #endif
|
---|