2

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?

madth3
  • 7,275
  • 12
  • 50
  • 74
user52804
  • 319
  • 2
  • 5
  • 1
    If all you're doing is formatting an integer, you can use a preallocated buffer of a fixed size, since integers have a known maximum value, and hence a known largest decimal representation. – dreamlax Sep 07 '10 at 00:33
  • 1
    If you must workaround, a lesser eyesore would be to re-implement `asprintf` rather than wrapping it: call vsnprintf to get the length, allocate, call it again (with a re-initialized `va_list`) to write the data. Unfortunately you'd have to repeat for all memory-allocating string functions. – Steve Jessop Sep 07 '10 at 00:46

2 Answers2

2

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
  • 93,976
  • 29
  • 161
  • 209
  • 1
    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 Sep 07 '10 at 00:56
  • D'oh of course (although, sizeof "ULONG_MAX" is only 10, not 11). – dreamlax Sep 07 '10 at 00:58
  • 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 Sep 07 '10 at 01:04
  • 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 Sep 07 '10 at 01:08
  • 1
    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 Sep 07 '10 at 01:14
  • I think the standard says that the macros must be constant expressions suitable for use in preprocessor directives. – dreamlax Sep 07 '10 at 01:19
  • I just use something like `#if __WORDSIZE == 64 #define LONG_LEN 20 #else #define LONG_LEN 10 #endif` – Michael Mior Feb 05 '12 at 21:43
-1

According to this page, you can compile libgc (the boehm-gc library) with

-DREDIRECT_MALLOC=GC_malloc -DIGNORE_FREE

which ought to intercept the call to malloc in asprintf. NB: haven't tried this.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Dreamcat4
  • 292
  • 1
  • 3
  • 12
  • 1
    No, it won't. All it'll do is to rewrite calls to `malloc()` when compiling your own code. It won't have any effect on the precompiled libc that your program is being linked against. – David Given Aug 17 '14 at 20:44