aboutsummaryrefslogtreecommitdiff
path: root/src/share/demo/jvmti/hprof/debug_malloc.c
blob: ea7fecece6b1d692facb9f7a9beb69f982b6e21e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
/*
 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* **************************************************************************
 *
 * Set of malloc/realloc/calloc/strdup/free replacement macros that
 *    insert some extra words around each allocation for debugging purposes
 *    and also attempt to detect invalid uses of the malloc heap through
 *    various tricks like inserting clobber words at the head and tail of
 *    the user's area, delayed free() calls, and setting the memory to
 *    a fixed pattern on allocation and when freed.  The allocations also
 *    can include warrants so that when an area is clobbered, this
 *    package can report where the allocation took place.
 *    The macros included are:
 *              malloc(size)
 *              realloc(ptr,size)
 *              calloc(nelem,elsize)
 *              strdup(s1)
 *              free(ptr)
 *              malloc_police()   <--- Not a system function
 *    The above macros match the standard behavior of the system functions.
 *
 *    They should be used through the include file "debug_malloc.h".
 *
 *       IMPORTANT: All source files that call any of these macros
 *                  should include debug_malloc.h. This package will
 *                  not work if the memory isn't allocated and freed
 *                  by the macros in debug_malloc.h. The important issue
 *                  is that any malloc() from debug_malloc.h must be
 *                  freed by the free() in debug_malloc.h.
 *
 *    The macros in debug_malloc.h will override the normal use of
 *       malloc, realloc, calloc, strdup, and free with the functions below.
 *
 *    These functions include:
 *         void *debug_malloc(size_t, void*, int);
 *         void *debug_realloc(void*, size_t, void*, int);
 *         void *debug_calloc(size_t, size_t, void*, int);
 *         void  debug_free(void *, void*, int);
 *
 *   In addition the function debug_malloc_police() can be called to
 *      tell you what memory has not been freed.
 *         void debug_malloc_police(void*, int);
 *      The function debug_malloc_police() is available through the macro
 *      malloc_police(). Normally you would want to call this at exit()
 *      time to find out what memory is still allocated.
 *
 *   The variable malloc_watch determines if the warrants are generated.
 *      warrants are structures that include the filename and line number
 *      of the caller who allocated the memory. This structure is stored
 *      at the tail of the malloc space, which is allocated large enough
 *      to hold some clobber words at the head and tail, the user's request
 *      and the warrant record (if malloc_watch is non-zero).
 *
 *   The macro LEFT_OVER_CHAR is what the trailing bytes of an allocation
 *     are set to (when the allocation is not a multiple of 8) on allocation.
 *     At free(0 time, these bytes are double checked to make sure they were
 *     not clobbered. To remove this feature #undef LEFT_OVER_CHAR.
 *
 *   The memory freed will have the FREED_CHAR put into it. To remove this
 *     feature #undef FREED_CHAR.
 *
 *   The memory allocated (not calloc'd) will have the ALLOC_CHAR put into it
 *     at the time of allocation. To remove this feature #undef ALLOC_CHAR.
 *
 *   The macro MAX_FREE_DELAY_COUNT controls how many free blocks will
 *     be kept around before being freed. This creates a delayed affect
 *     so that free space that gets clobbered just might get detected.
 *     The free() call will immediately set the user space to the FREED_CHAR,
 *     leaving the clobber words and warrant in place (making sure they
 *     haven't been clobbered). Then the free() pointer is added to a
 *     queue of MAX_FREE_DELAY_COUNT long, and if the queue was full, the
 *     oldest free()'d memory is actually freed, getting it's entire
 *     memory length set to the FREED_CHAR.
 *
 *  WARNING: This can significantly slow down an application, depending
 *           on how many allocations are made. Also the additional memory
 *           needed for the clobber words and the warrants can be significant
 *           again, depending on how many allocations are made.
 *           In addition, the delayed free calls can create situations
 *           where you might run out of memory prematurely.
 *
 * **************************************************************************
 */

#ifdef DEBUG

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include "hprof.h"

