added test alloc with working purecap

This commit is contained in:
2025-04-07 17:10:55 +01:00
parent 4894364ddf
commit 8cf048b6b2
5 changed files with 465 additions and 10 deletions

View File

@@ -0,0 +1,225 @@
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/types.h>
#include <cheriintrin.h>
#include <cheri/cheric.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#define MAXPAGESIZES 2
static char *heap_start;
static char *heap;
static size_t HEAP_SIZE = 1024 * 1024 * 1024;
void *ptr;
int MallocCounter;
int malloc_called = 0;
size_t sizeUsed;
// Instrcutor allocator to create the huge page
// of 1 GB
// __attribute__((constructor))
static void INITREGULARALLOC() {
size_t sz;
// Hard-coded for 1GB huge page
sz = 1073741824;
int error, fd, pscnt, pn;
size_t ps[2];
size_t size[3];
// pn = getpagesizes(size, 3);
// printf("page size is [%d]", size[2]);
// pscnt = pagesizes(ps);
fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, 1, SHM_LARGEPAGE_ALLOC_DEFAULT, 0);
if (fd < 0 && errno == ENOTTY) {
perror("sh_create_largepages");
close(fd);
exit(EXIT_FAILURE);
}
// if (fd < 0)
// perror("no large page supported");
// exit(EXIT_FAILURE);
// if (fd < 0 && errno == ENOTTY)
// atf_tc_skip("no large page support");
// ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; errno=%d", errno);
if (ftruncate(fd, sz) < 0) {
perror("ftruncate");
close(fd);
exit(EXIT_FAILURE);
}
// if (error != 0 && errno == ENOMEM)
// /*
// * The test system might not have enough memory to accommodate
// * the request.
// */
// atf_tc_skip("failed to allocate %zu-byte superpage", sz);
// ATF_REQUIRE_MSG(error == 0, "ftruncate failed; errno=%d", errno);
ptr = mmap(NULL, sz,
PROT_READ|PROT_WRITE, MAP_SHARED,fd,0);
// Added error handling
if(ptr == MAP_FAILED)
{
perror("mmap");
exit(EXIT_FAILURE);
}
// fprintf(stderr, "heap used alloc %lu\n", heap - heap_start);
MallocCounter = (int)sz;
}
// -- Custom malloc and free functions written
// This will be replaced with mmap since we already
// do a initial mmap.
int notrun = 0;
void *MALLOCCHERI(size_t sz)
{
if (notrun == 0){
INITREGULARALLOC();
notrun = 1;
}
sz = __builtin_align_up(sz, _Alignof(max_align_t));
// printf("%d \n", sz);
// printf("%d Malloc counter\n", MallocCounter);
MallocCounter -= sz;
void *ptrLink = &ptr[MallocCounter];
ptrLink = cheri_setbounds(ptrLink, sz);
return ptrLink;
// if (heap + sz > heap_start + HEAP_SIZE) return NULL;
// heap += sz;
// return heap - sz;
}
// Quick cheri free implementation
void FREECHERI(void *ptr) {
// printf("free called \n");
// get bounds from
int len = cheri_getlen(ptr);
// printf("free len %d \n", len);
munmap(ptr, len);
}
__attribute__((destructor))
static void malloc_exit() {
fprintf(stderr, "heap used %lu\n", malloc_called);
}
// void *malloc(size_t sz) {
// // if (!heap) heap = heap_start = mmap(NULL, HEAP_SIZE,
// // PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0);
// // char *new_ptr = __builtin_align_up(
// // heap, -cheri_representable_alignment_mask(sz));
// // size_t bounds = cheri_representable_length(sz);
// // sz = __builtin_align_up(sz, _Alignof(max_align_t));
// // if (new_ptr + sz > heap_start + HEAP_SIZE)
// // return NULL;
// // heap = new_ptr + sz;
// // return cheri_bounds_set_exact(new_ptr, bounds);
// return MALLOCCHERI(sz);
// }
void *malloc(size_t sz) {
// if (!heap) heap = heap_start = mmap(NULL, HEAP_SIZE,
// PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0);
// sz = __builtin_align_up(sz, _Alignof(max_align_t));
// if (heap + sz > heap_start + HEAP_SIZE) return NULL;
// heap += sz;
// return heap - sz;
malloc_called += 1;
return MALLOCCHERI(sz);
}
void free(void *ptr) {
FREECHERI(ptr);
}
void *realloc(void *ptr, size_t sz) {
void *new_ptr = malloc(sz);
if (ptr && new_ptr) memmove(new_ptr, ptr, sz);
return new_ptr;
}
void *calloc(size_t nmemb, size_t sz) {
char *ptr = malloc(nmemb * sz);
bzero(ptr, nmemb * sz);
return ptr;
}
void *reallocarray(void *ptr, size_t nmemb, size_t sz) {
return realloc(ptr, nmemb * sz);
}
void *recallocarray(void *ptr, size_t oldnmemb, size_t nmemb, size_t sz) {
void *new_ptr = malloc(nmemb * sz);
if (ptr && new_ptr) memmove(new_ptr, ptr, oldnmemb * sz);
if (new_ptr && nmemb > oldnmemb) bzero(new_ptr + oldnmemb * sz, (nmemb - oldnmemb) * sz);
return new_ptr;
}
void freezero(void *ptr, size_t sz) { }
void *aligned_alloc(size_t alignment, size_t sz) {
return malloc(sz);
}
void *malloc_conceal(size_t sz) {
return malloc(sz);
}
void *calloc_conceal(size_t nmemb, size_t sz) {
return calloc(nmemb, sz);
}
int posix_memalign(void **ptr, size_t alignment, size_t sz) {
*ptr = malloc(sz);
return *ptr == 0 ? 0 : ENOMEM;
}
void *memalign(size_t alignment, size_t sz) {
return malloc(sz);
}
void *valloc(size_t sz) {
return malloc(sz);
}

