16

I have to test a library that provides its own memory allocation routine:

void* allocation_routine(size_t size) throw();

Documentation states that this function allocates at least size bytes of memory (it's allowed to allocate more). By the way the function uses posix_memalign internally, but the implementation may change.

I'm wondering if it's possible to write a unit test for that kind of functions? How can we test whether the required amount of memory was allocated?

UPDATE:

If we can't write a unit test then what is the closest solution?

embedc
  • 1,485
  • 1
  • 6
  • 20
  • 3
    You will want to choose either C or C++ tags. The languages are quite different in exception handling, see [How to throw an exception in C?](https://stackoverflow.com/questions/2891766/how-to-throw-an-exception-in-c) or [throw expression - cppreference.com](https://en.cppreference.com/w/cpp/language/throw) – David C. Rankin Apr 16 '19 at 06:44
  • 1
    Possibly related: [Check if a pointer points to allocated memory on the heap](https://stackoverflow.com/q/3065092/580083). – Daniel Langr Apr 16 '19 at 06:45
  • I suggest mocking the standard memory allocation functions to check if they are called (and with which size). Most implementations allow this relatively easily. For specific help, you'll need to provide which specific implementation you're working with, and (as David C. Rankin asked) specify which language you're using (C and C++ are different languages). – Sander De Dycker Apr 16 '19 at 06:51
  • For example in gcc (C), you'd use [memory allocation hooks](https://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html). – Sander De Dycker Apr 16 '19 at 07:10
  • @DavidC.Rankin This routine is from the .cpp file. But the code is very C-style. I would suppose that C-solution is more appropriate there. – embedc Apr 16 '19 at 07:36
  • @SanderDeDycker Note that glibc memory allocation hooks are deprecated and from 2.24 they have been even removed from the API (see, e.g., http://man7.org/linux/man-pages/man3/malloc_hook.3.html). – Daniel Langr Apr 16 '19 at 07:41
  • @DanielLangr : good to know. That leaves [overriding the memory allocation functions](https://stackoverflow.com/questions/17803456/an-alternative-for-the-deprecated-malloc-hook-functionality-of-glibc) then. – Sander De Dycker Apr 16 '19 at 07:48
  • unrelated: you should replace `throw()` with `noexcept` https://stackoverflow.com/a/12833405/2805305 – bolov Apr 16 '19 at 10:00
  • On Linux, consider also looking at `/proc/self/maps`. – d33tah Apr 16 '19 at 20:41

3 Answers3

16

You cannot write a unit test for this function, because you cannot allocate memory on the heap without a system call. Hence, this is an integration test, as you are unable of isolating the unit under test from the operating system.

I would create a new, small executable that calls allocation_routine for n bytes. Depending on what allocation_routine is supposed to return, you can assert that it's non-nullptr. Then, write n bytes into this region of memory. Compile and link it using the address sanitizer (available with both gcc and clang), then try to integrate it into the test runner of your application (ctest etc.).

You might also want to restrict the available heap via the POSIX setrlimit to verify the behvaior when the allocation fails.

lubgr
  • 37,368
  • 3
  • 66
  • 117
  • 2
    Also I suggest to fill each allocated block with some pseudorandom sequence (for example, you can use PRNG generator based on xoroshiro hash). You can store seed value and later play back same sequence and check memory block correctness. Or you can just fill memory block with random values, compute and store it's hash, and check it later (but when you are using first method you can find which bytes damaged exactly). – Kirill Frolov Apr 16 '19 at 07:22
  • And as basic test, on each step (among of thousands) you can do one of the following (select randomly): 1) allocate random sized memory block, fill it with PRNG values; or 2) check correctness of some randomly selected block and deallocate it. – Kirill Frolov Apr 16 '19 at 07:24
  • Thank you! I'm new to linux. Could you please explain what did you mean by address sanitizer? I'm currently using Intel C/C++ compiler and LLVM's lit to run the tests. I'd like to avoid using external programs. – embedc Apr 16 '19 at 07:25
  • Have a look at [the docs](https://clang.llvm.org/docs/AddressSanitizer.html). Start with the example given, get familiar with the command line flag. `gcc` has a very similar sanitizer/flag, for `icc` I can't tell. An address sanitizer adds runtime checks that will report memory violations like out of bounds access, overflows etc. – lubgr Apr 16 '19 at 07:27
  • Also, every time when selecting random size of next allocated memory block, you can increase this size a little. So blocks sizes will grow in time. I that way test will show you how custom allocator is sustaned of fragmentation. – Kirill Frolov Apr 16 '19 at 07:29
  • Is it possible to limit an application to N bytes of dynamic memory, then to malloc N-n bytes, and then make a call allocation_routine(n+1). If the routine fails it means that it really trys to allocate at least n bytes. Is this test scenario realistic? – embedc Apr 17 '19 at 07:43
  • Not sure if I understand this scenario correctly. If you want to test the failure, why not just limit the application to `N` bytes and allocate `N + 1` bytes? This should fail, right? – lubgr Apr 17 '19 at 08:09
0

It's not unit test, but you can use Valgrind to get different information about memory.

  • memory leaks (which are not free)
  • memory issues

It's mainly used for debugging, but it will warns you if something is not allocated well.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nicolas Roche
  • 105
  • 1
  • 1
  • 14
0

Note that while you can't unit test a malloc replacement directly you can if there is any level of indirection. Otherwise as @lubgr has stated this is an integration test.

Indirection can help on both sides. If you have a malloc wrapper that counts memory for example:

extern "C" void* malloc(const size_t size) __THROW
{
   return getAllocator().malloc(size);
}

struct Allocator
{
   using MallocFunc = std::function<void*(size_t)>;

   unsigned long numAllocs = 0;
   size_t totalMemAllocated = 0;
   MallocFunc = __libc_malloc;

   void* malloc(const size_t size)
   {
      ++numAllocs;
      totalMemAllocated += size;
      return __libc_malloc(size);
   }
}

You could instead set MallocFunc to point to your own mock version of malloc() instead of __libc_malloc to test the accounting mechanism works properly.

Likewise you can test the allocation routine itself if you provide interposed or mock versions of the system calls it uses in the unit test code. If using gcc you might consider using symbol wrapping or the LD_PRELOAD mechanism for this.

Remember that integration tests are important as well. The assumptions you make during unit testing may not hold.

Bruce Adams
  • 4,953
  • 4
  • 48
  • 111