/* ***************************************************************************
 * Space normally looks like (clobber Word is 64 bits and aligned to 8 bytes):
 *
 *                  -----------------
 * malloc/free get->| clobber Word  |   ---> contains -size requested by user
 *                  -----------------
 *    User gets --->| user space    |
 *                  |               |
 *                  |  | left_over  |  ---> left_over bytes will be <= 7
 *                  -----------------
 *                  | clobber Word  |   ---> contains -size requested by user
 *                  -----------------
 *                  |   Warrant     |   ---> Optional (malloc_watch!=0)
 *                  |               |        Contains filename and line number
 *                  |               |          where allocation happened
 *                  |               |
 *                  -----------------
 ***************************************************************************/

/*
 *  Flag that tells debug_malloc/debug_free/debug_realloc to police
 *   heap space usage. (This is a dynamic flag that can be turned on/off)
 */
static int      malloc_watch = 1;

/* Character to stuff into freed space */
#define FREED_CHAR  'F'

/* Character to stuff into allocated space */
#define ALLOC_CHAR  'A'

/* Character to stuff into left over trailing bytes */
#define LEFT_OVER_CHAR  'Z'

/* Number of 'free' calls that will be delayed until the end */
#define MAX_FREE_DELAY_COUNT    1
#undef MAX_FREE_DELAY_COUNT

/* Maximum name of __FILE_ stored in each malloc'd area */
#define WARRANT_NAME_MAX (32-1) /* 1 less than multiple of 8 is best */

/* Macro to convert a user pointer to the malloc pointer */
#define user2malloc_(uptr)   (((char*)(void*)uptr)-sizeof(Word))

/* Macro to convert a macro pointer to the user pointer */
#define malloc2user_(mptr)   (((char*)(void*)(mptr))+sizeof(Word))

/* Size of the warrant record (this is dynamic) */
#define warrant_space  ( malloc_watch?sizeof(Warrant_Record):0 )

/* Macro to round up a number of bytes to a multiple of sizeof(Word) bytes */
#define round_up_(n) \
        ((n)==0?0:(sizeof(Word)+(((n)-1)/sizeof(Word))*sizeof(Word)))

/* Macro to calculate the needed malloc bytes from the user's request. */
#define rbytes_(nbytes) \
    (size_t)( sizeof(Word) + round_up_(nbytes) + sizeof(Word) + warrant_space )

/* Macro to get the -size stored in space through the malloc pointer */
#define nsize1_(mptr)           (((Word*)(void*)(mptr))->nsize1)
#define nsize2_(mptr)           (((Word*)(void*)(mptr))->nsize2)

/* Macro to get the -size stored in the tail of the space through */
/*     the malloc pointer */
#define tail_nsize1_(mptr)     \
        nsize1_(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))+sizeof(Word))
#define tail_nsize2_(mptr)     \
        nsize2_(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))+sizeof(Word))

/* Macro to get the -size stored in space through the user pointer */
#define user_nsize1_(uptr)      nsize1_(user2malloc_(uptr))
#define user_nsize2_(uptr)      nsize2_(user2malloc_(uptr))

/* Macro to get the -size stored in the tail of the space through */
/*     the user pointer */
#define user_tail_nsize1_(uptr) tail_nsize1_(user2malloc_(uptr))
#define user_tail_nsize2_(uptr) tail_nsize2_(user2malloc_(uptr))

/* Macro to get the int* of the last 32bit word of user space */
#define last_user_word_(mptr)   \
        ((int*)(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))))

/* Macros to get at the warrant contents from the malloc pointer */
#define warrant_(mptr) \
  (*((Warrant_Record*)(void*)(((char*)(void*)(mptr))+round_up_(-nsize1_(mptr))+sizeof(Word)*2)))

/* This struct is allocated after the tail clobber word if malloc_watch */
/*    is true. */
typedef struct {
    void           *link;       /* Next mptr in list */
    char            name[WARRANT_NAME_MAX + 1]; /* Name of allocator */
    int             line;       /* Line number where allocated */
    int             id;         /* Nth allocation */
}               Warrant_Record;
#define warrant_link_(mptr) warrant_(mptr).link
#define warrant_name_(mptr) warrant_(mptr).name
#define warrant_line_(mptr) warrant_(mptr).line
#define warrant_id_(mptr)   warrant_(mptr).id
#define MFILE(mptr) (malloc_watch?warrant_name_(mptr):"?")
#define MLINE(mptr) (malloc_watch?warrant_line_(mptr):0)
#define MID(mptr)   (malloc_watch?warrant_id_(mptr):0)

