← Back to context

Comment by 1718627440

3 months ago

> This blog post talks as if mocking the `open` function is a good thing that people should be told how to do. If you are mocking anything in the standard library your code is probably structured poorly.

Valgrind is a mock of standard library/OS functions and I think its existence is a good thing. Simulating OOM is also only possible by mocking stuff like open.

> Valgrind is a mock of standard library/OS functions and I think its existence is a good thing.

That is mostly wrong.

Valgrind wraps syscalls. For the most part it just checks the arguments and records any reads or writes to memory. For a small number of syscalls it replaces the syscall rather than wrapping it (for instance calls like getcontext where it needs to get the context from the VEX synthetic CPU rather than the real CPU).

Depending on the tool it can also wrap or replace libc and libpthread functions. memcheck will replace all allocation functions. DRD and Helgrind wrap all pthread functions.

  •     $ cat test.c
        void main (void) {
          malloc (1000);
        }
        
        $ make test
        cc     test.c   -o test
        
        $ valgrind --leak-check=full --show-leak-kinds=all -s ./test
        Memcheck, a memory error detector
        Command: ./test
        
        HEAP SUMMARY:
            in use at exit: 1,000 bytes in 1 blocks
          total heap usage: 1 allocs, 0 frees, 1,000 bytes allocated
        
        1,000 bytes in 1 blocks are still reachable in loss record 1 of 1
           at 0x483877F: malloc (vg_replace_malloc.c:307)
           by 0x109142: main (in test.c:2)
        
        LEAK SUMMARY:
           definitely lost: 0 bytes in 0 blocks
           indirectly lost: 0 bytes in 0 blocks
             possibly lost: 0 bytes in 0 blocks
           still reachable: 1,000 bytes in 1 blocks
                suppressed: 0 bytes in 0 blocks
    

    > vg_replace_malloc.c:307

    What do you think that is? Valgrind tracks allocations by providing other implementations for malloc/free/... .

    • Are you trying to explain to me how Valgrind works? If you do know more than me then please join us and become a Valgrind developer.

      Mostly it wraps system calls and library calls. Wrapping means that it does some checking or recording before and maybe after the call. Very occasionally it needs to modify the arguments to the call. The rest of the time it passes the arguments on to the kernel or libc/libpthread/C++ lib.

      There are also functions and syscalls that it needs to replace. That needs to be a fully functional replacement, not just looking the same as in mocking.

      I don’t have any exact figures. The number of syscalls varies quite a lot by platform and on most platforms there are many obsolete syscalls that are not implemented. At a rough guess, I’d say there are something like 300 syscalls and 100 lib calls that are handled of which 3/4 are wrapped and 1/4 are replaced.

      1 reply →

All rules exist to be broken in the right circumstances. But in 99.9% of test code, there's no reason to do any of that.

  • I think when testing code with an open call, it is a good idea to test what happens on different return values of open. If that is not what you intent to test for this method, then that method shouldn't contain open at all, as already pointed out by other comments.

    • That depends on what your error recovery plan is.

      If the code's running in a space shuttle, you probably want to test that path.

      If it's bootstrapping a replicated service, it's likely desirable to crash early if a config file couldn't be opened.

      If it's plausible that the file in question is missing, you can absolutely test that code path, without mocking open.

      If you want to explicitly handle different reasons for why opening a file failed differently, by all means, stress all of that in your tests. But if all you have is a happy path and an unhappy path, where your code doesn't care why opening a file failed, all you need to test is the case where the file is present, and one where it is not.

      2 replies →