FFI to POSIX libc; strerror (errnum); unfreed memory

I need to turn POSIX errno to the error string. StrError.hs import Foreign.C.String import Foreign.C.Types foreign import ccall unsafe "strerror" c_strerror :: CInt -> IO CString strError :: Int -> IO String strError i = do cstr <- c_strerror ci peekCAString cstr where ci = fromIntegral i main = mapM_ (>>= putStrLn) $ map strError [1 .. 450] $ ghc -g StrError.hs $ valgrind --leak-check=full --show-leak-kinds=all ./StrError ==2350== ==2350== HEAP SUMMARY: ==2350== in use at exit: 18 bytes in 1 blocks ==2350== total heap usage: 685 allocs, 684 frees, 97,998 bytes allocated ==2350== ==2350== 18 bytes in 1 blocks are still reachable in loss record 1 of 1 ==2350== at 0x48407B4: malloc (vg_replace_malloc.c:381) ==2350== by 0x496A427: __vasprintf_internal (vasprintf.c:71) ==2350== by 0x493DBD5: asprintf (asprintf.c:31) ==2350== by 0x498A9F0: strerror_l (strerror_l.c:45) ==2350== by 0x408BC0: ??? (StrError.hs:8) ==2350== ==2350== LEAK SUMMARY: ==2350== definitely lost: 0 bytes in 0 blocks ==2350== indirectly lost: 0 bytes in 0 blocks ==2350== possibly lost: 0 bytes in 0 blocks ==2350== still reachable: 18 bytes in 1 blocks ==2350== suppressed: 0 bytes in 0 blocks ==2350== ==2350== For lists of detected and suppressed errors, rerun with: -s ==2350== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) I am already frustrated with having to use IO for this. I feel like it is better to manually create a pure solution by copying whatever the C code from libc does. But I still do not understand what causes this memory to remain reachable. Calling strerror from pure C code does not leave reachable memory regions. Can anyone explain? And maybe you have a suggestion as to how to implement this strError function properly? Just in case you are the type of a person to inquire as to *why* I need this, for fun. I need it for fun.

Hi,
I get exactly this valgrind profile with the following C code:
#include
I need to turn POSIX errno to the error string.
StrError.hs
import Foreign.C.String import Foreign.C.Types
foreign import ccall unsafe "strerror" c_strerror :: CInt -> IO CString
strError :: Int -> IO String strError i = do cstr <- c_strerror ci peekCAString cstr where ci = fromIntegral i
main = mapM_ (>>= putStrLn) $ map strError [1 .. 450]
$ ghc -g StrError.hs $ valgrind --leak-check=full --show-leak-kinds=all ./StrError ==2350== ==2350== HEAP SUMMARY: ==2350== in use at exit: 18 bytes in 1 blocks ==2350== total heap usage: 685 allocs, 684 frees, 97,998 bytes allocated ==2350== ==2350== 18 bytes in 1 blocks are still reachable in loss record 1 of 1 ==2350== at 0x48407B4: malloc (vg_replace_malloc.c:381) ==2350== by 0x496A427: __vasprintf_internal (vasprintf.c:71) ==2350== by 0x493DBD5: asprintf (asprintf.c:31) ==2350== by 0x498A9F0: strerror_l (strerror_l.c:45) ==2350== by 0x408BC0: ??? (StrError.hs:8) ==2350== ==2350== LEAK SUMMARY: ==2350== definitely lost: 0 bytes in 0 blocks ==2350== indirectly lost: 0 bytes in 0 blocks ==2350== possibly lost: 0 bytes in 0 blocks ==2350== still reachable: 18 bytes in 1 blocks ==2350== suppressed: 0 bytes in 0 blocks ==2350== ==2350== For lists of detected and suppressed errors, rerun with: -s ==2350== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I am already frustrated with having to use IO for this. I feel like it is better to manually create a pure solution by copying whatever the C code from libc does.
But I still do not understand what causes this memory to remain reachable. Calling strerror from pure C code does not leave reachable memory regions.
Can anyone explain? And maybe you have a suggestion as to how to implement this strError function properly?
Just in case you are the type of a person to inquire as to *why* I need this, for fun. I need it for fun. _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