/* This should be one machine word and is also the clobber word struct */
typedef struct {
    int             nsize1;
    int             nsize2;
}               Word;           /* Largest basic type , sizeof(double)? */

/* The first malloc pointer for the warrants */
static void    *first_warrant_mptr = NULL;

/* Counter of allocations */
static int id_counter = 0;
static int largest_size = 0;
static void * largest_addr = NULL;
static void * smallest_addr = NULL;

/* Used to isolate what the error is */
static char *debug_check;
static void *clobbered_ptr;

/* Minumum macro */
#define minimum(a,b) ((a)<(b)?(a):(b))

/* Message routine */
static void
error_message(const char * format, ...)
{
    FILE *error_fp = stderr; /* All debug_malloc.c messages */
    va_list ap;
    va_start(ap, format);
    (void)fprintf(error_fp, "debug_malloc: ");
    (void)vfprintf(error_fp, format, ap);
    (void)fprintf(error_fp, "\n");
    (void)fflush(error_fp);
    va_end(ap);
}

/* This function prints out a memory error for the memory function
 *   'name' which was called in file 'file' at line number 'line'.  The malloc
 *   pointer with the error is in 'mptr'.
 */
static void
memory_error(void *mptr, const char *name, int mid, const char *mfile, int mline, const char *file, int line)
{
    char  nice_words[512];
    char  temp[256];
    int   len;
    void *mptr_walk;

    if (name == NULL)
        name = "UNKNOWN_NAME";
    if (file == NULL)
        file = "UNKNOWN_FILE";
    md_system_error(temp, (int)sizeof(temp));
    (void)strcpy(nice_words, temp);
    if ( debug_check!=NULL ) {
       (void)md_snprintf(nice_words, sizeof(nice_words),
                    "%s The %s at %p appears to have been hit.",
                    temp, debug_check, clobbered_ptr);
    }
    len = -nsize1_(mptr);
    error_message("Error: "
                   "%s The malloc space #%d is at %p [user size=%d(0x%x)],"
                   " and was allocated from file \"%s\" at line %d."
                   " [The debug function %s() detected this error "
                   "in file \"%s\" at line %d.]",
                   nice_words, mid, mptr, len, len, mfile, mline,
                   name, file, line);

    /* Print out contents of this allocation */
    {
        int i;
        void *uptr = malloc2user_(mptr);
        char *pmess;
        pmess = temp;
        for(i=0;i<(int)sizeof(temp);i++) {
            int ch = ((unsigned char*)uptr)[i];
            if ( isprint(ch) ) {
                *pmess++ = ch;
            } else {
                *pmess++ = '\\';
                *pmess++ = 'x';
                (void)sprintf(pmess,"%02x",ch);
                pmess+=2;
            }
        }
        *pmess = 0;
        error_message("Error: %p contains user data: %s", uptr, temp);
    }

    /* Try and print out table */
    if (!malloc_watch) {
        return;
    }
    mptr_walk = first_warrant_mptr;
    if (mptr_walk != NULL) {
        error_message("Active allocations: "
           "count=%d, largest_size=%d, address range (%p,%p)",
                        id_counter, largest_size, smallest_addr, largest_addr);
        do {
            int size1;
            int size2;
            char *mfile_walk;

            if ( mptr_walk > largest_addr || mptr_walk < smallest_addr ) {
                error_message("Terminating list due to pointer corruption");
                break;
            }
            size1 = -nsize1_(mptr_walk);
            size2 = -nsize2_(mptr_walk);
            mfile_walk = MFILE(mptr_walk);
            error_message("#%d: addr=%p size1=%d size2=%d file=\"%.*s\" line=%d",
                MID(mptr_walk), mptr_walk, size1, size2,
                WARRANT_NAME_MAX, mfile_walk, MLINE(mptr_walk));
            if ( size1 != size2 || size1 > largest_size || size1 < 0 ) {
                error_message("Terminating list due to size corruption");
                break;
            }
            mptr_walk = warrant_link_(mptr_walk);
        } while (mptr_walk != NULL);
    }
    abort();
}

/* This function sets the clobber word and sets up the warrant for the input
 *   malloc pointer "mptr".
 */
