summaryrefslogtreecommitdiffstats
path: root/source/lib/talloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/lib/talloc.c')
-rw-r--r--source/lib/talloc.c295
1 files changed, 234 insertions, 61 deletions
diff --git a/source/lib/talloc.c b/source/lib/talloc.c
index 2df4588f425..00f889d682c 100644
--- a/source/lib/talloc.c
+++ b/source/lib/talloc.c
@@ -1,4 +1,4 @@
-/*
+/*
Samba Unix SMB/CIFS implementation.
Samba trivial allocation library - new interface
@@ -6,27 +6,30 @@
NOTE: Please read talloc_guide.txt for full documentation
Copyright (C) Andrew Tridgell 2004
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
+
+ ** NOTE! The following LGPL license applies to the talloc
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
inspired by http://swapped.cc/halloc/
*/
-
#ifdef _SAMBA_BUILD_
#include "includes.h"
#if ((SAMBA_VERSION_MAJOR==3)&&(SAMBA_VERSION_MINOR<9))
@@ -56,8 +59,9 @@
#define MAX_TALLOC_SIZE 0x10000000
-#define TALLOC_MAGIC 0xe814ec4f
-#define TALLOC_MAGIC_FREE 0x7faebef3
+#define TALLOC_MAGIC 0xe814ec70
+#define TALLOC_FLAG_FREE 0x01
+#define TALLOC_FLAG_LOOP 0x02
#define TALLOC_MAGIC_REFERENCE ((const char *)1)
/* by default we abort when given a bad pointer (such as when talloc_free() is called
@@ -93,27 +97,27 @@ struct talloc_chunk {
struct talloc_chunk *next, *prev;
struct talloc_chunk *parent, *child;
struct talloc_reference_handle *refs;
- size_t size;
talloc_destructor_t destructor;
const char *name;
- union {
- unsigned magic;
- double align_dummy;
- } u;
+ size_t size;
+ unsigned flags;
};
+/* 16 byte alignment seems to keep everyone happy */
+#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
+#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
+
/* panic if we get a bad magic value */
static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
{
- struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, ptr)-1;
- if (tc->u.magic != TALLOC_MAGIC) {
- if (tc->u.magic == TALLOC_MAGIC_FREE) {
- TALLOC_ABORT("Bad talloc magic value - double free");
- } else {
- TALLOC_ABORT("Bad talloc magic value - unknown value");
- }
+ const char *pp = ptr;
+ struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
+ if ((tc->flags & ~0xF) != TALLOC_MAGIC) {
+ TALLOC_ABORT("Bad talloc magic value - unknown value");
+ }
+ if (tc->flags & TALLOC_FLAG_FREE) {
+ TALLOC_ABORT("Bad talloc magic value - double free");
}
-
return tc;
}
@@ -158,7 +162,7 @@ static struct talloc_chunk *talloc_parent_chunk(const void *ptr)
void *talloc_parent(const void *ptr)
{
struct talloc_chunk *tc = talloc_parent_chunk(ptr);
- return tc ? (void *)(tc+1) : NULL;
+ return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
}
/*
@@ -176,11 +180,11 @@ void *_talloc(const void *context, size_t size)
return NULL;
}
- tc = malloc(sizeof(*tc)+size);
+ tc = malloc(TC_HDR_SIZE+size);
if (tc == NULL) return NULL;
tc->size = size;
- tc->u.magic = TALLOC_MAGIC;
+ tc->flags = TALLOC_MAGIC;
tc->destructor = NULL;
tc->child = NULL;
tc->name = NULL;
@@ -200,7 +204,7 @@ void *_talloc(const void *context, size_t size)
tc->next = tc->prev = tc->parent = NULL;
}
- return (void *)(tc+1);
+ return TC_PTR_FROM_CHUNK(tc);
}
@@ -285,7 +289,7 @@ static int talloc_unreference(const void *context, const void *ptr)
for (h=tc->refs;h;h=h->next) {
struct talloc_chunk *p = talloc_parent_chunk(h);
- if ((p==NULL && context==NULL) || p+1 == context) break;
+ if ((p==NULL && context==NULL) || TC_PTR_FROM_CHUNK(p) == context) break;
}
if (h == NULL) {
return -1;
@@ -336,7 +340,7 @@ int talloc_unlink(const void *context, void *ptr)
new_p = talloc_parent_chunk(tc_p->refs);
if (new_p) {
- new_parent = new_p+1;
+ new_parent = TC_PTR_FROM_CHUNK(new_p);
} else {
new_parent = NULL;
}
@@ -464,6 +468,8 @@ void *talloc_init(const char *fmt, ...)
va_list ap;
void *ptr;
+ talloc_enable_null_tracking();
+
ptr = _talloc(NULL, 0);
if (ptr == NULL) return NULL;
@@ -495,16 +501,16 @@ void talloc_free_children(void *ptr)
choice is owner of any remaining reference to this
pointer, the second choice is our parent, and the
final choice is the null context. */
- void *child = tc->child+1;
+ void *child = TC_PTR_FROM_CHUNK(tc->child);
const void *new_parent = null_context;
if (tc->child->refs) {
struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
- if (p) new_parent = p+1;
+ if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
if (talloc_free(child) == -1) {
if (new_parent == null_context) {
struct talloc_chunk *p = talloc_parent_chunk(ptr);
- if (p) new_parent = p+1;
+ if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
talloc_steal(new_parent, child);
}
@@ -534,6 +540,11 @@ int talloc_free(void *ptr)
return -1;
}
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ /* we have a free loop - stop looping */
+ return 0;
+ }
+
if (tc->destructor) {
talloc_destructor_t d = tc->destructor;
if (d == (talloc_destructor_t)-1) {
@@ -547,6 +558,8 @@ int talloc_free(void *ptr)
tc->destructor = NULL;
}
+ tc->flags |= TALLOC_FLAG_LOOP;
+
talloc_free_children(ptr);
if (tc->parent) {
@@ -559,7 +572,7 @@ int talloc_free(void *ptr)
if (tc->next) tc->next->prev = tc->prev;
}
- tc->u.magic = TALLOC_MAGIC_FREE;
+ tc->flags |= TALLOC_FLAG_FREE;
free(tc);
return 0;
@@ -599,24 +612,24 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
}
/* by resetting magic we catch users of the old memory */
- tc->u.magic = TALLOC_MAGIC_FREE;
+ tc->flags |= TALLOC_FLAG_FREE;
#if ALWAYS_REALLOC
- new_ptr = malloc(size + sizeof(*tc));
+ new_ptr = malloc(size + TC_HDR_SIZE);
if (new_ptr) {
- memcpy(new_ptr, tc, tc->size + sizeof(*tc));
+ memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
free(tc);
}
#else
- new_ptr = realloc(tc, size + sizeof(*tc));
+ new_ptr = realloc(tc, size + TC_HDR_SIZE);
#endif
if (!new_ptr) {
- tc->u.magic = TALLOC_MAGIC;
+ tc->flags &= ~TALLOC_FLAG_FREE;
return NULL;
}
tc = new_ptr;
- tc->u.magic = TALLOC_MAGIC;
+ tc->flags &= ~TALLOC_FLAG_FREE;
if (tc->parent) {
tc->parent->child = new_ptr;
}
@@ -632,9 +645,9 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
}
tc->size = size;
- talloc_set_name_const(tc+1, name);
+ talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
- return (void *)(tc+1);
+ return TC_PTR_FROM_CHUNK(tc);
}
/*
@@ -711,10 +724,19 @@ off_t talloc_total_size(const void *ptr)
tc = talloc_chunk_from_ptr(ptr);
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ return 0;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
total = tc->size;
for (c=tc->child;c;c=c->next) {
- total += talloc_total_size(c+1);
+ total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
}
+
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+
return total;
}
@@ -726,10 +748,19 @@ off_t talloc_total_blocks(const void *ptr)
off_t total = 0;
struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ return 0;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
total++;
for (c=tc->child;c;c=c->next) {
- total += talloc_total_blocks(c+1);
+ total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
}
+
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+
return total;
}
@@ -755,23 +786,29 @@ void talloc_report_depth(const void *ptr, FILE *f, int depth)
{
struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ return;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
for (c=tc->child;c;c=c->next) {
if (c->name == TALLOC_MAGIC_REFERENCE) {
- struct talloc_reference_handle *handle = (void *)(c+1);
+ struct talloc_reference_handle *handle = TC_PTR_FROM_CHUNK(c);
const char *name2 = talloc_get_name(handle->ptr);
fprintf(f, "%*sreference to: %s\n", depth*4, "", name2);
} else {
- const char *name = talloc_get_name(c+1);
+ const char *name = talloc_get_name(TC_PTR_FROM_CHUNK(c));
fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n",
depth*4, "",
name,
- (unsigned long)talloc_total_size(c+1),
- (unsigned long)talloc_total_blocks(c+1),
- talloc_reference_count(c+1));
- talloc_report_depth(c+1, f, depth+1);
+ (unsigned long)talloc_total_size(TC_PTR_FROM_CHUNK(c)),
+ (unsigned long)talloc_total_blocks(TC_PTR_FROM_CHUNK(c)),
+ talloc_reference_count(TC_PTR_FROM_CHUNK(c)));
+ talloc_report_depth(TC_PTR_FROM_CHUNK(c), f, depth+1);
}
}
-
+ tc->flags &= ~TALLOC_FLAG_LOOP;
}
/*
@@ -814,9 +851,9 @@ void talloc_report(const void *ptr, FILE *f)
for (c=tc->child;c;c=c->next) {
fprintf(f, "\t%-30s contains %6lu bytes in %3lu blocks\n",
- talloc_get_name(c+1),
- (unsigned long)talloc_total_size(c+1),
- (unsigned long)talloc_total_blocks(c+1));
+ talloc_get_name(TC_PTR_FROM_CHUNK(c)),
+ (unsigned long)talloc_total_size(TC_PTR_FROM_CHUNK(c)),
+ (unsigned long)talloc_total_blocks(TC_PTR_FROM_CHUNK(c)));
}
fflush(f);
}
@@ -851,6 +888,74 @@ void talloc_enable_null_tracking(void)
}
}
+#ifdef _SAMBA_BUILD_
+/* Ugly calls to Samba-specific sprintf_append... JRA. */
+
+/*
+ report on memory usage by all children of a pointer, giving a full tree view
+*/
+static void talloc_report_depth_str(const void *ptr, char **pps, ssize_t *plen, size_t *pbuflen, int depth)
+{
+ struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
+
+ if (tc->flags & TALLOC_FLAG_LOOP) {
+ return;
+ }
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
+ for (c=tc->child;c;c=c->next) {
+ if (c->name == TALLOC_MAGIC_REFERENCE) {
+ struct talloc_reference_handle *handle = TC_PTR_FROM_CHUNK(c);
+ const char *name2 = talloc_get_name(handle->ptr);
+
+ sprintf_append(NULL, pps, plen, pbuflen,
+ "%*sreference to: %s\n", depth*4, "", name2);
+
+ } else {
+ const char *name = talloc_get_name(TC_PTR_FROM_CHUNK(c));
+
+ sprintf_append(NULL, pps, plen, pbuflen,
+ "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n",
+ depth*4, "",
+ name,
+ (unsigned long)talloc_total_size(TC_PTR_FROM_CHUNK(c)),
+ (unsigned long)talloc_total_blocks(TC_PTR_FROM_CHUNK(c)),
+ talloc_reference_count(TC_PTR_FROM_CHUNK(c)));
+
+ talloc_report_depth_str(TC_PTR_FROM_CHUNK(c), pps, plen, pbuflen, depth+1);
+ }
+ }
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+}
+
+/*
+ report on memory usage by all children of a pointer
+*/
+char *talloc_describe_all(void)
+{
+ ssize_t len = 0;
+ size_t buflen = 512;
+ char *s = NULL;
+
+ if (null_context == NULL) {
+ return NULL;
+ }
+
+ sprintf_append(NULL, &s, &len, &buflen,
+ "full talloc report on '%s' (total %lu bytes in %lu blocks)\n",
+ talloc_get_name(null_context),
+ (unsigned long)talloc_total_size(null_context),
+ (unsigned long)talloc_total_blocks(null_context));
+
+ if (!s) {
+ return NULL;
+ }
+ talloc_report_depth_str(null_context, &s, &len, &buflen, 1);
+ return s;
+}
+#endif
+
/*
enable leak reporting on exit
*/
@@ -915,6 +1020,28 @@ char *talloc_strdup(const void *t, const char *p)
}
/*
+ append to a talloced string
+*/
+char *talloc_append_string(const void *t, char *orig, const char *append)
+{
+ char *ret;
+ size_t olen = strlen(orig);
+ size_t alenz = strlen(append) + 1;
+
+ if (!append)
+ return orig;
+
+ ret = talloc_realloc(t, orig, char, olen + alenz);
+ if (!ret)
+ return NULL;
+
+ /* append the string with the trailing \0 */
+ memcpy(&ret[olen], append, alenz);
+
+ return ret;
+}
+
+/*
strndup with a talloc
*/
char *talloc_strndup(const void *t, const char *p, size_t n)
@@ -989,6 +1116,7 @@ static char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINT
static char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
{
+ struct talloc_chunk *tc;
int len, s_len;
va_list ap2;
@@ -996,9 +1124,11 @@ static char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
return talloc_vasprintf(NULL, fmt, ap);
}
+ tc = talloc_chunk_from_ptr(s);
+
VA_COPY(ap2, ap);
- s_len = strlen(s);
+ s_len = tc->size - 1;
len = vsnprintf(NULL, 0, fmt, ap2);
s = talloc_realloc(NULL, s, char, s_len + len+1);
@@ -1102,3 +1232,46 @@ size_t talloc_get_size(const void *context)
return tc->size;
}
+
+/*
+ find a parent of this context that has the given name, if any
+*/
+void *talloc_find_parent_byname(const void *context, const char *name)
+{
+ struct talloc_chunk *tc;
+
+ if (context == NULL) {
+ return NULL;
+ }
+
+ tc = talloc_chunk_from_ptr(context);
+ while (tc) {
+ if (tc->name && strcmp(tc->name, name) == 0) {
+ return TC_PTR_FROM_CHUNK(tc);
+ }
+ while (tc && tc->prev) tc = tc->prev;
+ tc = tc->parent;
+ }
+ return NULL;
+}
+
+/*
+ show the parentage of a context
+*/
+void talloc_show_parents(const void *context, FILE *file)
+{
+ struct talloc_chunk *tc;
+
+ if (context == NULL) {
+ fprintf(file, "talloc no parents for NULL\n");
+ return;
+ }
+
+ tc = talloc_chunk_from_ptr(context);
+ fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
+ while (tc) {
+ fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
+ while (tc && tc->prev) tc = tc->prev;
+ tc = tc->parent;
+ }
+}