View File

@@ -0,0 +1,7 @@
# build glibc
cc -g -Wall -o glibc-bench.out -march=morello -mabi=purecap -Xclang -morello-vararg=new -lpthread glibc.c
# build shared object library
cc -O3 -g -W -Wall -shared -o ./malloc.so -mabi=purecap -Wno-unused-parameter -lpthread -fPIC alloc.c
LD_PRELOAD=malloc.so ./glibc-bench.out

View File

@@ -0,0 +1,217 @@
/* Benchmark malloc and free functions.
Copyright (C) 2019-2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
// modified by Daan Leijen to fit the bench suite and add lifo/fifo free order.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
//#include "malloc.h"
// #define malloc MALLOCCHERI
// #define free FREECHERI
// #include "bench-timing.h"
// #include "json-lib.h"
/* Benchmark the malloc/free performance of a varying number of blocks of a
given size. This enables performance tracking of the t-cache and fastbins.
It tests 3 different scenarios: single-threaded using main arena,
multi-threaded using thread-arena, and main arena with SINGLE_THREAD_P
false. */
// source: https://github.com/daanx/mimalloc-bench/blob/master/bench/glibc-bench/bench-malloc-thread.c
#define NUM_ITERS 2000000
#define NUM_ALLOCS 4
#define MAX_ALLOCS 1600
// Daan: disable timing
typedef long timing_t;
#define TIMING_NOW(s)
#define TIMING_DIFF(e,start,stop)
typedef struct
{
size_t iters;
size_t size;
int n;
timing_t elapsed;
} malloc_args;
static void
do_benchmark (malloc_args *args, char**arr)
{
timing_t start, stop;
size_t iters = args->iters;
size_t size = args->size;
int n = args->n;
TIMING_NOW (start);
for (int j = 0; j < iters; j++)
{
for (int i = 0; i < n; i++) {
arr[i] = malloc (size);
for(int g = 0; g < size; g++) { arr[i][g] =(char)g; }
}
// free half in fifo order
for (int i = 0; i < n/2; i++) {
free (arr[i]);
}
// and the other half in lifo order
for(int i = n-1; i >= n/2; i--) {
free(arr[i]);
}
}
TIMING_NOW (stop);
TIMING_DIFF (args->elapsed, start, stop);
}
static malloc_args tests[3][NUM_ALLOCS];
static int allocs[NUM_ALLOCS] = { 25, 100, 400, MAX_ALLOCS };
static void *
thread_test (void *p)
{
char **arr = (char**)p;
/* Run benchmark multi-threaded. */
for (int i = 0; i < NUM_ALLOCS; i++)
do_benchmark (&tests[2][i], arr);
return p;
}
void
bench (unsigned long size)
{
size_t iters = NUM_ITERS;
char**arr = (char**)malloc (MAX_ALLOCS * sizeof (void*));
for (int t = 0; t < 3; t++)
for (int i = 0; i < NUM_ALLOCS; i++)
{
tests[t][i].n = allocs[i];
tests[t][i].size = size;
tests[t][i].iters = iters / allocs[i];
/* Do a quick warmup run. */
if (t == 0)
do_benchmark (&tests[0][i], arr);
}
/* Run benchmark single threaded in main_arena. */
for (int i = 0; i < NUM_ALLOCS; i++)
do_benchmark (&tests[0][i], arr);
/* Run benchmark in a thread_arena. */
pthread_t t;
pthread_create (&t, NULL, thread_test, (void*)arr);
pthread_join (t, NULL);
/* Repeat benchmark in main_arena with SINGLE_THREAD_P == false. */
for (int i = 0; i < NUM_ALLOCS; i++)
do_benchmark (&tests[1][i], arr);
free (arr);
/*
json_ctx_t json_ctx;
json_init (&json_ctx, 0, stdout);
json_document_begin (&json_ctx);
json_attr_string (&json_ctx, "timing_type", TIMING_TYPE);
json_attr_object_begin (&json_ctx, "functions");
json_attr_object_begin (&json_ctx, "malloc");
char s[100];
double iters2 = iters;
json_attr_object_begin (&json_ctx, "");
json_attr_double (&json_ctx, "malloc_block_size", size);
struct rusage usage;
getrusage (RUSAGE_SELF, &usage);
json_attr_double (&json_ctx, "max_rss", usage.ru_maxrss);
for (int i = 0; i < NUM_ALLOCS; i++)
{
sprintf (s, "main_arena_st_allocs_%04d_time", allocs[i]);
json_attr_double (&json_ctx, s, tests[0][i].elapsed / iters2);
}
for (int i = 0; i < NUM_ALLOCS; i++)
{
sprintf (s, "main_arena_mt_allocs_%04d_time", allocs[i]);
json_attr_double (&json_ctx, s, tests[1][i].elapsed / iters2);
}
for (int i = 0; i < NUM_ALLOCS; i++)
{
sprintf (s, "thread_arena__allocs_%04d_time", allocs[i]);
json_attr_double (&json_ctx, s, tests[2][i].elapsed / iters2);
}
json_attr_object_end (&json_ctx);
json_attr_object_end (&json_ctx);
json_attr_object_end (&json_ctx);
json_document_end (&json_ctx);
*/
}
static void usage (const char *name)
{
fprintf (stderr, "%s: <alloc_size>\n", name);
exit (1);
}
int
main (int argc, char **argv)
{
//INITREGULARALLOC();
long size = 16;
if (argc == 2)
size = strtol (argv[1], NULL, 0);
if (argc > 2 || size <= 0)
usage (argv[0]);
// bench (size);
bench (2*size);
printf("done");
//bench (4*size);
//bench (8*size);
// bench (16*size);
// bench (32*size);
return 0;
}