On Fri, 12 Apr 2024 10:59:44 +0200
Sylvain Henry
Hi,
I get exactly this valgrind profile with the following C code:
#include
#include int main() { for (int i=0; i<=450; i++) { printf("%s\n", strerror(i)); } }
So there isn't anything Haskell specific here. Looking at glibc sources, there is a "strerror_l_buf" buffer kept in the TLS (https://elixir.bootlin.com/glibc/latest/source/string/strerror_l.c#L45) so that's expected.
Use strerror_r instead to use a buffer you manage explicitly (e.g. with allocaBytes).
Sylvain
PS: are you running valgrind on your Haskell program? A valgrind profile for a Haskell program has many more entries. E.g. for a program compiled with GHC 9.6.4:
==36067== 2,097,152 bytes in 1 blocks are still reachable in loss record 21 of 21 ==36067== at 0x4843788: malloc (vg_replace_malloc.c:442) ==36067== by 0x2AD470: stgMallocBytes (in /home/hsyl20/projects/ghc/scratch/strerror/Test) ==36067== by 0x2B96D8: initEventLogging (in /home/hsyl20/projects/ghc/scratch/strerror/Test) ==36067== by 0x2B7CFE: initTracing (in /home/hsyl20/projects/ghc/scratch/strerror/Test) ==36067== by 0x2A60AE: hs_init_ghc (in /home/hsyl20/projects/ghc/scratch/strerror/Test) ==36067== by 0x2A56F0: hs_main (in /home/hsyl20/projects/ghc/scratch/strerror/Test) ==36067== by 0x22D725: main (in /home/hsyl20/projects/ghc/scratch/strerror/Test)
On 12/04/2024 07:58, Folsk Pratima wrote:
I need to turn POSIX errno to the error string.
StrError.hs
import Foreign.C.String import Foreign.C.Types
foreign import ccall unsafe "strerror" c_strerror :: CInt -> IO CString
strError :: Int -> IO String strError i = do cstr <- c_strerror ci peekCAString cstr where ci = fromIntegral i
main = mapM_ (>>= putStrLn) $ map strError [1 .. 450]
$ ghc -g StrError.hs $ valgrind --leak-check=full --show-leak-kinds=all ./StrError ==2350== ==2350== HEAP SUMMARY: ==2350== in use at exit: 18 bytes in 1 blocks ==2350== total heap usage: 685 allocs, 684 frees, 97,998 bytes allocated ==2350== ==2350== 18 bytes in 1 blocks are still reachable in loss record 1 of 1 ==2350== at 0x48407B4: malloc (vg_replace_malloc.c:381) ==2350== by 0x496A427: __vasprintf_internal (vasprintf.c:71) ==2350== by 0x493DBD5: asprintf (asprintf.c:31) ==2350== by 0x498A9F0: strerror_l (strerror_l.c:45) ==2350== by 0x408BC0: ??? (StrError.hs:8) ==2350== ==2350== LEAK SUMMARY: ==2350== definitely lost: 0 bytes in 0 blocks ==2350== indirectly lost: 0 bytes in 0 blocks ==2350== possibly lost: 0 bytes in 0 blocks ==2350== still reachable: 18 bytes in 1 blocks ==2350== suppressed: 0 bytes in 0 blocks ==2350== ==2350== For lists of detected and suppressed errors, rerun with: -s ==2350== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I am already frustrated with having to use IO for this. I feel like it is better to manually create a pure solution by copying whatever the C code from libc does.
But I still do not understand what causes this memory to remain reachable. Calling strerror from pure C code does not leave reachable memory regions.
Can anyone explain? And maybe you have a suggestion as to how to implement this strError function properly?
Just in case you are the type of a person to inquire as to *why* I need this, for fun. I need it for fun. _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
participants (2)
-
Folsk Pratima
-
Sylvain Henry