static void
setup_space_and_issue_warrant(void *mptr, size_t size, const char *file, int line)
{
    register int    nbytes;

    /*LINTED*/
    nbytes = (int)size;
    if ( nbytes > largest_size || largest_addr == NULL ) largest_size = nbytes;
    /*LINTED*/
    if ( mptr > largest_addr ) largest_addr = mptr;
    /*LINTED*/
    if ( mptr < smallest_addr || smallest_addr == NULL ) smallest_addr = mptr;

    /* Must be done first: */
    nsize1_(mptr) = -nbytes;
    nsize2_(mptr) = -nbytes;
    tail_nsize1_(mptr) = -nbytes;
    tail_nsize2_(mptr) = -nbytes;

#ifdef LEFT_OVER_CHAR
    /* Fill in those few extra bytes just before the tail Word structure */
    {
        register int    trailing_extra_bytes;
        /* LINTED */
        trailing_extra_bytes = (int) (round_up_(nbytes) - nbytes);
        if (  trailing_extra_bytes > 0 ) {
            register char  *p;
            register int    i;
            p = ((char *) mptr) + sizeof(Word) + nbytes;
            for (i = 0; i < trailing_extra_bytes; i++)
                p[i] = LEFT_OVER_CHAR;
        }
    }
#endif

    /* Fill out warrant */
    if (malloc_watch) {
        static Warrant_Record zero_warrant;
        register void  *p1,
                       *p2;
        size_t len;
        int start_pos = 0;
        warrant_(mptr) = zero_warrant;
        p1 = warrant_name_(mptr);
        len = strlen(file);
        if ( len >  WARRANT_NAME_MAX )  {
            /*LINTED*/
            start_pos = (int)len - WARRANT_NAME_MAX;
        }
        p2 = ((char*)file) + start_pos;
        /*LINTED*/
        (void) memcpy(p1, p2, minimum(((int)len), WARRANT_NAME_MAX));
        warrant_line_(mptr) = line;
        warrant_id_(mptr)   = ++id_counter;
        warrant_link_(mptr) = first_warrant_mptr;
        first_warrant_mptr = mptr;
    }
}

/* This function checks the clobber words at the beginning and end of the
 *   allocated space.
 */
static void
memory_check(void *uptr, int mid, const char *mfile, int mline, const char *file, int line)
{
    int             neg_nbytes;
    int             nbytes;

    debug_check = "pointer value itself";
    clobbered_ptr = uptr;
    if (uptr == NULL)
        memory_error((void *) NULL, "memory_check", mid, mfile, mline, file, line);

    /* Check both Word structures */

    debug_check = "first beginning clobber word";
    clobbered_ptr = (char*)&user_nsize1_(uptr);
    neg_nbytes = user_nsize1_(uptr);
    if (neg_nbytes >= 0)
        memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);

    debug_check = "second beginning clobber word";
    clobbered_ptr = (char*)&user_nsize2_(uptr);
    if (neg_nbytes != user_nsize2_(uptr))
        memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);

    debug_check = "first ending clobber word";
    clobbered_ptr = (char*)&user_tail_nsize1_(uptr);
    if (neg_nbytes != user_tail_nsize1_(uptr))
        memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);

    debug_check = "second ending clobber word";
    clobbered_ptr = (char*)&user_tail_nsize2_(uptr);
    if (neg_nbytes != user_tail_nsize2_(uptr))
        memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);

    /* Get a positive count of bytes */
    nbytes = -neg_nbytes;

#ifdef LEFT_OVER_CHAR
    {
        /* Check those few extra bytes just before the tail Word structure */
        register int    trailing_extra_bytes;
        register int    i;
        register char  *p;
        /* LINTED */
        trailing_extra_bytes = (int) (round_up_(nbytes) - nbytes);
        p = ((char *) (uptr)) + nbytes;
        debug_check = "trailing left over area";
        for (i = 0; i < trailing_extra_bytes; i++) {
            clobbered_ptr = p+1;
            if (p[i] != LEFT_OVER_CHAR) {
                memory_error(user2malloc_(uptr), "memory_check", mid, mfile, mline, file, line);
            }
        }
    }
#endif

    /* Make sure debug_check is cleared */
    debug_check = NULL;
}

