107dc1947SRichard Lowe /*
207dc1947SRichard Lowe
307dc1947SRichard Lowe Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved.
407dc1947SRichard Lowe
507dc1947SRichard Lowe This program is free software; you can redistribute it and/or modify it
607dc1947SRichard Lowe under the terms of version 2.1 of the GNU Lesser General Public License
707dc1947SRichard Lowe as published by the Free Software Foundation.
807dc1947SRichard Lowe
907dc1947SRichard Lowe This program is distributed in the hope that it would be useful, but
1007dc1947SRichard Lowe WITHOUT ANY WARRANTY; without even the implied warranty of
1107dc1947SRichard Lowe MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1207dc1947SRichard Lowe
1307dc1947SRichard Lowe Further, this software is distributed without any warranty that it is
1407dc1947SRichard Lowe free of the rightful claim of any third person regarding infringement
1507dc1947SRichard Lowe or the like. Any license provided herein, whether implied or
1607dc1947SRichard Lowe otherwise, applies only to this software file. Patent licenses, if
1707dc1947SRichard Lowe any, provided herein do not apply to combinations of this program with
1807dc1947SRichard Lowe other software, or any other product whatsoever.
1907dc1947SRichard Lowe
2007dc1947SRichard Lowe You should have received a copy of the GNU Lesser General Public
2107dc1947SRichard Lowe License along with this program; if not, write the Free Software
2207dc1947SRichard Lowe Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301,
2307dc1947SRichard Lowe USA.
2407dc1947SRichard Lowe
2507dc1947SRichard Lowe */
2607dc1947SRichard Lowe
27*4d9fdb46SRobert Mustacchi /* malloc_check.c For checking dealloc completeness.
2807dc1947SRichard Lowe
2907dc1947SRichard Lowe This code is as simple as possible and works ok for
3007dc1947SRichard Lowe reasonable size allocation counts.
3107dc1947SRichard Lowe
3207dc1947SRichard Lowe It treats allocation as global, and so will not
3307dc1947SRichard Lowe work very well if an application opens more than one
3407dc1947SRichard Lowe Dwarf_Debug.
3507dc1947SRichard Lowe
3607dc1947SRichard Lowe */
3707dc1947SRichard Lowe
3807dc1947SRichard Lowe #include <stdio.h>
39*4d9fdb46SRobert Mustacchi #ifdef HAVE_STDLIB_H
40*4d9fdb46SRobert Mustacchi #include <stdlib.h>
41*4d9fdb46SRobert Mustacchi #endif /* HAVE_STDLIB_H */
42*4d9fdb46SRobert Mustacchi #ifdef HAVE_MALLOC_H
43*4d9fdb46SRobert Mustacchi /* Useful include for some Windows compilers. */
44*4d9fdb46SRobert Mustacchi #include <malloc.h>
45*4d9fdb46SRobert Mustacchi #endif /* HAVE_MALLOC_H */
4607dc1947SRichard Lowe #include "config.h"
4707dc1947SRichard Lowe #include "dwarf_incl.h"
4807dc1947SRichard Lowe #include "malloc_check.h"
4907dc1947SRichard Lowe #ifdef WANT_LIBBDWARF_MALLOC_CHECK
5007dc1947SRichard Lowe
51*4d9fdb46SRobert Mustacchi /* To turn off printing every entry, just change the define
52*4d9fdb46SRobert Mustacchi to set PRINT_MALLOC_DETAILS 0.
5307dc1947SRichard Lowe */
5407dc1947SRichard Lowe #define PRINT_MALLOC_DETAILS 0
5507dc1947SRichard Lowe
5607dc1947SRichard Lowe #define MC_TYPE_UNKNOWN 0
5707dc1947SRichard Lowe #define MC_TYPE_ALLOC 1
5807dc1947SRichard Lowe #define MC_TYPE_DEALLOC 2
5907dc1947SRichard Lowe
6007dc1947SRichard Lowe struct mc_data_s {
6107dc1947SRichard Lowe struct mc_data_s *mc_prev;
62*4d9fdb46SRobert Mustacchi unsigned long mc_address; /* Assumes this is large enough to hold
63*4d9fdb46SRobert Mustacchi a pointer! */
6407dc1947SRichard Lowe
6507dc1947SRichard Lowe long mc_alloc_number; /* Assigned in order by when record
66*4d9fdb46SRobert Mustacchi created. */
6707dc1947SRichard Lowe unsigned char mc_alloc_code; /* Allocation code, libdwarf. */
6807dc1947SRichard Lowe unsigned char mc_type;
6907dc1947SRichard Lowe unsigned char mc_dealloc_noted; /* Used on an ALLOC node. */
7007dc1947SRichard Lowe unsigned char mc_dealloc_noted_count; /* Used on an ALLOC
71*4d9fdb46SRobert Mustacchi node. */
7207dc1947SRichard Lowe };
7307dc1947SRichard Lowe
74*4d9fdb46SRobert Mustacchi /*
75*4d9fdb46SRobert Mustacchi
7607dc1947SRichard Lowe */
7707dc1947SRichard Lowe #define HASH_TABLE_SIZE 10501
7807dc1947SRichard Lowe static struct mc_data_s *mc_data_hash[HASH_TABLE_SIZE];
7907dc1947SRichard Lowe static long mc_data_list_size = 0;
8007dc1947SRichard Lowe
8107dc1947SRichard Lowe static char *alloc_type_name[MAX_DW_DLA + 1] = {
8207dc1947SRichard Lowe "",
8307dc1947SRichard Lowe "DW_DLA_STRING",
8407dc1947SRichard Lowe "DW_DLA_LOC",
8507dc1947SRichard Lowe "DW_DLA_LOCDESC",
8607dc1947SRichard Lowe "DW_DLA_ELLIST",
8707dc1947SRichard Lowe "DW_DLA_BOUNDS",
8807dc1947SRichard Lowe "DW_DLA_BLOCK",
8907dc1947SRichard Lowe "DW_DLA_DEBUG",
9007dc1947SRichard Lowe "DW_DLA_DIE",
9107dc1947SRichard Lowe "DW_DLA_LINE",
9207dc1947SRichard Lowe "DW_DLA_ATTR",
9307dc1947SRichard Lowe "DW_DLA_TYPE",
9407dc1947SRichard Lowe "DW_DLA_SUBSCR",
9507dc1947SRichard Lowe "DW_DLA_GLOBAL",
9607dc1947SRichard Lowe "DW_DLA_ERROR",
9707dc1947SRichard Lowe "DW_DLA_LIST",
9807dc1947SRichard Lowe "DW_DLA_LINEBUF",
9907dc1947SRichard Lowe "DW_DLA_ARANGE",
10007dc1947SRichard Lowe "DW_DLA_ABBREV",
10107dc1947SRichard Lowe "DW_DLA_FRAME_OP",
10207dc1947SRichard Lowe "DW_DLA_CIE",
10307dc1947SRichard Lowe "DW_DLA_FDE",
10407dc1947SRichard Lowe "DW_DLA_LOC_BLOCK",
10507dc1947SRichard Lowe "DW_DLA_FRAME_BLOCK",
10607dc1947SRichard Lowe "DW_DLA_FUNC",
10707dc1947SRichard Lowe "DW_DLA_TYPENAME",
10807dc1947SRichard Lowe "DW_DLA_VAR",
10907dc1947SRichard Lowe "DW_DLA_WEAK",
11007dc1947SRichard Lowe "DW_DLA_ADDR",
11107dc1947SRichard Lowe "DW_DLA_ABBREV_LIST",
11207dc1947SRichard Lowe "DW_DLA_CHAIN",
11307dc1947SRichard Lowe "DW_DLA_CU_CONTEXT",
11407dc1947SRichard Lowe "DW_DLA_FRAME",
11507dc1947SRichard Lowe "DW_DLA_GLOBAL_CONTEXT",
11607dc1947SRichard Lowe "DW_DLA_FILE_ENTRY",
11707dc1947SRichard Lowe "DW_DLA_LINE_CONTEXT",
11807dc1947SRichard Lowe "DW_DLA_LOC_CHAIN",
11907dc1947SRichard Lowe "DW_DLA_HASH_TABLE",
12007dc1947SRichard Lowe "DW_DLA_FUNC_CONTEXT",
12107dc1947SRichard Lowe "DW_DLA_TYPENAME_CONTEXT",
12207dc1947SRichard Lowe "DW_DLA_VAR_CONTEXT",
12307dc1947SRichard Lowe "DW_DLA_WEAK_CONTEXT",
12407dc1947SRichard Lowe "DW_DLA_PUBTYPES_CONTEXT"
125*4d9fdb46SRobert Mustacchi /* Don't forget to expand this list if the list of codes
126*4d9fdb46SRobert Mustacchi expands. */
12707dc1947SRichard Lowe };
12807dc1947SRichard Lowe
12907dc1947SRichard Lowe static unsigned
hash_address(unsigned long addr)13007dc1947SRichard Lowe hash_address(unsigned long addr)
13107dc1947SRichard Lowe {
13207dc1947SRichard Lowe unsigned long a = addr >> 2;
13307dc1947SRichard Lowe
13407dc1947SRichard Lowe return a % HASH_TABLE_SIZE;
13507dc1947SRichard Lowe }
13607dc1947SRichard Lowe
13707dc1947SRichard Lowe #if PRINT_MALLOC_DETAILS
13807dc1947SRichard Lowe static void
print_alloc_dealloc_detail(unsigned long addr,int code,char * whichisit)13907dc1947SRichard Lowe print_alloc_dealloc_detail(unsigned long addr,
140*4d9fdb46SRobert Mustacchi int code, char *whichisit)
14107dc1947SRichard Lowe {
14207dc1947SRichard Lowe fprintf(stderr,
143*4d9fdb46SRobert Mustacchi "%s addr 0x%lx code %d (%s) entry %ld\n",
144*4d9fdb46SRobert Mustacchi whichisit, addr, code, alloc_type_name[code],
145*4d9fdb46SRobert Mustacchi mc_data_list_size);
14607dc1947SRichard Lowe }
14707dc1947SRichard Lowe #else
14807dc1947SRichard Lowe #define print_alloc_dealloc_detail(a,b,c) /* nothing */
14907dc1947SRichard Lowe #endif
15007dc1947SRichard Lowe
15107dc1947SRichard Lowe /* Create a zeroed struct or die. */
15207dc1947SRichard Lowe static void *
newone(void)15307dc1947SRichard Lowe newone(void)
15407dc1947SRichard Lowe {
15507dc1947SRichard Lowe struct mc_data_s *newd = malloc(sizeof(struct mc_data_s));
15607dc1947SRichard Lowe
15707dc1947SRichard Lowe if (newd == 0) {
15807dc1947SRichard Lowe fprintf(stderr, "out of memory , # %ld\n", mc_data_list_size);
15907dc1947SRichard Lowe exit(1);
16007dc1947SRichard Lowe }
16107dc1947SRichard Lowe memset(newd, 0, sizeof(struct mc_data_s));
16207dc1947SRichard Lowe return newd;
16307dc1947SRichard Lowe }
16407dc1947SRichard Lowe
16507dc1947SRichard Lowe /* Notify checker that get_alloc has allocated user data. */
16607dc1947SRichard Lowe void
dwarf_malloc_check_alloc_data(void * addr_in,unsigned char code)16707dc1947SRichard Lowe dwarf_malloc_check_alloc_data(void *addr_in, unsigned char code)
16807dc1947SRichard Lowe {
16907dc1947SRichard Lowe struct mc_data_s *newd = newone();
17007dc1947SRichard Lowe unsigned long addr = (unsigned long) addr_in;
17107dc1947SRichard Lowe struct mc_data_s **base = &mc_data_hash[hash_address(addr)];
17207dc1947SRichard Lowe
17307dc1947SRichard Lowe print_alloc_dealloc_detail(addr, code, "alloc ");
17407dc1947SRichard Lowe newd->mc_address = addr;
17507dc1947SRichard Lowe newd->mc_alloc_code = code;
17607dc1947SRichard Lowe newd->mc_type = MC_TYPE_ALLOC;
17707dc1947SRichard Lowe newd->mc_alloc_number = mc_data_list_size;
17807dc1947SRichard Lowe newd->mc_prev = *base;
17907dc1947SRichard Lowe *base = newd;
18007dc1947SRichard Lowe newd->mc_alloc_number = mc_data_list_size;
18107dc1947SRichard Lowe mc_data_list_size += 1;
18207dc1947SRichard Lowe }
18307dc1947SRichard Lowe
18407dc1947SRichard Lowe static void
print_entry(char * msg,struct mc_data_s * data)18507dc1947SRichard Lowe print_entry(char *msg, struct mc_data_s *data)
18607dc1947SRichard Lowe {
18707dc1947SRichard Lowe fprintf(stderr,
188*4d9fdb46SRobert Mustacchi "%s: 0x%08lx code %2d (%s) type %s dealloc noted %u ct %u\n",
189*4d9fdb46SRobert Mustacchi msg,
190*4d9fdb46SRobert Mustacchi (long) data->mc_address,
191*4d9fdb46SRobert Mustacchi data->mc_alloc_code,
192*4d9fdb46SRobert Mustacchi alloc_type_name[data->mc_alloc_code],
193*4d9fdb46SRobert Mustacchi (data->mc_type == MC_TYPE_ALLOC) ? "alloc " :
194*4d9fdb46SRobert Mustacchi (data->mc_type == MC_TYPE_DEALLOC) ? "dealloc" : "unknown",
195*4d9fdb46SRobert Mustacchi (unsigned) data->mc_dealloc_noted,
196*4d9fdb46SRobert Mustacchi (unsigned) data->mc_dealloc_noted_count);
19707dc1947SRichard Lowe }
19807dc1947SRichard Lowe
19907dc1947SRichard Lowe /* newd is a 'dealloc'.
20007dc1947SRichard Lowe */
20107dc1947SRichard Lowe 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)20207dc1947SRichard Lowe balanced_by_alloc_p(struct mc_data_s *newd,
203*4d9fdb46SRobert Mustacchi long *addr_match_num,
204*4d9fdb46SRobert Mustacchi struct mc_data_s **addr_match,
205*4d9fdb46SRobert Mustacchi struct mc_data_s *base)
20607dc1947SRichard Lowe {
20707dc1947SRichard Lowe struct mc_data_s *cur = base;
20807dc1947SRichard Lowe
20907dc1947SRichard Lowe for (; cur; cur = cur->mc_prev) {
21007dc1947SRichard Lowe if (cur->mc_address == newd->mc_address) {
21107dc1947SRichard Lowe if (cur->mc_type == MC_TYPE_ALLOC) {
21207dc1947SRichard Lowe if (cur->mc_alloc_code == newd->mc_alloc_code) {
21307dc1947SRichard Lowe *addr_match = cur;
21407dc1947SRichard Lowe *addr_match_num = cur->mc_alloc_number;
21507dc1947SRichard Lowe return cur->mc_alloc_number;
21607dc1947SRichard Lowe } else {
21707dc1947SRichard Lowe /* code mismatch */
21807dc1947SRichard Lowe *addr_match = cur;
21907dc1947SRichard Lowe *addr_match_num = cur->mc_alloc_number;
22007dc1947SRichard Lowe return -1;
22107dc1947SRichard Lowe }
22207dc1947SRichard Lowe } else {
22307dc1947SRichard Lowe /* Unbalanced new/del */
22407dc1947SRichard Lowe *addr_match = cur;
22507dc1947SRichard Lowe *addr_match_num = cur->mc_alloc_number;
22607dc1947SRichard Lowe return -1;
22707dc1947SRichard Lowe }
22807dc1947SRichard Lowe }
22907dc1947SRichard Lowe }
23007dc1947SRichard Lowe return -1;
23107dc1947SRichard Lowe }
23207dc1947SRichard Lowe
23307dc1947SRichard Lowe /* A dealloc is to take place. Ensure it balances an alloc.
23407dc1947SRichard Lowe */
23507dc1947SRichard Lowe void
dwarf_malloc_check_dealloc_data(void * addr_in,unsigned char code)23607dc1947SRichard Lowe dwarf_malloc_check_dealloc_data(void *addr_in, unsigned char code)
23707dc1947SRichard Lowe {
23807dc1947SRichard Lowe struct mc_data_s *newd = newone();
23907dc1947SRichard Lowe long prev;
24007dc1947SRichard Lowe long addr_match_num = -1;
24107dc1947SRichard Lowe struct mc_data_s *addr_match = 0;
24207dc1947SRichard Lowe unsigned long addr = (unsigned long) addr_in;
24307dc1947SRichard Lowe struct mc_data_s **base = &mc_data_hash[hash_address(addr)];
24407dc1947SRichard Lowe
24507dc1947SRichard Lowe
24607dc1947SRichard Lowe print_alloc_dealloc_detail(addr, code, "dealloc ");
24707dc1947SRichard Lowe newd->mc_address = (unsigned long) addr;
24807dc1947SRichard Lowe newd->mc_alloc_code = code;
24907dc1947SRichard Lowe newd->mc_type = MC_TYPE_DEALLOC;
25007dc1947SRichard Lowe newd->mc_prev = *base;
25107dc1947SRichard Lowe prev =
25207dc1947SRichard Lowe balanced_by_alloc_p(newd, &addr_match_num, &addr_match, *base);
25307dc1947SRichard Lowe if (prev < 0) {
25407dc1947SRichard Lowe fprintf(stderr,
255*4d9fdb46SRobert Mustacchi "Unbalanced dealloc at index %ld\n", mc_data_list_size);
25607dc1947SRichard Lowe print_entry("new", newd);
25707dc1947SRichard Lowe fprintf(stderr, "addr-match_num? %ld\n", addr_match_num);
25807dc1947SRichard Lowe if (addr_match) {
25907dc1947SRichard Lowe print_entry("prev entry", addr_match);
26007dc1947SRichard Lowe if (addr_match->mc_dealloc_noted > 1) {
26107dc1947SRichard Lowe fprintf(stderr, "Above is Duplicate dealloc!\n");
26207dc1947SRichard Lowe }
26307dc1947SRichard Lowe }
26407dc1947SRichard Lowe abort();
26507dc1947SRichard Lowe exit(3);
26607dc1947SRichard Lowe }
26707dc1947SRichard Lowe addr_match->mc_dealloc_noted = 1;
26807dc1947SRichard Lowe addr_match->mc_dealloc_noted_count += 1;
26907dc1947SRichard Lowe if (addr_match->mc_dealloc_noted_count > 1) {
27007dc1947SRichard Lowe fprintf(stderr, "Double dealloc entry %ld\n", addr_match_num);
27107dc1947SRichard Lowe print_entry("new dealloc entry", newd);
27207dc1947SRichard Lowe print_entry("bad alloc entry", addr_match);
27307dc1947SRichard Lowe }
27407dc1947SRichard Lowe *base = newd;
27507dc1947SRichard Lowe mc_data_list_size += 1;
27607dc1947SRichard Lowe }
27707dc1947SRichard Lowe
27807dc1947SRichard Lowe /* Final check for leaks.
27907dc1947SRichard Lowe */
28007dc1947SRichard Lowe void
dwarf_malloc_check_complete(char * msg)28107dc1947SRichard Lowe dwarf_malloc_check_complete(char *msg)
28207dc1947SRichard Lowe {
28307dc1947SRichard Lowe long i = 0;
28407dc1947SRichard Lowe long total = mc_data_list_size;
28507dc1947SRichard Lowe long hash_slots_used = 0;
28607dc1947SRichard Lowe long max_chain_length = 0;
28707dc1947SRichard Lowe
28807dc1947SRichard Lowe fprintf(stderr, "Run complete, %s. %ld entries\n", msg, total);
28907dc1947SRichard Lowe for (; i < HASH_TABLE_SIZE; ++i) {
29007dc1947SRichard Lowe struct mc_data_s *cur = mc_data_hash[i];
29107dc1947SRichard Lowe long cur_chain_length = 0;
29207dc1947SRichard Lowe
29307dc1947SRichard Lowe if (cur == 0)
29407dc1947SRichard Lowe continue;
29507dc1947SRichard Lowe ++hash_slots_used;
29607dc1947SRichard Lowe for (; cur; cur = cur->mc_prev) {
29707dc1947SRichard Lowe ++cur_chain_length;
29807dc1947SRichard Lowe if (cur->mc_type == MC_TYPE_ALLOC) {
29907dc1947SRichard Lowe if (cur->mc_dealloc_noted) {
30007dc1947SRichard Lowe if (cur->mc_dealloc_noted > 1) {
30107dc1947SRichard Lowe fprintf(stderr,
302*4d9fdb46SRobert Mustacchi " Duplicate dealloc! entry %ld\n",
303*4d9fdb46SRobert Mustacchi cur->mc_alloc_number);
30407dc1947SRichard Lowe print_entry("duplicate dealloc", cur);
30507dc1947SRichard Lowe
30607dc1947SRichard Lowe }
30707dc1947SRichard Lowe continue;
30807dc1947SRichard Lowe } else {
30907dc1947SRichard Lowe fprintf(stderr, "malloc no dealloc, entry %ld\n",
310*4d9fdb46SRobert Mustacchi cur->mc_alloc_number);
31107dc1947SRichard Lowe print_entry("dangle", cur);
31207dc1947SRichard Lowe }
31307dc1947SRichard Lowe } else {
31407dc1947SRichard Lowe /* mc_type is MC_TYPE_DEALLOC, already checked */
31507dc1947SRichard Lowe
31607dc1947SRichard Lowe }
31707dc1947SRichard Lowe }
31807dc1947SRichard Lowe if (cur_chain_length > max_chain_length) {
31907dc1947SRichard Lowe max_chain_length = cur_chain_length;
32007dc1947SRichard Lowe }
32107dc1947SRichard Lowe }
32207dc1947SRichard Lowe fprintf(stderr, "mc hash table slots=%ld, "
323*4d9fdb46SRobert Mustacchi "used=%ld, maxchain=%ld\n",
324*4d9fdb46SRobert Mustacchi (long) HASH_TABLE_SIZE, hash_slots_used, max_chain_length);
32507dc1947SRichard Lowe return;
32607dc1947SRichard Lowe }
32707dc1947SRichard Lowe
32807dc1947SRichard Lowe #else
32907dc1947SRichard Lowe
330*4d9fdb46SRobert Mustacchi extern void *libdwarf_an_unused_function_so_not_empty_c_file(void);
33107dc1947SRichard Lowe
33207dc1947SRichard Lowe #endif /* WANT_LIBBDWARF_MALLOC_CHECK */
333