Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Valgrind: stdio.h's function "puts" allocates memory

Tags:

c

valgrind

I just noticed that valgrind is showing that this code allocates memory:

#include <stdio.h>

int main(void)
{
    puts("Hello world");
}

Result when compiled with gcc (Ubuntu 5.3.1-14ubuntu2.1):

==25080== Memcheck, a memory error detector
==25080== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==25080== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==25080== Command: ./a.out
==25080== 
Hello world
==25080== 
==25080== HEAP SUMMARY:
==25080==     in use at exit: 0 bytes in 0 blocks
==25080==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==25080== 
==25080== All heap blocks were freed -- no leaks are possible
==25080== 
==25080== For counts of detected and suppressed errors, rerun with: -v
==25080== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Why does it show that puts allocates memory?

like image 429
Erik W Avatar asked Jan 06 '23 19:01

Erik W


2 Answers

The GNU C Library ("glibc", the usual C runtime on Ubuntu) allocates memory for internal use the first time stdout is used for anything. (It probably does the same for stderr and stdin.) To see this happening, run your program under gdb and, after it reaches main, set breakpoints on brk and mmap, which are the system calls that ultimately get used to allocate memory. Then continue.

Reading symbols from ./a.out...done.
(gdb) b main
Breakpoint 1 at 0x4004ea: file test.c, line 5.
(gdb) r
Starting program: /home/zack/a.out 

Breakpoint 1, main () at test.c:5
5     puts("Hello world");
(gdb) b brk
Breakpoint 2 at 0x7ffff7b183c0: file ../sysdeps/unix/sysv/linux/x86_64/brk.c, line 31.
(gdb) b mmap
Breakpoint 3 at 0x7ffff7b1bc00: file ../sysdeps/unix/syscall-template.S, line 84.
(gdb) c
Continuing.

Breakpoint 3, mmap64 () at ../sysdeps/unix/syscall-template.S:84
84  ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0  mmap64 () at ../sysdeps/unix/syscall-template.S:84
#1  0x00007ffff7a9f49c in __GI__IO_file_doallocate (
    fp=0x7ffff7dd6620 <_IO_2_1_stdout_>) at filedoalloc.c:128
#2  0x00007ffff7aac550 in __GI__IO_doallocbuf (
    fp=fp@entry=0x7ffff7dd6620 <_IO_2_1_stdout_>) at genops.c:418
#3  0x00007ffff7aab9d8 in _IO_new_file_overflow (
    f=0x7ffff7dd6620 <_IO_2_1_stdout_>, ch=-1) at fileops.c:820
#4  0x00007ffff7aaab8a in _IO_new_file_xsputn (
    f=0x7ffff7dd6620 <_IO_2_1_stdout_>, data=0x400584, n=11) at fileops.c:1331
#5  0x00007ffff7aa18d8 in _IO_puts (str=0x400584 "Hello world") at ioputs.c:41
#6  0x00000000004004f4 in main () at test.c:5

You can see that the call stack goes through a bunch of internal stuff and ends up at puts.

Perhaps you are wondering why the library does this. The reason is hinted at in Chuck Walbourn's answer: the memory will be used for buffering output, and it isn't allocated until stdout is used for the first time, because some programs never use stdout and they didn't want to waste memory. (This design decision may have made more sense 20 years ago.)

like image 98
zwol Avatar answered Jan 13 '23 16:01

zwol


Standard output stdout is allowed by the standard to be buffered by default. You can change this with setvbuf.

like image 45
Chuck Walbourn Avatar answered Jan 13 '23 17:01

Chuck Walbourn