ram-smalloc.c 11 KB
Newer Older
Linus Tolke's avatar
Linus Tolke committed
1
/*
2
3
 * $Id: ram-smalloc.c,v 0.41 2003/08/23 16:38:14 ceder Exp $
 * Copyright (C) 1991-1996, 1998-1999, 2001-2003  Lysator Academic Computer Association.
Linus Tolke's avatar
Linus Tolke committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * This file is part of the LysKOM server.
 * 
 * LysKOM is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 1, or (at your option) 
 * any later version.
 * 
 * LysKOM is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with LysKOM; see the file COPYING.  If not, write to
 * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
 * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
 * MA 02139, USA.
 *
Per Cederqvist's avatar
Per Cederqvist committed
23
 * Please report bugs at http://bugzilla.lysator.liu.se/. 
Linus Tolke's avatar
Linus Tolke committed
24
 */
Per Cederqvist's avatar
Per Cederqvist committed
25
26
27
28
29
30
31
32
/*
 * smalloc.c
 *
 * Contains memory allocator routines
 *
 *	TEST VERSION by ceder
 */

David Byers's avatar
David Byers committed
33
34
35
36
37

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

38
/*
39
40
41
42
43
44
 * Finding memory leaks
 * ====================
 *
 * This code contains some stubs that can be useful when hunting for
 * memory leaks.  To use it, you must configure with
 * --with-traced-allocations and recompile. You actually only need to
Per Cederqvist's avatar
Per Cederqvist committed
45
46
47
 * recompile ram-smalloc.c, ramkomd.c and the programs in testsuite
 * after changing it, so "make -t; rm src/server/ram-smalloc.o \
 * src/server/ramkomd.o src/server/testsuite/test-*.o; make" is the 
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
 * fastest way to change between tracing and a normal compile.
 *
 * Run the resulting lyskomd under gdb, like this:
 *            bash$ gdb lyskomd
 *            (gdb) source trace-mem.gdb
 *            (gdb) run
 *            Where does the trace want to go today? [stderr] RET
 * gdb will print a backtrace and continue whenever memory is
 * allocated, reallocated or released.  The program itself will also
 * print out some information.  The result is a long log file.
 *
 * Run M-x resolve-trace (from handle-malloc-dump.el on the resulting
 * output.  Look in the *Result* buffer for the result.
 *
 * Using the test suite to find memory leaks
 * =========================================
 *
 * This code can also be used together with the test suite, even
 * though that is slightly more complicated.  Begin by starting a gdb
 * whose output is saved, for example by running it in a shell buffer
 * in emacs (note: do not use M-x gdb -- that will slow down the
 * process and it is slow enough as it is):
 *            bash$ gdb lyskomd
 *            (gdb) source trace-mem.gdb
 *            (gdb) shell tty
 *            /dev/ttypd
74
75
76
 * Pass ATTACH=yes and MEMTRACE to runtest. MEMTRACE should be set
 * to the tty where gdb is running.  Start the test suite:
 *	      bash$ runtest --tool lyskomd leaks.0/99.exp ATTACH=yes MEMTRACE=/dev/ttypd
77
78
79
80
81
82
83
84
 *            [...]
 *            Please attach to lyskomd pid 4711 and hit RETURN
 * Attach to the process:
 *            (gdb) attach 4711
 *            (gdb) continue
 * Press enter to resume the test suite.  Lots of output should appear
 * from gdb.
 *
85
86
87
88
89
90
91
92
93
94
95
 * You can do all this automatically for test cases that succeed in
 * starting the server and only start it once.
 *
 *     emacs -batch -l handle-malloc-dump.el --tool leaks --test 99.exp
 *
 * This will do the above in an Emacs, then run the trace analysis
 * and print the results on standard output. Use --usage to get a
 * summary of available options (--help was already taken by Emacs.)
 *
 * This does not work with all Emacs versions.
 *
96
97
 */

98
99
#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
Per Cederqvist's avatar
Per Cederqvist committed
100
#endif
Per Cederqvist's avatar
Per Cederqvist committed
101
#include <stdio.h>
Per Cederqvist's avatar
Per Cederqvist committed
102
#include <sys/types.h>
103
104
#include <assert.h>
#include <errno.h>
105
106
107
108
109
110
111
112
/* The order between inttypes.h and stdint.h is mandated by autoconf-2.57. */
#if HAVE_INTTYPES_H
#  include <inttypes.h>
#else
#  if HAVE_STDINT_H
#    include <stdint.h>
#  endif
#endif
Per Cederqvist's avatar
Per Cederqvist committed
113

