Skip to content

Commit

Permalink
Add malloc compliance tests
Browse files Browse the repository at this point in the history
They will be useful to test pool managers.

Signed-off-by: Lukasz Dorau <lukasz.dorau@intel.com>
  • Loading branch information
ldorau committed Dec 12, 2023
1 parent 443ed0a commit 3416f68
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 0 deletions.
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ add_umf_test(NAME memoryPool SRCS memoryPoolAPI.cpp)
add_umf_test(NAME memoryProvider SRCS memoryProviderAPI.cpp)
add_umf_test(NAME disjointPool SRCS disjoint_pool.cpp)
add_umf_test(NAME c_api_disjoint_pool SRCS c_api/disjoint_pool.c)
add_umf_test(NAME malloc_compliance_tests_glibc SRCS malloc_compliance_tests.cpp malloc_compliance_tests_glibc.cpp)

if(LINUX) # OS-specific functions are implemented only for Linux now
add_umf_test(NAME provider_os_memory SRCS provider_os_memory.cpp LIBS numa)
Expand Down
27 changes: 27 additions & 0 deletions test/common/test_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
// This file contains tests for UMF pool API

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pool_null.h"
#include "pool_trace.h"
Expand All @@ -12,6 +15,30 @@
#include "umf/memory_pool.h"
#include "umf/memory_provider.h"

#include "test_helpers.h"

void debug_message(const char *expr, const char *msg, const char *function,
const char *file, int line) {
printf("[DEBUG][%s:%s:%d] - %s: %s\n", file, function, line, expr,
msg ? msg : "failed");
fflush(stdout);
}

// Check if the memory is zero filled
int is_zero_mem(void *ptr, size_t size) {
unsigned char *mem = (unsigned char *)ptr;
return (*mem == 0) && memcmp(mem, mem + 1, size - 1) == 0;
}

// Check if two memory regions has the same content
int is_same_content(void *first, void *second, size_t size) {
return memcmp(first, second, size) == 0;
}

int is_aligned(void *ptr, size_t alignment) {
return ((uintptr_t)ptr & (alignment - 1)) == 0;
}

