views:

59

answers:

1

As far as I can tell, asprintf calls malloc. If I replace malloc with the Boehm GC, a call to asprintf still calls the traditional malloc - at least that's what valgrind is telling me:

Here's the malloc macro:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include <gc.h>
#define malloc(n)    GC_MALLOC(n)
#define calloc(m,n)  GC_MALLOC((m)*(n))
#define realloc(p,n) GC_REALLOC((p),(n))

typedef char * string;

And here is the valgrind report:

hopcroft:didactic_scheme(flexible_strings) scotttaylor$ valgrind --suppressions=./boehm-gc.suppressions --leak-check=full bin/escheme -e 1
==16130== Memcheck, a memory error detector
==16130== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==16130== Using Valgrind-3.6.0.SVN and LibVEX; rerun with -h for copyright info
==16130== Command: bin/escheme -e 1
==16130== 
--16130-- bin/escheme:
--16130-- dSYM directory is missing; consider using --dsymutil=yes
1==16130== 
==16130== HEAP SUMMARY:
==16130==     in use at exit: 4,312 bytes in 3 blocks
==16130==   total heap usage: 3 allocs, 0 frees, 4,312 bytes allocated
==16130== 
==16130== 128 bytes in 1 blocks are definitely lost in loss record 2 of 3
==16130==    at 0x100012D75: malloc (vg_replace_malloc.c:236)
==16130==    by 0x1000918EC: asprintf (in /usr/lib/libSystem.B.dylib)
==16130==    by 0x1000013FA: printInt (in bin/escheme)
==16130==    by 0x100001D38: print (in bin/escheme)
==16130==    by 0x100001DC5: main (in bin/escheme)
==16130== 
==16130== LEAK SUMMARY:
==16130==    definitely lost: 128 bytes in 1 blocks
==16130==    indirectly lost: 0 bytes in 0 blocks
==16130==      possibly lost: 0 bytes in 0 blocks
==16130==    still reachable: 4,184 bytes in 2 blocks
==16130==         suppressed: 0 bytes in 0 blocks
==16130== Reachable blocks (those to which a pointer was found) are not shown.
==16130== To see them, rerun with: --leak-check=full --show-reachable=yes
==16130== 
==16130== For counts of detected and suppressed errors, rerun with: -v
==16130== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 66 from 13)

Here's the code in which the malloc call is coming from:

static string printInt(Object self) {
  string str;
  asprintf(&str, "%lu", getValueInt(self));
  return str;
}

A workaround might be to use asprintf, then use malloc to copy it so that the malloc macro is used instead of the primitive function:

static string printInt(Object self) {
  string tmp;
  string str;

  asprintf(&tmp, "%lu", getValueInt(self));
  str = calloc(sizeof(string), strlen(tmp) + 1);

  strcpy(str, tmp);
  free(tmp);

  return str;
}

That seems silly - it involves a bunch of unnecessary copying and also happens to be a code eye sore IHMO. So is there a safe way to use asprintf and other system libraries that might call the native malloc while still using the Boehm GC? Is there an alternative to asprintf that I should be using instead?

+2  A: 

snprintf returns the number of characters that would have been written had the provided buffer been large enough. You could call this method twice (once to get the right buffer size and then again with a buffer large enough to obtain the output), however this is probably going to be less efficient than just copying the output of asprintf into a collectable buffer. Here's an example containing code that allocates a buffer large enough to contain the maximum value of an unsigned long for 32-bit systems. On systems where there is not enough space, the buffer is reallocated and reformatted.

#include <limits.h>

...

unsigned long intValue = getValueInt(self);
size_t maxLength = 11; // heuristic
char *buf = malloc(maxLength);

int result = snprintf(buf, maxLength, "%lu", intValue);
if (result > maxLength)
{
    // shouldn't ever get here, but just in case the buffer is too small
    // we reallocate it to the correct size and try again.
    buf = malloc(result);
    snprintf(buf, result, "%lu", intValue);
}

return buf;
dreamlax
Um. `STRINGIFY(ULONG_MAX)` is `"ULONG_MAX"`, which by pure fluke is 11 chars. So remarkably, this code assumes a 32 bit long ;-) You need `#define EXPAND_AND_STRINGIFY(X) STRINGIFY(X)`.
Steve Jessop
D'oh of course (although, sizeof "ULONG_MAX" is only 10, not 11).
dreamlax
Good point, I think I counted the double-quotes and not the nul! Since you add 1 in the calculation, 10 chars is right for unsigned, anyway, it's signed that needs 11, so even better. One for an underhanded C contest (http://underhanded.xcott.com/) if you ever need code that overruns a buffer on 64 bit linux systems only. Not that there's a world shortage of code that goes wrong on 64 bit systems.
Steve Jessop
Hah, I just checked, `EXPAND_AND_STRINGIFY` doesn't quite work as I expected. For my system, `ULONG_MAX` expands into `(2147483647L * 2UL + 1UL)`, so its not all that usable/portable.
dreamlax
Which is certainly long enough :-). Actually, I think I remember this being discussed before, but I can't remember whether ULONG_MAX is allowed to expand to a compiler intrinsic, which could then be too short. A reasonable bound is `(sizeof (long) * CHAR_BIT) / 3 + 1`.
Steve Jessop
I think the standard says that the macros must be constant expressions suitable for use in preprocessor directives.
dreamlax