Per Cederqvist's avatar
Per Cederqvist committed
114
#include "exp.h"
115
#include "s-string.h"
Per Cederqvist's avatar
Per Cederqvist committed
116
117
118
#include "kom-types.h"
#include "lyskomd.h"
#include "server/smalloc.h"
119
120
121
#ifdef TRACED_ALLOCATIONS
#  include "trace-alloc.h"
#endif
122
#include "eintr.h"
Per Cederqvist's avatar
Per Cederqvist committed
123

124
static int no_of_allocated_blocks = 0;
Per Cederqvist's avatar
Per Cederqvist committed
125

126
/* When using our own malloc guard areas, the memory layout looks like
127
   this, where U is sizeof(union overhead), and S is the size of the
128
129
130
131
   requested block.

   offset   what
   ======   ====
132
133
         0: magic cookie (stored in an union overhead) (see below)
         U: the size S (an union overhead)
134
135
136
137
138
139
140
141
142
143
       2*U: S bytes of data returned to the user.
     S+2*U: guard byte: 0x89
   1+S+2*U: guard byte: 0xA7
   2+S+2*U: one past the end of the block allocated with malloc().

   The total overhead is thus 2*U + 2.  The macro OVERHEAD adds the
   specified overead to S and returns the size we request from the
   system malloc.

   The magic cookie reflects the status of the block and is used for
144
   defensive checks.  The constants are defined below:
145
146
147
148
149
150
151

     SMALLOC_MAGIC_ALLOC: an allocated block.
     SMALLOC_MAGIC_FREE: a block that was previously allocated.

*/

#ifdef USE_MALLOC_GUARDS
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

/* A union of "all types", used to get the maximum alignment needed
   for any type. */
union overhead {

    /* We prefer to store stuf in a size_t, since we are storing a
       size.  Use an unsigned int if size_t isn't available.  */
#  ifdef HAVE_SIZE_T
    size_t val;
    unsigned int a;
#  else
    unsigned int val;
#  endif

    void *b;
    long c;
    long *d;
    long long e;
    long long *f;
    float g;
    double h;
    void (*i)(void);
    long double j;

#  ifdef HAVE_INTPTR_T
    intptr_t k;
#  endif

#  ifdef HAVE_INTMAX_T
    intmax_t l;
#  endif

};

#  define OVERHEAD(s) ((s) + (2*sizeof(union overhead) + 2))
187
188
#  define SMALLOC_MAGIC_ALLOC 0x12FE56A0u
#  define SMALLOC_MAGIC_FREE  0xCA348E63u
189
190
191
192
#else

/* When malloc() or realloc() is asked to allocate a 0-sized area,
   they may return NULL to indicate success.  We are not prepared for
Per Cederqvist's avatar
Per Cederqvist committed
193
   that behaviour, so we always allocate at least 1 byte. */
194
195
196
197
#  define OVERHEAD(s) (((s) == 0) ? 1 : s)

#endif

198
199
200
201
202
203
204
205
206
207
#ifdef TRACED_ALLOCATIONS

static FILE *malloc_fp = NULL;

void
trace_alloc_file(const char *loc)
{
    if (loc[0] == '\0')
	malloc_fp = stderr;
    else
208
	malloc_fp = i_fopen(loc, "w");
209
210
211
212
213
214

    if (malloc_fp == NULL)
	restart_kom("init_malloc_fp failed: %s, %d\n", loc, errno);
    fprintf(malloc_fp, "A new trace from lyskomd starts here\n");
}

215
216
217
218
static void
trace_smalloc(size_t size,
	      void *result)
{
219
220
221
222
223
    assert(malloc_fp != NULL);
    fprintf(malloc_fp, "smalloc:\nArg: 0x%lx\nRes: 0x%lx\n",
	    (long)size, (long)result);
    fprintf(malloc_fp, "==== end ====\n");
    fflush(malloc_fp);
224
}
225

226
227
#endif

Per Cederqvist's avatar
Per Cederqvist committed
228
229
230
231
/*
 * "safe" malloc. Handles the case when malloc returns NULL.
 * smalloc cannot fail.
 */
