2

I am currently using function fscanf to parse a file with some char and float point. I confirmed the results by printing them out and memory check with valgrind. Now, the printing is correct but there are always a definitely loss of memory.

This is a sample code:

FILE* table;
table = fopen("table", "r");
double mass;
while (fscanf(table, %lf ", &mass) != EOF){
    printf("mass: %lf\n", mass);
}

and the valgrind with --leak-check=full option says:

==7104== 513 bytes in 1 blocks are definitely lost in loss record 52 of 62
==7104==    at 0x100008EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==7104==    by 0x1001EF66C: __parsefloat_buf (in /usr/lib/system/libsystem_c.dylib)
==7104==    by 0x1001ED9EF: __svfscanf_l (in /usr/lib/system/libsystem_c.dylib)
==7104==    by 0x1001E1492: fscanf (in /usr/lib/system/libsystem_c.dylib)
==7104==    by 0x100000F3F: main (in ./prtm)

I think it's the problem of format. I've also tried to use %f and float but just get more similar error. Can anyone tell me what goes wrong?

  • 6
    You don't close the file. – 2501 Aug 20 '16 at 16:58
  • Just to rule this out as a possibly related reason: you *do* get an additional memory loss report for that missing `fclose`, right? (As should be reported according to http://stackoverflow.com/q/31630583/2564301) And properly closing the file does *not* make this one go away? – Jongware Aug 20 '16 at 17:51
  • 1
    If you ran `valgrind` and your code does NOT allocate any memory explicitly, but `valgrind` still reports memory in use at exit, look first for other function calls you make, such as `fopen`, which allocate memory for their own use. If you do not allocate memory and do not call any additional library functions, and you still have `valgrind` reporting memory in use at exit, then the likely cause is proper *exclusion* files are not available in your version of `valgrind` for the OS you are currently running it on. It happens, just ask those running macs. – David C. Rankin Aug 20 '16 at 19:22
  • Thank you guys for help. I did add the fclose and just forget to copy that line here. I think @JonathanLeffler gave the great answer. It also surprised me that such a short problem bring about a lot of discussion :b – Steven.Chang Aug 21 '16 at 15:06

1 Answers1

3

Although you do not fclose() to file in the code fragment posted, I doubt this is causing trouble. In any case, make sure you fclose() the file.

The function fscanf seems to allocate memory for its own purposes and does not free it at program exit. valgrind usually knows about such unavoidable memory leak and suppresses the output, for for some reason it missed this one.

This message does not seem to indicate a problem in your code. The reported lost block is allocated by the OS/X version of fscanf() as can be seen from the call stack, for its internal floating point parser __parsefloat_buf.

More precisely, the source code to the LibC is available from http://opensource.apple.com (Libc-763.11/stdio/vfscanf-fbsd.c:965) and the block should have been free'd upon exit.

You could try and free it yourself with this, but I do not recommend adding this fragment to production code.

#include <stdlib.h>
#include <pthread.h>
#include <sys/cdefs.h>

...

free(pthread_getspecific(__LIBC_PTHREAD_KEY_PARSEFLOAT));
pthread_setspecific(__LIBC_PTHREAD_KEY_PARSEFLOAT, NULL);

Instead, as pointed out by Rad Lexus, you should tell valgrind to ignore this warning as shown in this question: On OSX Valgrind reports this memory leak, Where is it coming from?

Community
  • 1
  • 1
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    I'm with you on that - But an indication how you can distinguish this situation (only library functions in the call stack of the allocation, no documentation says you need to `free` anything after `fscanf`) from a situation where your code actually does something wrong would really beef up the answer. – tofro Aug 20 '16 at 17:57
  • 1
    "Not much"? Jonathan Leffler says you can ask valgrind [to ignore it](http://stackoverflow.com/a/9039682/2564301). – Jongware Aug 20 '16 at 18:05
  • 1
    Note about libraries not freeing allocated memory: it is not generally feasible/possible for the libraries to free the memory while program is running, there's no practiacal mechanism for it. They could add an exit handler and free the memory just before program exits, but... why? There is no benefit, the whole memory of the program will be gone in a moment anyway. – hyde Aug 20 '16 at 19:17
  • @tofro: Ultimately, it comes down to reading the manual pages, and sometimes some experimentation. It the manual says it returns a resource that you have to release with another part of the API, that's what you do. With files, there's a handle (the file stream `FILE *`), and `fclose()` might release the memory if the allocation was made by `fscanf()` to data that is stored so that it is associated with the stream. If the data is stored independently of the stream, then there's really nothing you can do except create a suppression. _[…continued…]_ – Jonathan Leffler Aug 20 '16 at 21:03
  • _[…continuation…]_ Looking at my suppressions file, I have an entry for this leak: `{ Mac-OS-X-10.11.5-Leak-029 Memcheck:Leak match-leak-kinds: definite fun:malloc fun:__parsefloat_buf fun:__svfscanf_l fun:vsscanf_l }` —— The name is arbitrary. At the moment, I have one extra level of function (`vsscanf_l`) in the call chain. You would not want that, and I might need to get rid of it too. – Jonathan Leffler Aug 20 '16 at 21:05
  • 1
    Taking the code in the question and converting it into a test case, I can affirm that I get the leak shown in the question, even with `fclose(table)` added. Further, removing the `fun:vsscanf_l` line from the suppression shown in my prior comment suppresses the leak for this example, too. And yes, it's a borderline bug in the code underlying the `scanf()` family. It goes along with the other suppressed problems: `suppressed: 22,770 bytes in 188 blocks`. Apple will probably argue, with some justification, that as the problem is finite and only shows as the program exits, it doesn't need fixing. – Jonathan Leffler Aug 20 '16 at 21:17
  • @JonathanLeffler: looking at the source code, the first time this function is called, it registers a cleanup function with `pthread_key_init_np(parsefloat_tsd_key, free);` which I assume is a variant of `pthread_key_create`. They at least attempt to cleanup the allocated buffer upon thread termination or program termination. Why this fails is beyond my level of expertise. If I find the motivation, I will report this problem. – chqrlie Aug 20 '16 at 21:44
  • If you find the energy and motivation to report the issue, great. Be aware, though, that there's a modest chance that the problem is simply that Valgrind analyzes for leaks before the cleanup is invoked — that it is a false positive. That would still leave a suppression as the way to fix it without being involved in a war between Valgrind and Apple over who is at fault. (We must be about due for a new version of Valgrind, just before MacOS Sierra is released, so that we can get to play with a new set of suppressions when the new o/s arrives. That's how it's worked the last couple of years.) – Jonathan Leffler Aug 20 '16 at 21:51
  • @JonathanLeffler: are you involved in Valgrind development? What an amazing tool! – chqrlie Aug 20 '16 at 21:54
  • It is an amazing tool. No, I'm not involved in Valgrind development, though I've considered offering to help with Mac porting working. I haven't actually gotten around to doing so yet. – Jonathan Leffler Aug 20 '16 at 21:55
  • Thanks a lot for these information. I agree with that it may be a bug of the valgrind or function itself. To be sorry, I am still new to the code in os library and can't get all part of the answer. I will try to figure it all myself. – Steven.Chang Aug 21 '16 at 15:19