View File

@@ -49,7 +49,7 @@
// perror("mmap");
// return;
// }
// #else
// #else
// mem = calloc(pool, 1);
// if (!mem) {
// fprintf(stderr, "alloc_init(): could not allocate heap\n");
@@ -58,7 +58,7 @@
// #endif
// // Each bitmap entry can represent 8 blocks and each block is 16 bytes
// // So space representable in one uint8_t is 16 * 8 = 128 bytes
// uint64_t sz = pool / 128;
// uint64_t sz = pool / 128;
// if (sz == 0)
// sz = 1; // allocate at least one to keep track of small pools
@@ -89,19 +89,19 @@
// int rem = sz % 16;
// sz += (16 - rem);
// }
// // Allocate two extra blocks
// // Allocate two extra blocks
// // First will be allocated at just behind the first user accessible block
// // This block will have the number of blocks allocated and a randomly generated magic number each 8 bytes long
// // The last block has the "magic number" present in the first block
// // If this magic number gets modified then when free() tries to free the memory
// // If this magic number gets modified then when free() tries to free the memory
// // Buffer overruns will be caught and this allocator gets poisoned i.e it can no longer allocate memory
// // This is because all blocks are laid out sequentially and if the user overruns the blocks allocated
// // This is because all blocks are laid out sequentially and if the user overruns the blocks allocated
// // Then the user may have overwritten the contents of other blocks and it is not possible to estimate the damage caused
// // and data corrupted. All pointers to blocks allocated immediately become invalid and free() posions the allocator
// // This helps catch buffer overflows early on
// // This helps catch buffer overflows early on
// uint64_t blk = (sz / 16) + 2;
// // if we are posioned, all allocation requests will fail
// // if we are posioned, all allocation requests will fail
// if (poison)
// return 0;
// // Loop through the entire bitmap. If a free block is found, check if there are at least blk free blocks after it.
@@ -134,7 +134,7 @@
// // Return the user a pointer which points to the region just above our metadata block
// return mem + ((i + 1) * 16);
// }
// next:
// next:
// }
// fprintf(stderr, "Pool has been exhausted...Cannot allocate more memory");
// return 0;
@@ -165,7 +165,7 @@
// offset -= 16;
// offset /= 16;
// // Clear all bits representing this block so next call to alloc() can use this
// // Clear all bits representing this block so next call to alloc() can use this
// for (uint64_t j = offset; j < offset + blk + 1; j++) {
// MARK(j);
// }
@@ -174,4 +174,4 @@
// // Do not call this unless you are absolutely sure about the cause of poisoning
// void clear_posion() {
// poison = 0;
// }
// }

View File

@@ -843,6 +843,12 @@ for use with huge pages, demonstrates a clear advantage in scenarios where memor
patterns benefit from its design. The results align with expectations, showcasing the impact
of its capability to handle memory more efficiently by leveraging huge pages.
<points metric based>
- Each benchmark reflection for the metric
- summary based on each benchmark reflected
- summarize all the bullet points.
A particularly striking observation is the significant reduction in data TLB walks,
L2 data TLB reads, and TLB refills—consistently showing a 90\% decrease across all
benchmarks compared to Jemalloc. This improvement is due to the modified allocator's