Per Cederqvist's avatar
Per Cederqvist committed
232
EXPORT  void *
Per Cederqvist's avatar
Per Cederqvist committed
233
234
smalloc(size_t size)
{
235
    union overhead *p;
236

237
    p = malloc(OVERHEAD(size));
Per Cederqvist's avatar
Per Cederqvist committed
238

239
240
    if (p == NULL)
	restart_kom("Can't allocate %lu bytes.\n", (unsigned long)size);
Per Cederqvist's avatar
Per Cederqvist committed
241

242
243
244
    ++no_of_allocated_blocks;

#ifdef USE_MALLOC_GUARDS
245
246
247
248
    p->val = SMALLOC_MAGIC_ALLOC;
    p++;
    p->val = size;
    p++;
249
250
251
    ((unsigned char *) p)[size]   = 0x89;
    ((unsigned char *) p)[size+1] = 0xA7;
#endif
252

253
#ifdef TRACED_ALLOCATIONS
254
    trace_smalloc(size, p);
255
256
#endif

257
    return p;
Per Cederqvist's avatar
Per Cederqvist committed
258
259
}

260
#ifdef TRACED_ALLOCATIONS
261
262
263
static void
trace_free(void *block)
{
264
265
266
267
    assert(malloc_fp != NULL);
    fprintf(malloc_fp, "sfree:\nArg: 0x%lx\n", (long)block);
    fprintf(malloc_fp, "==== end ====\n");
    fflush(malloc_fp);
268
269
}
#endif
Per Cederqvist's avatar
Per Cederqvist committed
270

Per Cederqvist's avatar
Per Cederqvist committed
271
EXPORT  void
Per Cederqvist's avatar
Per Cederqvist committed
272
273
sfree(void * ptr)	/* it is legal to sfree a NULL pointer */
{
274
#ifdef USE_MALLOC_GUARDS
275
    union overhead *ip;
276
#endif
Per Cederqvist's avatar
Per Cederqvist committed
277
278
279
    
    if ( ptr != NULL )
    {
280
#ifdef TRACED_ALLOCATIONS
281
282
	trace_free(ptr);
#endif
283
284
285
286
287

#ifndef USE_MALLOC_GUARDS
	free(ptr);
	--no_of_allocated_blocks;
#else
288
        ip = (union overhead *)ptr;
Per Cederqvist's avatar
Per Cederqvist committed
289
	ip -= 2;
290
	switch (ip->val)
Per Cederqvist's avatar
Per Cederqvist committed
291
292
	{
	    case SMALLOC_MAGIC_ALLOC:
293
294
295
296
		if (((unsigned char *) (ip+2))[ip[1].val] != 0x89 ||
		    ((unsigned char *) (ip+2))[ip[1].val+1] != 0xA7)
		    restart_kom("SFREE: Buffer overflow, bsize = %ul\n",
			      ip[1].val);
Per Cederqvist's avatar
Per Cederqvist committed
297
	        --no_of_allocated_blocks;
298
299
		ip->val = SMALLOC_MAGIC_FREE;
		free(ip);
Per Cederqvist's avatar
Per Cederqvist committed
300
301
302
303
304
305
306
307
		break;

	    case SMALLOC_MAGIC_FREE:
		restart_kom("SFREE: Trying to free already freed block\n");

	    default:
		restart_kom("SFREE: Illegal magic number\n");
	}
308
#endif
Per Cederqvist's avatar
Per Cederqvist committed
309
310
311
    }
}

312
#ifdef TRACED_ALLOCATIONS
313
314
315
316
317
static void
trace_srealloc(size_t size,
	       void *arg,		   
	       void *result)
{
318
319
320
321
322
    assert(malloc_fp != NULL);
    fprintf(malloc_fp, "srealloc:\nSize: 0x%lx\nArg: 0x%lx\nRes: 0x%lx\n",
	    (long)size, (long)arg, (long)result);
    fprintf(malloc_fp, "==== end ====\n");
    fflush(malloc_fp);
323
324
}
#endif
Per Cederqvist's avatar
Per Cederqvist committed
325