umf_memory_provider_handle_t nullProviderCreate(void) {
umf_memory_provider_handle_t hProvider;
umf_result_t ret =
Expand Down
13 changes: 13 additions & 0 deletions test/common/test_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
extern "C" {
#endif

#define DEBUG_MSG(expr, msg) \
((expr) ? ((void)0) \
: debug_message(#expr, msg, __func__, __FILE__, __LINE__))

void debug_message(const char *expr, const char *msg, const char *function,
const char *file, int line);

int is_zero_mem(void *ptr, size_t size);

int is_same_content(void *first, void *second, size_t size);

int is_aligned(void *ptr, size_t alignment);

umf_memory_provider_handle_t nullProviderCreate(void);
umf_memory_provider_handle_t
traceProviderCreate(umf_memory_provider_handle_t hUpstreamProvider,
Expand Down
156 changes: 156 additions & 0 deletions test/malloc_compliance_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright (C) 2023 Intel Corporation
// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// Basic tests
// ISO/IEC 9899:201x 7.22.3 compliance

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "malloc_compliance_tests.hpp"
#include "test_helpers.h"
#include "umf/memory_pool.h"

#include "base.hpp"
using umf_test::test;

//------------------------------------------------------------------------
// Configurable defs
//------------------------------------------------------------------------

#define KB 1024
#define MB (1024 * KB)

#define MAX_ALLOC_SIZE (1 * MB)
#define ITERATIONS 100

//------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------

#define ASSERT_MSG(expr, msg) \
do { \
if (!(expr)) { \
DEBUG_MSG(expr, msg); \
ASSERT_NE((intptr_t)(expr), 0); \
} \
} while (0)

static inline size_t rand_alloc_size(int max) { return rand() % max; }

static inline void free_memory(umf_memory_pool_handle_t hPool,
void *ptr[ITERATIONS]) {
for (int i = 0; i < ITERATIONS; i++) {
umfPoolFree(hPool, ptr[i]);
}
}

//------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------

// ISO/IEC 9899:TC3 7.20.3.3
void malloc_compliance_test(umf_memory_pool_handle_t hPool) {
void *alloc_ptr[ITERATIONS];
size_t alloc_ptr_size[ITERATIONS];

// Each allocation shall yield a pointer to an object disjoint from
// any other object.
for (int i = 0; i < ITERATIONS; i++) {
alloc_ptr_size[i] = rand_alloc_size(MAX_ALLOC_SIZE);
alloc_ptr[i] = umfPoolMalloc(hPool, alloc_ptr_size[i]);

// Supported under USE_WEAK_ALIGNMENT_RULES macro
// For compatibility, on 64-bit systems malloc should align to 16 bytes
size_t alignment =
(sizeof(intptr_t) > 4 && alloc_ptr_size[i] > 8) ? 16 : 8;
ASSERT_MSG(
is_aligned(alloc_ptr[i], alignment),
"Malloc should return pointer that is suitably aligned so that "
"it may be assigned to a pointer to any type of object ");

// Fill memory with zeros
ASSERT_MSG(alloc_ptr[i],
"malloc returned NULL, couldn't allocate much memory");
memset(alloc_ptr[i], 0, alloc_ptr_size[i]);
}
for (int i = 0; i < ITERATIONS; i++) {
ASSERT_MSG(is_zero_mem(alloc_ptr[i], alloc_ptr_size[i]),
"Object returned by malloc is not disjoined from others");
memset(alloc_ptr[i], 1, alloc_ptr_size[i]);
}
free_memory(hPool, alloc_ptr);
}

// ISO/IEC 9899:TC3 7.20.3.1
void calloc_compliance_test(umf_memory_pool_handle_t hPool) {
void *alloc_ptr[ITERATIONS];
size_t alloc_size;

// Checking that the memory returned by calloc is zero filled
for (int i = 0; i < ITERATIONS; i++) {
alloc_size = rand_alloc_size(MAX_ALLOC_SIZE);
alloc_ptr[i] = umfPoolCalloc(hPool, i + 1, alloc_size);

ASSERT_MSG(alloc_ptr[i],
"calloc returned NULL, couldn't allocate much memory");
ASSERT_MSG(is_zero_mem(alloc_ptr[i], alloc_size),
"Memory returned by calloc was not zeroed");
}
free_memory(hPool, alloc_ptr);
}

// ISO/IEC 9899:TC3 7.20.3.4
void realloc_compliance_test(umf_memory_pool_handle_t hPool) {
// If ptr is a null pointer, the realloc function behaves
// like the malloc function for the specified size
void *ret = umfPoolRealloc(hPool, NULL, 10);
ASSERT_MSG(ret, "If ptr is a NULL, realloc should behave like malloc");
// SIGSEGV if memory was allocated wrong
memset(ret, 1, 10);
umfPoolFree(hPool, ret);

// The contents of the new object shall be the same
// as that of the old object prior to deallocation
void *realloc_ptr[ITERATIONS];
size_t alloc_size;
for (int i = 0; i < ITERATIONS; i++) {
// Generate allocation size
alloc_size = rand_alloc_size(MAX_ALLOC_SIZE);
void *malloc_obj = umfPoolMalloc(hPool, alloc_size);
ASSERT_MSG(malloc_obj,
"malloc returned NULL, couldn't allocate much memory");

// Fit memory region with data and store
// it's content somehere before realloc
void *saved_obj = umfPoolMalloc(hPool, alloc_size);
ASSERT_MSG(saved_obj,
"malloc returned NULL, couldn't allocate much memory");
memset(malloc_obj, 1, alloc_size);
memcpy(saved_obj, malloc_obj, alloc_size);

// Reallocate with 100 byte size increasing
realloc_ptr[i] = umfPoolRealloc(hPool, malloc_obj, alloc_size + 100);
ASSERT_MSG(is_same_content(realloc_ptr[i], saved_obj, alloc_size),
"Content after realloc should remain the same");

// Delete saved memory
umfPoolFree(hPool, saved_obj);
}
free_memory(hPool, realloc_ptr);
}

// ISO/IEC 9899:TC3 7.20.3.2
void free_compliance_test(umf_memory_pool_handle_t hPool) {
// If ptr is a null pointer, no action occurs
errno = 0;
for (int i = 0; i < ITERATIONS; i++) {
umfPoolFree(hPool, NULL);
}
ASSERT_MSG(errno == 0,
"Error was found by a free call with NULL parameter");
}
15 changes: 15 additions & 0 deletions test/malloc_compliance_tests.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (C) 2023 Intel Corporation
// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef UMF_TEST_MALLOC_COMPLIANCE_TESTS_H
#define UMF_TEST_MALLOC_COMPLIANCE_TESTS_H

#include "umf/memory_pool.h"

void malloc_compliance_test(umf_memory_pool_handle_t hPool);
void calloc_compliance_test(umf_memory_pool_handle_t hPool);
void realloc_compliance_test(umf_memory_pool_handle_t hPool);
void free_compliance_test(umf_memory_pool_handle_t hPool);

#endif /* UMF_TEST_MALLOC_COMPLIANCE_TESTS_H */
80 changes: 80 additions & 0 deletions test/malloc_compliance_tests_glibc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (C) 2023 Intel Corporation
// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// run basic compliance tests for glibc's functions (sanity check)

#include "base.hpp"

using umf_test::test;

#include <assert.h>
#include <stdlib.h>

#include "../src/memory_pool_internal.h"
#include "malloc_compliance_tests.hpp"
#include <umf/memory_pool_ops.h>

// casting to (uintptr_t) because of:
// warning C4312: 'type cast': conversion from 'unsigned int' to 'void *' of greater size
#define POOL_PRIV ((void *)(uintptr_t)0xDEADBEAF)

static void *glibc_malloc(void *pool, size_t size) {
(void)pool; // unused
assert(pool == POOL_PRIV);
return malloc(size);
}

static void *glibc_calloc(void *pool, size_t num, size_t size) {
(void)pool; // unused
assert(pool == POOL_PRIV);
return calloc(num, size);
}

static void *glibc_realloc(void *pool, void *ptr, size_t size) {
(void)pool; // unused
assert(pool == POOL_PRIV);
return realloc(ptr, size);
}

static enum umf_result_t glibc_free(void *pool, void *ptr) {
(void)pool; // unused
assert(pool == POOL_PRIV);
free(ptr);
return UMF_RESULT_SUCCESS;
}

static const struct umf_memory_pool_ops_t UMF_GLIBC_POOL_OPS = {
/* .version = */ UMF_VERSION_CURRENT,
/* .initialize = */ NULL,
/* .finalize = */ NULL,
/* .malloc = */ glibc_malloc,
/* .calloc = */ glibc_calloc,
/* .realloc = */ glibc_realloc,
/* .aligned_malloc = */ NULL,
/* .malloc_usable_size = */ NULL,
/* .free = */ glibc_free,
/* .get_last_allocation_error = */ NULL,
};

static struct umf_memory_pool_t UMF_GLIBC_POOL = {
/* .pool_priv = */ POOL_PRIV,
/* .ops = */ UMF_GLIBC_POOL_OPS,
/* .provider = */ NULL,
};

TEST_F(test, glibc_malloc_compliance_test) {
malloc_compliance_test(&UMF_GLIBC_POOL);
}

TEST_F(test, glibc_calloc_compliance_test) {
calloc_compliance_test(&UMF_GLIBC_POOL);
}

TEST_F(test, glibc_realloc_compliance_test) {
realloc_compliance_test(&UMF_GLIBC_POOL);
}

TEST_F(test, glibc_free_compliance_test) {
free_compliance_test(&UMF_GLIBC_POOL);
}

0 comments on commit 3416f68

Please sign in to comment.