summaryrefslogtreecommitdiff
path: root/libdwarf/malloc_check.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdwarf/malloc_check.c')
-rw-r--r--libdwarf/malloc_check.c339
1 files changed, 339 insertions, 0 deletions
diff --git a/libdwarf/malloc_check.c b/libdwarf/malloc_check.c
new file mode 100644
index 0000000..35a1483
--- /dev/null
+++ b/libdwarf/malloc_check.c
@@ -0,0 +1,339 @@
+/*
+
+ Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2.1 of the GNU Lesser General Public License
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it would be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ Further, this software is distributed without any warranty that it is
+ free of the rightful claim of any third person regarding infringement
+ or the like. Any license provided herein, whether implied or
+ otherwise, applies only to this software file. Patent licenses, if
+ any, provided herein do not apply to combinations of this program with
+ other software, or any other product whatsoever.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301,
+ USA.
+
+ Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane,
+ Mountain View, CA 94043, or:
+
+ http://www.sgi.com
+
+ For further information regarding this notice, see:
+
+ http://oss.sgi.com/projects/GenInfo/NoticeExplan
+
+*/
+
+
+
+/* malloc_check.c For checking dealloc completeness.
+
+ This code is as simple as possible and works ok for
+ reasonable size allocation counts.
+
+ It treats allocation as global, and so will not
+ work very well if an application opens more than one
+ Dwarf_Debug.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h> /* for exit() and various malloc
+ prototypes */
+#include "config.h"
+#include "dwarf_incl.h"
+#include "malloc_check.h"
+#ifdef WANT_LIBBDWARF_MALLOC_CHECK
+
+/* To turn off printing every entry, just change the define
+ to set PRINT_MALLOC_DETAILS 0.
+*/
+#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; /* Assumes this is large enough to hold
+ a pointer! */
+
+ long mc_alloc_number; /* Assigned in order by when record
+ created. */
+ unsigned char mc_alloc_code; /* Allocation code, libdwarf. */
+ unsigned char mc_type;
+ unsigned char mc_dealloc_noted; /* Used on an ALLOC node. */
+ unsigned char mc_dealloc_noted_count; /* Used on an ALLOC
+ node. */
+};
+
+/*
+
+
+*/
+#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"
+ /* Don't forget to expand this list if the list of codes
+ expands. */
+};
+
+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) /* nothing */
+#endif
+
+/* Create a zeroed struct or die. */
+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;
+}
+
+/* Notify checker that get_alloc has allocated user data. */
+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);
+}
+
+/* newd is a 'dealloc'.
+*/
+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 {
+ /* code mismatch */
+ *addr_match = cur;
+ *addr_match_num = cur->mc_alloc_number;
+ return -1;
+ }
+ } else {
+ /* Unbalanced new/del */
+ *addr_match = cur;
+ *addr_match_num = cur->mc_alloc_number;
+ return -1;
+ }
+ }
+ }
+ return -1;
+}
+
+/* A dealloc is to take place. Ensure it balances an alloc.
+*/
+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;
+}
+
+/* Final check for leaks.
+*/
+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 {
+ /* mc_type is MC_TYPE_DEALLOC, already checked */
+
+ }
+ }
+ 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;
+}
+
+#else
+
+extern void *libdwarf_an_unused_function_so_not_empty_c_file();
+
+#endif /* WANT_LIBBDWARF_MALLOC_CHECK */