NFC: sizeclass: differentiate minimum step size and minimum allocation sizes (#651)

* Move sizeclass debugging code to sizeclass test

The sizeclass was already testing most of this, so just add the missing bits.
Forgo some tests whose failure would have implied earlier failures.

This moves the last dynamic call of size_to_sizeclass_const into tests
(and so, too, to_exp_mant_const).  sizeclasstable.h still contains a static
call to compute NUM_SMALL_SIZECLASSES from MAX_SMALL_SIZECLASS_SIZE.

* Remove unused to_exp_mant

Only its _const sibling is used, and little at that, now that almost everything
to do with sizes and size classes is table-driven.

* test/memcpy: trap, if we can, before exiting

This just means I don't need to remember to set a breakpoint on exit

* test/memcpy: don't assume sizeclass 0 is allocable

* test/memory: don't assume sizeclass 0 is allocable

* test/sizeclass: handle nonzero minimum sizeclasses

* sizeclass: distinguish min alloc and step size

Add support for a minimum allocation size that isn't the minimum step of
the sizeclass table.

* Expose MIN_ALLOC_{,STEP}_SIZE through cmake

* test/sizeclass: report MIN_ALLOC_{STEP_,}SIZE
This commit is contained in:
Nathaniel Filardo
2024-05-24 18:49:39 +01:00
committed by GitHub
parent b1d0d7dc78
commit 846a926155
8 changed files with 116 additions and 51 deletions

View File

@@ -57,6 +57,9 @@ extern "C" void abort()
{
longjmp(jmp, 1);
}
# if __has_builtin(__builtin_trap)
__builtin_trap();
# endif
exit(-1);
}
@@ -152,7 +155,11 @@ int main()
// Some sizes to check for out-of-bounds access. As we are only able to
// catch overflows past the end of the sizeclass-padded allocation, make
// sure we don't try to test on smaller allocations.
std::initializer_list<size_t> sizes = {MIN_ALLOC_SIZE, 1024, 2 * 1024 * 1024};
static constexpr size_t min_class_size =
sizeclass_to_size(size_to_sizeclass(MIN_ALLOC_SIZE));
std::initializer_list<size_t> sizes = {min_class_size, 1024, 2 * 1024 * 1024};
static_assert(
MIN_ALLOC_SIZE < 1024,
"Can't detect overflow except at sizeclass boundaries");

View File

@@ -237,7 +237,9 @@ void test_external_pointer()
// Malloc does not have an external pointer querying mechanism.
auto& alloc = ThreadAlloc::get();
for (uint8_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
for (snmalloc::smallsizeclass_t sc = size_to_sizeclass(MIN_ALLOC_SIZE);
sc < NUM_SMALL_SIZECLASSES;
sc++)
{
size_t size = sizeclass_to_size(sc);
void* p1 = alloc.alloc(size);
@@ -470,7 +472,9 @@ void test_static_sized_allocs()
void test_remaining_bytes()
{
auto& alloc = ThreadAlloc::get();
for (size_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
for (snmalloc::smallsizeclass_t sc = size_to_sizeclass(MIN_ALLOC_SIZE);
sc < NUM_SMALL_SIZECLASSES;
sc++)
{
auto size = sizeclass_to_size(sc);
char* p = (char*)alloc.alloc(size);

View File

@@ -8,6 +8,9 @@ snmalloc::smallsizeclass_t size_to_sizeclass(size_t size)
return snmalloc::size_to_sizeclass(size);
}
static constexpr snmalloc::smallsizeclass_t minimum_sizeclass =
snmalloc::size_to_sizeclass_const(snmalloc::MIN_ALLOC_SIZE);
void test_align_size()
{
bool failed = false;
@@ -72,6 +75,10 @@ int main(int, char**)
bool failed = false;
size_t size_low = 0;
std::cout << "Configured with minimum allocation size "
<< snmalloc::MIN_ALLOC_SIZE << " and step size "
<< snmalloc::MIN_ALLOC_STEP_SIZE << std::endl;
std::cout << "0 has sizeclass: " << (size_t)snmalloc::size_to_sizeclass(0)
<< std::endl;
@@ -86,12 +93,14 @@ int main(int, char**)
slab_size != snmalloc::sizeclass_to_slab_size(sz))
{
slab_size = snmalloc::sizeclass_to_slab_size(sz);
std::cout << std::endl;
std::cout << std::endl << "slab size: " << slab_size << std::endl;
}
size_t size = snmalloc::sizeclass_to_size(sz);
std::cout << (size_t)sz << " |-> "
<< "[" << size_low + 1 << ", " << size << "]" << std::endl;
<< "[" << size_low + 1 << ", " << size << "]"
<< (sz == minimum_sizeclass ? " is minimum class" : "")
<< std::endl;
if (size < size_low)
{
@@ -102,7 +111,30 @@ int main(int, char**)
for (size_t i = size_low + 1; i <= size; i++)
{
if (size_to_sizeclass(i) != sz)
/* All sizes should, via bit-math, come back to their class value */
if (snmalloc::size_to_sizeclass_const(i) != sz)
{
std::cout << "Size " << i << " has _const sizeclass "
<< (size_t)snmalloc::size_to_sizeclass_const(i)
<< " but expected sizeclass " << (size_t)sz << std::endl;
failed = true;
}
if (size < snmalloc::MIN_ALLOC_SIZE)
{
/*
* It is expected that these sizes have the "wrong" class from tabular
* lookup: they will have been clipped up to the minimum class.
*/
if (size_to_sizeclass(i) != minimum_sizeclass)
{
std::cout << "Size " << i << " below minimum size; sizeclass "
<< (size_t)size_to_sizeclass(i) << " not expected minimum "
<< (size_t)minimum_sizeclass << std::endl;
failed = true;
}
}
else if (size_to_sizeclass(i) != sz)
{
std::cout << "Size " << i << " has sizeclass "
<< (size_t)size_to_sizeclass(i) << " but expected sizeclass "