1
by brian
clean slate |
1 |
/************************************************************************
|
2 |
The memory management: the debug code. This is not a compilation module,
|
|
3 |
but is included in mem0mem.* !
|
|
4 |
||
5 |
(c) 1994, 1995 Innobase Oy
|
|
6 |
||
7 |
Created 6/9/1994 Heikki Tuuri
|
|
8 |
*************************************************************************/
|
|
9 |
||
10 |
#ifdef UNIV_MEM_DEBUG
|
|
11 |
mutex_t mem_hash_mutex; /* The mutex which protects in the |
|
12 |
debug version the hash table containing
|
|
13 |
the list of live memory heaps, and
|
|
14 |
also the global variables below. */
|
|
15 |
||
16 |
/* The following variables contain information about the
|
|
17 |
extent of memory allocations. Only used in the debug version.
|
|
18 |
Protected by mem_hash_mutex above. */
|
|
19 |
||
20 |
static ulint mem_n_created_heaps = 0; |
|
21 |
static ulint mem_n_allocations = 0; |
|
22 |
static ulint mem_total_allocated_memory = 0; |
|
23 |
ulint mem_current_allocated_memory = 0; |
|
24 |
static ulint mem_max_allocated_memory = 0; |
|
25 |
static ulint mem_last_print_info = 0; |
|
26 |
||
27 |
/* Size of the hash table for memory management tracking */
|
|
28 |
#define MEM_HASH_SIZE 997
|
|
29 |
||
30 |
/* The node of the list containing currently allocated memory heaps */
|
|
31 |
||
32 |
typedef struct mem_hash_node_struct mem_hash_node_t; |
|
33 |
struct mem_hash_node_struct { |
|
34 |
UT_LIST_NODE_T(mem_hash_node_t) |
|
35 |
list; /* hash list node */ |
|
36 |
mem_heap_t* heap; /* memory heap */ |
|
37 |
const char* file_name;/* file where heap was created*/ |
|
38 |
ulint line; /* file line of creation */ |
|
39 |
ulint nth_heap;/* this is the nth heap created */ |
|
40 |
UT_LIST_NODE_T(mem_hash_node_t) |
|
41 |
all_list;/* list of all created heaps */ |
|
42 |
};
|
|
43 |
||
44 |
typedef UT_LIST_BASE_NODE_T(mem_hash_node_t) mem_hash_cell_t; |
|
45 |
||
46 |
/* The hash table of allocated heaps */
|
|
47 |
static mem_hash_cell_t mem_hash_table[MEM_HASH_SIZE]; |
|
48 |
||
49 |
/* The base node of the list of all allocated heaps */
|
|
50 |
static mem_hash_cell_t mem_all_list_base; |
|
51 |
||
52 |
static ibool mem_hash_initialized = FALSE; |
|
53 |
||
54 |
||
55 |
UNIV_INLINE
|
|
56 |
mem_hash_cell_t* |
|
57 |
mem_hash_get_nth_cell(ulint i); |
|
58 |
||
59 |
/* Accessor function for the hash table. Returns a pointer to the
|
|
60 |
table cell. */
|
|
61 |
UNIV_INLINE
|
|
62 |
mem_hash_cell_t* |
|
63 |
mem_hash_get_nth_cell(ulint i) |
|
64 |
{
|
|
65 |
ut_a(i < MEM_HASH_SIZE); |
|
66 |
||
67 |
return(&(mem_hash_table[i])); |
|
68 |
}
|
|
69 |
||
70 |
/* Accessor functions for a memory field in the debug version */
|
|
71 |
||
72 |
void
|
|
73 |
mem_field_header_set_len(byte* field, ulint len) |
|
74 |
{
|
|
75 |
mach_write_to_4(field - 2 * sizeof(ulint), len); |
|
76 |
}
|
|
77 |
||
78 |
ulint
|
|
79 |
mem_field_header_get_len(byte* field) |
|
80 |
{
|
|
81 |
return(mach_read_from_4(field - 2 * sizeof(ulint))); |
|
82 |
}
|
|
83 |
||
84 |
void
|
|
85 |
mem_field_header_set_check(byte* field, ulint check) |
|
86 |
{
|
|
87 |
mach_write_to_4(field - sizeof(ulint), check); |
|
88 |
}
|
|
89 |
||
90 |
ulint
|
|
91 |
mem_field_header_get_check(byte* field) |
|
92 |
{
|
|
93 |
return(mach_read_from_4(field - sizeof(ulint))); |
|
94 |
}
|
|
95 |
||
96 |
void
|
|
97 |
mem_field_trailer_set_check(byte* field, ulint check) |
|
98 |
{
|
|
99 |
mach_write_to_4(field + mem_field_header_get_len(field), check); |
|
100 |
}
|
|
101 |
||
102 |
ulint
|
|
103 |
mem_field_trailer_get_check(byte* field) |
|
104 |
{
|
|
105 |
return(mach_read_from_4(field |
|
106 |
+ mem_field_header_get_len(field))); |
|
107 |
}
|
|
108 |
#endif /* UNIV_MEM_DEBUG */ |
|
109 |
||
110 |
/**********************************************************************
|
|
111 |
Initializes the memory system. */
|
|
112 |
||
113 |
void
|
|
114 |
mem_init( |
|
115 |
/*=====*/
|
|
116 |
ulint size) /* in: common pool size in bytes */ |
|
117 |
{
|
|
118 |
#ifdef UNIV_MEM_DEBUG
|
|
119 |
||
120 |
ulint i; |
|
121 |
||
122 |
/* Initialize the hash table */
|
|
123 |
ut_a(FALSE == mem_hash_initialized); |
|
124 |
||
125 |
mutex_create(&mem_hash_mutex, SYNC_MEM_HASH); |
|
126 |
||
127 |
for (i = 0; i < MEM_HASH_SIZE; i++) { |
|
128 |
UT_LIST_INIT(*mem_hash_get_nth_cell(i)); |
|
129 |
}
|
|
130 |
||
131 |
UT_LIST_INIT(mem_all_list_base); |
|
132 |
||
133 |
mem_hash_initialized = TRUE; |
|
134 |
#endif
|
|
135 |
||
136 |
mem_comm_pool = mem_pool_create(size); |
|
137 |
}
|
|
138 |
||
139 |
#ifdef UNIV_MEM_DEBUG
|
|
140 |
/**********************************************************************
|
|
141 |
Initializes an allocated memory field in the debug version. */
|
|
142 |
||
143 |
void
|
|
144 |
mem_field_init( |
|
145 |
/*===========*/
|
|
146 |
byte* buf, /* in: memory field */ |
|
147 |
ulint n) /* in: how many bytes the user requested */ |
|
148 |
{
|
|
149 |
ulint rnd; |
|
150 |
byte* usr_buf; |
|
151 |
||
152 |
usr_buf = buf + MEM_FIELD_HEADER_SIZE; |
|
153 |
||
154 |
/* In the debug version write the length field and the
|
|
155 |
check fields to the start and the end of the allocated storage.
|
|
156 |
The field header consists of a length field and
|
|
157 |
a random number field, in this order. The field trailer contains
|
|
158 |
the same random number as a check field. */
|
|
159 |
||
160 |
mem_field_header_set_len(usr_buf, n); |
|
161 |
||
162 |
rnd = ut_rnd_gen_ulint(); |
|
163 |
||
164 |
mem_field_header_set_check(usr_buf, rnd); |
|
165 |
mem_field_trailer_set_check(usr_buf, rnd); |
|
166 |
||
167 |
/* Update the memory allocation information */
|
|
168 |
||
169 |
mutex_enter(&mem_hash_mutex); |
|
170 |
||
171 |
mem_total_allocated_memory += n; |
|
172 |
mem_current_allocated_memory += n; |
|
173 |
mem_n_allocations++; |
|
174 |
||
175 |
if (mem_current_allocated_memory > mem_max_allocated_memory) { |
|
176 |
mem_max_allocated_memory = mem_current_allocated_memory; |
|
177 |
}
|
|
178 |
||
179 |
mutex_exit(&mem_hash_mutex); |
|
180 |
||
181 |
/* In the debug version set the buffer to a random
|
|
182 |
combination of 0xBA and 0xBE */
|
|
183 |
||
184 |
mem_init_buf(usr_buf, n); |
|
185 |
}
|
|
186 |
||
187 |
/**********************************************************************
|
|
188 |
Erases an allocated memory field in the debug version. */
|
|
189 |
||
190 |
void
|
|
191 |
mem_field_erase( |
|
192 |
/*============*/
|
|
193 |
byte* buf, /* in: memory field */ |
|
194 |
ulint n __attribute__((unused))) |
|
195 |
/* in: how many bytes the user requested */
|
|
196 |
{
|
|
197 |
byte* usr_buf; |
|
198 |
||
199 |
usr_buf = buf + MEM_FIELD_HEADER_SIZE; |
|
200 |
||
201 |
mutex_enter(&mem_hash_mutex); |
|
202 |
mem_current_allocated_memory -= n; |
|
203 |
mutex_exit(&mem_hash_mutex); |
|
204 |
||
205 |
/* Check that the field lengths agree */
|
|
206 |
ut_ad(n == (ulint)mem_field_header_get_len(usr_buf)); |
|
207 |
||
208 |
/* In the debug version, set the freed space to a random
|
|
209 |
combination of 0xDE and 0xAD */
|
|
210 |
||
211 |
mem_erase_buf(buf, MEM_SPACE_NEEDED(n)); |
|
212 |
}
|
|
213 |
||
214 |
/*******************************************************************
|
|
215 |
Initializes a buffer to a random combination of hex BA and BE.
|
|
216 |
Used to initialize allocated memory. */
|
|
217 |
||
218 |
void
|
|
219 |
mem_init_buf( |
|
220 |
/*=========*/
|
|
221 |
byte* buf, /* in: pointer to buffer */ |
|
222 |
ulint n) /* in: length of buffer */ |
|
223 |
{
|
|
224 |
byte* ptr; |
|
225 |
||
226 |
UNIV_MEM_ASSERT_W(buf, n); |
|
227 |
||
228 |
for (ptr = buf; ptr < buf + n; ptr++) { |
|
229 |
||
230 |
if (ut_rnd_gen_ibool()) { |
|
231 |
*ptr = 0xBA; |
|
232 |
} else { |
|
233 |
*ptr = 0xBE; |
|
234 |
}
|
|
235 |
}
|
|
236 |
||
237 |
UNIV_MEM_INVALID(buf, n); |
|
238 |
}
|
|
239 |
||
240 |
/*******************************************************************
|
|
241 |
Initializes a buffer to a random combination of hex DE and AD.
|
|
242 |
Used to erase freed memory.*/
|
|
243 |
||
244 |
void
|
|
245 |
mem_erase_buf( |
|
246 |
/*==========*/
|
|
247 |
byte* buf, /* in: pointer to buffer */ |
|
248 |
ulint n) /* in: length of buffer */ |
|
249 |
{
|
|
250 |
byte* ptr; |
|
251 |
||
252 |
UNIV_MEM_ASSERT_W(buf, n); |
|
253 |
||
254 |
for (ptr = buf; ptr < buf + n; ptr++) { |
|
255 |
if (ut_rnd_gen_ibool()) { |
|
256 |
*ptr = 0xDE; |
|
257 |
} else { |
|
258 |
*ptr = 0xAD; |
|
259 |
}
|
|
260 |
}
|
|
261 |
||
262 |
UNIV_MEM_FREE(buf, n); |
|
263 |
}
|
|
264 |
||
265 |
/*******************************************************************
|
|
266 |
Inserts a created memory heap to the hash table of current allocated
|
|
267 |
memory heaps. */
|
|
268 |
||
269 |
void
|
|
270 |
mem_hash_insert( |
|
271 |
/*============*/
|
|
272 |
mem_heap_t* heap, /* in: the created heap */ |
|
273 |
const char* file_name, /* in: file name of creation */ |
|
274 |
ulint line) /* in: line where created */ |
|
275 |
{
|
|
276 |
mem_hash_node_t* new_node; |
|
277 |
ulint cell_no ; |
|
278 |
||
279 |
ut_ad(mem_heap_check(heap)); |
|
280 |
||
281 |
mutex_enter(&mem_hash_mutex); |
|
282 |
||
283 |
cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE); |
|
284 |
||
285 |
/* Allocate a new node to the list */
|
|
286 |
new_node = ut_malloc(sizeof(mem_hash_node_t)); |
|
287 |
||
288 |
new_node->heap = heap; |
|
289 |
new_node->file_name = file_name; |
|
290 |
new_node->line = line; |
|
291 |
new_node->nth_heap = mem_n_created_heaps; |
|
292 |
||
293 |
/* Insert into lists */
|
|
294 |
UT_LIST_ADD_FIRST(list, *mem_hash_get_nth_cell(cell_no), new_node); |
|
295 |
||
296 |
UT_LIST_ADD_LAST(all_list, mem_all_list_base, new_node); |
|
297 |
||
298 |
mem_n_created_heaps++; |
|
299 |
||
300 |
mutex_exit(&mem_hash_mutex); |
|
301 |
}
|
|
302 |
||
303 |
/*******************************************************************
|
|
304 |
Removes a memory heap (which is going to be freed by the caller)
|
|
305 |
from the list of live memory heaps. Returns the size of the heap
|
|
306 |
in terms of how much memory in bytes was allocated for the user of
|
|
307 |
the heap (not the total space occupied by the heap).
|
|
308 |
Also validates the heap.
|
|
309 |
NOTE: This function does not free the storage occupied by the
|
|
310 |
heap itself, only the node in the list of heaps. */
|
|
311 |
||
312 |
void
|
|
313 |
mem_hash_remove( |
|
314 |
/*============*/
|
|
315 |
mem_heap_t* heap, /* in: the heap to be freed */ |
|
316 |
const char* file_name, /* in: file name of freeing */ |
|
317 |
ulint line) /* in: line where freed */ |
|
318 |
{
|
|
319 |
mem_hash_node_t* node; |
|
320 |
ulint cell_no; |
|
321 |
ibool error; |
|
322 |
ulint size; |
|
323 |
||
324 |
ut_ad(mem_heap_check(heap)); |
|
325 |
||
326 |
mutex_enter(&mem_hash_mutex); |
|
327 |
||
328 |
cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE); |
|
329 |
||
330 |
/* Look for the heap in the hash table list */
|
|
331 |
node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(cell_no)); |
|
332 |
||
333 |
while (node != NULL) { |
|
334 |
if (node->heap == heap) { |
|
335 |
||
336 |
break; |
|
337 |
}
|
|
338 |
||
339 |
node = UT_LIST_GET_NEXT(list, node); |
|
340 |
}
|
|
341 |
||
342 |
if (node == NULL) { |
|
343 |
fprintf(stderr, |
|
344 |
"Memory heap or buffer freed in %s line %lu"
|
|
345 |
" did not exist.\n", |
|
346 |
file_name, (ulong) line); |
|
347 |
ut_error; |
|
348 |
}
|
|
349 |
||
350 |
/* Remove from lists */
|
|
351 |
UT_LIST_REMOVE(list, *mem_hash_get_nth_cell(cell_no), node); |
|
352 |
||
353 |
UT_LIST_REMOVE(all_list, mem_all_list_base, node); |
|
354 |
||
355 |
/* Validate the heap which will be freed */
|
|
356 |
mem_heap_validate_or_print(node->heap, NULL, FALSE, &error, &size, |
|
357 |
NULL, NULL); |
|
358 |
if (error) { |
|
359 |
fprintf(stderr, |
|
360 |
"Inconsistency in memory heap or"
|
|
361 |
" buffer n:o %lu created\n" |
|
362 |
"in %s line %lu and tried to free in %s line %lu.\n" |
|
363 |
"Hex dump of 400 bytes around memory heap"
|
|
364 |
" first block start:\n", |
|
365 |
node->nth_heap, node->file_name, (ulong) node->line, |
|
366 |
file_name, (ulong) line); |
|
367 |
ut_print_buf(stderr, (byte*)node->heap - 200, 400); |
|
368 |
fputs("\nDump of the mem heap:\n", stderr); |
|
369 |
mem_heap_validate_or_print(node->heap, NULL, TRUE, &error, |
|
370 |
&size, NULL, NULL); |
|
371 |
ut_error; |
|
372 |
}
|
|
373 |
||
374 |
/* Free the memory occupied by the node struct */
|
|
375 |
ut_free(node); |
|
376 |
||
377 |
mem_current_allocated_memory -= size; |
|
378 |
||
379 |
mutex_exit(&mem_hash_mutex); |
|
380 |
}
|
|
381 |
#endif /* UNIV_MEM_DEBUG */ |
|
382 |
||
383 |
#if defined UNIV_MEM_DEBUG || defined UNIV_DEBUG
|
|
384 |
/*******************************************************************
|
|
385 |
Checks a memory heap for consistency and prints the contents if requested.
|
|
386 |
Outputs the sum of sizes of buffers given to the user (only in
|
|
387 |
the debug version), the physical size of the heap and the number of
|
|
388 |
blocks in the heap. In case of error returns 0 as sizes and number
|
|
389 |
of blocks. */
|
|
390 |
||
391 |
void
|
|
392 |
mem_heap_validate_or_print( |
|
393 |
/*=======================*/
|
|
394 |
mem_heap_t* heap, /* in: memory heap */ |
|
395 |
byte* top __attribute__((unused)), |
|
396 |
/* in: calculate and validate only until
|
|
397 |
this top pointer in the heap is reached,
|
|
398 |
if this pointer is NULL, ignored */
|
|
399 |
ibool print, /* in: if TRUE, prints the contents |
|
400 |
of the heap; works only in
|
|
401 |
the debug version */
|
|
402 |
ibool* error, /* out: TRUE if error */ |
|
403 |
ulint* us_size,/* out: allocated memory |
|
404 |
(for the user) in the heap,
|
|
405 |
if a NULL pointer is passed as this
|
|
406 |
argument, it is ignored; in the
|
|
407 |
non-debug version this is always -1 */
|
|
408 |
ulint* ph_size,/* out: physical size of the heap, |
|
409 |
if a NULL pointer is passed as this
|
|
410 |
argument, it is ignored */
|
|
411 |
ulint* n_blocks) /* out: number of blocks in the heap, |
|
412 |
if a NULL pointer is passed as this
|
|
413 |
argument, it is ignored */
|
|
414 |
{
|
|
415 |
mem_block_t* block; |
|
416 |
ulint total_len = 0; |
|
417 |
ulint block_count = 0; |
|
418 |
ulint phys_len = 0; |
|
419 |
#ifdef UNIV_MEM_DEBUG
|
|
420 |
ulint len; |
|
421 |
byte* field; |
|
422 |
byte* user_field; |
|
423 |
ulint check_field; |
|
424 |
#endif
|
|
425 |
||
426 |
/* Pessimistically, we set the parameters to error values */
|
|
427 |
if (us_size != NULL) { |
|
428 |
*us_size = 0; |
|
429 |
}
|
|
430 |
if (ph_size != NULL) { |
|
431 |
*ph_size = 0; |
|
432 |
}
|
|
433 |
if (n_blocks != NULL) { |
|
434 |
*n_blocks = 0; |
|
435 |
}
|
|
436 |
*error = TRUE; |
|
437 |
||
438 |
block = heap; |
|
439 |
||
440 |
if (block->magic_n != MEM_BLOCK_MAGIC_N) { |
|
441 |
return; |
|
442 |
}
|
|
443 |
||
444 |
if (print) { |
|
445 |
fputs("Memory heap:", stderr); |
|
446 |
}
|
|
447 |
||
448 |
while (block != NULL) { |
|
449 |
phys_len += mem_block_get_len(block); |
|
450 |
||
451 |
if ((block->type == MEM_HEAP_BUFFER) |
|
452 |
&& (mem_block_get_len(block) > UNIV_PAGE_SIZE)) { |
|
453 |
||
454 |
fprintf(stderr, |
|
455 |
"InnoDB: Error: mem block %p"
|
|
456 |
" length %lu > UNIV_PAGE_SIZE\n", |
|
457 |
(void*) block, |
|
458 |
(ulong) mem_block_get_len(block)); |
|
459 |
/* error */
|
|
460 |
||
461 |
return; |
|
462 |
}
|
|
463 |
||
464 |
#ifdef UNIV_MEM_DEBUG
|
|
465 |
/* We can trace the fields of the block only in the debug
|
|
466 |
version */
|
|
467 |
if (print) { |
|
468 |
fprintf(stderr, " Block %ld:", block_count); |
|
469 |
}
|
|
470 |
||
471 |
field = (byte*)block + mem_block_get_start(block); |
|
472 |
||
473 |
if (top && (field == top)) { |
|
474 |
||
475 |
goto completed; |
|
476 |
}
|
|
477 |
||
478 |
while (field < (byte*)block + mem_block_get_free(block)) { |
|
479 |
||
480 |
/* Calculate the pointer to the storage
|
|
481 |
which was given to the user */
|
|
482 |
||
483 |
user_field = field + MEM_FIELD_HEADER_SIZE; |
|
484 |
||
485 |
len = mem_field_header_get_len(user_field); |
|
486 |
||
487 |
if (print) { |
|
488 |
ut_print_buf(stderr, user_field, len); |
|
489 |
}
|
|
490 |
||
491 |
total_len += len; |
|
492 |
check_field = mem_field_header_get_check(user_field); |
|
493 |
||
494 |
if (check_field |
|
495 |
!= mem_field_trailer_get_check(user_field)) { |
|
496 |
/* error */
|
|
497 |
||
498 |
fprintf(stderr, |
|
499 |
"InnoDB: Error: block %lx mem"
|
|
500 |
" field %lx len %lu\n" |
|
501 |
"InnoDB: header check field is"
|
|
502 |
" %lx but trailer %lx\n", |
|
503 |
(ulint)block, |
|
504 |
(ulint)field, len, check_field, |
|
505 |
mem_field_trailer_get_check( |
|
506 |
user_field)); |
|
507 |
||
508 |
return; |
|
509 |
}
|
|
510 |
||
511 |
/* Move to next field */
|
|
512 |
field = field + MEM_SPACE_NEEDED(len); |
|
513 |
||
514 |
if (top && (field == top)) { |
|
515 |
||
516 |
goto completed; |
|
517 |
}
|
|
518 |
||
519 |
}
|
|
520 |
||
521 |
/* At the end check that we have arrived to the first free
|
|
522 |
position */
|
|
523 |
||
524 |
if (field != (byte*)block + mem_block_get_free(block)) { |
|
525 |
/* error */
|
|
526 |
||
527 |
fprintf(stderr, |
|
528 |
"InnoDB: Error: block %lx end of"
|
|
529 |
" mem fields %lx\n" |
|
530 |
"InnoDB: but block free at %lx\n", |
|
531 |
(ulint)block, (ulint)field, |
|
532 |
(ulint)((byte*)block |
|
533 |
+ mem_block_get_free(block))); |
|
534 |
||
535 |
return; |
|
536 |
}
|
|
537 |
||
538 |
#endif
|
|
539 |
||
540 |
block = UT_LIST_GET_NEXT(list, block); |
|
541 |
block_count++; |
|
542 |
}
|
|
543 |
#ifdef UNIV_MEM_DEBUG
|
|
544 |
completed: |
|
545 |
#endif
|
|
546 |
if (us_size != NULL) { |
|
547 |
*us_size = total_len; |
|
548 |
}
|
|
549 |
if (ph_size != NULL) { |
|
550 |
*ph_size = phys_len; |
|
551 |
}
|
|
552 |
if (n_blocks != NULL) { |
|
553 |
*n_blocks = block_count; |
|
554 |
}
|
|
555 |
*error = FALSE; |
|
556 |
}
|
|
557 |
||
558 |
/******************************************************************
|
|
559 |
Prints the contents of a memory heap. */
|
|
560 |
static
|
|
561 |
void
|
|
562 |
mem_heap_print( |
|
563 |
/*===========*/
|
|
564 |
mem_heap_t* heap) /* in: memory heap */ |
|
565 |
{
|
|
566 |
ibool error; |
|
567 |
ulint us_size; |
|
568 |
ulint phys_size; |
|
569 |
ulint n_blocks; |
|
570 |
||
571 |
ut_ad(mem_heap_check(heap)); |
|
572 |
||
573 |
mem_heap_validate_or_print(heap, NULL, TRUE, &error, |
|
574 |
&us_size, &phys_size, &n_blocks); |
|
575 |
fprintf(stderr, |
|
576 |
"\nheap type: %lu; size: user size %lu;" |
|
577 |
" physical size %lu; blocks %lu.\n", |
|
578 |
(ulong) heap->type, (ulong) us_size, |
|
579 |
(ulong) phys_size, (ulong) n_blocks); |
|
580 |
ut_a(!error); |
|
581 |
}
|
|
582 |
||
583 |
/******************************************************************
|
|
584 |
Validates the contents of a memory heap. */
|
|
585 |
||
586 |
ibool
|
|
587 |
mem_heap_validate( |
|
588 |
/*==============*/
|
|
589 |
/* out: TRUE if ok */
|
|
590 |
mem_heap_t* heap) /* in: memory heap */ |
|
591 |
{
|
|
592 |
ibool error; |
|
593 |
ulint us_size; |
|
594 |
ulint phys_size; |
|
595 |
ulint n_blocks; |
|
596 |
||
597 |
ut_ad(mem_heap_check(heap)); |
|
598 |
||
599 |
mem_heap_validate_or_print(heap, NULL, FALSE, &error, &us_size, |
|
600 |
&phys_size, &n_blocks); |
|
601 |
if (error) { |
|
602 |
mem_heap_print(heap); |
|
603 |
}
|
|
604 |
||
605 |
ut_a(!error); |
|
606 |
||
607 |
return(TRUE); |
|
608 |
}
|
|
609 |
#endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */ |
|
610 |
||
611 |
#ifdef UNIV_DEBUG
|
|
612 |
/******************************************************************
|
|
613 |
Checks that an object is a memory heap (or a block of it). */
|
|
614 |
||
615 |
ibool
|
|
616 |
mem_heap_check( |
|
617 |
/*===========*/
|
|
618 |
/* out: TRUE if ok */
|
|
619 |
mem_heap_t* heap) /* in: memory heap */ |
|
620 |
{
|
|
621 |
ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N); |
|
622 |
||
623 |
return(TRUE); |
|
624 |
}
|
|
625 |
#endif /* UNIV_DEBUG */ |
|
626 |
||
627 |
#ifdef UNIV_MEM_DEBUG
|
|
628 |
/*********************************************************************
|
|
629 |
TRUE if no memory is currently allocated. */
|
|
630 |
||
631 |
ibool
|
|
632 |
mem_all_freed(void) |
|
633 |
/*===============*/
|
|
634 |
/* out: TRUE if no heaps exist */
|
|
635 |
{
|
|
636 |
mem_hash_node_t* node; |
|
637 |
ulint heap_count = 0; |
|
638 |
ulint i; |
|
639 |
||
640 |
mem_validate(); |
|
641 |
||
642 |
mutex_enter(&mem_hash_mutex); |
|
643 |
||
644 |
for (i = 0; i < MEM_HASH_SIZE; i++) { |
|
645 |
||
646 |
node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i)); |
|
647 |
while (node != NULL) { |
|
648 |
heap_count++; |
|
649 |
node = UT_LIST_GET_NEXT(list, node); |
|
650 |
}
|
|
651 |
}
|
|
652 |
||
653 |
mutex_exit(&mem_hash_mutex); |
|
654 |
||
655 |
if (heap_count == 0) { |
|
656 |
||
657 |
ut_a(mem_pool_get_reserved(mem_comm_pool) == 0); |
|
658 |
||
659 |
return(TRUE); |
|
660 |
} else { |
|
661 |
return(FALSE); |
|
662 |
}
|
|
663 |
}
|
|
664 |
||
665 |
/*********************************************************************
|
|
666 |
Validates the dynamic memory allocation system. */
|
|
667 |
||
668 |
ibool
|
|
669 |
mem_validate_no_assert(void) |
|
670 |
/*========================*/
|
|
671 |
/* out: TRUE if error */
|
|
672 |
{
|
|
673 |
mem_hash_node_t* node; |
|
674 |
ulint n_heaps = 0; |
|
675 |
ulint allocated_mem; |
|
676 |
ulint ph_size; |
|
677 |
ulint total_allocated_mem = 0; |
|
678 |
ibool error = FALSE; |
|
679 |
ulint n_blocks; |
|
680 |
ulint i; |
|
681 |
||
682 |
mem_pool_validate(mem_comm_pool); |
|
683 |
||
684 |
mutex_enter(&mem_hash_mutex); |
|
685 |
||
686 |
for (i = 0; i < MEM_HASH_SIZE; i++) { |
|
687 |
||
688 |
node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i)); |
|
689 |
||
690 |
while (node != NULL) { |
|
691 |
n_heaps++; |
|
692 |
||
693 |
mem_heap_validate_or_print(node->heap, NULL, |
|
694 |
FALSE, &error, |
|
695 |
&allocated_mem, |
|
696 |
&ph_size, &n_blocks); |
|
697 |
||
698 |
if (error) { |
|
699 |
fprintf(stderr, |
|
700 |
"\nERROR!!!!!!!!!!!!!!!!!!!" |
|
701 |
"!!!!!!!!!!!!!!!!!!!!!!!\n\n" |
|
702 |
"Inconsistency in memory heap"
|
|
703 |
" or buffer created\n" |
|
704 |
"in %s line %lu.\n", |
|
705 |
node->file_name, node->line); |
|
706 |
||
707 |
mutex_exit(&mem_hash_mutex); |
|
708 |
||
709 |
return(TRUE); |
|
710 |
}
|
|
711 |
||
712 |
total_allocated_mem += allocated_mem; |
|
713 |
node = UT_LIST_GET_NEXT(list, node); |
|
714 |
}
|
|
715 |
}
|
|
716 |
||
717 |
if ((n_heaps == 0) && (mem_current_allocated_memory != 0)) { |
|
718 |
error = TRUE; |
|
719 |
}
|
|
720 |
||
721 |
if (mem_total_allocated_memory < mem_current_allocated_memory) { |
|
722 |
error = TRUE; |
|
723 |
}
|
|
724 |
||
725 |
if (mem_max_allocated_memory > mem_total_allocated_memory) { |
|
726 |
error = TRUE; |
|
727 |
}
|
|
728 |
||
729 |
if (mem_n_created_heaps < n_heaps) { |
|
730 |
error = TRUE; |
|
731 |
}
|
|
732 |
||
733 |
mutex_exit(&mem_hash_mutex); |
|
734 |
||
735 |
return(error); |
|
736 |
}
|
|
737 |
||
738 |
/****************************************************************
|
|
739 |
Validates the dynamic memory */
|
|
740 |
||
741 |
ibool
|
|
742 |
mem_validate(void) |
|
743 |
/*==============*/
|
|
744 |
/* out: TRUE if ok */
|
|
745 |
{
|
|
746 |
ut_a(!mem_validate_no_assert()); |
|
747 |
||
748 |
return(TRUE); |
|
749 |
}
|
|
750 |
#endif /* UNIV_MEM_DEBUG */ |
|
751 |
||
752 |
/****************************************************************
|
|
753 |
Tries to find neigboring memory allocation blocks and dumps to stderr
|
|
754 |
the neighborhood of a given pointer. */
|
|
755 |
||
756 |
void
|
|
757 |
mem_analyze_corruption( |
|
758 |
/*===================*/
|
|
759 |
void* ptr) /* in: pointer to place of possible corruption */ |
|
760 |
{
|
|
761 |
byte* p; |
|
762 |
ulint i; |
|
763 |
ulint dist; |
|
764 |
||
765 |
fputs("InnoDB: Apparent memory corruption: mem dump ", stderr); |
|
766 |
ut_print_buf(stderr, (byte*)ptr - 250, 500); |
|
767 |
||
768 |
fputs("\nInnoDB: Scanning backward trying to find" |
|
769 |
" previous allocated mem blocks\n", stderr); |
|
770 |
||
771 |
p = (byte*)ptr; |
|
772 |
dist = 0; |
|
773 |
||
774 |
for (i = 0; i < 10; i++) { |
|
775 |
for (;;) { |
|
776 |
if (((ulint)p) % 4 == 0) { |
|
777 |
||
778 |
if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) { |
|
779 |
fprintf(stderr, |
|
780 |
"Mem block at - %lu,"
|
|
781 |
" file %s, line %lu\n", |
|
782 |
(ulong) dist, |
|
783 |
(p + sizeof(ulint)), |
|
784 |
(ulong) |
|
785 |
(*(ulint*)(p + 8 |
|
786 |
+ sizeof(ulint)))); |
|
787 |
||
788 |
break; |
|
789 |
}
|
|
790 |
||
791 |
if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) { |
|
792 |
fprintf(stderr, |
|
793 |
"Freed mem block at - %lu,"
|
|
794 |
" file %s, line %lu\n", |
|
795 |
(ulong) dist, |
|
796 |
(p + sizeof(ulint)), |
|
797 |
(ulong) |
|
798 |
(*(ulint*)(p + 8 |
|
799 |
+ sizeof(ulint)))); |
|
800 |
||
801 |
break; |
|
802 |
}
|
|
803 |
}
|
|
804 |
||
805 |
p--; |
|
806 |
dist++; |
|
807 |
}
|
|
808 |
||
809 |
p--; |
|
810 |
dist++; |
|
811 |
}
|
|
812 |
||
813 |
fprintf(stderr, |
|
814 |
"InnoDB: Scanning forward trying to find next"
|
|
815 |
" allocated mem blocks\n"); |
|
816 |
||
817 |
p = (byte*)ptr; |
|
818 |
dist = 0; |
|
819 |
||
820 |
for (i = 0; i < 10; i++) { |
|
821 |
for (;;) { |
|
822 |
if (((ulint)p) % 4 == 0) { |
|
823 |
||
824 |
if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) { |
|
825 |
fprintf(stderr, |
|
826 |
"Mem block at + %lu, file %s,"
|
|
827 |
" line %lu\n", |
|
828 |
(ulong) dist, |
|
829 |
(p + sizeof(ulint)), |
|
830 |
(ulong) |
|
831 |
(*(ulint*)(p + 8 |
|
832 |
+ sizeof(ulint)))); |
|
833 |
||
834 |
break; |
|
835 |
}
|
|
836 |
||
837 |
if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) { |
|
838 |
fprintf(stderr, |
|
839 |
"Freed mem block at + %lu,"
|
|
840 |
" file %s, line %lu\n", |
|
841 |
(ulong) dist, |
|
842 |
(p + sizeof(ulint)), |
|
843 |
(ulong) |
|
844 |
(*(ulint*)(p + 8 |
|
845 |
+ sizeof(ulint)))); |
|
846 |
||
847 |
break; |
|
848 |
}
|
|
849 |
}
|
|
850 |
||
851 |
p++; |
|
852 |
dist++; |
|
853 |
}
|
|
854 |
||
855 |
p++; |
|
856 |
dist++; |
|
857 |
}
|
|
858 |
}
|
|
859 |
||
860 |
/*********************************************************************
|
|
861 |
Prints information of dynamic memory usage and currently allocated
|
|
862 |
memory heaps or buffers. Can only be used in the debug version. */
|
|
863 |
static
|
|
864 |
void
|
|
865 |
mem_print_info_low( |
|
866 |
/*===============*/
|
|
867 |
ibool print_all) /* in: if TRUE, all heaps are printed, |
|
868 |
else only the heaps allocated after the
|
|
869 |
previous call of this function */
|
|
870 |
{
|
|
871 |
#ifdef UNIV_MEM_DEBUG
|
|
872 |
mem_hash_node_t* node; |
|
873 |
ulint n_heaps = 0; |
|
874 |
ulint allocated_mem; |
|
875 |
ulint ph_size; |
|
876 |
ulint total_allocated_mem = 0; |
|
877 |
ibool error; |
|
878 |
ulint n_blocks; |
|
879 |
#endif
|
|
880 |
FILE* outfile; |
|
881 |
||
882 |
/* outfile = fopen("ibdebug", "a"); */
|
|
883 |
||
884 |
outfile = stdout; |
|
885 |
||
886 |
fprintf(outfile, "\n"); |
|
887 |
fprintf(outfile, |
|
888 |
"________________________________________________________\n"); |
|
889 |
fprintf(outfile, "MEMORY ALLOCATION INFORMATION\n\n"); |
|
890 |
||
891 |
#ifndef UNIV_MEM_DEBUG
|
|
892 |
||
893 |
UT_NOT_USED(print_all); |
|
894 |
||
895 |
mem_pool_print_info(outfile, mem_comm_pool); |
|
896 |
||
897 |
fprintf(outfile, |
|
898 |
"Sorry, non-debug version cannot give more memory info\n"); |
|
899 |
||
900 |
/* fclose(outfile); */
|
|
901 |
||
902 |
return; |
|
903 |
#else
|
|
904 |
mutex_enter(&mem_hash_mutex); |
|
905 |
||
906 |
fprintf(outfile, "LIST OF CREATED HEAPS AND ALLOCATED BUFFERS: \n\n"); |
|
907 |
||
908 |
if (!print_all) { |
|
909 |
fprintf(outfile, "AFTER THE LAST PRINT INFO\n"); |
|
910 |
}
|
|
911 |
||
912 |
node = UT_LIST_GET_FIRST(mem_all_list_base); |
|
913 |
||
914 |
while (node != NULL) { |
|
915 |
n_heaps++; |
|
916 |
||
917 |
if (!print_all && node->nth_heap < mem_last_print_info) { |
|
918 |
||
919 |
goto next_heap; |
|
920 |
}
|
|
921 |
||
922 |
mem_heap_validate_or_print(node->heap, NULL, |
|
923 |
FALSE, &error, &allocated_mem, |
|
924 |
&ph_size, &n_blocks); |
|
925 |
total_allocated_mem += allocated_mem; |
|
926 |
||
927 |
fprintf(outfile, |
|
928 |
"%lu: file %s line %lu of size %lu phys.size %lu"
|
|
929 |
" with %lu blocks, type %lu\n", |
|
930 |
node->nth_heap, node->file_name, node->line, |
|
931 |
allocated_mem, ph_size, n_blocks, |
|
932 |
(node->heap)->type); |
|
933 |
next_heap: |
|
934 |
node = UT_LIST_GET_NEXT(all_list, node); |
|
935 |
}
|
|
936 |
||
937 |
fprintf(outfile, "\n"); |
|
938 |
||
939 |
fprintf(outfile, "Current allocated memory : %lu\n", |
|
940 |
mem_current_allocated_memory); |
|
941 |
fprintf(outfile, "Current allocated heaps and buffers : %lu\n", |
|
942 |
n_heaps); |
|
943 |
fprintf(outfile, "Cumulative allocated memory : %lu\n", |
|
944 |
mem_total_allocated_memory); |
|
945 |
fprintf(outfile, "Maximum allocated memory : %lu\n", |
|
946 |
mem_max_allocated_memory); |
|
947 |
fprintf(outfile, "Cumulative created heaps and buffers : %lu\n", |
|
948 |
mem_n_created_heaps); |
|
949 |
fprintf(outfile, "Cumulative number of allocations : %lu\n", |
|
950 |
mem_n_allocations); |
|
951 |
||
952 |
mem_last_print_info = mem_n_created_heaps; |
|
953 |
||
954 |
mutex_exit(&mem_hash_mutex); |
|
955 |
||
956 |
mem_pool_print_info(outfile, mem_comm_pool); |
|
957 |
||
958 |
/* mem_validate(); */
|
|
959 |
||
960 |
/* fclose(outfile); */
|
|
961 |
#endif
|
|
962 |
}
|
|
963 |
||
964 |
/*********************************************************************
|
|
965 |
Prints information of dynamic memory usage and currently allocated memory
|
|
966 |
heaps or buffers. Can only be used in the debug version. */
|
|
967 |
||
968 |
void
|
|
969 |
mem_print_info(void) |
|
970 |
/*================*/
|
|
971 |
{
|
|
972 |
mem_print_info_low(TRUE); |
|
973 |
}
|
|
974 |
||
975 |
/*********************************************************************
|
|
976 |
Prints information of dynamic memory usage and currently allocated memory
|
|
977 |
heaps or buffers since the last ..._print_info or..._print_new_info. */
|
|
978 |
||
979 |
void
|
|
980 |
mem_print_new_info(void) |
|
981 |
/*====================*/
|
|
982 |
{
|
|
983 |
mem_print_info_low(FALSE); |
|
984 |
}
|