Skip to content

Commit d901503

Browse files
committed
Add tests for tracking provider
Signed-off-by: Lukasz Dorau <[email protected]>
1 parent 3ec6936 commit d901503

File tree

2 files changed

+378
-0
lines changed

2 files changed

+378
-0
lines changed

test/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ if(LINUX AND (NOT UMF_DISABLE_HWLOC)) # OS-specific functions are implemented
343343
NAME provider_fixed_memory
344344
SRCS provider_fixed_memory.cpp
345345
LIBS ${UMF_UTILS_FOR_TEST})
346+
add_umf_test(
347+
NAME provider_tracking
348+
SRCS provider_tracking.cpp
349+
LIBS ${UMF_UTILS_FOR_TEST})
346350

347351
# This test requires Linux-only file memory provider
348352
if(UMF_POOL_JEMALLOC_ENABLED)

test/provider_tracking.cpp

+374
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "base.hpp"
6+
7+
#include "cpp_helpers.hpp"
8+
#include "test_helpers.h"
9+
#ifndef _WIN32
10+
#include "test_helpers_linux.h"
11+
#endif
12+
13+
#include <umf/memory_provider.h>
14+
#include <umf/pools/pool_proxy.h>
15+
#include <umf/providers/provider_fixed_memory.h>
16+
17+
using umf_test::test;
18+
19+
#define FIXED_BUFFER_SIZE (512 * utils_get_page_size())
20+
#define INVALID_PTR ((void *)0x01)
21+
22+
using providerCreateExtParams = std::tuple<umf_memory_provider_ops_t *, void *>;
23+
24+
static void providerCreateExt(providerCreateExtParams params,
25+
umf::provider_unique_handle_t *handle) {
26+
umf_memory_provider_handle_t hProvider = nullptr;
27+
auto [provider_ops, provider_params] = params;
28+
29+
auto ret =
30+
umfMemoryProviderCreate(provider_ops, provider_params, &hProvider);
31+
ASSERT_EQ(ret, UMF_RESULT_SUCCESS);
32+
ASSERT_NE(hProvider, nullptr);
33+
34+
*handle =
35+
umf::provider_unique_handle_t(hProvider, &umfMemoryProviderDestroy);
36+
}
37+
38+
struct TrackingProviderTest
39+
: umf_test::test,
40+
::testing::WithParamInterface<providerCreateExtParams> {
41+
void SetUp() override {
42+
test::SetUp();
43+
44+
// Allocate a memory buffer to use with the fixed memory provider
45+
memory_size = FIXED_BUFFER_SIZE;
46+
memory_buffer = malloc(memory_size);
47+
ASSERT_NE(memory_buffer, nullptr);
48+
49+
// Create provider parameters
50+
umf_fixed_memory_provider_params_handle_t params = nullptr;
51+
umf_result_t res = umfFixedMemoryProviderParamsCreate(
52+
&params, memory_buffer, memory_size);
53+
ASSERT_EQ(res, UMF_RESULT_SUCCESS);
54+
ASSERT_NE(params, nullptr);
55+
56+
providerCreateExt(std::make_tuple(umfFixedMemoryProviderOps(), params),
57+
&provider);
58+
59+
umfFixedMemoryProviderParamsDestroy(params);
60+
umf_result_t umf_result =
61+
umfMemoryProviderGetMinPageSize(provider.get(), NULL, &page_size);
62+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
63+
64+
page_plus_64 = page_size + 64;
65+
66+
umf_memory_pool_handle_t hPool = nullptr;
67+
umf_result = umfPoolCreate(umfProxyPoolOps(), provider.get(), nullptr,
68+
0, &hPool);
69+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
70+
71+
pool = umf::pool_unique_handle_t(hPool, &umfPoolDestroy);
72+
}
73+
74+
void TearDown() override {
75+
if (memory_buffer) {
76+
free(memory_buffer);
77+
memory_buffer = nullptr;
78+
}
79+
test::TearDown();
80+
}
81+
82+
umf::provider_unique_handle_t provider;
83+
umf::pool_unique_handle_t pool;
84+
size_t page_size;
85+
size_t page_plus_64;
86+
void *memory_buffer = nullptr;
87+
size_t memory_size = 0;
88+
};
89+
90+
static void
91+
createPoolFromAllocation(void *ptr0, size_t size1,
92+
umf_memory_provider_handle_t *_providerFromPtr,
93+
umf_memory_pool_handle_t *_poolFromPtr) {
94+
umf_result_t umf_result;
95+
96+
// Create provider parameters
97+
umf_fixed_memory_provider_params_handle_t params = nullptr;
98+
umf_result = umfFixedMemoryProviderParamsCreate(&params, ptr0, size1);
99+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
100+
ASSERT_NE(params, nullptr);
101+
102+
umf_memory_provider_handle_t provider1 = nullptr;
103+
umf_result = umfMemoryProviderCreate(umfFixedMemoryProviderOps(), params,
104+
&provider1);
105+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
106+
ASSERT_NE(provider1, nullptr);
107+
108+
umf_memory_pool_handle_t pool1 = nullptr;
109+
umf_result =
110+
umfPoolCreate(umfProxyPoolOps(), provider1, nullptr, 0, &pool1);
111+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
112+
113+
umfFixedMemoryProviderParamsDestroy(params);
114+
115+
*_providerFromPtr = provider1;
116+
*_poolFromPtr = pool1;
117+
}
118+
119+
// TESTS
120+
121+
INSTANTIATE_TEST_SUITE_P(trackingProviderTest, TrackingProviderTest,
122+
::testing::Values(providerCreateExtParams{
123+
umfFixedMemoryProviderOps(), nullptr}));
124+
125+
TEST_P(TrackingProviderTest, create_destroy) {
126+
// Creation and destruction are handled in SetUp and TearDown
127+
}
128+
129+
TEST_P(TrackingProviderTest, whole_size_success) {
130+
umf_result_t umf_result;
131+
size_t size0;
132+
size_t size1;
133+
void *ptr0 = nullptr;
134+
void *ptr1 = nullptr;
135+
136+
umf_memory_pool_handle_t pool0 = pool.get();
137+
138+
size0 = FIXED_BUFFER_SIZE - (2 * page_size);
139+
ptr0 = umfPoolAlignedMalloc(pool0, size0, utils_get_page_size());
140+
ASSERT_NE(ptr0, nullptr);
141+
142+
size1 = size0; // whole size
143+
144+
umf_memory_provider_handle_t provider1 = nullptr;
145+
umf_memory_pool_handle_t pool1 = nullptr;
146+
createPoolFromAllocation(ptr0, size1, &provider1, &pool1);
147+
148+
ptr1 = umfPoolMalloc(pool1, size1);
149+
ASSERT_NE(ptr1, nullptr);
150+
151+
umf_result = umfPoolFree(pool1, ptr1);
152+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
153+
154+
umfPoolDestroy(pool1);
155+
umfMemoryProviderDestroy(provider1);
156+
157+
umf_result = umfPoolFree(pool0, ptr0);
158+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
159+
}
160+
161+
TEST_P(TrackingProviderTest, half_size_success) {
162+
umf_result_t umf_result;
163+
size_t size0;
164+
size_t size1;
165+
void *ptr0 = nullptr;
166+
void *ptr1 = nullptr;
167+
168+
umf_memory_pool_handle_t pool0 = pool.get();
169+
170+
size0 = FIXED_BUFFER_SIZE - (2 * page_size);
171+
ptr0 = umfPoolAlignedMalloc(pool0, size0, utils_get_page_size());
172+
ASSERT_NE(ptr0, nullptr);
173+
174+
size1 = size0 / 2; // half size
175+
176+
umf_memory_provider_handle_t provider1 = nullptr;
177+
umf_memory_pool_handle_t pool1 = nullptr;
178+
createPoolFromAllocation(ptr0, size1, &provider1, &pool1);
179+
180+
ptr1 = umfPoolMalloc(pool1, size1);
181+
ASSERT_NE(ptr1, nullptr);
182+
183+
umf_result = umfPoolFree(pool1, ptr1);
184+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
185+
186+
umfPoolDestroy(pool1);
187+
umfMemoryProviderDestroy(provider1);
188+
189+
umf_result = umfPoolFree(pool0, ptr0);
190+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
191+
}
192+
193+
TEST_P(TrackingProviderTest, failure_exceeding_size) {
194+
umf_result_t umf_result;
195+
size_t size0;
196+
size_t size1;
197+
void *ptr0 = nullptr;
198+
void *ptr1 = nullptr;
199+
200+
umf_memory_pool_handle_t pool0 = pool.get();
201+
202+
size0 = FIXED_BUFFER_SIZE - (2 * page_size);
203+
ptr0 = umfPoolAlignedMalloc(pool0, size0, utils_get_page_size());
204+
ASSERT_NE(ptr0, nullptr);
205+
206+
size1 = FIXED_BUFFER_SIZE - page_size; // exceeding size
207+
208+
umf_memory_provider_handle_t provider1 = nullptr;
209+
umf_memory_pool_handle_t pool1 = nullptr;
210+
createPoolFromAllocation(ptr0, size1, &provider1, &pool1);
211+
212+
ptr1 = umfPoolMalloc(pool1, size1);
213+
ASSERT_EQ(ptr1, nullptr);
214+
215+
umfPoolDestroy(pool1);
216+
umfMemoryProviderDestroy(provider1);
217+
218+
umf_result = umfPoolFree(pool0, ptr0);
219+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
220+
}
221+
222+
#define MAX_ARRAY 9
223+
#define TEST_LEVEL_SUCCESS 7
224+
#define TEST_LEVEL_FAILURE 8
225+
226+
TEST_P(TrackingProviderTest, success_max_levels) {
227+
umf_result_t umf_result;
228+
size_t size;
229+
void *ptr[MAX_ARRAY] = {0};
230+
umf_memory_provider_handle_t providers[MAX_ARRAY] = {0};
231+
umf_memory_pool_handle_t pools[MAX_ARRAY] = {0};
232+
233+
size = FIXED_BUFFER_SIZE - (2 * page_size);
234+
pools[0] = pool.get();
235+
236+
for (int i = 0; i < TEST_LEVEL_SUCCESS; i++) {
237+
fprintf(stderr, "Alloc #%d\n", i);
238+
ptr[i] = umfPoolAlignedMalloc(pools[i], size, utils_get_page_size());
239+
ASSERT_NE(ptr[i], nullptr);
240+
241+
createPoolFromAllocation(ptr[i], size, &providers[i + 1],
242+
&pools[i + 1]);
243+
}
244+
245+
int s = TEST_LEVEL_SUCCESS;
246+
fprintf(stderr, "Alloc #%d\n", s);
247+
ptr[s] = umfPoolAlignedMalloc(pools[s], size, utils_get_page_size());
248+
ASSERT_NE(ptr[s], nullptr);
249+
250+
fprintf(stderr, "Free #%d\n", s);
251+
umf_result = umfPoolFree(pools[s], ptr[s]);
252+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
253+
254+
for (int i = TEST_LEVEL_SUCCESS - 1; i >= 0; i--) {
255+
umfPoolDestroy(pools[i + 1]);
256+
umfMemoryProviderDestroy(providers[i + 1]);
257+
258+
fprintf(stderr, "Free #%d\n", i);
259+
umf_result = umfPoolFree(pools[i], ptr[i]);
260+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
261+
}
262+
}
263+
264+
TEST_P(TrackingProviderTest, failure_exceeding_levels) {
265+
umf_result_t umf_result;
266+
size_t size;
267+
void *ptr[MAX_ARRAY] = {0};
268+
umf_memory_provider_handle_t providers[MAX_ARRAY] = {0};
269+
umf_memory_pool_handle_t pools[MAX_ARRAY] = {0};
270+
271+
size = FIXED_BUFFER_SIZE - (2 * page_size);
272+
pools[0] = pool.get();
273+
274+
for (int i = 0; i < TEST_LEVEL_FAILURE; i++) {
275+
fprintf(stderr, "Alloc #%d\n", i);
276+
ptr[i] = umfPoolAlignedMalloc(pools[i], size, utils_get_page_size());
277+
ASSERT_NE(ptr[i], nullptr);
278+
279+
createPoolFromAllocation(ptr[i], size, &providers[i + 1],
280+
&pools[i + 1]);
281+
}
282+
283+
// tracker level is too high
284+
int f = TEST_LEVEL_FAILURE;
285+
fprintf(stderr, "Alloc #%d\n", f);
286+
ptr[f] = umfPoolAlignedMalloc(pools[f], size, utils_get_page_size());
287+
ASSERT_EQ(ptr[f], nullptr);
288+
289+
for (int i = TEST_LEVEL_FAILURE - 1; i >= 0; i--) {
290+
umfPoolDestroy(pools[i + 1]);
291+
umfMemoryProviderDestroy(providers[i + 1]);
292+
293+
fprintf(stderr, "Free #%d\n", i);
294+
umf_result = umfPoolFree(pools[i], ptr[i]);
295+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
296+
}
297+
}
298+
299+
TEST_P(TrackingProviderTest, reverted_free_half_size) {
300+
umf_result_t umf_result;
301+
size_t size0;
302+
size_t size1;
303+
void *ptr0 = nullptr;
304+
void *ptr1 = nullptr;
305+
306+
umf_memory_pool_handle_t pool0 = pool.get();
307+
308+
size0 = FIXED_BUFFER_SIZE - (2 * page_size);
309+
ptr0 = umfPoolAlignedMalloc(pool0, size0, utils_get_page_size());
310+
ASSERT_NE(ptr0, nullptr);
311+
312+
umf_memory_provider_handle_t provider1 = nullptr;
313+
umf_memory_pool_handle_t pool1 = nullptr;
314+
createPoolFromAllocation(ptr0, size0, &provider1, &pool1);
315+
316+
size1 = size0 / 2; // half size
317+
318+
ptr1 = umfPoolMalloc(pool1, size1);
319+
ASSERT_NE(ptr1, nullptr);
320+
321+
// Freeing the "busy" pointer from the first pool is an Undefined Behavior
322+
// It fails now if the sizes are different.
323+
// see: https://github.com/oneapi-src/unified-memory-framework/pull/1161
324+
umf_result = umfPoolFree(pool0, ptr0);
325+
326+
umf_result = umfPoolFree(pool1, ptr1);
327+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
328+
329+
umfPoolDestroy(pool1);
330+
umfMemoryProviderDestroy(provider1);
331+
332+
// It could have been freed above,
333+
// so we cannot verify the result here.
334+
umf_result = umfPoolFree(pool0, ptr0);
335+
}
336+
337+
TEST_P(TrackingProviderTest, reverted_free_the_same_size) {
338+
umf_result_t umf_result;
339+
size_t size0;
340+
size_t size1;
341+
void *ptr0 = nullptr;
342+
void *ptr1 = nullptr;
343+
344+
umf_memory_pool_handle_t pool0 = pool.get();
345+
346+
size0 = FIXED_BUFFER_SIZE - (2 * page_size);
347+
ptr0 = umfPoolAlignedMalloc(pool0, size0, utils_get_page_size());
348+
ASSERT_NE(ptr0, nullptr);
349+
350+
umf_memory_provider_handle_t provider1 = nullptr;
351+
umf_memory_pool_handle_t pool1 = nullptr;
352+
createPoolFromAllocation(ptr0, size0, &provider1, &pool1);
353+
354+
size1 = size0; // the same size
355+
356+
ptr1 = umfPoolMalloc(pool1, size1);
357+
ASSERT_NE(ptr1, nullptr);
358+
359+
// Freeing the "busy" pointer from the first pool is an Undefined Behavior
360+
// It succeeds now if the sizes are equal.
361+
// see: https://github.com/oneapi-src/unified-memory-framework/pull/1161
362+
umf_result = umfPoolFree(pool0, ptr0);
363+
364+
// try to free the pointer from the second pool (the same size)
365+
umf_result = umfPoolFree(pool1, ptr1);
366+
ASSERT_EQ(umf_result, UMF_RESULT_SUCCESS);
367+
368+
umfPoolDestroy(pool1);
369+
umfMemoryProviderDestroy(provider1);
370+
371+
// It could have been freed above,
372+
// so we cannot verify the result here.
373+
umf_result = umfPoolFree(pool0, ptr0);
374+
}

0 commit comments

Comments
 (0)