/* This function looks for the given malloc pointer in the police line up
 *   and removes it from the warrant list.
 *      mptr            The pointer to the malloc space being removed
 */
static int
remove_warrant(void *mptr)
{
    void           *mptr1,
                   *last_mptr1;

    /* Free it up from the list */
    if (malloc_watch && mptr != NULL) {
        int found;

        found = 0;
        last_mptr1 = NULL;
        mptr1 = first_warrant_mptr;
        while (mptr1 != NULL) {
            if (mptr1 == mptr) {
                if (last_mptr1 == NULL)
                    first_warrant_mptr = warrant_link_(mptr1);
                else
                    warrant_link_(last_mptr1) = warrant_link_(mptr1);
                found = 1;
                break;
            }
            last_mptr1 = mptr1;
            mptr1 = warrant_link_(mptr1);
        }
        return found;
    }
    return 1;
}

static void
actual_free(void *uptr, const char *file, int line)
{
    void *mptr;
    const char *mfile;
    int mline;
    int mid;
    if ( uptr == NULL )
        return;
    mptr = user2malloc_(uptr);
    memory_check(uptr, (mid=MID(mptr)), (mfile=MFILE(mptr)), (mline=MLINE(mptr)), file, line);
    if (malloc_watch && remove_warrant(mptr)==0 )
        memory_check(uptr, mid, mfile, mline, file, line);
#ifdef FREED_CHAR
    if ( mptr!=NULL ) {
        size_t nbytes = -nsize1_(mptr);
        /* LINTED */
        (void)memset(mptr, FREED_CHAR, rbytes_(nbytes));
    }
#endif
    free(mptr);
}

#ifdef MAX_FREE_DELAY_COUNT

static void *free_delay[MAX_FREE_DELAY_COUNT];
static int free_delay_pos = 0;

static void
delayed_free(void *uptr, const char* file, int line)
{
    void *mptr;
    void *olduptr = free_delay[free_delay_pos];
    size_t nbytes;
    if ( uptr==NULL )
        return;
    mptr = user2malloc_(uptr);
    memory_check(uptr, MID(mptr), MFILE(mptr), MLINE(mptr), file, line);
    if ( olduptr!=NULL ) {
        actual_free(olduptr, file, line);
    }
    free_delay[free_delay_pos] = uptr;
    free_delay_pos++;
    free_delay_pos = free_delay_pos % MAX_FREE_DELAY_COUNT;
    nbytes = -user_nsize1_(uptr);
#ifdef FREED_CHAR
    (void)memset(uptr, FREED_CHAR, (size_t)nbytes);
#endif
}

static void
delayed_free_all(const char *file, int line)
{
    int i;
    for ( i=0; i< MAX_FREE_DELAY_COUNT; i++) {
        void *olduptr = free_delay[i];
        free_delay[i] = NULL;
        if ( olduptr!=NULL ) {
            actual_free(olduptr, file, line);
        }
    }
}

#endif

void
debug_free(void *uptr, const char *file, int line)
{
    int mid = 0;

    if (uptr == NULL)
        memory_error((void *) NULL, "debug_free", mid, file, line, file, line);
#ifdef MAX_FREE_DELAY_COUNT
    delayed_free(uptr, file, line);
#else
    actual_free(uptr, file, line);
#endif
}

