#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "dwarf_incl.h"
#include "malloc_check.h"
#ifdef WANT_LIBBDWARF_MALLOC_CHECK
#define PRINT_MALLOC_DETAILS 0
#define MC_TYPE_UNKNOWN 0
#define MC_TYPE_ALLOC 1
#define MC_TYPE_DEALLOC 2
struct mc_data_s {
struct mc_data_s *mc_prev;
unsigned long mc_address;
long mc_alloc_number;
unsigned char mc_alloc_code;
unsigned char mc_type;
unsigned char mc_dealloc_noted;
unsigned char mc_dealloc_noted_count;
};
#define HASH_TABLE_SIZE 10501
static struct mc_data_s *mc_data_hash[HASH_TABLE_SIZE];
static long mc_data_list_size = 0;
static char *alloc_type_name[MAX_DW_DLA + 1] = {
"",
"DW_DLA_STRING",
"DW_DLA_LOC",
"DW_DLA_LOCDESC",
"DW_DLA_ELLIST",
"DW_DLA_BOUNDS",
"DW_DLA_BLOCK",
"DW_DLA_DEBUG",
"DW_DLA_DIE",
"DW_DLA_LINE",
"DW_DLA_ATTR",
"DW_DLA_TYPE",
"DW_DLA_SUBSCR",
"DW_DLA_GLOBAL",
"DW_DLA_ERROR",
"DW_DLA_LIST",
"DW_DLA_LINEBUF",
"DW_DLA_ARANGE",
"DW_DLA_ABBREV",
"DW_DLA_FRAME_OP",
"DW_DLA_CIE",
"DW_DLA_FDE",
"DW_DLA_LOC_BLOCK",
"DW_DLA_FRAME_BLOCK",
"DW_DLA_FUNC",
"DW_DLA_TYPENAME",
"DW_DLA_VAR",
"DW_DLA_WEAK",
"DW_DLA_ADDR",
"DW_DLA_ABBREV_LIST",
"DW_DLA_CHAIN",
"DW_DLA_CU_CONTEXT",
"DW_DLA_FRAME",
"DW_DLA_GLOBAL_CONTEXT",
"DW_DLA_FILE_ENTRY",
"DW_DLA_LINE_CONTEXT",
"DW_DLA_LOC_CHAIN",
"DW_DLA_HASH_TABLE",
"DW_DLA_FUNC_CONTEXT",
"DW_DLA_TYPENAME_CONTEXT",
"DW_DLA_VAR_CONTEXT",
"DW_DLA_WEAK_CONTEXT",
"DW_DLA_PUBTYPES_CONTEXT"
};
static unsigned
hash_address(unsigned long addr)
{
unsigned long a = addr >> 2;
return a % HASH_TABLE_SIZE;
}
#if PRINT_MALLOC_DETAILS
static void
print_alloc_dealloc_detail(unsigned long addr,
int code, char *whichisit)
{
fprintf(stderr,
"%s addr 0x%lx code %d (%s) entry %ld\n",
whichisit, addr, code, alloc_type_name[code],
mc_data_list_size);
}
#else
#define print_alloc_dealloc_detail(a,b,c)
#endif
static void *
newone(void)
{
struct mc_data_s *newd = malloc(sizeof(struct mc_data_s));
if (newd == 0) {
fprintf(stderr, "out of memory , # %ld\n", mc_data_list_size);
exit(1);
}
memset(newd, 0, sizeof(struct mc_data_s));
return newd;
}
void
dwarf_malloc_check_alloc_data(void *addr_in, unsigned char code)
{
struct mc_data_s *newd = newone();
unsigned long addr = (unsigned long) addr_in;
struct mc_data_s **base = &mc_data_hash[hash_address(addr)];
print_alloc_dealloc_detail(addr, code, "alloc ");
newd->mc_address = addr;
newd->mc_alloc_code = code;
newd->mc_type = MC_TYPE_ALLOC;
newd->mc_alloc_number = mc_data_list_size;
newd->mc_prev = *base;
*base = newd;
newd->mc_alloc_number = mc_data_list_size;
mc_data_list_size += 1;
}
static void
print_entry(char *msg, struct mc_data_s *data)
{
fprintf(stderr,
"%s: 0x%08lx code %2d (%s) type %s dealloc noted %u ct %u\n",
msg,
(long) data->mc_address,
data->mc_alloc_code,
alloc_type_name[data->mc_alloc_code],
(data->mc_type == MC_TYPE_ALLOC) ? "alloc " :
(data->mc_type == MC_TYPE_DEALLOC) ? "dealloc" : "unknown",
(unsigned) data->mc_dealloc_noted,
(unsigned) data->mc_dealloc_noted_count);
}
static long
balanced_by_alloc_p(struct mc_data_s *newd,
long *addr_match_num,
struct mc_data_s **addr_match,
struct mc_data_s *base)
{
struct mc_data_s *cur = base;
for (; cur; cur = cur->mc_prev) {
if (cur->mc_address == newd->mc_address) {
if (cur->mc_type == MC_TYPE_ALLOC) {
if (cur->mc_alloc_code == newd->mc_alloc_code) {
*addr_match = cur;
*addr_match_num = cur->mc_alloc_number;
return cur->mc_alloc_number;
} else {
*addr_match = cur;
*addr_match_num = cur->mc_alloc_number;
return -1;
}
} else {
*addr_match = cur;
*addr_match_num = cur->mc_alloc_number;
return -1;
}
}
}
return -1;
}
void
dwarf_malloc_check_dealloc_data(void *addr_in, unsigned char code)
{
struct mc_data_s *newd = newone();
long prev;
long addr_match_num = -1;
struct mc_data_s *addr_match = 0;
unsigned long addr = (unsigned long) addr_in;
struct mc_data_s **base = &mc_data_hash[hash_address(addr)];
print_alloc_dealloc_detail(addr, code, "dealloc ");
newd->mc_address = (unsigned long) addr;
newd->mc_alloc_code = code;
newd->mc_type = MC_TYPE_DEALLOC;
newd->mc_prev = *base;
prev =
balanced_by_alloc_p(newd, &addr_match_num, &addr_match, *base);
if (prev < 0) {
fprintf(stderr,
"Unbalanced dealloc at index %ld\n", mc_data_list_size);
print_entry("new", newd);
fprintf(stderr, "addr-match_num? %ld\n", addr_match_num);
if (addr_match) {
print_entry("prev entry", addr_match);
if (addr_match->mc_dealloc_noted > 1) {
fprintf(stderr, "Above is Duplicate dealloc!\n");
}
}
abort();
exit(3);
}
addr_match->mc_dealloc_noted = 1;
addr_match->mc_dealloc_noted_count += 1;
if (addr_match->mc_dealloc_noted_count > 1) {
fprintf(stderr, "Double dealloc entry %ld\n", addr_match_num);
print_entry("new dealloc entry", newd);
print_entry("bad alloc entry", addr_match);
}
*base = newd;
mc_data_list_size += 1;
}
void
dwarf_malloc_check_complete(char *msg)
{
long i = 0;
long total = mc_data_list_size;
long hash_slots_used = 0;
long max_chain_length = 0;
fprintf(stderr, "Run complete, %s. %ld entries\n", msg, total);
for (; i < HASH_TABLE_SIZE; ++i) {
struct mc_data_s *cur = mc_data_hash[i];
long cur_chain_length = 0;
if (cur == 0)
continue;
++hash_slots_used;
for (; cur; cur = cur->mc_prev) {
++cur_chain_length;
if (cur->mc_type == MC_TYPE_ALLOC) {
if (cur->mc_dealloc_noted) {
if (cur->mc_dealloc_noted > 1) {
fprintf(stderr,
" Duplicate dealloc! entry %ld\n",
cur->mc_alloc_number);
print_entry("duplicate dealloc", cur);
}
continue;
} else {
fprintf(stderr, "malloc no dealloc, entry %ld\n",
cur->mc_alloc_number);
print_entry("dangle", cur);
}
} else {
}
}
if (cur_chain_length > max_chain_length) {
max_chain_length = cur_chain_length;
}
}
fprintf(stderr, "mc hash table slots=%ld, "
"used=%ld, maxchain=%ld\n",
(long) HASH_TABLE_SIZE, hash_slots_used, max_chain_length);
return;
}
#endif