Per Cederqvist's avatar
Per Cederqvist committed
326
EXPORT  void *
Per Cederqvist's avatar
Per Cederqvist committed
327
328
srealloc(void * ptr, size_t size) /* Never fails. It is legal to */
{				    /* realloc the NULL ptr. */
329
330
    union overhead * ip;
    union overhead * new_ptr;
Per Cederqvist's avatar
Per Cederqvist committed
331
332
333
334

    if ( ptr == NULL )
	return smalloc(size);

335
    ip = (union overhead *)ptr;
336
#ifdef USE_MALLOC_GUARDS
Per Cederqvist's avatar
Per Cederqvist committed
337
    ip -= 2;
338
    switch (ip->val)
Per Cederqvist's avatar
Per Cederqvist committed
339
340
341
342
343
344
345
346
347
348
349
    {
        case SMALLOC_MAGIC_ALLOC:
            break;

	case SMALLOC_MAGIC_FREE:
	    restart_kom("SREALLOC: Trying to realloc freed block\n");

	default:
	    restart_kom("SREALLOC: Illegal magic number\n");
    }

350
351
    if (((unsigned char *) (ip+2))[ip[1].val] != 0x89 ||
	((unsigned char *) (ip+2))[ip[1].val+1] != 0xA7)
352
      restart_kom("SREALLOC: Buffer overflow, osize = %ul, nsize = %lu.\n",
353
		  ip[1].val, (unsigned long)size);
Per Cederqvist's avatar
Per Cederqvist committed
354

355
    ip->val = SMALLOC_MAGIC_FREE;
356
#endif
357
    if ((new_ptr = realloc(ip, OVERHEAD(size))) == NULL)
Per Cederqvist's avatar
Per Cederqvist committed
358
    {
359
360
	restart_kom("Out of memory - can't realloc. ptr = %lu size = %lu.\n",
		    (unsigned long)ptr, (unsigned long)size);
Per Cederqvist's avatar
Per Cederqvist committed
361
    }
362
#ifdef USE_MALLOC_GUARDS
363
364
365
366
    new_ptr->val = SMALLOC_MAGIC_ALLOC;
    new_ptr++;
    new_ptr->val = size;
    new_ptr++;
Per Cederqvist's avatar
Per Cederqvist committed
367
368
369

    ((unsigned char *) new_ptr)[size]   = 0x89;
    ((unsigned char *) new_ptr)[size+1] = 0xA7;
370
#endif
371

372
#ifdef TRACED_ALLOCATIONS
373
374
375
    trace_srealloc(size, ptr, new_ptr);
#endif

Per Cederqvist's avatar
Per Cederqvist committed
376
377
378
379
380
381
382
383
384
385
386
387
388
    return (void *) new_ptr;
}


/*
 * Allocate temporary memory, which is automatically freed after this
 * atomic call.
 */

static void **tmp_alloc_table   = NULL;
static int tmp_alloc_table_size = 0; /* Size */
static int tmp_alloc_table_use  = 0; /* Used size */

Per Cederqvist's avatar
Per Cederqvist committed
389
EXPORT  void *
Per Cederqvist's avatar
Per Cederqvist committed
390
tmp_alloc(unsigned long size)
Per Cederqvist's avatar
Per Cederqvist committed
391
392
393
394
395
396
397
398
{
    if ( tmp_alloc_table_size <= tmp_alloc_table_use )
    {
	/* Need to increas table. */
	tmp_alloc_table = srealloc (tmp_alloc_table,
				    ((++tmp_alloc_table_size)
				     * sizeof (void *)));
    }
399

Per Cederqvist's avatar
Per Cederqvist committed
400
401
402
403
404
405
406
407
408
    return (tmp_alloc_table[ tmp_alloc_table_use++ ]
	    = smalloc (size));
}


/*
 * Free all core which is allocated with tmp_alloc(). This is called from
 * end_of_atomic().
 */
Per Cederqvist's avatar
Per Cederqvist committed
409
EXPORT void
Per Cederqvist's avatar
Per Cederqvist committed
410
411
412
413
414
415
416
417
418
419
420
421
422
free_tmp(void)
{
    int i;

    for ( i = 0; i < tmp_alloc_table_use; i++ )
    {
	sfree ( tmp_alloc_table[ i ] );
	tmp_alloc_table[ i ] = NULL;
    }

    tmp_alloc_table_use = 0;
}

Per Cederqvist's avatar
Per Cederqvist committed
423
EXPORT  void
Per Cederqvist's avatar
Per Cederqvist committed
424
425
426
427
428
429
430
free_all_tmp(void)
{
    free_tmp();
    sfree( tmp_alloc_table );
    tmp_alloc_table = NULL;
    tmp_alloc_table_size = 0;
}
Per Cederqvist's avatar
Per Cederqvist committed
431
432
433
434

EXPORT void
dump_smalloc_counts(FILE *stat_file)
{
Per Cederqvist's avatar
Per Cederqvist committed
435
436
    fprintf(stat_file, "---ram-smalloc.c:\n%s%d\n",
	    "\tAllocated blocks (grand total): ",
Per Cederqvist's avatar
Per Cederqvist committed
437
438
	    no_of_allocated_blocks);
}