Richard W.M. Jones
2013-Jan-24 17:13 UTC
[Libguestfs] [PATCH 1/2] lib: Add CLEANUP_FREE macro which automatically calls 'free' when leaving scope.
From: "Richard W.M. Jones" <rjones@redhat.com> Use the macro like this to create temporary variables which are automatically cleaned up when the scope is exited: { CLEANUP_FREE (char *, foo, strdup (bar)); /* char *foo = strdup (bar) */ ... // no need to call free (foo)! } On GCC and LLVM, this is implemented using __attribute__((cleanup(...))). On other compilers, we fall back to a less efficient implementation which saves up the memory allocations and frees them all when the handle is closed. --- configure.ac | 35 +++++++++++++++++++++++++++++++++++ src/alloc.c | 19 +++++++++++++++++++ src/guestfs-internal.h | 33 +++++++++++++++++++++++++++++++++ src/handle.c | 15 +++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/configure.ac b/configure.ac index 39c79cf..7745ab5 100644 --- a/configure.ac +++ b/configure.ac @@ -191,6 +191,41 @@ AC_SYS_LARGEFILE dnl Check sizeof long. AC_CHECK_SIZEOF([long]) +dnl Check if __attribute__((cleanup(...))) works. +dnl XXX It would be nice to use AC_COMPILE_IFELSE here, but gcc just +dnl emits a warning for attributes that it doesn't understand. +AC_MSG_CHECKING([if __attribute__((cleanup(...))) works with this compiler]) +AC_RUN_IFELSE([ +AC_LANG_SOURCE([[ +#include <stdio.h> +#include <stdlib.h> + +void +freep (void *ptr) +{ + exit (0); +} + +void +test (void) +{ + __attribute__((cleanup(freep))) char *ptr = malloc (100); +} + +int +main (int argc, char *argv[]) +{ + test (); + exit (1); +} +]]) + ],[ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_ATTRIBUTE_CLEANUP],[1],[Define to 1 if '__attribute__((cleanup(...)))' works with this compiler.]) + ],[ + AC_MSG_RESULT([no]) + ]) + dnl Check if dirent (readdir) supports d_type member. AC_STRUCT_DIRENT_D_TYPE diff --git a/src/alloc.c b/src/alloc.c index 25d7f42..cf3741a 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -122,3 +122,22 @@ guestfs___safe_asprintf (guestfs_h *g, const char *fs, ...) return msg; } + +void +guestfs___cleanup_free (void *ptr) +{ + free (* (void **) ptr); +} + +#ifndef HAVE_ATTRIBUTE_CLEANUP +void +guestfs___defer_free (guestfs_h *g, void (*freefn) (void *), void *data) +{ + struct deferred_free *p = safe_malloc (g, sizeof *p); + + p->freefn = freefn; + p->data = data; + p->next = g->deferred_frees; + g->deferred_frees = p; +} +#endif diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 870207b..d27a3c2 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -72,6 +72,19 @@ #define TRACE4(name, arg1, arg2, arg3, arg4) #endif +#ifdef HAVE_ATTRIBUTE_CLEANUP +#define CLEANUP(type, var, init, freefn) \ + __attribute__((cleanup(freefn))) type var = init +#else +#define CLEANUP(type, var, init, freefn) \ + type var = init; \ + guestfs___defer_free ((g), (freefn), (var)) +#endif + +#define CLEANUP_FREE(type, var, init) \ + CLEANUP(type, var, (init), guestfs___cleanup_free) +/* other CLEANUP_* macros to follow */ + #define TMP_TEMPLATE_ON_STACK(g,var) \ char *ttos_tmpdir = guestfs_get_tmpdir (g); \ char var[strlen (ttos_tmpdir) + 32]; \ @@ -324,6 +337,10 @@ struct guestfs_h virConnectCredentialPtr requested_credentials; #endif +#ifndef HAVE_ATTRIBUTE_CLEANUP + struct deferred_free *deferred_frees; +#endif + /**** Private data for attach-methods. ****/ /* NB: This cannot be a union because of a pathological case where * the user changes attach-method while reusing the handle to launch @@ -356,6 +373,14 @@ struct guestfs_h #endif }; +#ifndef HAVE_ATTRIBUTE_CLEANUP +struct deferred_free { + struct deferred_free *next; + void (*freefn) (void *); + void *data; +}; +#endif + /* Per-filesystem data stored for inspect_os. */ enum inspect_os_format { OS_FORMAT_UNKNOWN = 0, @@ -474,6 +499,14 @@ extern char *guestfs___safe_asprintf (guestfs_h *g, const char *fs, ...) #define safe_memdup guestfs___safe_memdup #define safe_asprintf guestfs___safe_asprintf +/* These functions are used internally by the CLEANUP_* macros. + * Don't call them directly. + */ +extern void guestfs___cleanup_free (void *ptr); +#ifndef HAVE_ATTRIBUTE_CLEANUP +extern void guestfs___defer_free (guestfs_h *g, void (*freefn) (void *), void *data); +#endif + /* errors.c */ extern void guestfs___init_error_handler (guestfs_h *g); diff --git a/src/handle.c b/src/handle.c index 39e30c7..8cf7a40 100644 --- a/src/handle.c +++ b/src/handle.c @@ -253,6 +253,9 @@ void guestfs_close (guestfs_h *g) { struct qemu_param *qp, *qp_next; +#ifndef HAVE_ATTRIBUTE_CLEANUP + struct deferred_free *dfp, *dfp_next; +#endif guestfs_h **gg; if (g->state == NO_HANDLE) { @@ -324,6 +327,18 @@ guestfs_close (guestfs_h *g) while (g->error_cb_stack) guestfs_pop_error_handler (g); +#ifndef HAVE_ATTRIBUTE_CLEANUP + /* For compilers that don't support __attribute__((cleanup(...))), + * free any temporary data that we allocated in CLEANUP_* macros + * here. + */ + for (dfp = g->deferred_frees; dfp; dfp = dfp_next) { + dfp->freefn (&dfp->data); + dfp_next = dfp->next; + free (dfp); + } +#endif + if (g->pda) hash_free (g->pda); free (g->tmpdir); -- 1.8.1
Richard W.M. Jones
2013-Jan-24 17:13 UTC
[Libguestfs] [PATCH 2/2] inspect: Use CLEANUP_FREE macro.
From: "Richard W.M. Jones" <rjones@redhat.com> --- src/inspect-fs.c | 17 ++++------------- src/inspect.c | 8 ++------ 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/inspect-fs.c b/src/inspect-fs.c index fed7761..724d4ee 100644 --- a/src/inspect-fs.c +++ b/src/inspect-fs.c @@ -89,7 +89,6 @@ int guestfs___check_for_filesystem_on (guestfs_h *g, const char *device, int is_block, int is_partnum) { - char *vfs_type; int is_swap, r; struct inspect_fs *fs; @@ -98,7 +97,7 @@ guestfs___check_for_filesystem_on (guestfs_h *g, const char *device, * temporarily replace the error handler with a null one. */ guestfs_push_error_handler (g, NULL, NULL); - vfs_type = guestfs_vfs_type (g, device); + CLEANUP_FREE (char *, vfs_type, guestfs_vfs_type (g, device)); guestfs_pop_error_handler (g); is_swap = vfs_type && STREQ (vfs_type, "swap"); @@ -108,7 +107,6 @@ guestfs___check_for_filesystem_on (guestfs_h *g, const char *device, vfs_type ? vfs_type : "failed to get vfs type"); if (is_swap) { - free (vfs_type); if (extend_fses (g) == -1) return -1; fs = &g->fses[g->nr_fses-1]; @@ -145,7 +143,6 @@ guestfs___check_for_filesystem_on (guestfs_h *g, const char *device, } else { r = guestfs_mount_ro (g, device, "/"); } - free (vfs_type); guestfs_pop_error_handler (g); if (r == -1) return 0; @@ -341,28 +338,24 @@ extend_fses (guestfs_h *g) int guestfs___is_file_nocase (guestfs_h *g, const char *path) { - char *p; int r; - p = guestfs___case_sensitive_path_silently (g, path); + CLEANUP_FREE (char *, p, guestfs___case_sensitive_path_silently (g, path)); if (!p) return 0; r = guestfs_is_file (g, p); - free (p); return r > 0; } int guestfs___is_dir_nocase (guestfs_h *g, const char *path) { - char *p; int r; - p = guestfs___case_sensitive_path_silently (g, path); + CLEANUP_FREE (char *, p, guestfs___case_sensitive_path_silently (g, path)); if (!p) return 0; r = guestfs_is_dir (g, p); - free (p); return r > 0; } @@ -532,7 +525,6 @@ guestfs___check_package_management (guestfs_h *g, struct inspect_fs *fs) char * guestfs___first_line_of_file (guestfs_h *g, const char *filename) { - char **lines; int64_t size; char *ret; @@ -549,7 +541,7 @@ guestfs___first_line_of_file (guestfs_h *g, const char *filename) return NULL; } - lines = guestfs_head_n (g, 1, filename); + CLEANUP_FREE (char **, lines, guestfs_head_n (g, 1, filename)); if (lines == NULL) return NULL; if (lines[0] == NULL) { @@ -560,7 +552,6 @@ guestfs___first_line_of_file (guestfs_h *g, const char *filename) /* lines[1] should be NULL because of '1' argument above ... */ ret = lines[0]; /* caller frees */ - free (lines); /* free the array */ return ret; } diff --git a/src/inspect.c b/src/inspect.c index 30e9762..f165957 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -143,23 +143,19 @@ guestfs__inspect_os (guestfs_h *g) static int parent_device_already_probed (guestfs_h *g, const char *partition) { - char *device; size_t i; guestfs_push_error_handler (g, NULL, NULL); - device = guestfs_part_to_dev (g, partition); + CLEANUP_FREE (char *, device, guestfs_part_to_dev (g, partition)); guestfs_pop_error_handler (g); if (!device) return 0; for (i = 0; i < g->nr_fses; ++i) { - if (STREQ (device, g->fses[i].device)) { - free (device); + if (STREQ (device, g->fses[i].device)) return 1; - } } - free (device); return 0; } -- 1.8.1
Daniel P. Berrange
2013-Jan-24 17:41 UTC
Re: [Libguestfs] [PATCH 1/2] lib: Add CLEANUP_FREE macro which automatically calls 'free' when leaving scope.
On Thu, Jan 24, 2013 at 05:13:11PM +0000, Richard W.M. Jones wrote:> From: "Richard W.M. Jones" <rjones@redhat.com> > > Use the macro like this to create temporary variables which are > automatically cleaned up when the scope is exited: > > { > CLEANUP_FREE (char *, foo, strdup (bar)); /* char *foo = strdup (bar) */ > ... > // no need to call free (foo)! > } > > On GCC and LLVM, this is implemented using __attribute__((cleanup(...))). > > On other compilers, we fall back to a less efficient implementation > which saves up the memory allocations and frees them all when the > handle is closed. > --- > configure.ac | 35 +++++++++++++++++++++++++++++++++++ > src/alloc.c | 19 +++++++++++++++++++ > src/guestfs-internal.h | 33 +++++++++++++++++++++++++++++++++ > src/handle.c | 15 +++++++++++++++ > 4 files changed, 102 insertions(+) >> +#ifndef HAVE_ATTRIBUTE_CLEANUP > +void > +guestfs___defer_free (guestfs_h *g, void (*freefn) (void *), void *data) > +{ > + struct deferred_free *p = safe_malloc (g, sizeof *p); > + > + p->freefn = freefn; > + p->data = data; > + p->next = g->deferred_frees; > + g->deferred_frees = p; > +} > +#endif...> guestfs_close (guestfs_h *g) > { > struct qemu_param *qp, *qp_next; > +#ifndef HAVE_ATTRIBUTE_CLEANUP > + struct deferred_free *dfp, *dfp_next; > +#endif > guestfs_h **gg; > > if (g->state == NO_HANDLE) { > @@ -324,6 +327,18 @@ guestfs_close (guestfs_h *g) > while (g->error_cb_stack) > guestfs_pop_error_handler (g); > > +#ifndef HAVE_ATTRIBUTE_CLEANUP > + /* For compilers that don't support __attribute__((cleanup(...))), > + * free any temporary data that we allocated in CLEANUP_* macros > + * here. > + */ > + for (dfp = g->deferred_frees; dfp; dfp = dfp_next) { > + dfp->freefn (&dfp->data); > + dfp_next = dfp->next; > + free (dfp); > + } > +#endif > +So if I'm understanding correctly, in the fallback case, this means that no memory is free'd until guest_close() is invoked. This may work if you have a fairly short lived guestfs_h * handle, but if the application opens a handle and uses it for alot of operations for a very long time without closing, I think this design is effectively a memory leak, or least leads to ever increasing memory usage for a long time. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|