summaryrefslogtreecommitdiffstats
path: root/libc/stdlib/malloc/heap_debug.c
blob: f1ccc6f2d94c04d6d6825f475926694470eaa029 (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
/*
 * libc/stdlib/malloc/heap_debug.c -- optional heap debugging routines
 *
 *  Copyright (C) 2002  NEC Corporation
 *  Copyright (C) 2002  Miles Bader <miles@gnu.org>
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License.  See the file COPYING.LIB in the main
 * directory of this archive for more details.
 *
 * Written by Miles Bader <miles@gnu.org>
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>

/* libc_hidden_proto(vfprintf) */
/* libc_hidden_proto(fprintf) */
/* libc_hidden_proto(_exit) */

#include "malloc.h"
#include "heap.h"


#ifdef HEAP_DEBUGGING
int __heap_debug = 0;
#endif


static void
__heap_dump_freelist (struct heap_free_area *heap)
{
  struct heap_free_area *fa;
  for (fa = heap; fa; fa = fa->next)
    __malloc_debug_printf (0,
			   "0x%lx:  0x%lx - 0x%lx  (%d)\tP=0x%lx, N=0x%lx",
			   (long)fa,
			   (long)HEAP_FREE_AREA_START (fa),
			   (long)HEAP_FREE_AREA_END (fa),
			   fa->size,
			   (long)fa->prev,
			   (long)fa->next);
}

/* Output a text representation of HEAP to stderr, labelling it with STR.  */
void
__heap_dump (struct heap_free_area *heap, const char *str)
{
  static smallint recursed;

  if (! recursed)
    {
      __heap_check (heap, str);

      recursed = 1;

      __malloc_debug_printf (1, "%s: heap @0x%lx:", str, (long)heap);
      __heap_dump_freelist (heap);
      __malloc_debug_indent (-1);

      recursed = 0;
    }
}


/* Output an error message to stderr, and exit.  STR is printed with the
   failure message.  */
static void attribute_noreturn
__heap_check_failure (struct heap_free_area *heap, struct heap_free_area *fa,
		      const char *str, char *fmt, ...)
{
  va_list val;

  if (str)
    fprintf (stderr, "\nHEAP CHECK FAILURE %s: ", str);
  else
    fprintf (stderr, "\nHEAP CHECK FAILURE: ");

  va_start (val, fmt);
  vfprintf (stderr, fmt, val);
  va_end (val);

  fprintf (stderr, "\n");

  __malloc_debug_set_indent (0);
  __malloc_debug_printf (1, "heap dump:");
  __heap_dump_freelist (heap);

  _exit (22);
}

/* Do some consistency checks on HEAP.  If they fail, output an error
   message to stderr, and exit.  STR is printed with the failure message.  */
void
__heap_check (struct heap_free_area *heap, const char *str)
{
  typedef unsigned long ul_t;
  struct heap_free_area *fa, *prev;
  struct heap_free_area *first_fa = heap;

  if (first_fa && first_fa->prev)
    __heap_check_failure (heap, first_fa, str,
"first free-area has non-zero prev pointer:\n\
    first free-area = 0x%lx\n\
    (0x%lx)->prev   = 0x%lx\n",
			      (ul_t)first_fa,
			      (ul_t)first_fa, (ul_t)first_fa->prev);

  for (prev = 0, fa = first_fa; fa; prev = fa, fa = fa->next)
    {
      if (((ul_t)HEAP_FREE_AREA_END (fa) & (HEAP_GRANULARITY - 1))
	  || (fa->size & (HEAP_GRANULARITY - 1)))
	__heap_check_failure (heap, fa, str, "alignment error:\n\
    (0x%lx)->start = 0x%lx\n\
    (0x%lx)->size  = 0x%lx\n",
			      (ul_t)fa,
			      (ul_t)HEAP_FREE_AREA_START (fa),
			      (ul_t)fa, fa->size);

      if (fa->prev != prev)
	__heap_check_failure (heap, fa, str, "prev pointer corrupted:\n\
    (0x%lx)->next = 0x%lx\n\
    (0x%lx)->prev = 0x%lx\n",
			      (ul_t)prev, (ul_t)prev->next,
			      (ul_t)fa, (ul_t)fa->prev);

      if (prev)
	{
	  ul_t start = (ul_t)HEAP_FREE_AREA_START (fa);
	  ul_t prev_end = (ul_t)HEAP_FREE_AREA_END (prev);

	  if (prev_end >= start)
	    __heap_check_failure (heap, fa, str,
				  "start %s with prev free-area end:\n\
    (0x%lx)->prev  = 0x%lx\n\
    (0x%lx)->start = 0x%lx\n\
    (0x%lx)->end   = 0x%lx\n",
				  (prev_end == start ? "unmerged" : "overlaps"),
				  (ul_t)fa, (ul_t)prev,
				  (ul_t)fa, start,
				  (ul_t)prev, prev_end);
	}
    }
}