printf() failure and [m/c/re]alloc() handling

P

Pr0 Lurk3r

Hello. I was just experimenting with some uses of macros in C...
here's some code:

<code>
#include <stdlib.h>
#include <stdio.h>
#define smalloc(sz) (safe_malloc(sz, __FILE__, __LINE__))

void* safe_malloc(size_t sz, char const* file, int line) { /* malloc
which bails with helpful message */
void *ptr;
if((ptr = malloc(sz)) == NULL) {
printf("*** malloc() failed in file %s, on line %d. ***\n", file,
line);
abort(); /* or some other error handling */
}
return ptr;
} /* above may be repeated for realloc, calloc, and generalized to
other functions */

int main() { /* demo */
int *leak;
for(;;) leak = smalloc(10000*sizeof(int)); /* for a faster fail */
return 0;
}
</code>

I may be a bit of a n00b, but this code looks reasonable to me (I
looked at the preprocessed output of the code after gcc, and the
smalloc line gets replaced correctly); however, it fails... either the
code never reaches the printf() block (surely not, for an infinite
loop the OS will run out of memory chunks to dole out to malloc soon
enough :p) or printf() fails and in the process launches some new
code which prevents the abort() from occurring... nonetheless, it (my
code running on my Ubuntu VM) simply hangs!

This leads me to a few questions because, if this is the case, then
how would one go about logging failed mallocs (I assume that this is
somewhat unrecoverable and the best one could do would likely be to
print some stuff to std(out/err))? Is my approach/code incorrect?
Furthermore, does everyone experience the same behaviour (I compiled
it using GCC 4.2.4 on a Ubuntu 8.04 VM image - perhaps it's a vmware
issue)? What could be going on here?

P.S. If you do run the code, beware! Your system might end up hanging,
if you're lucky the process will just be killed, but if not you might
have to force a reboot... so save your stuff before doing so!
 
M

Morris Keesan

if((ptr = malloc(sz)) == NULL) {
printf("*** malloc() failed in file %s, on line %d. ***\n", file,
line);
abort(); /* or some other error handling */

I would prefer fprintf(stderr to printf(, to make it more likely that
the text actually gets written before the call to abort(). Also because
I prefer sending error messages to stderr instead of stdout.

int main() { /* demo */
int *leak;
for(;;) leak = smalloc(10000*sizeof(int)); /* for a faster fail */
return 0;
}
</code>

I may be a bit of a n00b, but this code looks reasonable to me (I
looked at the preprocessed output of the code after gcc, and the
smalloc line gets replaced correctly); however, it fails... either the
code never reaches the printf() block (surely not, for an infinite
loop the OS will run out of memory chunks to dole out to malloc soon
enough :p) or printf() fails and in the process launches some new
code which prevents the abort() from occurring... nonetheless, it (my
code running on my Ubuntu VM) simply hangs!

Your memory may be larger than you think. You might just be hanging in
the loop waiting for malloc to fail. You could try increasing the amount
your malloc by a few orders of magnitude.

Also, I'm not at all familiar with the implementation details, but it could
be that by the time malloc fails, you used up so much of your address space
that the heap has collided with the stack, and there's no room to push the
arguments for the call to printf. This is pure speculation.
 
N

Nobody

Hello. I was just experimenting with some uses of macros in C...
here's some code:
printf("*** malloc() failed in file %s, on line %d. ***\n", file, line);
abort(); /* or some other error handling */

There's no guarantee that buffers will be flushed upon an abort().

Change the printf() call to fprintf(stderr, ...). Apart from being
intended for error messages, stderr should be unbuffered. If you
must write to stdout, put a fflush() after the printf() call.
 
S

Scooser

There is no problem with your program, it's Linux which lets your
program fail. Because of the so called "optimistic" memory allocation
approach Linux uses, you will almost never get a NULL pointer out of
malloc as long as the virtual memory system can handle the size of
memory you asked for. But what does Linux do when it tries to map your
virtual memory page to real memory - which happens when you access the
memory you malloced - and than reconizes that it had run out of
memory? The Kernel will use it's OOM Killer and kill some processes
till it has enough memory freed to map the page if this is still
necessary.
 
A

Alan Curry

There is no problem with your program, it's Linux which lets your
program fail. Because of the so called "optimistic" memory allocation
approach Linux uses, you will almost never get a NULL pointer out of
malloc as long as the virtual memory system can handle the size of
memory you asked for. But what does Linux do when it tries to map your
virtual memory page to real memory - which happens when you access the
memory you malloced - and than reconizes that it had run out of
memory? The Kernel will use it's OOM Killer and kill some processes
till it has enough memory freed to map the page if this is still
necessary.

That's configurable, by the way. echo 2 > /proc/sys/vm/overcommit_memory and
a more strict memory allocation policy will be used. You'll find malloc
returning NULL a lot sooner.
 
C

chad

   I would prefer fprintf(stderr to printf(, to make it more likely that
the text actually gets written before the call to abort().  Also because
I prefer sending error messages to stderr instead of stdout.

How do you figure that 'fprintf(stderr' (vs 'printf(,' ) increases the
chances that text gets written before the call to abort()?
 
K

Keith Thompson

chad said:
How do you figure that 'fprintf(stderr' (vs 'printf(,' ) increases the
chances that text gets written before the call to abort()?

stderr is typically unbuffered by default; stdout is typically line
buffered by default. The actual requirement is in C99 7.19.3p7:

As initially opened, the standard error stream is not fully
buffered; the standard input and standard output streams are
fully buffered if and only if the stream can be determined not
to refer to an interactive device.

which allows stderr to be line-buffered.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top