From 29149d39d59c6b46535f237563732092397331f0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 6 Jan 2008 08:17:48 +1100 Subject: [PATCH 1/1] First cut of ccan-izing talloc --- config.h | 1 + talloc/TODO | 2 + talloc/_info.c | 101 +++ talloc/talloc.3.xml | 739 +++++++++++++++++++++ talloc/talloc.c | 1398 +++++++++++++++++++++++++++++++++++++++ talloc/talloc.h | 174 +++++ talloc/talloc_guide.txt | 670 +++++++++++++++++++ talloc/test/run.c | 871 ++++++++++++++++++++++++ talloc/testsuite.c | 1115 +++++++++++++++++++++++++++++++ 9 files changed, 5071 insertions(+) create mode 100644 talloc/TODO create mode 100644 talloc/_info.c create mode 100644 talloc/talloc.3.xml create mode 100644 talloc/talloc.c create mode 100644 talloc/talloc.h create mode 100644 talloc/talloc_guide.txt create mode 100644 talloc/test/run.c create mode 100644 talloc/testsuite.c diff --git a/config.h b/config.h index 6cde6a41..0c2cf11e 100644 --- a/config.h +++ b/config.h @@ -1,3 +1,4 @@ /* Simple config.h for gcc. */ #define HAVE_TYPEOF 1 #define HAVE_STATEMENT_EXPR 1 +#define HAVE_BUILTIN_EXPECT 1 diff --git a/talloc/TODO b/talloc/TODO new file mode 100644 index 00000000..0671a6dd --- /dev/null +++ b/talloc/TODO @@ -0,0 +1,2 @@ +- Remove talloc.h cruft +- Restore errno around (successful) talloc_free. diff --git a/talloc/_info.c b/talloc/_info.c new file mode 100644 index 00000000..2f067a42 --- /dev/null +++ b/talloc/_info.c @@ -0,0 +1,101 @@ +#include +#include +#include "config.h" + +/** + * talloc - tree allocator routines + * + * Talloc is a hierarchical memory pool system with destructors: you keep your + * objects in heirarchies reflecting their lifetime. Every pointer returned + * from talloc() is itself a valid talloc context, from which other talloc()s + * can be attached. This means you can do this: + * + * struct foo *X = talloc(mem_ctx, struct foo); + * X->name = talloc_strdup(X, "foo"); + * + * and the pointer X->name would be a "child" of the talloc context "X" which + * is itself a child of mem_ctx. So if you do talloc_free(mem_ctx) then it is + * all destroyed, whereas if you do talloc_free(X) then just X and X->name are + * destroyed, and if you do talloc_free(X->name) then just the name element of + * X is destroyed. + * + * If you think about this, then what this effectively gives you is an n-ary + * tree, where you can free any part of the tree with talloc_free(). + * + * Talloc has been measured with a time overhead of around 4% over glibc + * malloc, and 48/80 bytes per allocation (32/64 bit). + * + * This version is based on svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc revision 23158. + * + * Example: + * #include + * #include + * #include + * #include "talloc/talloc.h" + * + * // A structure containing a popened comman. + * struct command + * { + * FILE *f; + * const char *command; + * }; + * + * // When struct command is freed, we also want to pclose pipe. + * static int close_cmd(struct command *cmd) + * { + * pclose(cmd->f); + * // 0 means "we succeeded, continue freeing" + * return 0; + * } + * + * // This function opens a writable pipe to the given command. + * struct command *open_output_cmd(const void *ctx, char *fmt, ...) + * { + * va_list ap; + * struct command *cmd = talloc(ctx, struct command); + * + * if (!cmd) + * return NULL; + * + * va_start(ap, fmt); + * cmd->command = talloc_vasprintf(cmd, fmt, ap); + * va_end(ap); + * if (!cmd->command) { + * talloc_free(cmd); + * return NULL; + * } + * + * cmd->f = popen(cmd->command, "w"); + * if (!cmd->f) { + * talloc_free(cmd); + * return NULL; + * } + * talloc_set_destructor(cmd, close_cmd); + * return cmd; + * } + * + * int main(int argc, char *argv[]) + * { + * struct command *cmd; + * + * if (argc != 2) + * errx(1, "Usage: %s \n"); + * + * cmd = open_output_cmd(NULL, "%s hello", argv[1]); + * if (!cmd) + * err(1, "Running '%s hello'", argv[1]); + * fprintf(cmd->f, "This is a test\n"); + * talloc_free(cmd); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) + return 0; + + return 1; +} diff --git a/talloc/talloc.3.xml b/talloc/talloc.3.xml new file mode 100644 index 00000000..83ca67a4 --- /dev/null +++ b/talloc/talloc.3.xml @@ -0,0 +1,739 @@ + + + + + talloc + 3 + + + talloc +hierarchical reference counted memory pool system with destructors + + +#include <talloc/talloc.h> + + DESCRIPTION + + If you are used to talloc from Samba3 then please read this + carefully, as talloc has changed a lot. + + + The new talloc is a hierarchical, reference counted memory pool + system with destructors. Quite a mouthful really, but not too bad + once you get used to it. + + + Perhaps the biggest change from Samba3 is that there is no + distinction between a "talloc context" and a "talloc pointer". Any + pointer returned from talloc() is itself a valid talloc context. + This means you can do this: + + + struct foo *X = talloc(mem_ctx, struct foo); + X->name = talloc_strdup(X, "foo"); + + + and the pointer X->name + would be a "child" of the talloc context X which is itself a child of + mem_ctx. So if you do + talloc_free(mem_ctx) then + it is all destroyed, whereas if you do talloc_free(X) then just X and X->name are destroyed, and if + you do talloc_free(X->name) then just + the name element of X is + destroyed. + + + If you think about this, then what this effectively gives you is an + n-ary tree, where you can free any part of the tree with + talloc_free(). + + + If you find this confusing, then I suggest you run the testsuite program to watch talloc + in action. You may also like to add your own tests to testsuite.c to clarify how some + particular situation is handled. + + + TALLOC API + + The following is a complete guide to the talloc API. Read it all at + least twice. + + (type *)talloc(const void *ctx, type); + + The talloc() macro is the core of the talloc library. It takes a + memory ctx and a type, and returns a pointer to a new + area of memory of the given type. + + + The returned pointer is itself a talloc context, so you can use + it as the ctx argument to more + calls to talloc() if you wish. + + + The returned pointer is a "child" of the supplied context. This + means that if you talloc_free() the ctx then the new child disappears as + well. Alternatively you can free just the child. + + + The ctx argument to talloc() + can be NULL, in which case a new top level context is created. + + + void *talloc_size(const void *ctx, size_t size); + + The function talloc_size() should be used when you don't have a + convenient type to pass to talloc(). Unlike talloc(), it is not + type safe (as it returns a void *), so you are on your own for + type checking. + + + (typeof(ptr)) talloc_ptrtype(const void *ctx, ptr); + + The talloc_ptrtype() macro should be used when you have a pointer and + want to allocate memory to point at with this pointer. When compiling + with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() + and talloc_get_name() will return the current location in the source file. + and not the type. + + + int talloc_free(void *ptr); + + The talloc_free() function frees a piece of talloc memory, and + all its children. You can call talloc_free() on any pointer + returned by talloc(). + + + The return value of talloc_free() indicates success or failure, + with 0 returned for success and -1 for failure. The only + possible failure condition is if ptr had a destructor attached to it and + the destructor returned -1. See talloc_set_destructor() + for details on destructors. + + + If this pointer has an additional parent when talloc_free() is + called then the memory is not actually released, but instead the + most recently established parent is destroyed. See talloc_reference() + for details on establishing additional parents. + + + For more control on which parent is removed, see talloc_unlink(). + + + talloc_free() operates recursively on its children. + + + void *talloc_reference(const void *ctx, const void *ptr); + + The talloc_reference() function makes ctx an additional parent of ptr. + + + The return value of talloc_reference() is always the original + pointer ptr, unless talloc ran + out of memory in creating the reference in which case it will + return NULL (each additional reference consumes around 48 bytes + of memory on intel x86 platforms). + + + If ptr is NULL, then the + function is a no-op, and simply returns NULL. + + + After creating a reference you can free it in one of the + following ways: + + + + + + you can talloc_free() any parent of the original pointer. + That will reduce the number of parents of this pointer by 1, + and will cause this pointer to be freed if it runs out of + parents. + + + + + you can talloc_free() the pointer itself. That will destroy + the most recently established parent to the pointer and leave + the pointer as a child of its current parent. + + + + + + For more control on which parent to remove, see talloc_unlink(). + + + int talloc_unlink(const void *ctx, const void *ptr); + + The talloc_unlink() function removes a specific parent from + ptr. The ctx passed must either be a context used + in talloc_reference() with this pointer, or must be a direct + parent of ptr. + + + Note that if the parent has already been removed using + talloc_free() then this function will fail and will return -1. + Likewise, if ptr is NULL, then + the function will make no modifications and return -1. + + + Usually you can just use talloc_free() instead of + talloc_unlink(), but sometimes it is useful to have the + additional control on which parent is removed. + + + void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); + + The function talloc_set_destructor() sets the destructor for the pointer ptr. A destructor is a function that is called + when the memory used by a pointer is about to be released. The + destructor receives ptr as an + argument, and should return 0 for success and -1 for failure. + + + The destructor can do anything + it wants to, including freeing other pieces of memory. A common + use for destructors is to clean up operating system resources + (such as open file descriptors) contained in the structure the + destructor is placed on. + + + You can only place one destructor on a pointer. If you need more + than one destructor then you can create a zero-length child of + the pointer and place an additional destructor on that. + + + To remove a destructor call talloc_set_destructor() with NULL for + the destructor. + + + If your destructor attempts to talloc_free() the pointer that it + is the destructor for then talloc_free() will return -1 and the + free will be ignored. This would be a pointless operation + anyway, as the destructor is only called when the memory is just + about to go away. + + + int talloc_increase_ref_count(const void *<emphasis role="italic">ptr</emphasis>); + + The talloc_increase_ref_count(ptr) function is exactly equivalent to: + + talloc_reference(NULL, ptr); + + You can use either syntax, depending on which you think is + clearer in your code. + + + It returns 0 on success and -1 on failure. + + + size_t talloc_reference_count(const void *<emphasis role="italic">ptr</emphasis>); + + Return the number of references to the pointer. + + + void talloc_set_name(const void *ptr, const char *fmt, ...); + + Each talloc pointer has a "name". The name is used principally + for debugging purposes, although it is also possible to set and + get the name on a pointer in as a way of "marking" pointers in + your code. + + + The main use for names on pointer is for "talloc reports". See + talloc_report_depth_cb(), + talloc_report_depth_file(), + talloc_report() + talloc_report() + and talloc_report_full() + for details. Also see talloc_enable_leak_report() + and talloc_enable_leak_report_full(). + + + The talloc_set_name() function allocates memory as a child of the + pointer. It is logically equivalent to: + + talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); + + Note that multiple calls to talloc_set_name() will allocate more + memory without releasing the name. All of the memory is released + when the ptr is freed using talloc_free(). + + + void talloc_set_name_const(const void *<emphasis role="italic">ptr</emphasis>, const char *<emphasis role="italic">name</emphasis>); + + The function talloc_set_name_const() is just like + talloc_set_name(), but it takes a string constant, and is much + faster. It is extensively used by the "auto naming" macros, such + as talloc_p(). + + + This function does not allocate any memory. It just copies the + supplied pointer into the internal representation of the talloc + ptr. This means you must not pass a name pointer to memory that will + disappear before ptr is freed + with talloc_free(). + + + void *talloc_named(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, ...); + + The talloc_named() function creates a named talloc pointer. It + is equivalent to: + + ptr = talloc_size(ctx, size); +talloc_set_name(ptr, fmt, ....); + + void *talloc_named_const(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>, const char *<emphasis role="italic">name</emphasis>); + + This is equivalent to: + + ptr = talloc_size(ctx, size); +talloc_set_name_const(ptr, name); + + const char *talloc_get_name(const void *<emphasis role="italic">ptr</emphasis>); + + This returns the current name for the given talloc pointer, + ptr. See talloc_set_name() + for details. + + + void *talloc_init(const char *<emphasis role="italic">fmt</emphasis>, ...); + + This function creates a zero length named talloc context as a top + level context. It is equivalent to: + + talloc_named(NULL, 0, fmt, ...); + + void *talloc_new(void *<emphasis role="italic">ctx</emphasis>); + + This is a utility macro that creates a new memory context hanging + off an exiting context, automatically naming it "talloc_new: + __location__" where __location__ is the source line it is called + from. It is particularly useful for creating a new temporary + working context. + + + (<emphasis role="italic">type</emphasis> *)talloc_realloc(const void *<emphasis role="italic">ctx</emphasis>, void *<emphasis role="italic">ptr</emphasis>, <emphasis role="italic">type</emphasis>, <emphasis role="italic">count</emphasis>); + + The talloc_realloc() macro changes the size of a talloc pointer. + It has the following equivalences: + + talloc_realloc(ctx, NULL, type, 1) ==> talloc(ctx, type); +talloc_realloc(ctx, ptr, type, 0) ==> talloc_free(ptr); + + The ctx argument is only used + if ptr is not NULL, otherwise + it is ignored. + + + talloc_realloc() returns the new pointer, or NULL on failure. + The call will fail either due to a lack of memory, or because the + pointer has more than one parent (see talloc_reference()). + + + void *talloc_realloc_size(const void *ctx, void *ptr, size_t size); + + the talloc_realloc_size() function is useful when the type is not + known so the type-safe talloc_realloc() cannot be used. + + + TYPE *talloc_steal(const void *<emphasis role="italic">new_ctx</emphasis>, const TYPE *<emphasis role="italic">ptr</emphasis>); + + The talloc_steal() function changes the parent context of a + talloc pointer. It is typically used when the context that the + pointer is currently a child of is going to be freed and you wish + to keep the memory for a longer time. + + + The talloc_steal() function returns the pointer that you pass it. + It does not have any failure modes. + + + NOTE: It is possible to produce loops in the parent/child + relationship if you are not careful with talloc_steal(). No + guarantees are provided as to your sanity or the safety of your + data if you do this. + + + TYPE *talloc_move(const void *<emphasis role="italic">new_ctx</emphasis>, TYPE **<emphasis role="italic">ptr</emphasis>); + + The talloc_move() function is a wrapper around + talloc_steal() which zeros the source pointer after the + move. This avoids a potential source of bugs where a + programmer leaves a pointer in two structures, and uses the + pointer from the old structure after it has been moved to a + new one. + + + size_t talloc_total_size(const void *<emphasis role="italic">ptr</emphasis>); + + The talloc_total_size() function returns the total size in bytes + used by this pointer and all child pointers. Mostly useful for + debugging. + + + Passing NULL is allowed, but it will only give a meaningful + result if talloc_enable_leak_report() or + talloc_enable_leak_report_full() has been called. + + + size_t talloc_total_blocks(const void *<emphasis role="italic">ptr</emphasis>); + + The talloc_total_blocks() function returns the total memory block + count used by this pointer and all child pointers. Mostly useful + for debugging. + + + Passing NULL is allowed, but it will only give a meaningful + result if talloc_enable_leak_report() or + talloc_enable_leak_report_full() has been called. + + + void talloc_report(const void *ptr, FILE *f); + + The talloc_report() function prints a summary report of all + memory used by ptr. One line + of report is printed for each immediate child of ptr, showing the + total memory and number of blocks used by that child. + + + You can pass NULL for the pointer, in which case a report is + printed for the top level memory context, but only if + talloc_enable_leak_report() or talloc_enable_leak_report_full() + has been called. + + + void talloc_report_full(const void *<emphasis role="italic">ptr</emphasis>, FILE *<emphasis role="italic">f</emphasis>); + + This provides a more detailed report than talloc_report(). It + will recursively print the entire tree of memory referenced by + the pointer. References in the tree are shown by giving the name + of the pointer that is referenced. + + + You can pass NULL for the pointer, in which case a report is + printed for the top level memory context, but only if + talloc_enable_leak_report() or talloc_enable_leak_report_full() + has been called. + + + + + void talloc_report_depth_cb + const void *ptr + int depth + int max_depth + void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *priv) + void *priv + + + This provides a more flexible reports than talloc_report(). It + will recursively call the callback for the entire tree of memory + referenced by the pointer. References in the tree are passed with + is_ref = 1 and the pointer that is referenced. + + + You can pass NULL for the pointer, in which case a report is + printed for the top level memory context, but only if + talloc_enable_leak_report() or talloc_enable_leak_report_full() + has been called. + + + The recursion is stopped when depth >= max_depth. + max_depth = -1 means only stop at leaf nodes. + + + + + void talloc_report_depth_file + const void *ptr + int depth + int max_depth + FILE *f + + + This provides a more flexible reports than talloc_report(). It + will let you specify the depth and max_depth. + + + void talloc_enable_leak_report(void); + + This enables calling of talloc_report(NULL, stderr) when the + program exits. In Samba4 this is enabled by using the + --leak-report command line option. + + + For it to be useful, this function must be called before any + other talloc function as it establishes a "null context" that + acts as the top of the tree. If you don't call this function + first then passing NULL to talloc_report() or + talloc_report_full() won't give you the full tree printout. + + + Here is a typical talloc report: + + talloc report on 'null_context' (total 267 bytes in 15 blocks) +libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks +libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks +iconv(UTF8,CP850) contains 42 bytes in 2 blocks +libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks +iconv(CP850,UTF8) contains 42 bytes in 2 blocks +iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks +iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks + + + void talloc_enable_leak_report_full(void); + + This enables calling of talloc_report_full(NULL, stderr) when the + program exits. In Samba4 this is enabled by using the + --leak-report-full command line option. + + + For it to be useful, this function must be called before any + other talloc function as it establishes a "null context" that + acts as the top of the tree. If you don't call this function + first then passing NULL to talloc_report() or + talloc_report_full() won't give you the full tree printout. + + + Here is a typical full report: + + full talloc report on 'root' (total 18 bytes in 8 blocks) +p1 contains 18 bytes in 7 blocks (ref 0) + r1 contains 13 bytes in 2 blocks (ref 0) + reference to: p2 + p2 contains 1 bytes in 1 blocks (ref 1) + x3 contains 1 bytes in 1 blocks (ref 0) + x2 contains 1 bytes in 1 blocks (ref 0) + x1 contains 1 bytes in 1 blocks (ref 0) + + + (<emphasis role="italic">type</emphasis> *)talloc_zero(const void *<emphasis role="italic">ctx</emphasis>, <emphasis role="italic">type</emphasis>); + + The talloc_zero() macro is equivalent to: + + ptr = talloc(ctx, type); +if (ptr) memset(ptr, 0, sizeof(type)); + + void *talloc_zero_size(const void *<emphasis role="italic">ctx</emphasis>, size_t <emphasis role="italic">size</emphasis>) + + The talloc_zero_size() function is useful when you don't have a + known type. + + + void *talloc_memdup(const void *<emphasis role="italic">ctx</emphasis>, const void *<emphasis role="italic">p</emphasis>, size_t size); + + The talloc_memdup() function is equivalent to: + + ptr = talloc_size(ctx, size); +if (ptr) memcpy(ptr, p, size); + + char *talloc_strdup(const void *<emphasis role="italic">ctx</emphasis>, const char *<emphasis role="italic">p</emphasis>); + + The talloc_strdup() function is equivalent to: + + ptr = talloc_size(ctx, strlen(p)+1); +if (ptr) memcpy(ptr, p, strlen(p)+1); + + This function sets the name of the new pointer to the passed + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_strndup(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">p</emphasis>, size_t <emphasis role="italic">n</emphasis>); + + The talloc_strndup() function is the talloc equivalent of the C + library function strndup(3). + + + This function sets the name of the new pointer to the passed + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_append_string(const void *<emphasis role="italic">t</emphasis>, char *<emphasis role="italic">orig</emphasis>, const char *<emphasis role="italic">append</emphasis>); + + The talloc_append_string() function appends the given formatted + string to the given string. + + + This function sets the name of the new pointer to the new + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_vasprintf(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, va_list <emphasis role="italic">ap</emphasis>); + + The talloc_vasprintf() function is the talloc equivalent of the C + library function vasprintf(3). + + + This function sets the name of the new pointer to the new + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_asprintf(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, ...); + + The talloc_asprintf() function is the talloc equivalent of the C + library function asprintf(3). + + + This function sets the name of the new pointer to the passed + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + char *talloc_asprintf_append(char *s, const char *fmt, ...); + + The talloc_asprintf_append() function appends the given formatted + string to the given string. + + + This function sets the name of the new pointer to the new + string. This is equivalent to: + + talloc_set_name_const(ptr, ptr) + + (type *)talloc_array(const void *ctx, type, uint_t count); + + The talloc_array() macro is equivalent to: + + (type *)talloc_size(ctx, sizeof(type) * count); + + except that it provides integer overflow protection for the + multiply, returning NULL if the multiply overflows. + + + void *talloc_array_size(const void *ctx, size_t size, uint_t count); + + The talloc_array_size() function is useful when the type is not + known. It operates in the same way as talloc_array(), but takes a + size instead of a type. + + + (typeof(ptr)) talloc_array_ptrtype(const void *ctx, ptr, uint_t count); + + The talloc_ptrtype() macro should be used when you have a pointer to an array + and want to allocate memory of an array to point at with this pointer. When compiling + with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_array_size() + and talloc_get_name() will return the current location in the source file. + and not the type. + + + void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size) + + This is a non-macro version of talloc_realloc(), which is useful + as libraries sometimes want a realloc function pointer. A + realloc(3) implementation encapsulates the functionality of + malloc(3), free(3) and realloc(3) in one call, which is why it is + useful to be able to pass around a single function pointer. + + + void *talloc_autofree_context(void); + + This is a handy utility function that returns a talloc context + which will be automatically freed on program exit. This can be + used to reduce the noise in memory leak reports. + + + void *talloc_check_name(const void *ptr, const char *name); + + This function checks if a pointer has the specified name. If it does then the pointer is + returned. It it doesn't then NULL is returned. + + + (type *)talloc_get_type(const void *ptr, type); + + This macro allows you to do type checking on talloc pointers. It + is particularly useful for void* private pointers. It is + equivalent to this: + + (type *)talloc_check_name(ptr, #type) + + talloc_set_type(const void *ptr, type); + + This macro allows you to force the name of a pointer to be a + particular type. This can be + used in conjunction with talloc_get_type() to do type checking on + void* pointers. + + + It is equivalent to this: + + talloc_set_name_const(ptr, #type) + + + PERFORMANCE + + All the additional features of talloc(3) over malloc(3) do come at a + price. We have a simple performance test in Samba4 that measures + talloc() versus malloc() performance, and it seems that talloc() is + about 10% slower than malloc() on my x86 Debian Linux box. For + Samba, the great reduction in code complexity that we get by using + talloc makes this worthwhile, especially as the total overhead of + talloc/malloc in Samba is already quite small. + + + SEE ALSO + + malloc(3), strndup(3), vasprintf(3), asprintf(3), + + + + COPYRIGHT/LICENSE + + 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, 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. + + + diff --git a/talloc/talloc.c b/talloc/talloc.c new file mode 100644 index 00000000..70c2947f --- /dev/null +++ b/talloc/talloc.c @@ -0,0 +1,1398 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** 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 + 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/ +*/ + +#include "talloc.h" +#include +#include + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#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 + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(INTPTR_MIN) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* these macros gain us a few percent of speed on gcc */ +#if HAVE_BUILTIN_EXPECT +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static void *null_context; +static void *autofree_context; + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + talloc_destructor_t destructor; + const char *name; + 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 inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + const char *pp = (const char *)ptr; + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); + if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { + if (tc->flags & TALLOC_FLAG_FREE) { + TALLOC_ABORT("Bad talloc magic value - double free"); + } else { + TALLOC_ABORT("Bad talloc magic value - unknown value"); + } + } + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + + return tc->parent; +} + +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? TC_PTR_FROM_CHUNK(tc) : NULL; +} + +/* + find parents name +*/ +const char *talloc_parent_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? tc->name : NULL; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +static inline void *__talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc; + + if (unlikely(context == NULL)) { + context = null_context; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + if (unlikely(tc == NULL)) return NULL; + + tc->size = size; + tc->flags = TALLOC_MAGIC; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (likely(context)) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + if (parent->child) { + parent->child->parent = NULL; + tc->next = parent->child; + tc->next->prev = tc; + } else { + tc->next = NULL; + } + tc->parent = parent; + tc->prev = NULL; + parent->child = tc; + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +int talloc_increase_ref_count(const void *ptr) +{ + if (unlikely(!talloc_reference(null_context, ptr))) { + return -1; + } + return 0; +} + +/* + helper for talloc_reference() + + this is referenced by a function pointer and should not be inline +*/ +static int talloc_reference_destructor(struct talloc_reference_handle *handle) +{ + struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); + _TLIST_REMOVE(ptr_tc->refs, handle); + return 0; +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +static inline void _talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + internal talloc_named_const() +*/ +static inline void *_talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) { + return NULL; + } + + _talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +void *_talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (unlikely(ptr == NULL)) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = (struct talloc_reference_handle *)_talloc_named_const(context, + sizeof(struct talloc_reference_handle), + TALLOC_MAGIC_REFERENCE); + if (unlikely(handle == NULL)) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + + +/* + internal talloc_free call +*/ +static inline int _talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs)) { + int is_child; + /* check this is a reference from a child or grantchild + * back to it's parent or grantparent + * + * in that case we need to remove the reference and + * call another instance of talloc_free() on the current + * pointer. + */ + is_child = talloc_is_parent(tc->refs, ptr); + _talloc_free(tc->refs); + if (is_child) { + return _talloc_free(ptr); + } + return -1; + } + + if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { + /* we have a free loop - stop looping */ + return 0; + } + + if (unlikely(tc->destructor)) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + 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_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + tc->flags |= TALLOC_FLAG_FREE; + free(tc); + return 0; +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *_talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (unlikely(!ptr)) { + return NULL; + } + + if (unlikely(new_ctx == NULL)) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(new_ctx == NULL)) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (unlikely(tc == new_tc || tc->parent == new_tc)) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + + + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static inline int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (unlikely(context == NULL)) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if (p == NULL) { + if (context == NULL) break; + } else if (TC_PTR_FROM_CHUNK(p) == context) { + break; + } + } + if (h == NULL) { + return -1; + } + + return _talloc_free(h); +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return _talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = TC_PTR_FROM_CHUNK(new_p); + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (likely(tc->name)) { + _talloc_set_name_const(tc->name, ".name"); + } + return tc->name; +} + +/* + add a name to an existing pointer +*/ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) +{ + const char *name; + va_list ap; + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + return name; +} + + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { + return ".reference"; + } + if (likely(tc->name)) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (unlikely(ptr == NULL)) return NULL; + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + return NULL; +} + + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + /* + * samba3 expects talloc_report_depth_cb(NULL, ...) + * reports all talloc'ed memory, so we need to enable + * null_tracking + */ + talloc_enable_null_tracking(); + + ptr = __talloc(NULL, 0); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + 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_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + return __talloc(context, size); +} + +/* + externally callable talloc_set_name_const() +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + _talloc_set_name_const(ptr, name); +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + return _talloc_named_const(context, size, name); +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + return _talloc_free(ptr); +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + + /* size zero is equivalent to free() */ + if (unlikely(size == 0)) { + _talloc_free(ptr); + return NULL; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + /* realloc(NULL) is equivalent to malloc() */ + if (ptr == NULL) { + return _talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (unlikely(tc->refs)) { + return NULL; + } + + /* by resetting magic we catch users of the old memory */ + tc->flags |= TALLOC_FLAG_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + free(tc); + } +#else + new_ptr = realloc(tc, size + TC_HDR_SIZE); +#endif + if (unlikely(!new_ptr)) { + tc->flags &= ~TALLOC_FLAG_FREE; + return NULL; + } + + tc = (struct talloc_chunk *)new_ptr; + tc->flags &= ~TALLOC_FLAG_FREE; + if (tc->parent) { + tc->parent->child = tc; + } + if (tc->child) { + tc->child->parent = tc; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + a wrapper around talloc_steal() for situations where you are moving a pointer + between two structures, and want the old pointer to be set to NULL +*/ +void *_talloc_move(const void *new_ctx, const void *_pptr) +{ + const void **pptr = discard_const_p(const void *,_pptr); + void *ret = _talloc_steal(new_ctx, *pptr); + (*pptr) = NULL; + return ret; +} + +/* + return the total size of a talloc pool (subtree) +*/ +size_t talloc_total_size(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + 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(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +size_t talloc_total_blocks(const void *ptr) +{ + size_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(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the number of external references to a pointer +*/ +size_t talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + size_t ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return; + } + + callback(ptr, depth, max_depth, 0, private_data); + + if (max_depth >= 0 && depth >= max_depth) { + return; + } + + tc->flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); + callback(h->ptr, depth + 1, max_depth, 1, private_data); + } else { + talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); + } + } + tc->flags &= ~TALLOC_FLAG_LOOP; +} + +static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) +{ + const char *name = talloc_get_name(ptr); + FILE *f = (FILE *)_f; + + if (is_ref) { + fprintf(f, "%*sreference to: %s\n", depth*4, "", name); + return; + } + + if (depth == 0) { + fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", + (max_depth < 0 ? "full " :""), name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + return; + } + + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr), + (int)talloc_reference_count(ptr), ptr); + +#if 0 + fprintf(f, "content: "); + if (talloc_total_size(ptr)) { + int tot = talloc_total_size(ptr); + int i; + + for (i = 0; i < tot; i++) { + if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { + fprintf(f, "%c", ((char *)ptr)[i]); + } else { + fprintf(f, "~%02x", ((char *)ptr)[i]); + } + } + } + fprintf(f, "\n"); +#endif +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +{ + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, -1, f); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, 1, f); +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + disable tracking of the NULL context +*/ +void talloc_disable_null_tracking(void) +{ + _talloc_free(null_context); + null_context = NULL; +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = _talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = _talloc_named_const(t, size, name); + + if (likely(newp)) { + memcpy(newp, p, size); + } + + return newp; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + char *ret; + if (!p) { + return NULL; + } + ret = (char *)talloc_memdup(t, p, strlen(p) + 1); + if (likely(ret)) { + _talloc_set_name_const(ret, ret); + } + return ret; +} + +/* + 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; + + if (!append) + return orig; + + alenz = strlen(append) + 1; + + ret = talloc_realloc(t, orig, char, olen + alenz); + if (!ret) + return NULL; + + /* append the string with the trailing \0 */ + memcpy(&ret[olen], append, alenz); + + _talloc_set_name_const(ret, ret); + + return ret; +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + size_t len; + char *ret; + + for (len=0; lensize - 1; + + va_copy(ap2, ap); + len = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + + if (len <= 0) { + /* Either the vsnprintf failed or the format resulted in + * no characters being formatted. In the former case, we + * ought to return NULL, in the latter we ought to return + * the original string. Most current callers of this + * function expect it to never return NULL. + */ + return s; + } + + s = talloc_realloc(NULL, s, char, s_len + len+1); + if (!s) return NULL; + + va_copy(ap2, ap); + vsnprintf(s+s_len, len+1, fmt, ap2); + va_end(ap2); + _talloc_set_name_const(s, s); + + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static int talloc_autofree_destructor(void *ptr) +{ + autofree_context = NULL; + return 0; +} + +static void talloc_autofree(void) +{ + _talloc_free(autofree_context); +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +void *talloc_autofree_context(void) +{ + if (autofree_context == NULL) { + autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); + talloc_set_destructor(autofree_context, talloc_autofree_destructor); + atexit(talloc_autofree); + } + return autofree_context; +} + +size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) + return 0; + + tc = talloc_chunk_from_ptr(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; + if (tc) { + 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; + if (tc) { + tc = tc->parent; + } + } + fflush(file); +} + +/* + return 1 if ptr is a parent of context +*/ +int talloc_is_parent(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return 0; +} diff --git a/talloc/talloc.h b/talloc/talloc.h new file mode 100644 index 00000000..dc73df0c --- /dev/null +++ b/talloc/talloc.h @@ -0,0 +1,174 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** 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 + 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 +*/ + +#include +#include +#include +#include "config.h" + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if (__GNUC__ >= 3) +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#define talloc_set_destructor(ptr, function) \ + do { \ + int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \ + _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \ + } while(0) +/* this extremely strange macro is to avoid some braindamaged warning + stupidity in gcc 4.1.x */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) +#else +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), (int (*)(void *))(function)) +#define _TALLOC_TYPEOF(ptr) void * +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#endif + +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) +#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) + +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) + +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#endif + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); +int talloc_increase_ref_count(const void *ptr); +size_t talloc_reference_count(const void *ptr); +void *_talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void *talloc_parent(const void *ptr); +const char *talloc_parent_name(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void talloc_free_children(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_move(const void *new_ctx, const void *pptr); +size_t talloc_total_size(const void *ptr); +size_t talloc_total_blocks(const void *ptr); +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_disable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +char *talloc_strdup(const void *t, const char *p); +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_append_string(const void *t, char *orig, const char *append); +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); +size_t talloc_get_size(const void *ctx); +void *talloc_find_parent_byname(const void *ctx, const char *name); +void talloc_show_parents(const void *context, FILE *file); +int talloc_is_parent(const void *context, const void *ptr); + +#endif diff --git a/talloc/talloc_guide.txt b/talloc/talloc_guide.txt new file mode 100644 index 00000000..c4634ae1 --- /dev/null +++ b/talloc/talloc_guide.txt @@ -0,0 +1,670 @@ +Using talloc in Samba4 +---------------------- + +Andrew Tridgell +September 2004 + +The most current version of this document is available at + http://samba.org/ftp/unpacked/samba4/source/lib/talloc/talloc_guide.txt + +If you are used to the "old" talloc from Samba3 before 3.0.20 then please read +this carefully, as talloc has changed a lot. With 3.0.20 (or 3.0.14?) the +Samba4 talloc has been ported back to Samba3, so this guide applies to both. + +The new talloc is a hierarchical, reference counted memory pool system +with destructors. Quite a mouthful really, but not too bad once you +get used to it. + +Perhaps the biggest change from Samba3 is that there is no distinction +between a "talloc context" and a "talloc pointer". Any pointer +returned from talloc() is itself a valid talloc context. This means +you can do this: + + struct foo *X = talloc(mem_ctx, struct foo); + X->name = talloc_strdup(X, "foo"); + +and the pointer X->name would be a "child" of the talloc context "X" +which is itself a child of mem_ctx. So if you do talloc_free(mem_ctx) +then it is all destroyed, whereas if you do talloc_free(X) then just X +and X->name are destroyed, and if you do talloc_free(X->name) then +just the name element of X is destroyed. + +If you think about this, then what this effectively gives you is an +n-ary tree, where you can free any part of the tree with +talloc_free(). + +If you find this confusing, then I suggest you run the testsuite to +watch talloc in action. You may also like to add your own tests to +testsuite.c to clarify how some particular situation is handled. + + +Performance +----------- + +All the additional features of talloc() over malloc() do come at a +price. We have a simple performance test in Samba4 that measures +talloc() versus malloc() performance, and it seems that talloc() is +about 4% slower than malloc() on my x86 Debian Linux box. For Samba, +the great reduction in code complexity that we get by using talloc +makes this worthwhile, especially as the total overhead of +talloc/malloc in Samba is already quite small. + + +talloc API +---------- + +The following is a complete guide to the talloc API. Read it all at +least twice. + +Multi-threading +--------------- + +talloc itself does not deal with threads. It is thread-safe (assuming +the underlying "malloc" is), as long as each thread uses different +memory contexts. +If two threads uses the same context then they need to synchronize in +order to be safe. In particular: +- when using talloc_enable_leak_report(), giving directly NULL as a +parent context implicitly refers to a hidden "null context" global +variable, so this should not be used in a multi-threaded environment +without proper synchronization ; +- the context returned by talloc_autofree_context() is also global so +shouldn't be used by several threads simultaneously without +synchronization. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(type *)talloc(const void *context, type); + +The talloc() macro is the core of the talloc library. It takes a +memory context and a type, and returns a pointer to a new area of +memory of the given type. + +The returned pointer is itself a talloc context, so you can use it as +the context argument to more calls to talloc if you wish. + +The returned pointer is a "child" of the supplied context. This means +that if you talloc_free() the context then the new child disappears as +well. Alternatively you can free just the child. + +The context argument to talloc() can be NULL, in which case a new top +level context is created. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_size(const void *context, size_t size); + +The function talloc_size() should be used when you don't have a +convenient type to pass to talloc(). Unlike talloc(), it is not type +safe (as it returns a void *), so you are on your own for type checking. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(typeof(ptr)) talloc_ptrtype(const void *ctx, ptr); + +The talloc_ptrtype() macro should be used when you have a pointer and +want to allocate memory to point at with this pointer. When compiling +with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() +and talloc_get_name() will return the current location in the source file. +and not the type. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +int talloc_free(void *ptr); + +The talloc_free() function frees a piece of talloc memory, and all its +children. You can call talloc_free() on any pointer returned by +talloc(). + +The return value of talloc_free() indicates success or failure, with 0 +returned for success and -1 for failure. The only possible failure +condition is if the pointer had a destructor attached to it and the +destructor returned -1. See talloc_set_destructor() for details on +destructors. + +If this pointer has an additional parent when talloc_free() is called +then the memory is not actually released, but instead the most +recently established parent is destroyed. See talloc_reference() for +details on establishing additional parents. + +For more control on which parent is removed, see talloc_unlink() + +talloc_free() operates recursively on its children. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +int talloc_free_children(void *ptr); + +The talloc_free_children() walks along the list of all children of a +talloc context and talloc_free()s only the children, not the context +itself. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_reference(const void *context, const void *ptr); + +The talloc_reference() function makes "context" an additional parent +of "ptr". + +The return value of talloc_reference() is always the original pointer +"ptr", unless talloc ran out of memory in creating the reference in +which case it will return NULL (each additional reference consumes +around 48 bytes of memory on intel x86 platforms). + +If "ptr" is NULL, then the function is a no-op, and simply returns NULL. + +After creating a reference you can free it in one of the following +ways: + + - you can talloc_free() any parent of the original pointer. That + will reduce the number of parents of this pointer by 1, and will + cause this pointer to be freed if it runs out of parents. + + - you can talloc_free() the pointer itself. That will destroy the + most recently established parent to the pointer and leave the + pointer as a child of its current parent. + +For more control on which parent to remove, see talloc_unlink() + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +int talloc_unlink(const void *context, const void *ptr); + +The talloc_unlink() function removes a specific parent from ptr. The +context passed must either be a context used in talloc_reference() +with this pointer, or must be a direct parent of ptr. + +Note that if the parent has already been removed using talloc_free() +then this function will fail and will return -1. Likewise, if "ptr" +is NULL, then the function will make no modifications and return -1. + +Usually you can just use talloc_free() instead of talloc_unlink(), but +sometimes it is useful to have the additional control on which parent +is removed. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); + +The function talloc_set_destructor() sets the "destructor" for the +pointer "ptr". A destructor is a function that is called when the +memory used by a pointer is about to be released. The destructor +receives the pointer as an argument, and should return 0 for success +and -1 for failure. + +The destructor can do anything it wants to, including freeing other +pieces of memory. A common use for destructors is to clean up +operating system resources (such as open file descriptors) contained +in the structure the destructor is placed on. + +You can only place one destructor on a pointer. If you need more than +one destructor then you can create a zero-length child of the pointer +and place an additional destructor on that. + +To remove a destructor call talloc_set_destructor() with NULL for the +destructor. + +If your destructor attempts to talloc_free() the pointer that it is +the destructor for then talloc_free() will return -1 and the free will +be ignored. This would be a pointless operation anyway, as the +destructor is only called when the memory is just about to go away. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +int talloc_increase_ref_count(const void *ptr); + +The talloc_increase_ref_count(ptr) function is exactly equivalent to: + + talloc_reference(NULL, ptr); + +You can use either syntax, depending on which you think is clearer in +your code. + +It returns 0 on success and -1 on failure. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +size_t talloc_reference_count(const void *ptr); + +Return the number of references to the pointer. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_name(const void *ptr, const char *fmt, ...); + +Each talloc pointer has a "name". The name is used principally for +debugging purposes, although it is also possible to set and get the +name on a pointer in as a way of "marking" pointers in your code. + +The main use for names on pointer is for "talloc reports". See +talloc_report() and talloc_report_full() for details. Also see +talloc_enable_leak_report() and talloc_enable_leak_report_full(). + +The talloc_set_name() function allocates memory as a child of the +pointer. It is logically equivalent to: + talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); + +Note that multiple calls to talloc_set_name() will allocate more +memory without releasing the name. All of the memory is released when +the ptr is freed using talloc_free(). + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_name_const(const void *ptr, const char *name); + +The function talloc_set_name_const() is just like talloc_set_name(), +but it takes a string constant, and is much faster. It is extensively +used by the "auto naming" macros, such as talloc_p(). + +This function does not allocate any memory. It just copies the +supplied pointer into the internal representation of the talloc +ptr. This means you must not pass a name pointer to memory that will +disappear before the ptr is freed with talloc_free(). + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_named(const void *context, size_t size, const char *fmt, ...); + +The talloc_named() function creates a named talloc pointer. It is +equivalent to: + + ptr = talloc_size(context, size); + talloc_set_name(ptr, fmt, ....); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_named_const(const void *context, size_t size, const char *name); + +This is equivalent to: + + ptr = talloc_size(context, size); + talloc_set_name_const(ptr, name); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +const char *talloc_get_name(const void *ptr); + +This returns the current name for the given talloc pointer. See +talloc_set_name() for details. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_init(const char *fmt, ...); + +This function creates a zero length named talloc context as a top +level context. It is equivalent to: + + talloc_named(NULL, 0, fmt, ...); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_new(void *ctx); + +This is a utility macro that creates a new memory context hanging +off an exiting context, automatically naming it "talloc_new: __location__" +where __location__ is the source line it is called from. It is +particularly useful for creating a new temporary working context. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(type *)talloc_realloc(const void *context, void *ptr, type, count); + +The talloc_realloc() macro changes the size of a talloc +pointer. The "count" argument is the number of elements of type "type" +that you want the resulting pointer to hold. + +talloc_realloc() has the following equivalences: + + talloc_realloc(context, NULL, type, 1) ==> talloc(context, type); + talloc_realloc(context, NULL, type, N) ==> talloc_array(context, type, N); + talloc_realloc(context, ptr, type, 0) ==> talloc_free(ptr); + +The "context" argument is only used if "ptr" is NULL, otherwise it is +ignored. + +talloc_realloc() returns the new pointer, or NULL on failure. The call +will fail either due to a lack of memory, or because the pointer has +more than one parent (see talloc_reference()). + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_realloc_size(const void *context, void *ptr, size_t size); + +the talloc_realloc_size() function is useful when the type is not +known so the typesafe talloc_realloc() cannot be used. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_steal(const void *new_ctx, const void *ptr); + +The talloc_steal() function changes the parent context of a talloc +pointer. It is typically used when the context that the pointer is +currently a child of is going to be freed and you wish to keep the +memory for a longer time. + +The talloc_steal() function returns the pointer that you pass it. It +does not have any failure modes. + +NOTE: It is possible to produce loops in the parent/child relationship +if you are not careful with talloc_steal(). No guarantees are provided +as to your sanity or the safety of your data if you do this. + +talloc_steal (new_ctx, NULL) will return NULL with no sideeffects. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +size_t talloc_total_size(const void *ptr); + +The talloc_total_size() function returns the total size in bytes used +by this pointer and all child pointers. Mostly useful for debugging. + +Passing NULL is allowed, but it will only give a meaningful result if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +size_t talloc_total_blocks(const void *ptr); + +The talloc_total_blocks() function returns the total memory block +count used by this pointer and all child pointers. Mostly useful for +debugging. + +Passing NULL is allowed, but it will only give a meaningful result if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *priv), + void *priv); + +This provides a more flexible reports than talloc_report(). It +will recursively call the callback for the entire tree of memory +referenced by the pointer. References in the tree are passed with +is_ref = 1 and the pointer that is referenced. + +You can pass NULL for the pointer, in which case a report is +printed for the top level memory context, but only if +talloc_enable_leak_report() or talloc_enable_leak_report_full() +has been called. + +The recursion is stopped when depth >= max_depth. +max_depth = -1 means only stop at leaf nodes. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); + +This provides a more flexible reports than talloc_report(). It +will let you specify the depth and max_depth. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_report(const void *ptr, FILE *f); + +The talloc_report() function prints a summary report of all memory +used by ptr. One line of report is printed for each immediate child of +ptr, showing the total memory and number of blocks used by that child. + +You can pass NULL for the pointer, in which case a report is printed +for the top level memory context, but only if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_report_full(const void *ptr, FILE *f); + +This provides a more detailed report than talloc_report(). It will +recursively print the ensire tree of memory referenced by the +pointer. References in the tree are shown by giving the name of the +pointer that is referenced. + +You can pass NULL for the pointer, in which case a report is printed +for the top level memory context, but only if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_enable_leak_report(void); + +This enables calling of talloc_report(NULL, stderr) when the program +exits. In Samba4 this is enabled by using the --leak-report command +line option. + +For it to be useful, this function must be called before any other +talloc function as it establishes a "null context" that acts as the +top of the tree. If you don't call this function first then passing +NULL to talloc_report() or talloc_report_full() won't give you the +full tree printout. + +Here is a typical talloc report: + +talloc report on 'null_context' (total 267 bytes in 15 blocks) + libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + iconv(UTF8,CP850) contains 42 bytes in 2 blocks + libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + iconv(CP850,UTF8) contains 42 bytes in 2 blocks + iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks + iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_enable_leak_report_full(void); + +This enables calling of talloc_report_full(NULL, stderr) when the +program exits. In Samba4 this is enabled by using the +--leak-report-full command line option. + +For it to be useful, this function must be called before any other +talloc function as it establishes a "null context" that acts as the +top of the tree. If you don't call this function first then passing +NULL to talloc_report() or talloc_report_full() won't give you the +full tree printout. + +Here is a typical full report: + +full talloc report on 'root' (total 18 bytes in 8 blocks) + p1 contains 18 bytes in 7 blocks (ref 0) + r1 contains 13 bytes in 2 blocks (ref 0) + reference to: p2 + p2 contains 1 bytes in 1 blocks (ref 1) + x3 contains 1 bytes in 1 blocks (ref 0) + x2 contains 1 bytes in 1 blocks (ref 0) + x1 contains 1 bytes in 1 blocks (ref 0) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_enable_null_tracking(void); + +This enables tracking of the NULL memory context without enabling leak +reporting on exit. Useful for when you want to do your own leak +reporting call via talloc_report_null_full(); + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_disable_null_tracking(void); + +This disables tracking of the NULL memory context. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(type *)talloc_zero(const void *ctx, type); + +The talloc_zero() macro is equivalent to: + + ptr = talloc(ctx, type); + if (ptr) memset(ptr, 0, sizeof(type)); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_zero_size(const void *ctx, size_t size) + +The talloc_zero_size() function is useful when you don't have a known type + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_memdup(const void *ctx, const void *p, size_t size); + +The talloc_memdup() function is equivalent to: + + ptr = talloc_size(ctx, size); + if (ptr) memcpy(ptr, p, size); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_strdup(const void *ctx, const char *p); + +The talloc_strdup() function is equivalent to: + + ptr = talloc_size(ctx, strlen(p)+1); + if (ptr) memcpy(ptr, p, strlen(p)+1); + +This functions sets the name of the new pointer to the passed +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_strndup(const void *t, const char *p, size_t n); + +The talloc_strndup() function is the talloc equivalent of the C +library function strndup() + +This functions sets the name of the new pointer to the passed +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_append_string(const void *t, char *orig, const char *append); + +The talloc_append_string() function appends the given formatted +string to the given string. + +This function sets the name of the new pointer to the new +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap); + +The talloc_vasprintf() function is the talloc equivalent of the C +library function vasprintf() + +This functions sets the name of the new pointer to the new +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_asprintf(const void *t, const char *fmt, ...); + +The talloc_asprintf() function is the talloc equivalent of the C +library function asprintf() + +This functions sets the name of the new pointer to the new +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_asprintf_append(char *s, const char *fmt, ...); + +The talloc_asprintf_append() function appends the given formatted +string to the given string. + +This functions sets the name of the new pointer to the new +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(type *)talloc_array(const void *ctx, type, uint_t count); + +The talloc_array() macro is equivalent to: + + (type *)talloc_size(ctx, sizeof(type) * count); + +except that it provides integer overflow protection for the multiply, +returning NULL if the multiply overflows. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_array_size(const void *ctx, size_t size, uint_t count); + +The talloc_array_size() function is useful when the type is not +known. It operates in the same way as talloc_array(), but takes a size +instead of a type. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(typeof(ptr)) talloc_array_ptrtype(const void *ctx, ptr, uint_t count); + +The talloc_ptrtype() macro should be used when you have a pointer to an array +and want to allocate memory of an array to point at with this pointer. When compiling +with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_array_size() +and talloc_get_name() will return the current location in the source file. +and not the type. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size); + +This is a non-macro version of talloc_realloc(), which is useful +as libraries sometimes want a ralloc function pointer. A realloc() +implementation encapsulates the functionality of malloc(), free() and +realloc() in one call, which is why it is useful to be able to pass +around a single function pointer. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_autofree_context(void); + +This is a handy utility function that returns a talloc context +which will be automatically freed on program exit. This can be used +to reduce the noise in memory leak reports. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_check_name(const void *ptr, const char *name); + +This function checks if a pointer has the specified name. If it does +then the pointer is returned. It it doesn't then NULL is returned. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(type *)talloc_get_type(const void *ptr, type); + +This macro allows you to do type checking on talloc pointers. It is +particularly useful for void* private pointers. It is equivalent to +this: + + (type *)talloc_check_name(ptr, #type) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +talloc_set_type(const void *ptr, type); + +This macro allows you to force the name of a pointer to be a +particular type. This can be used in conjunction with +talloc_get_type() to do type checking on void* pointers. + +It is equivalent to this: + talloc_set_name_const(ptr, #type) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +talloc_get_size(const void *ctx); + +This function lets you know the amount of memory alloced so far by +this context. It does NOT account for subcontext memory. +This can be used to calculate the size of an array. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_find_parent_byname(const void *ctx, const char *name); + +Find a parent memory context of the current context that has the given +name. This can be very useful in complex programs where it may be +difficult to pass all information down to the level you need, but you +know the structure you want is a parent of another context. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +(type *)talloc_find_parent_bytype(ctx, type); + +Like talloc_find_parent_byname() but takes a type, making it typesafe. + diff --git a/talloc/test/run.c b/talloc/test/run.c new file mode 100644 index 00000000..6157c383 --- /dev/null +++ b/talloc/test/run.c @@ -0,0 +1,871 @@ +/* + Unix SMB/CIFS implementation. + + local testing of talloc routines. + + Copyright (C) Andrew Tridgell 2004 + Converted to ccan tests by Rusty Russell 2008 + + ** 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 + 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 +*/ + +#include "talloc/talloc.c" +#include +#include "tap.h" + +#define torture_assert(test, expr, str) \ + ok(expr, "failure: %s [\n%s: Expression %s failed: %s\n]\n", \ + test, __location__, #expr, str) + +#define torture_assert_str_equal(test, arg1, arg2, desc) \ + ok(strcmp(arg1, arg2) == 0, \ + "failure: %s [\n%s: Expected %s, got %s: %s\n]\n", \ + test, __location__, arg1, arg2, desc) + +#define CHECK_SIZE(test, ptr, tsize) \ + ok(talloc_total_size(ptr) == (tsize), \ + "failed: %s [\nwrong '%s' tree size: got %u expected %u\n]\n", \ + test, #ptr, \ + (unsigned)talloc_total_size(ptr), \ + (unsigned)tsize) + +#define CHECK_BLOCKS(test, ptr, tblocks) \ + ok(talloc_total_blocks(ptr) == (tblocks), \ + "failed: %s [\nwrong '%s' tree blocks: got %u expected %u\n]\n", \ + test, #ptr, \ + (unsigned)talloc_total_blocks(ptr), \ + (unsigned)tblocks) + +#define CHECK_PARENT(test, ptr, parent) \ + ok(talloc_parent(ptr) == (parent), \ + "failed: %s [\n'%s' has wrong parent: got %p expected %p\n]\n", \ + test, #ptr, \ + talloc_parent(ptr), \ + (parent)) + +/* + test references +*/ +static bool test_ref1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 2, "x2"); + talloc_named_const(p1, 3, "x3"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("ref1", p1, 5); + CHECK_BLOCKS("ref1", p2, 1); + CHECK_BLOCKS("ref1", r1, 2); + + talloc_free(p2); + + CHECK_BLOCKS("ref1", p1, 5); + CHECK_BLOCKS("ref1", p2, 1); + CHECK_BLOCKS("ref1", r1, 1); + + talloc_free(p1); + + CHECK_BLOCKS("ref1", r1, 1); + + talloc_free(r1); + + if (talloc_reference(root, NULL)) { + return false; + } + + CHECK_BLOCKS("ref1", root, 1); + + CHECK_SIZE("ref1", root, 0); + + talloc_free(root); + return true; +} + +/* + test references +*/ +static bool test_ref2(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("ref2", p1, 5); + CHECK_BLOCKS("ref2", p2, 1); + CHECK_BLOCKS("ref2", r1, 2); + + talloc_free(ref); + + CHECK_BLOCKS("ref2", p1, 5); + CHECK_BLOCKS("ref2", p2, 1); + CHECK_BLOCKS("ref2", r1, 1); + + talloc_free(p2); + + CHECK_BLOCKS("ref2", p1, 4); + CHECK_BLOCKS("ref2", r1, 1); + + talloc_free(p1); + + CHECK_BLOCKS("ref2", r1, 1); + + talloc_free(r1); + + CHECK_SIZE("ref2", root, 0); + + talloc_free(root); + return true; +} + +/* + test references +*/ +static bool test_ref3(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(root, 1, "p2"); + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(p2, r1); + + CHECK_BLOCKS("ref3", p1, 2); + CHECK_BLOCKS("ref3", p2, 2); + CHECK_BLOCKS("ref3", r1, 1); + + talloc_free(p1); + + CHECK_BLOCKS("ref3", p2, 2); + CHECK_BLOCKS("ref3", r1, 1); + + talloc_free(p2); + + CHECK_SIZE("ref3", root, 0); + + talloc_free(root); + + return true; +} + +/* + test references +*/ +static bool test_ref4(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("ref4", p1, 5); + CHECK_BLOCKS("ref4", p2, 1); + CHECK_BLOCKS("ref4", r1, 2); + + talloc_free(r1); + + CHECK_BLOCKS("ref4", p1, 5); + CHECK_BLOCKS("ref4", p2, 1); + + talloc_free(p2); + + CHECK_BLOCKS("ref4", p1, 4); + + talloc_free(p1); + + CHECK_SIZE("ref4", root, 0); + + talloc_free(root); + + return true; +} + + +/* + test references +*/ +static bool test_unlink1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(r1, p2); + + CHECK_BLOCKS("unlink", p1, 7); + CHECK_BLOCKS("unlink", p2, 1); + CHECK_BLOCKS("unlink", r1, 2); + + talloc_unlink(r1, p2); + + CHECK_BLOCKS("unlink", p1, 6); + CHECK_BLOCKS("unlink", p2, 1); + CHECK_BLOCKS("unlink", r1, 1); + + talloc_free(p1); + + CHECK_SIZE("unlink", root, 0); + + talloc_free(root); + + return true; +} + +static int fail_destructor(void *ptr) +{ + return -1; +} + +/* + miscellaneous tests to try to get a higher test coverage percentage +*/ +static bool test_misc(void) +{ + void *root, *p1; + char *p2; + double *d; + const char *name; + + root = talloc_new(NULL); + + p1 = talloc_size(root, 0x7fffffff); + torture_assert("misc", !p1, "failed: large talloc allowed\n"); + + p1 = talloc_strdup(root, "foo"); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_free(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_unlink(NULL, p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + p2 = talloc_strdup(p1, "foo"); + torture_assert("misc", talloc_unlink(root, p2) == -1, + "failed: talloc_unlink() of non-reference context should return -1\n"); + torture_assert("misc", talloc_unlink(p1, p2) == 0, + "failed: talloc_unlink() of parent should succeed\n"); + talloc_free(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + + name = talloc_set_name(p1, "my name is %s", "foo"); + torture_assert_str_equal("misc", talloc_get_name(p1), "my name is foo", + "failed: wrong name after talloc_set_name(my name is foo)"); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + + talloc_set_name_const(p1, NULL); + torture_assert_str_equal ("misc", talloc_get_name(p1), "UNNAMED", + "failed: wrong name after talloc_set_name(NULL)"); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + + torture_assert("misc", talloc_free(NULL) == -1, + "talloc_free(NULL) should give -1\n"); + + talloc_set_destructor(p1, fail_destructor); + torture_assert("misc", talloc_free(p1) == -1, + "Failed destructor should cause talloc_free to fail\n"); + talloc_set_destructor(p1, NULL); + + + p2 = (char *)talloc_zero_size(p1, 20); + torture_assert("misc", p2[19] == 0, "Failed to give zero memory\n"); + talloc_free(p2); + + torture_assert("misc", talloc_strdup(root, NULL) == NULL, + "failed: strdup on NULL should give NULL\n"); + + p2 = talloc_strndup(p1, "foo", 2); + torture_assert("misc", strcmp("fo", p2) == 0, + "strndup doesn't work\n"); + p2 = talloc_asprintf_append(p2, "o%c", 'd'); + torture_assert("misc", strcmp("food", p2) == 0, + "talloc_asprintf_append doesn't work\n"); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 3); + + p2 = talloc_asprintf_append(NULL, "hello %s", "world"); + torture_assert("misc", strcmp("hello world", p2) == 0, + "talloc_asprintf_append doesn't work\n"); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 3); + talloc_free(p2); + + d = talloc_array(p1, double, 0x20000000); + torture_assert("misc", !d, "failed: integer overflow not detected\n"); + + d = talloc_realloc(p1, d, double, 0x20000000); + torture_assert("misc", !d, "failed: integer overflow not detected\n"); + + talloc_free(p1); + CHECK_BLOCKS("misc", root, 1); + + p1 = talloc_named(root, 100, "%d bytes", 100); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + talloc_unlink(root, p1); + + p1 = talloc_init("%d bytes", 200); + p2 = talloc_asprintf(p1, "my test '%s'", "string"); + torture_assert_str_equal("misc", p2, "my test 'string'", + "failed: talloc_asprintf(\"my test '%%s'\", \"string\") gave: \"%s\""); + CHECK_BLOCKS("misc", p1, 3); + CHECK_SIZE("misc", p2, 17); + CHECK_BLOCKS("misc", root, 1); + talloc_unlink(NULL, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = (char *)talloc_named_const(root, 20, "p2"); + (void)talloc_reference(p1, p2); + talloc_unlink(root, p2); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + talloc_unlink(p1, p2); + talloc_unlink(root, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = (char *)talloc_named_const(root, 20, "p2"); + (void)talloc_reference(NULL, p2); + talloc_unlink(root, p2); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_unlink(NULL, p2); + talloc_unlink(root, p1); + + /* Test that talloc_unlink is a no-op */ + + torture_assert("misc", talloc_unlink(root, NULL) == -1, + "failed: talloc_unlink(root, NULL) == -1\n"); + + CHECK_SIZE("misc", root, 0); + + talloc_free(root); + + CHECK_SIZE("misc", NULL, 0); + + talloc_enable_leak_report(); + talloc_enable_leak_report_full(); + + return true; +} + + +/* + test realloc +*/ +static bool test_realloc(void) +{ + void *root, *p1, *p2; + + root = talloc_new(NULL); + + p1 = talloc_size(root, 10); + CHECK_SIZE("realloc", p1, 10); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE("realloc", p1, 20); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, NULL, 30); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, p2, 40); + + CHECK_SIZE("realloc", p2, 40); + CHECK_SIZE("realloc", root, 60); + CHECK_BLOCKS("realloc", p1, 4); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE("realloc", p1, 60); + + talloc_increase_ref_count(p2); + torture_assert("realloc", talloc_realloc_size(NULL, p2, 5) == NULL, + "failed: talloc_realloc() on a referenced pointer should fail\n"); + CHECK_BLOCKS("realloc", p1, 4); + + talloc_realloc_size(NULL, p2, 0); + talloc_realloc_size(NULL, p2, 0); + CHECK_BLOCKS("realloc", p1, 3); + + torture_assert("realloc", talloc_realloc_size(NULL, p1, 0x7fffffff) == NULL, + "failed: oversize talloc should fail\n"); + + talloc_realloc_size(NULL, p1, 0); + + CHECK_BLOCKS("realloc", root, 1); + CHECK_SIZE("realloc", root, 0); + + talloc_free(root); + + return true; +} + +/* + test realloc with a child +*/ +static bool test_realloc_child(void) +{ + void *root; + struct el2 { + const char *name; + } *el2; + struct el1 { + int count; + struct el2 **list, **list2, **list3; + } *el1; + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + el1->list = talloc(el1, struct el2 *); + el1->list[0] = talloc(el1->list, struct el2); + el1->list[0]->name = talloc_strdup(el1->list[0], "testing"); + + el1->list2 = talloc(el1, struct el2 *); + el1->list2[0] = talloc(el1->list2, struct el2); + el1->list2[0]->name = talloc_strdup(el1->list2[0], "testing2"); + + el1->list3 = talloc(el1, struct el2 *); + el1->list3[0] = talloc(el1->list3, struct el2); + el1->list3[0]->name = talloc_strdup(el1->list3[0], "testing2"); + + el2 = talloc(el1->list, struct el2); + el2 = talloc(el1->list2, struct el2); + el2 = talloc(el1->list3, struct el2); + + el1->list = talloc_realloc(el1, el1->list, struct el2 *, 100); + el1->list2 = talloc_realloc(el1, el1->list2, struct el2 *, 200); + el1->list3 = talloc_realloc(el1, el1->list3, struct el2 *, 300); + + talloc_free(root); + + return true; +} + +/* + test type checking +*/ +static bool test_type(void) +{ + void *root; + struct el1 { + int count; + }; + struct el2 { + int count; + }; + struct el1 *el1; + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + + el1->count = 1; + + torture_assert("type", talloc_get_type(el1, struct el1) == el1, + "type check failed on el1\n"); + torture_assert("type", talloc_get_type(el1, struct el2) == NULL, + "type check failed on el1 with el2\n"); + talloc_set_type(el1, struct el2); + torture_assert("type", talloc_get_type(el1, struct el2) == (struct el2 *)el1, + "type set failed on el1 with el2\n"); + + talloc_free(root); + + return true; +} + +/* + test steal +*/ +static bool test_steal(void) +{ + void *root, *p1, *p2; + + root = talloc_new(NULL); + + p1 = talloc_array(root, char, 10); + CHECK_SIZE("steal", p1, 10); + + p2 = talloc_realloc(root, NULL, char, 20); + CHECK_SIZE("steal", p1, 10); + CHECK_SIZE("steal", root, 30); + + torture_assert("steal", talloc_steal(p1, NULL) == NULL, + "failed: stealing NULL should give NULL\n"); + + torture_assert("steal", talloc_steal(p1, p1) == p1, + "failed: stealing to ourselves is a nop\n"); + CHECK_BLOCKS("steal", root, 3); + CHECK_SIZE("steal", root, 30); + + talloc_steal(NULL, p1); + talloc_steal(NULL, p2); + CHECK_BLOCKS("steal", root, 1); + CHECK_SIZE("steal", root, 0); + + talloc_free(p1); + talloc_steal(root, p2); + CHECK_BLOCKS("steal", root, 2); + CHECK_SIZE("steal", root, 20); + + talloc_free(p2); + + CHECK_BLOCKS("steal", root, 1); + CHECK_SIZE("steal", root, 0); + + talloc_free(root); + + p1 = talloc_size(NULL, 3); + CHECK_SIZE("steal", NULL, 3); + talloc_free(p1); + + return true; +} + +/* + test move +*/ +static bool test_move(void) +{ + void *root; + struct t_move { + char *p; + int *x; + } *t1, *t2; + + root = talloc_new(NULL); + + t1 = talloc(root, struct t_move); + t2 = talloc(root, struct t_move); + t1->p = talloc_strdup(t1, "foo"); + t1->x = talloc(t1, int); + *t1->x = 42; + + t2->p = talloc_move(t2, &t1->p); + t2->x = talloc_move(t2, &t1->x); + torture_assert("move", t1->p == NULL && t1->x == NULL && + strcmp(t2->p, "foo") == 0 && *t2->x == 42, + "talloc move failed"); + + talloc_free(root); + + return true; +} + +/* + test talloc_realloc_fn +*/ +static bool test_realloc_fn(void) +{ + void *root, *p1; + + root = talloc_new(NULL); + + p1 = talloc_realloc_fn(root, NULL, 10); + CHECK_BLOCKS("realloc_fn", root, 2); + CHECK_SIZE("realloc_fn", root, 10); + p1 = talloc_realloc_fn(root, p1, 20); + CHECK_BLOCKS("realloc_fn", root, 2); + CHECK_SIZE("realloc_fn", root, 20); + p1 = talloc_realloc_fn(root, p1, 0); + CHECK_BLOCKS("realloc_fn", root, 1); + CHECK_SIZE("realloc_fn", root, 0); + + talloc_free(root); + + return true; +} + + +static bool test_unref_reparent(void) +{ + void *root, *p1, *p2, *c1; + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "orig parent"); + p2 = talloc_named_const(root, 1, "parent by reference"); + + c1 = talloc_named_const(p1, 1, "child"); + talloc_reference(p2, c1); + + CHECK_PARENT("unref_reparent", c1, p1); + + talloc_free(p1); + + CHECK_PARENT("unref_reparent", c1, p2); + + talloc_unlink(p2, c1); + + CHECK_SIZE("unref_reparent", root, 1); + + talloc_free(p2); + talloc_free(root); + + return true; +} + +static bool test_lifeless(void) +{ + void *top = talloc_new(NULL); + char *parent, *child; + void *child_owner = talloc_new(NULL); + + parent = talloc_strdup(top, "parent"); + child = talloc_strdup(parent, "child"); + (void)talloc_reference(child, parent); + (void)talloc_reference(child_owner, child); + talloc_unlink(top, parent); + talloc_free(child); + talloc_free(top); + talloc_free(child_owner); + talloc_free(child); + + return true; +} + +static int loop_destructor_count; + +static int test_loop_destructor(char *ptr) +{ + loop_destructor_count++; + return 0; +} + +static bool test_loop(void) +{ + void *top = talloc_new(NULL); + char *parent; + struct req1 { + char *req2, *req3; + } *req1; + + parent = talloc_strdup(top, "parent"); + req1 = talloc(parent, struct req1); + req1->req2 = talloc_strdup(req1, "req2"); + talloc_set_destructor(req1->req2, test_loop_destructor); + req1->req3 = talloc_strdup(req1, "req3"); + (void)talloc_reference(req1->req3, req1); + talloc_free(parent); + talloc_free(top); + + torture_assert("loop", loop_destructor_count == 1, + "FAILED TO FIRE LOOP DESTRUCTOR\n"); + loop_destructor_count = 0; + + return true; +} + +static int fail_destructor_str(char *ptr) +{ + return -1; +} + +static bool test_free_parent_deny_child(void) +{ + void *top = talloc_new(NULL); + char *level1; + char *level2; + char *level3; + + level1 = talloc_strdup(top, "level1"); + level2 = talloc_strdup(level1, "level2"); + level3 = talloc_strdup(level2, "level3"); + + talloc_set_destructor(level3, fail_destructor_str); + talloc_free(level1); + talloc_set_destructor(level3, NULL); + + CHECK_PARENT("free_parent_deny_child", level3, top); + + talloc_free(top); + + return true; +} + +static bool test_talloc_ptrtype(void) +{ + void *top = talloc_new(NULL); + struct struct1 { + int foo; + int bar; + } *s1, *s2, **s3, ***s4; + const char *location1; + const char *location2; + const char *location3; + const char *location4; + + s1 = talloc_ptrtype(top, s1);location1 = __location__; + + ok1(talloc_get_size(s1) == sizeof(struct struct1)); + + ok1(strcmp(location1, talloc_get_name(s1)) == 0); + + s2 = talloc_array_ptrtype(top, s2, 10);location2 = __location__; + + ok1(talloc_get_size(s2) == (sizeof(struct struct1) * 10)); + + ok1(strcmp(location2, talloc_get_name(s2)) == 0); + + s3 = talloc_array_ptrtype(top, s3, 10);location3 = __location__; + + ok1(talloc_get_size(s3) == (sizeof(struct struct1 *) * 10)); + + torture_assert_str_equal("ptrtype", location3, talloc_get_name(s3), + "talloc_array_ptrtype() sets the wrong name"); + + s4 = talloc_array_ptrtype(top, s4, 10);location4 = __location__; + + ok1(talloc_get_size(s4) == (sizeof(struct struct1 **) * 10)); + + torture_assert_str_equal("ptrtype", location4, talloc_get_name(s4), + "talloc_array_ptrtype() sets the wrong name"); + + talloc_free(top); + + return true; +} + +static int _test_talloc_free_in_destructor(void **ptr) +{ + talloc_free(*ptr); + return 0; +} + +static bool test_talloc_free_in_destructor(void) +{ + void *level0; + void *level1; + void *level2; + void *level3; + void *level4; + void **level5; + + level0 = talloc_new(NULL); + level1 = talloc_new(level0); + level2 = talloc_new(level1); + level3 = talloc_new(level2); + level4 = talloc_new(level3); + level5 = talloc(level4, void *); + + *level5 = level3; + (void)talloc_reference(level0, level3); + (void)talloc_reference(level3, level3); + (void)talloc_reference(level5, level3); + + talloc_set_destructor(level5, _test_talloc_free_in_destructor); + + talloc_free(level1); + + talloc_free(level0); + + return true; +} + +static bool test_autofree(void) +{ + /* autofree test would kill smbtorture */ + void *p; + p = talloc_autofree_context(); + talloc_free(p); + + p = talloc_autofree_context(); + talloc_free(p); + + return true; +} + +struct torture_context; +static bool torture_local_talloc(struct torture_context *tctx) +{ + bool ret = true; + + setlinebuf(stdout); + + talloc_disable_null_tracking(); + talloc_enable_null_tracking(); + + ret &= test_ref1(); + ret &= test_ref2(); + ret &= test_ref3(); + ret &= test_ref4(); + ret &= test_unlink1(); + ret &= test_misc(); + ret &= test_realloc(); + ret &= test_realloc_child(); + ret &= test_steal(); + ret &= test_move(); + ret &= test_unref_reparent(); + ret &= test_realloc_fn(); + ret &= test_type(); + ret &= test_lifeless(); + ret &= test_loop(); + ret &= test_free_parent_deny_child(); + ret &= test_talloc_ptrtype(); + ret &= test_talloc_free_in_destructor(); + ret &= test_autofree(); + + return ret; +} + +int main(void) +{ + plan_tests(134); + + torture_local_talloc(NULL); + return exit_status(); +} + diff --git a/talloc/testsuite.c b/talloc/testsuite.c new file mode 100644 index 00000000..587f2705 --- /dev/null +++ b/talloc/testsuite.c @@ -0,0 +1,1115 @@ +/* + Unix SMB/CIFS implementation. + + local testing of talloc routines. + + Copyright (C) Andrew Tridgell 2004 + + ** 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 + 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 +*/ + +#include "replace.h" +#include "system/time.h" +#include "talloc.h" + +static struct timeval timeval_current(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv; +} + +static double timeval_elapsed(struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + return (tv2.tv_sec - tv->tv_sec) + + (tv2.tv_usec - tv->tv_usec)*1.0e-6; +} + +#define torture_assert(test, expr, str) if (!(expr)) { \ + printf("failure: %s [\n%s: Expression %s failed: %s\n]\n", \ + test, __location__, #expr, str); \ + return false; \ +} + +#define torture_assert_str_equal(test, arg1, arg2, desc) \ + if (strcmp(arg1, arg2)) { \ + printf("failure: %s [\n%s: Expected %s, got %s: %s\n]\n", \ + test, __location__, arg1, arg2, desc); \ + return false; \ + } + +#if _SAMBA_BUILD_==3 +#ifdef malloc +#undef malloc +#endif +#ifdef strdup +#undef strdup +#endif +#endif + +#define CHECK_SIZE(test, ptr, tsize) do { \ + if (talloc_total_size(ptr) != (tsize)) { \ + printf("failed: %s [\nwrong '%s' tree size: got %u expected %u\n]\n", \ + test, #ptr, \ + (unsigned)talloc_total_size(ptr), \ + (unsigned)tsize); \ + talloc_report_full(ptr, stdout); \ + return false; \ + } \ +} while (0) + +#define CHECK_BLOCKS(test, ptr, tblocks) do { \ + if (talloc_total_blocks(ptr) != (tblocks)) { \ + printf("failed: %s [\nwrong '%s' tree blocks: got %u expected %u\n]\n", \ + test, #ptr, \ + (unsigned)talloc_total_blocks(ptr), \ + (unsigned)tblocks); \ + talloc_report_full(ptr, stdout); \ + return false; \ + } \ +} while (0) + +#define CHECK_PARENT(test, ptr, parent) do { \ + if (talloc_parent(ptr) != (parent)) { \ + printf("failed: %s [\n'%s' has wrong parent: got %p expected %p\n]\n", \ + test, #ptr, \ + talloc_parent(ptr), \ + (parent)); \ + talloc_report_full(ptr, stdout); \ + talloc_report_full(parent, stdout); \ + talloc_report_full(NULL, stdout); \ + return false; \ + } \ +} while (0) + + +/* + test references +*/ +static bool test_ref1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("test: ref1 [\nSINGLE REFERENCE FREE\n]\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 2, "x2"); + talloc_named_const(p1, 3, "x3"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref1", p1, 5); + CHECK_BLOCKS("ref1", p2, 1); + CHECK_BLOCKS("ref1", r1, 2); + + fprintf(stderr, "Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref1", p1, 5); + CHECK_BLOCKS("ref1", p2, 1); + CHECK_BLOCKS("ref1", r1, 1); + + fprintf(stderr, "Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref1", r1, 1); + + fprintf(stderr, "Freeing r1\n"); + talloc_free(r1); + talloc_report_full(NULL, stderr); + + fprintf(stderr, "Testing NULL\n"); + if (talloc_reference(root, NULL)) { + return false; + } + + CHECK_BLOCKS("ref1", root, 1); + + CHECK_SIZE("ref1", root, 0); + + talloc_free(root); + printf("success: ref1\n"); + return true; +} + +/* + test references +*/ +static bool test_ref2(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("test: ref2 [\nDOUBLE REFERENCE FREE\n]\n"); + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref2", p1, 5); + CHECK_BLOCKS("ref2", p2, 1); + CHECK_BLOCKS("ref2", r1, 2); + + fprintf(stderr, "Freeing ref\n"); + talloc_free(ref); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref2", p1, 5); + CHECK_BLOCKS("ref2", p2, 1); + CHECK_BLOCKS("ref2", r1, 1); + + fprintf(stderr, "Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref2", p1, 4); + CHECK_BLOCKS("ref2", r1, 1); + + fprintf(stderr, "Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref2", r1, 1); + + fprintf(stderr, "Freeing r1\n"); + talloc_free(r1); + talloc_report_full(root, stderr); + + CHECK_SIZE("ref2", root, 0); + + talloc_free(root); + printf("success: ref2\n"); + return true; +} + +/* + test references +*/ +static bool test_ref3(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("test: ref3 [\nPARENT REFERENCE FREE\n]\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(root, 1, "p2"); + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(p2, r1); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref3", p1, 2); + CHECK_BLOCKS("ref3", p2, 2); + CHECK_BLOCKS("ref3", r1, 1); + + fprintf(stderr, "Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref3", p2, 2); + CHECK_BLOCKS("ref3", r1, 1); + + fprintf(stderr, "Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stderr); + + CHECK_SIZE("ref3", root, 0); + + talloc_free(root); + + printf("success: ref3\n"); + return true; +} + +/* + test references +*/ +static bool test_ref4(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("test: ref4 [\nREFERRER REFERENCE FREE\n]\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref4", p1, 5); + CHECK_BLOCKS("ref4", p2, 1); + CHECK_BLOCKS("ref4", r1, 2); + + fprintf(stderr, "Freeing r1\n"); + talloc_free(r1); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref4", p1, 5); + CHECK_BLOCKS("ref4", p2, 1); + + fprintf(stderr, "Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("ref4", p1, 4); + + fprintf(stderr, "Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stderr); + + CHECK_SIZE("ref4", root, 0); + + talloc_free(root); + + printf("success: ref4\n"); + return true; +} + + +/* + test references +*/ +static bool test_unlink1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("test: unlink [\nUNLINK\n]\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("unlink", p1, 7); + CHECK_BLOCKS("unlink", p2, 1); + CHECK_BLOCKS("unlink", r1, 2); + + fprintf(stderr, "Unreferencing r1\n"); + talloc_unlink(r1, p2); + talloc_report_full(root, stderr); + + CHECK_BLOCKS("unlink", p1, 6); + CHECK_BLOCKS("unlink", p2, 1); + CHECK_BLOCKS("unlink", r1, 1); + + fprintf(stderr, "Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stderr); + + CHECK_SIZE("unlink", root, 0); + + talloc_free(root); + + printf("success: unlink\n"); + return true; +} + +static int fail_destructor(void *ptr) +{ + return -1; +} + +/* + miscellaneous tests to try to get a higher test coverage percentage +*/ +static bool test_misc(void) +{ + void *root, *p1; + char *p2; + double *d; + const char *name; + + printf("test: misc [\nMISCELLANEOUS\n]\n"); + + root = talloc_new(NULL); + + p1 = talloc_size(root, 0x7fffffff); + torture_assert("misc", !p1, "failed: large talloc allowed\n"); + + p1 = talloc_strdup(root, "foo"); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_free(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_unlink(NULL, p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + p2 = talloc_strdup(p1, "foo"); + torture_assert("misc", talloc_unlink(root, p2) == -1, + "failed: talloc_unlink() of non-reference context should return -1\n"); + torture_assert("misc", talloc_unlink(p1, p2) == 0, + "failed: talloc_unlink() of parent should succeed\n"); + talloc_free(p1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + + name = talloc_set_name(p1, "my name is %s", "foo"); + torture_assert_str_equal("misc", talloc_get_name(p1), "my name is foo", + "failed: wrong name after talloc_set_name(my name is foo)"); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + + talloc_set_name_const(p1, NULL); + torture_assert_str_equal ("misc", talloc_get_name(p1), "UNNAMED", + "failed: wrong name after talloc_set_name(NULL)"); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + + torture_assert("misc", talloc_free(NULL) == -1, + "talloc_free(NULL) should give -1\n"); + + talloc_set_destructor(p1, fail_destructor); + torture_assert("misc", talloc_free(p1) == -1, + "Failed destructor should cause talloc_free to fail\n"); + talloc_set_destructor(p1, NULL); + + talloc_report(root, stderr); + + + p2 = (char *)talloc_zero_size(p1, 20); + torture_assert("misc", p2[19] == 0, "Failed to give zero memory\n"); + talloc_free(p2); + + torture_assert("misc", talloc_strdup(root, NULL) == NULL, + "failed: strdup on NULL should give NULL\n"); + + p2 = talloc_strndup(p1, "foo", 2); + torture_assert("misc", strcmp("fo", p2) == 0, + "strndup doesn't work\n"); + p2 = talloc_asprintf_append(p2, "o%c", 'd'); + torture_assert("misc", strcmp("food", p2) == 0, + "talloc_asprintf_append doesn't work\n"); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 3); + + p2 = talloc_asprintf_append(NULL, "hello %s", "world"); + torture_assert("misc", strcmp("hello world", p2) == 0, + "talloc_asprintf_append doesn't work\n"); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 3); + talloc_free(p2); + + d = talloc_array(p1, double, 0x20000000); + torture_assert("misc", !d, "failed: integer overflow not detected\n"); + + d = talloc_realloc(p1, d, double, 0x20000000); + torture_assert("misc", !d, "failed: integer overflow not detected\n"); + + talloc_free(p1); + CHECK_BLOCKS("misc", root, 1); + + p1 = talloc_named(root, 100, "%d bytes", 100); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + talloc_unlink(root, p1); + + p1 = talloc_init("%d bytes", 200); + p2 = talloc_asprintf(p1, "my test '%s'", "string"); + torture_assert_str_equal("misc", p2, "my test 'string'", + "failed: talloc_asprintf(\"my test '%%s'\", \"string\") gave: \"%s\""); + CHECK_BLOCKS("misc", p1, 3); + CHECK_SIZE("misc", p2, 17); + CHECK_BLOCKS("misc", root, 1); + talloc_unlink(NULL, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = (char *)talloc_named_const(root, 20, "p2"); + (void)talloc_reference(p1, p2); + talloc_report_full(root, stderr); + talloc_unlink(root, p2); + talloc_report_full(root, stderr); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 2); + CHECK_BLOCKS("misc", root, 3); + talloc_unlink(p1, p2); + talloc_unlink(root, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = (char *)talloc_named_const(root, 20, "p2"); + (void)talloc_reference(NULL, p2); + talloc_report_full(root, stderr); + talloc_unlink(root, p2); + talloc_report_full(root, stderr); + CHECK_BLOCKS("misc", p2, 1); + CHECK_BLOCKS("misc", p1, 1); + CHECK_BLOCKS("misc", root, 2); + talloc_unlink(NULL, p2); + talloc_unlink(root, p1); + + /* Test that talloc_unlink is a no-op */ + + torture_assert("misc", talloc_unlink(root, NULL) == -1, + "failed: talloc_unlink(root, NULL) == -1\n"); + + talloc_report(root, stderr); + talloc_report(NULL, stderr); + + CHECK_SIZE("misc", root, 0); + + talloc_free(root); + + CHECK_SIZE("misc", NULL, 0); + + talloc_enable_leak_report(); + talloc_enable_leak_report_full(); + + printf("success: misc\n"); + + return true; +} + + +/* + test realloc +*/ +static bool test_realloc(void) +{ + void *root, *p1, *p2; + + printf("test: realloc [\nREALLOC\n]\n"); + + root = talloc_new(NULL); + + p1 = talloc_size(root, 10); + CHECK_SIZE("realloc", p1, 10); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE("realloc", p1, 20); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, NULL, 30); + + talloc_new(p1); + + p2 = talloc_realloc_size(p1, p2, 40); + + CHECK_SIZE("realloc", p2, 40); + CHECK_SIZE("realloc", root, 60); + CHECK_BLOCKS("realloc", p1, 4); + + p1 = talloc_realloc_size(NULL, p1, 20); + CHECK_SIZE("realloc", p1, 60); + + talloc_increase_ref_count(p2); + torture_assert("realloc", talloc_realloc_size(NULL, p2, 5) == NULL, + "failed: talloc_realloc() on a referenced pointer should fail\n"); + CHECK_BLOCKS("realloc", p1, 4); + + talloc_realloc_size(NULL, p2, 0); + talloc_realloc_size(NULL, p2, 0); + CHECK_BLOCKS("realloc", p1, 3); + + torture_assert("realloc", talloc_realloc_size(NULL, p1, 0x7fffffff) == NULL, + "failed: oversize talloc should fail\n"); + + talloc_realloc_size(NULL, p1, 0); + + CHECK_BLOCKS("realloc", root, 1); + CHECK_SIZE("realloc", root, 0); + + talloc_free(root); + + printf("success: REALLOC\n"); + + return true; +} + +/* + test realloc with a child +*/ +static bool test_realloc_child(void) +{ + void *root; + struct el2 { + const char *name; + } *el2; + struct el1 { + int count; + struct el2 **list, **list2, **list3; + } *el1; + + printf("test: REALLOC WITH CHILD\n"); + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + el1->list = talloc(el1, struct el2 *); + el1->list[0] = talloc(el1->list, struct el2); + el1->list[0]->name = talloc_strdup(el1->list[0], "testing"); + + el1->list2 = talloc(el1, struct el2 *); + el1->list2[0] = talloc(el1->list2, struct el2); + el1->list2[0]->name = talloc_strdup(el1->list2[0], "testing2"); + + el1->list3 = talloc(el1, struct el2 *); + el1->list3[0] = talloc(el1->list3, struct el2); + el1->list3[0]->name = talloc_strdup(el1->list3[0], "testing2"); + + el2 = talloc(el1->list, struct el2); + el2 = talloc(el1->list2, struct el2); + el2 = talloc(el1->list3, struct el2); + + el1->list = talloc_realloc(el1, el1->list, struct el2 *, 100); + el1->list2 = talloc_realloc(el1, el1->list2, struct el2 *, 200); + el1->list3 = talloc_realloc(el1, el1->list3, struct el2 *, 300); + + talloc_free(root); + + printf("success: REALLOC WITH CHILD\n"); + return true; +} + +/* + test type checking +*/ +static bool test_type(void) +{ + void *root; + struct el1 { + int count; + }; + struct el2 { + int count; + }; + struct el1 *el1; + + printf("test: type [\ntalloc type checking\n]\n"); + + root = talloc_new(NULL); + + el1 = talloc(root, struct el1); + + el1->count = 1; + + torture_assert("type", talloc_get_type(el1, struct el1) == el1, + "type check failed on el1\n"); + torture_assert("type", talloc_get_type(el1, struct el2) == NULL, + "type check failed on el1 with el2\n"); + talloc_set_type(el1, struct el2); + torture_assert("type", talloc_get_type(el1, struct el2) == (struct el2 *)el1, + "type set failed on el1 with el2\n"); + + talloc_free(root); + + printf("success: type\n"); + return true; +} + +/* + test steal +*/ +static bool test_steal(void) +{ + void *root, *p1, *p2; + + printf("test: steal [\nSTEAL\n]\n"); + + root = talloc_new(NULL); + + p1 = talloc_array(root, char, 10); + CHECK_SIZE("steal", p1, 10); + + p2 = talloc_realloc(root, NULL, char, 20); + CHECK_SIZE("steal", p1, 10); + CHECK_SIZE("steal", root, 30); + + torture_assert("steal", talloc_steal(p1, NULL) == NULL, + "failed: stealing NULL should give NULL\n"); + + torture_assert("steal", talloc_steal(p1, p1) == p1, + "failed: stealing to ourselves is a nop\n"); + CHECK_BLOCKS("steal", root, 3); + CHECK_SIZE("steal", root, 30); + + talloc_steal(NULL, p1); + talloc_steal(NULL, p2); + CHECK_BLOCKS("steal", root, 1); + CHECK_SIZE("steal", root, 0); + + talloc_free(p1); + talloc_steal(root, p2); + CHECK_BLOCKS("steal", root, 2); + CHECK_SIZE("steal", root, 20); + + talloc_free(p2); + + CHECK_BLOCKS("steal", root, 1); + CHECK_SIZE("steal", root, 0); + + talloc_free(root); + + p1 = talloc_size(NULL, 3); + talloc_report_full(NULL, stderr); + CHECK_SIZE("steal", NULL, 3); + talloc_free(p1); + + printf("success: steal\n"); + return true; +} + +/* + test move +*/ +static bool test_move(void) +{ + void *root; + struct t_move { + char *p; + int *x; + } *t1, *t2; + + printf("test: move [\nMOVE\n]\n"); + + root = talloc_new(NULL); + + t1 = talloc(root, struct t_move); + t2 = talloc(root, struct t_move); + t1->p = talloc_strdup(t1, "foo"); + t1->x = talloc(t1, int); + *t1->x = 42; + + t2->p = talloc_move(t2, &t1->p); + t2->x = talloc_move(t2, &t1->x); + torture_assert("move", t1->p == NULL && t1->x == NULL && + strcmp(t2->p, "foo") == 0 && *t2->x == 42, + "talloc move failed"); + + talloc_free(root); + + printf("success: move\n"); + + return true; +} + +/* + test talloc_realloc_fn +*/ +static bool test_realloc_fn(void) +{ + void *root, *p1; + + printf("test: realloc_fn [\ntalloc_realloc_fn\n]\n"); + + root = talloc_new(NULL); + + p1 = talloc_realloc_fn(root, NULL, 10); + CHECK_BLOCKS("realloc_fn", root, 2); + CHECK_SIZE("realloc_fn", root, 10); + p1 = talloc_realloc_fn(root, p1, 20); + CHECK_BLOCKS("realloc_fn", root, 2); + CHECK_SIZE("realloc_fn", root, 20); + p1 = talloc_realloc_fn(root, p1, 0); + CHECK_BLOCKS("realloc_fn", root, 1); + CHECK_SIZE("realloc_fn", root, 0); + + talloc_free(root); + + printf("success: realloc_fn\n"); + return true; +} + + +static bool test_unref_reparent(void) +{ + void *root, *p1, *p2, *c1; + + printf("test: unref_reparent [\nUNREFERENCE AFTER PARENT FREED\n]\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "orig parent"); + p2 = talloc_named_const(root, 1, "parent by reference"); + + c1 = talloc_named_const(p1, 1, "child"); + talloc_reference(p2, c1); + + CHECK_PARENT("unref_reparent", c1, p1); + + talloc_free(p1); + + CHECK_PARENT("unref_reparent", c1, p2); + + talloc_unlink(p2, c1); + + CHECK_SIZE("unref_reparent", root, 1); + + talloc_free(p2); + talloc_free(root); + + printf("success: unref_reparent\n"); + return true; +} + +/* + measure the speed of talloc versus malloc +*/ +static bool test_speed(void) +{ + void *ctx = talloc_new(NULL); + unsigned count; + const int loop = 1000; + int i; + struct timeval tv; + + printf("test: speed [\nTALLOC VS MALLOC SPEED\n]\n"); + + tv = timeval_current(); + count = 0; + do { + void *p1, *p2, *p3; + for (i=0;ireq2 = talloc_strdup(req1, "req2"); + talloc_set_destructor(req1->req2, test_loop_destructor); + req1->req3 = talloc_strdup(req1, "req3"); + (void)talloc_reference(req1->req3, req1); + talloc_report_full(top, stderr); + talloc_free(parent); + talloc_report_full(top, stderr); + talloc_report_full(NULL, stderr); + talloc_free(top); + + torture_assert("loop", loop_destructor_count == 1, + "FAILED TO FIRE LOOP DESTRUCTOR\n"); + loop_destructor_count = 0; + + printf("success: loop\n"); + return true; +} + +static int fail_destructor_str(char *ptr) +{ + return -1; +} + +static bool test_free_parent_deny_child(void) +{ + void *top = talloc_new(NULL); + char *level1; + char *level2; + char *level3; + + printf("test: free_parent_deny_child [\nTALLOC FREE PARENT DENY CHILD\n]\n"); + + level1 = talloc_strdup(top, "level1"); + level2 = talloc_strdup(level1, "level2"); + level3 = talloc_strdup(level2, "level3"); + + talloc_set_destructor(level3, fail_destructor_str); + talloc_free(level1); + talloc_set_destructor(level3, NULL); + + CHECK_PARENT("free_parent_deny_child", level3, top); + + talloc_free(top); + + printf("success: free_parent_deny_child\n"); + return true; +} + +static bool test_talloc_ptrtype(void) +{ + void *top = talloc_new(NULL); + struct struct1 { + int foo; + int bar; + } *s1, *s2, **s3, ***s4; + const char *location1; + const char *location2; + const char *location3; + const char *location4; + + printf("test: ptrtype [\nTALLOC PTRTYPE\n]\n"); + + s1 = talloc_ptrtype(top, s1);location1 = __location__; + + if (talloc_get_size(s1) != sizeof(struct struct1)) { + printf("failure: ptrtype [\n" + "talloc_ptrtype() allocated the wrong size %lu (should be %lu)\n" + "]\n", (unsigned long)talloc_get_size(s1), + (unsigned long)sizeof(struct struct1)); + return false; + } + + if (strcmp(location1, talloc_get_name(s1)) != 0) { + printf("failure: ptrtype [\n" + "talloc_ptrtype() sets the wrong name '%s' (should be '%s')\n]\n", + talloc_get_name(s1), location1); + return false; + } + + s2 = talloc_array_ptrtype(top, s2, 10);location2 = __location__; + + if (talloc_get_size(s2) != (sizeof(struct struct1) * 10)) { + printf("failure: ptrtype [\n" + "talloc_array_ptrtype() allocated the wrong size " + "%lu (should be %lu)\n]\n", + (unsigned long)talloc_get_size(s2), + (unsigned long)(sizeof(struct struct1)*10)); + return false; + } + + if (strcmp(location2, talloc_get_name(s2)) != 0) { + printf("failure: ptrtype [\n" + "talloc_array_ptrtype() sets the wrong name '%s' (should be '%s')\n]\n", + talloc_get_name(s2), location2); + return false; + } + + s3 = talloc_array_ptrtype(top, s3, 10);location3 = __location__; + + if (talloc_get_size(s3) != (sizeof(struct struct1 *) * 10)) { + printf("failure: ptrtype [\n" + "talloc_array_ptrtype() allocated the wrong size " + "%lu (should be %lu)\n]\n", + (unsigned long)talloc_get_size(s3), + (unsigned long)(sizeof(struct struct1 *)*10)); + return false; + } + + torture_assert_str_equal("ptrtype", location3, talloc_get_name(s3), + "talloc_array_ptrtype() sets the wrong name"); + + s4 = talloc_array_ptrtype(top, s4, 10);location4 = __location__; + + if (talloc_get_size(s4) != (sizeof(struct struct1 **) * 10)) { + printf("failure: ptrtype [\n" + "talloc_array_ptrtype() allocated the wrong size " + "%lu (should be %lu)\n]\n", + (unsigned long)talloc_get_size(s4), + (unsigned long)(sizeof(struct struct1 **)*10)); + return false; + } + + torture_assert_str_equal("ptrtype", location4, talloc_get_name(s4), + "talloc_array_ptrtype() sets the wrong name"); + + talloc_free(top); + + printf("success: ptrtype\n"); + return true; +} + +static int _test_talloc_free_in_destructor(void **ptr) +{ + talloc_free(*ptr); + return 0; +} + +static bool test_talloc_free_in_destructor(void) +{ + void *level0; + void *level1; + void *level2; + void *level3; + void *level4; + void **level5; + + printf("test: free_in_destructor [\nTALLOC FREE IN DESTRUCTOR\n]\n"); + + level0 = talloc_new(NULL); + level1 = talloc_new(level0); + level2 = talloc_new(level1); + level3 = talloc_new(level2); + level4 = talloc_new(level3); + level5 = talloc(level4, void *); + + *level5 = level3; + (void)talloc_reference(level0, level3); + (void)talloc_reference(level3, level3); + (void)talloc_reference(level5, level3); + + talloc_set_destructor(level5, _test_talloc_free_in_destructor); + + talloc_free(level1); + + talloc_free(level0); + + printf("success: free_in_destructor\n"); + return true; +} + +static bool test_autofree(void) +{ +#if _SAMBA_BUILD_ < 4 + /* autofree test would kill smbtorture */ + void *p; + printf("test: autofree [\nTALLOC AUTOFREE CONTEXT\n]\n"); + + p = talloc_autofree_context(); + talloc_free(p); + + p = talloc_autofree_context(); + talloc_free(p); + + printf("success: autofree\n"); +#endif + return true; +} + +struct torture_context; +bool torture_local_talloc(struct torture_context *tctx) +{ + bool ret = true; + + setlinebuf(stdout); + + talloc_disable_null_tracking(); + talloc_enable_null_tracking(); + + ret &= test_ref1(); + ret &= test_ref2(); + ret &= test_ref3(); + ret &= test_ref4(); + ret &= test_unlink1(); + ret &= test_misc(); + ret &= test_realloc(); + ret &= test_realloc_child(); + ret &= test_steal(); + ret &= test_move(); + ret &= test_unref_reparent(); + ret &= test_realloc_fn(); + ret &= test_type(); + ret &= test_lifeless(); + ret &= test_loop(); + ret &= test_free_parent_deny_child(); + ret &= test_talloc_ptrtype(); + ret &= test_talloc_free_in_destructor(); + + if (ret) { + ret &= test_speed(); + } + ret &= test_autofree(); + + return ret; +} + +#if _SAMBA_BUILD_ < 4 +int main(void) +{ + bool ret = torture_local_talloc(NULL); + if (!ret) + return -1; + return 0; +} +#endif -- 2.39.2