/* This function calls malloc(). */
void           *
debug_malloc(size_t nbytes, const char *file, int line)
{
    void           *mptr;
    void           *uptr;
    int mid = id_counter;

    /*LINTED*/
    if ((int)nbytes <= 0)
        memory_error((void *) NULL, "debug_malloc", mid, file, line, file, line);
    /* LINTED */
    mptr = malloc(rbytes_(nbytes));
    if (mptr == NULL)
        memory_error((void *) NULL, "debug_malloc", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    uptr = malloc2user_(mptr);
#ifdef ALLOC_CHAR
    (void)memset(uptr, ALLOC_CHAR, (size_t)nbytes);
#endif
    return uptr;
}

void           *
debug_realloc(void *uptr, size_t nbytes, const char *file, int line)
{
    void           *mptr;
    void           *oldmptr;
    void           *newuptr;
    size_t         oldnbytes;
    int mid = id_counter;

    oldmptr = user2malloc_(uptr);
    oldnbytes = 0;
    if ((int)nbytes <= 0)
        memory_error(oldmptr, "debug_realloc", mid, file, line, file, line);
    if (uptr != NULL) {
        memory_check(uptr, MID(oldmptr), MFILE(oldmptr), MLINE(oldmptr), file, line);
        oldnbytes = -user_nsize1_(uptr);
        if ( malloc_watch && remove_warrant(oldmptr)==0 )
            memory_check(uptr, MID(oldmptr), MFILE(oldmptr), MLINE(oldmptr), file, line);
    }
    if (uptr == NULL) {
        /* LINTED */
        mptr = malloc(rbytes_(nbytes));
    } else {
        /* LINTED */
        mptr = realloc(oldmptr, rbytes_(nbytes));
    }
    if (mptr == NULL)
        memory_error(oldmptr, "debug_realloc", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    newuptr = malloc2user_(mptr);
#ifdef ALLOC_CHAR
    if (uptr == NULL)
        (void)memset(newuptr, ALLOC_CHAR, (size_t)nbytes);
    else if ( nbytes > oldnbytes )
        (void)memset(((char*)newuptr)+oldnbytes, ALLOC_CHAR, (size_t)nbytes-oldnbytes);
#endif
    return newuptr;
}

/* This function calls calloc(). */
void           *
debug_calloc(size_t nelem, size_t elsize, const char *file, int line)
{
    void           *mptr;
    size_t          nbytes;
    int mid = id_counter;

    nbytes = nelem*elsize;
    /*LINTED*/
    if ((int)nbytes <= 0)
        memory_error((void *) NULL, "debug_calloc", mid, file, line, file, line);
    /* LINTED */
    mptr = calloc(rbytes_(nbytes),1);
    if (mptr == NULL)
        memory_error((void *) NULL, "debug_calloc", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    return malloc2user_(mptr);
}

/* This function replaces strdup(). */
char           *
debug_strdup(const char *s1, const char *file, int line)
{
    void           *mptr;
    void           *uptr;
    size_t          nbytes;
    int mid = id_counter;

    if (s1 == NULL)
        memory_error((void *) NULL, "debug_strdup", mid, file, line, file, line);
    nbytes = strlen(s1)+1;
    /*LINTED*/
    if ((int)nbytes < 0)
        memory_error((void *) NULL, "debug_strdup", mid, file, line, file, line);
    /* LINTED */
    mptr = malloc(rbytes_(nbytes));
    if (mptr == NULL)
        memory_error((void *) NULL, "debug_strdup", mid, file, line, file, line);
    setup_space_and_issue_warrant(mptr, nbytes, file, line);
    uptr = malloc2user_(mptr);
    (void)strcpy((char*)uptr, s1);
    return (char*)uptr;
}

void
debug_malloc_verify(const char *file, int line)
{
    void           *mptr;

#ifdef MAX_FREE_DELAY_COUNT
    delayed_free_all(file,line);
#endif

    if (!malloc_watch) {
        return;
    }
    mptr = first_warrant_mptr;
    if (mptr != NULL) {
        /* Check all this memory first */
        do {
            memory_check(malloc2user_(mptr), MID(mptr), MFILE(mptr), MLINE(mptr), file, line);
            mptr = warrant_link_(mptr);
        } while (mptr != NULL);
    }
}

/* Report outstanding space warrants to console. */
void
debug_malloc_police(const char *file, int line)
{
    void           *mptr;

#ifdef MAX_FREE_DELAY_COUNT
    delayed_free_all(file,line);
#endif

    if (!malloc_watch) {
        return;
    }

    mptr = first_warrant_mptr;
    if (mptr != NULL) {
        debug_malloc_verify(file, line);
        /* Now issue warrants */
        mptr = first_warrant_mptr;
        do {
            error_message("Outstanding space warrant: %p (%d bytes) allocated by %s at line %d, allocation #%d",
               mptr, -nsize1_(mptr), warrant_name_(mptr),
               warrant_line_(mptr), warrant_id_(mptr));

            mptr = warrant_link_(mptr);
        } while (mptr != NULL);
    }
}

#else

void
debug_malloc_verify(const char *file, int line)
{
    file = file;
    line = line;
}

void
debug_malloc_police(const char *file, int line)
{
    file = file;
    line = line;
}

#endif