ram-smalloc.c 11 KB
Newer Older
Linus Tolke Y's avatar
Linus Tolke Y 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 Y's avatar
Linus Tolke Y 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 Y's avatar
Linus Tolke Y 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);
}