From 5e8cf97343095bc04a80eb851d98e68839ad67fc Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Tue, 29 Jul 2025 12:47:51 -0600 Subject: [PATCH 1/8] initial generated optimization tool --- .../memory-bucket-optimizer/README.md | 61 ++ .../optimizer/Makefile | 32 + .../optimizer/memory_bucket_optimizer.c | 549 ++++++++++++++++++ 3 files changed, 642 insertions(+) create mode 100644 staticmemory/memory-bucket-optimizer/README.md create mode 100644 staticmemory/memory-bucket-optimizer/optimizer/Makefile create mode 100644 staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c diff --git a/staticmemory/memory-bucket-optimizer/README.md b/staticmemory/memory-bucket-optimizer/README.md new file mode 100644 index 000000000..551822ec5 --- /dev/null +++ b/staticmemory/memory-bucket-optimizer/README.md @@ -0,0 +1,61 @@ +# Memory Bucket Optimizer for wolfSSL + +** Note that the staticmemory build of wolfSSL does not use less memory ** +The staticmemory feature ends up using a bit more memory and is a simple sectioning up of a static buffer used dynamically instead of malloc/free. wolfSSL has the option for users to define custom XMALLOC/XFREE if wanting to use a different allocater. + + +This tool analyzes memory allocation patterns in wolfSSL and recommends optimal static memory bucket configurations to minimize wasted memory. + +## Overview + +When wolfSSL is built with the `--enable-staticmemory` option, it uses a static memory management system with memory buckets. The size and distribution of these buckets can significantly impact memory usage efficiency. This tool helps optimize these bucket configurations for specific TLS operations. + +## Directory Structure + +``` +memory-bucket-optimizer/ +├── optimizer/ # Source code for the optimizer +└── README.md # This file +``` + +## Prerequisites + +- wolfSSL (built with `CPPFLAGS="-DWOLFSSL_DEBUG_MEMORY -DWOLFSSL_DEBUG_MEMORY_PRINT` and `--enable-staticmemory`) +- GCC compiler +- gnuplot (for visualization) + +## Building + +```bash +make +``` + +## Usage + +### Basic Usage + +1. Build wolfSSL with memory logging enabled: + +```bash +cd ~/wolfssl +./configure CPPFLAGS="-DWOLFSSL_DEBUG_MEMORY -DWOLFSSL_DEBUG_MEMORY_PRINT" --enable-staticmemory +make +sudo make install +``` + +2. Run the application linked to wolfssl: + +```bash +./wolfcrypt/test/testwolfcrypt &> testwolfcrypt.log +``` + +This will run the application with memory log output. + +3. Run the log through the optimizer + +```bash +cd optimizer +make +./memory_bucket_optimizer testwolfcrypt.log +``` + diff --git a/staticmemory/memory-bucket-optimizer/optimizer/Makefile b/staticmemory/memory-bucket-optimizer/optimizer/Makefile new file mode 100644 index 000000000..0a087d1e0 --- /dev/null +++ b/staticmemory/memory-bucket-optimizer/optimizer/Makefile @@ -0,0 +1,32 @@ +# Makefile for memory_bucket_optimizer +# +# Copyright (C) 2006-2025 wolfSSL Inc. +# +# This file is part of wolfSSL. +# +# wolfSSL is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# wolfSSL 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + +CC = gcc +LDFLAGS = -lwolfssl + +all: memory_bucket_optimizer + +memory_bucket_optimizer: memory_bucket_optimizer.c + $(CC) $(CFLAGS) -o memory_bucket_optimizer memory_bucket_optimizer.c $(LDFLAGS) + +clean: + rm -f memory_bucket_optimizer + +.PHONY: all clean diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c new file mode 100644 index 000000000..f04e64707 --- /dev/null +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -0,0 +1,549 @@ +/* memory_bucket_optimizer.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include +#include // Required for INT_MAX + +#include +#include + +#define MAX_ALLOC_SIZES 1000 +#define MAX_LINE_LENGTH 1024 +#define MAX_BUCKETS 16 +#define MAX_ALLOCATIONS 10000 + +/* Memory overhead constants - these should match wolfSSL's actual values */ +#define WOLFSSL_HEAP_SIZE 64 /* Approximate size of WOLFSSL_HEAP structure */ +#define WOLFSSL_HEAP_HINT_SIZE 32 /* Approximate size of WOLFSSL_HEAP_HINT structure */ + +typedef struct { + int size; + int count; + int max_concurrent; // Maximum number of concurrent allocations of this size +} AllocSize; + +typedef struct { + int size; + int timestamp; // Simple counter for allocation order + int active; // 1 if allocated, 0 if freed +} AllocationEvent; + +/* Function to calculate memory padding size per bucket */ +int calculate_padding_size() { + return wolfSSL_MemoryPaddingSz(); +} + +/* Function to calculate bucket size including padding */ +int calculate_bucket_size_with_padding(int allocation_size) { + return allocation_size + calculate_padding_size(); +} + +/* Function to calculate total memory overhead */ +int calculate_total_overhead(int num_buckets) { + /* Total overhead includes: + * - WOLFSSL_HEAP structure + * - WOLFSSL_HEAP_HINT structure + * - Alignment padding + * Note: Padding is already included in bucket sizes + */ + int total_overhead = WOLFSSL_HEAP_SIZE + + WOLFSSL_HEAP_HINT_SIZE + + (WOLFSSL_STATIC_ALIGN - 1); + total_overhead += num_buckets * wolfSSL_MemoryPaddingSz(); + return total_overhead; +} + +/* Function to parse memory allocation logs with concurrent usage tracking */ +int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, int* num_sizes) { + FILE* file = fopen(filename, "r"); + if (!file) { + printf("Error: Could not open file %s\n", filename); + return -1; + } + + char line[MAX_LINE_LENGTH]; + *num_sizes = 0; + AllocationEvent events[MAX_ALLOCATIONS]; + int num_events = 0; + int timestamp = 0; + + while (fgets(line, sizeof(line), file) && num_events < MAX_ALLOCATIONS) { + /* Look for lines containing "Alloc:" or "Free:" */ + char* alloc_pos = strstr(line, "Alloc:"); + char* free_pos = strstr(line, "Free:"); + + if (alloc_pos) { + int size; + /* Handle multiple formats: + * Format 1: Alloc: 0x55fde046b490 -> 4 (11) at wolfTLSv1_3_client_method_ex:src/tls.c:16714 + * Format 2: [HEAP 0x1010e2110] Alloc: 0x101108a40 -> 1024 at simple_mem_test:18561 + * Format 3: (Using global heap hint 0x1010e2110) [HEAP 0x0] Alloc: 0x101107440 -> 1584 at _sp_exptmod_nct:14231 + */ + if (sscanf(alloc_pos, "Alloc: %*s -> %d", &size) == 1) { + events[num_events].size = size; + events[num_events].timestamp = timestamp++; + events[num_events].active = 1; + num_events++; + } + } else if (free_pos) { + int size; + /* Handle multiple formats: + * Format 1: Free: 0x55fde046b490 -> 4 at wolfTLSv1_3_client_method_ex:src/tls.c:16714 + * Format 2: [HEAP 0x1010e2110] Free: 0x101108a40 -> 1024 at simple_mem_test:18576 + * Format 3: (Using global heap hint 0x1010e2110) [HEAP 0x0] Free: 0x101107440 -> 1584 at _sp_exptmod_nct:14462 + */ + if (sscanf(free_pos, "Free: %*s -> %d", &size) == 1) { + events[num_events].size = size; + events[num_events].timestamp = timestamp++; + events[num_events].active = 0; + num_events++; + } + } + } + + fclose(file); + + /* Collect unique sizes from events */ + int unique_sizes[MAX_ALLOC_SIZES]; + int num_unique_sizes = 0; + + for (int i = 0; i < num_events; i++) { + int size = events[i].size; + int found = 0; + + /* Check if this size is already in our unique sizes list */ + for (int j = 0; j < num_unique_sizes; j++) { + if (unique_sizes[j] == size) { + found = 1; + break; + } + } + + /* Add to unique sizes if not found */ + if (!found && num_unique_sizes < MAX_ALLOC_SIZES) { + unique_sizes[num_unique_sizes++] = size; + } + } + + /* Calculate concurrent usage for each unique size */ + for (int s = 0; s < num_unique_sizes; s++) { + int size = unique_sizes[s]; + int current_concurrent = 0; + int max_concurrent = 0; + int total_count = 0; + + for (int i = 0; i < num_events; i++) { + if (events[i].size == size) { + if (events[i].active) { + current_concurrent++; + total_count++; + if (current_concurrent > max_concurrent) { + max_concurrent = current_concurrent; + } + } else { + current_concurrent--; + if (current_concurrent < 0) { + current_concurrent = 0; /* Handle mismatched free/alloc */ + } + } + } + } + + /* Only add sizes that were actually allocated */ + if (total_count > 0) { + if (*num_sizes < MAX_ALLOC_SIZES) { + alloc_sizes[*num_sizes].size = size; + alloc_sizes[*num_sizes].count = total_count; + alloc_sizes[*num_sizes].max_concurrent = max_concurrent; + (*num_sizes)++; + } else { + printf("Warning: Maximum number of allocation sizes reached\n"); + break; + } + } + } + + return 0; +} + +/* Function to compare allocation sizes for sorting */ +int compare_alloc_sizes(const void* a, const void* b) { + return ((AllocSize*)a)->size - ((AllocSize*)b)->size; +} + +/* Function to compare allocation counts for sorting (descending) */ +int compare_alloc_counts(const void* a, const void* b) { + return ((AllocSize*)b)->count - ((AllocSize*)a)->count; +} + +/* Function to optimize bucket sizes */ +void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, + int* dist, int* num_buckets) +{ + int i, j; + + /* Make a copy of the allocation sizes for frequency sorting */ + AllocSize* alloc_sizes_by_freq = (AllocSize*)malloc(num_sizes * sizeof(AllocSize)); + if (!alloc_sizes_by_freq) { + printf("Error: Memory allocation failed\n"); + *num_buckets = 0; + return; + } + + memcpy(alloc_sizes_by_freq, alloc_sizes, num_sizes * sizeof(AllocSize)); + + /* Sort by frequency (descending) */ + qsort(alloc_sizes_by_freq, num_sizes, sizeof(AllocSize), compare_alloc_counts); + + /* Find the largest allocation size */ + int largest_size = 0; + for (i = 0; i < num_sizes; i++) { + if (alloc_sizes[i].size > largest_size) { + largest_size = alloc_sizes[i].size; + } + } + + /* Calculate total allocations and find significant sizes */ + int total_allocations = 0; + for (i = 0; i < num_sizes; i++) { + total_allocations += alloc_sizes[i].count; + } + + /* Determine significant allocation sizes (those that represent >1% of total allocations) */ + int significant_sizes[MAX_BUCKETS]; + int num_significant = 0; + int min_threshold = total_allocations / 100; /* 1% threshold */ + + for (i = 0; i < num_sizes && num_significant < MAX_BUCKETS - 1; i++) { + if (alloc_sizes_by_freq[i].count >= min_threshold) { + significant_sizes[num_significant++] = alloc_sizes_by_freq[i].size; + } + } + + /* Initialize bucket count */ + *num_buckets = 0; + + /* Always include the largest allocation size first (with padding) */ + buckets[*num_buckets] = calculate_bucket_size_with_padding(largest_size); + + /* Find the largest size in our data to get its concurrent usage */ + int largest_concurrent = 1; /* Default to 1 */ + for (i = 0; i < num_sizes; i++) { + if (alloc_sizes[i].size == largest_size) { + largest_concurrent = alloc_sizes[i].max_concurrent; + break; + } + } + + /* Use actual concurrent usage, but minimum of 1 */ + dist[*num_buckets] = (largest_concurrent > 0) ? largest_concurrent : 1; + (*num_buckets)++; + + /* Add significant allocation sizes, avoiding duplicates */ + for (i = 0; i < num_significant && *num_buckets < MAX_BUCKETS; i++) { + int size = significant_sizes[i]; + + /* Skip if this size is already included (like the largest size) */ + int already_included = 0; + for (j = 0; j < *num_buckets; j++) { + /* Compare original allocation sizes, not bucket sizes with padding */ + int bucket_data_size = buckets[j] - calculate_padding_size(); + if (bucket_data_size == size) { + already_included = 1; + break; + } + } + + if (!already_included) { + buckets[*num_buckets] = calculate_bucket_size_with_padding(size); + + /* Calculate distribution based on concurrent usage and frequency */ + int count = 0; + int concurrent = 1; /* Default to 1 */ + for (j = 0; j < num_sizes; j++) { + if (alloc_sizes[j].size == size) { + count = alloc_sizes[j].count; + concurrent = alloc_sizes[j].max_concurrent; + break; + } + } + + /* Use concurrent usage as base, but scale based on frequency */ + int base_dist = (concurrent > 0) ? concurrent : 1; + + /* Scale distribution based on frequency */ + if (count > total_allocations / 10) { /* >10% of allocations */ + dist[*num_buckets] = base_dist * 2; /* Double for high frequency */ + } else if (count > total_allocations / 20) { /* >5% of allocations */ + dist[*num_buckets] = base_dist + 2; /* Add 2 for medium frequency */ + } else if (count > total_allocations / 50) { /* >2% of allocations */ + dist[*num_buckets] = base_dist + 1; /* Add 1 for low frequency */ + } else { + dist[*num_buckets] = base_dist; /* Use concurrent usage as is */ + } + + /* Cap distribution at reasonable maximum */ + if (dist[*num_buckets] > 16) { + dist[*num_buckets] = 16; + } + + (*num_buckets)++; + } + } + + /* If we still have space, add some medium-frequency sizes */ + if (*num_buckets < MAX_BUCKETS) { + for (i = 0; i < num_sizes && *num_buckets < MAX_BUCKETS; i++) { + int size = alloc_sizes_by_freq[i].size; + + /* Skip if already included */ + int already_included = 0; + for (j = 0; j < *num_buckets; j++) { + /* Compare original allocation sizes, not bucket sizes with padding */ + int bucket_data_size = buckets[j] - calculate_padding_size(); + if (bucket_data_size == size) { + already_included = 1; + break; + } + } + + if (!already_included && alloc_sizes_by_freq[i].count >= 3) { + buckets[*num_buckets] = calculate_bucket_size_with_padding(size); + dist[*num_buckets] = 2; /* Default to 2 buckets for medium frequency */ + (*num_buckets)++; + } + } + } + + /* Sort buckets by size (ascending) */ + for (i = 0; i < *num_buckets - 1; i++) { + for (j = 0; j < *num_buckets - i - 1; j++) { + if (buckets[j] > buckets[j + 1]) { + /* Swap bucket sizes */ + int temp = buckets[j]; + buckets[j] = buckets[j + 1]; + buckets[j + 1] = temp; + + /* Swap distribution values */ + temp = dist[j]; + dist[j] = dist[j + 1]; + dist[j + 1] = temp; + } + } + } + + free(alloc_sizes_by_freq); +} + +/* Function to calculate memory efficiency metrics */ +void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, + int* buckets, int* dist, int num_buckets) +{ + int i, j; + float total_waste = 0.0; + int total_allocations = 0; + int allocations_handled = 0; + + printf("Memory Efficiency Analysis:\n"); + printf("Size Count Concurrent Bucket Waste Coverage\n"); + printf("---- ----- ---------- ------ ----- --------\n"); + + for (i = 0; i < num_sizes; i++) { + int size = alloc_sizes[i].size; + int count = alloc_sizes[i].count; + total_allocations += count; + + /* Find the smallest bucket that can fit this allocation */ + int best_bucket = -1; + int min_waste = INT_MAX; + + for (j = 0; j < num_buckets; j++) { + /* Bucket sizes now include padding, so we need to subtract it for comparison */ + int bucket_data_size = buckets[j] - calculate_padding_size(); + if (bucket_data_size >= size) { + int waste = bucket_data_size - size; + if (waste < min_waste) { + min_waste = waste; + best_bucket = j; + } + } + } + + if (best_bucket >= 0) { + allocations_handled += count; + total_waste += (float)min_waste * count; + printf("%-7d %-7d %-10d %-7d %-7d %s\n", + size, count, alloc_sizes[i].max_concurrent, buckets[best_bucket], min_waste, "✓"); + } else { + printf("%-7d %-7d %-10d %-7s %-7s %s\n", + size, count, alloc_sizes[i].max_concurrent, "N/A", "N/A", "✗"); + } + } + + printf("\nEfficiency Summary:\n"); + printf("Total allocations: %d\n", total_allocations); + printf("Allocations handled: %d (%.1f%%)\n", + allocations_handled, (float)allocations_handled * 100 / total_allocations); + printf("Total memory waste: %.2f bytes\n", total_waste); + printf("Average waste per allocation: %.2f bytes\n", + total_waste / total_allocations); + + /* Calculate total memory needed for buckets */ + int total_bucket_memory = 0; + for (i = 0; i < num_buckets; i++) { + total_bucket_memory += buckets[i] * dist[i]; + } + + /* Calculate total overhead */ + int total_overhead = calculate_total_overhead(num_buckets); + int total_memory_needed = total_bucket_memory + total_overhead; + + printf("Total bucket memory: %d bytes\n", total_bucket_memory); + printf("Memory overhead: %d bytes\n", total_overhead); + printf(" - Padding per bucket: %d bytes (included in bucket sizes)\n", calculate_padding_size()); + printf(" - Total padding: %d bytes (included in bucket sizes)\n", calculate_padding_size() * num_buckets); + printf(" - Heap structures: %d bytes\n", WOLFSSL_HEAP_SIZE + WOLFSSL_HEAP_HINT_SIZE); + printf(" - Alignment: %d bytes\n", WOLFSSL_STATIC_ALIGN - 1); + printf("Total memory needed: %d bytes\n", total_memory_needed); + + /* Calculate efficiency based on actual data vs total memory */ + float data_memory = 0; + for (i = 0; i < num_sizes; i++) { + data_memory += alloc_sizes[i].size * alloc_sizes[i].count; + } + printf("Data memory: %.0f bytes\n", data_memory); +} + +/* Function to provide buffer size recommendations */ +void print_buffer_recommendations(int* buckets, int* dist, int num_buckets) +{ + int total_bucket_memory = 0, total_overhead, total_memory_needed, i; + + for (i = 0; i < num_buckets; i++) { + total_bucket_memory += buckets[i] * dist[i]; + } + + total_overhead = calculate_total_overhead(num_buckets); + total_memory_needed = total_bucket_memory + total_overhead; + + printf("\nBuffer Size Recommendations:\n"); + printf("============================\n"); + printf("Minimum buffer size needed: %d bytes\n", total_memory_needed); + printf("Recommended buffer size: %d bytes (add 10%% safety margin)\n", + (int)(total_memory_needed * 1.1)); + + printf("\nUsage in wolfSSL application:\n"); + printf("============================\n"); + printf("// Allocate buffer\n"); + printf("byte staticBuffer[%d];\n", total_memory_needed); + printf("\n// Load static memory\n"); + printf("WOLFSSL_HEAP_HINT* heapHint = NULL;\n"); + printf("if (wc_LoadStaticMemory_ex(&heapHint, %d, bucket_sizes, bucket_dist,\n", num_buckets); + printf(" staticBuffer, %d, 0, 0) != 0) {\n", total_memory_needed); + printf(" // Handle error\n"); + printf("}\n"); + printf("\n// Use in wolfSSL context\n"); + printf("wolfSSL_CTX_load_static_memory(&method, NULL, staticBuffer,\n"); + printf(" %d, 0, 1);\n", total_memory_needed); +} + +int main(int argc, char** argv) +{ + int i; + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + AllocSize alloc_sizes[MAX_ALLOC_SIZES]; + int num_sizes = 0; + + /* Parse memory allocation logs */ + if (parse_memory_logs(argv[1], alloc_sizes, &num_sizes) != 0) { + return 1; + } + + printf("Found %d unique allocation sizes\n\n", num_sizes); + + /* Sort allocation sizes */ + qsort(alloc_sizes, num_sizes, sizeof(AllocSize), compare_alloc_sizes); + + /* Print allocation sizes, frequencies, and concurrent usage */ + printf("Allocation Sizes, Frequencies, and Concurrent Usage:\n"); + printf("Size Count Max Concurrent\n"); + printf("---- ----- --------------\n"); + for (i = 0; i < num_sizes; i++) { + printf("%-7d %-7d %d\n", alloc_sizes[i].size, alloc_sizes[i].count, alloc_sizes[i].max_concurrent); + } + printf("\n"); + + /* Optimize bucket sizes */ + int buckets[MAX_BUCKETS]; + int dist[MAX_BUCKETS]; + int num_buckets = 0; + + optimize_buckets(alloc_sizes, num_sizes, buckets, dist, &num_buckets); + + /* Print optimized bucket sizes and distribution */ + printf("Optimized Bucket Sizes and Distribution:\n"); + printf("Data Size + Padding = Bucket Size Dist\n"); + printf("----------------------------------------\n"); + + for (i = 0; i < num_buckets; i++) { + int data_size = buckets[i] - calculate_padding_size(); + printf("%-7d + %-7d = %-7d %d\n", + data_size, calculate_padding_size(), buckets[i], dist[i]); + } + printf("\n"); + + /* Print WOLFMEM_BUCKETS and WOLFMEM_DIST macros */ + printf("WOLFMEM_BUCKETS and WOLFMEM_DIST Macros:\n"); + printf("#define WOLFMEM_BUCKETS "); + for (i = 0; i < num_buckets; i++) { + printf("%d", buckets[i]); + if (i < num_buckets - 1) { + printf(","); + } + } + printf("\n"); + + printf("#define WOLFMEM_DIST "); + for (i = 0; i < num_buckets; i++) { + printf("%d", dist[i]); + if (i < num_buckets - 1) { + printf(","); + } + } + printf("\n"); + + /* Calculate and print memory efficiency metrics */ + calculate_memory_efficiency(alloc_sizes, num_sizes, buckets, dist, + num_buckets); + + /* Print buffer size recommendations */ + print_buffer_recommendations(buckets, dist, num_buckets); + + return 0; +} From 58571d8ccb972febd5d3ef407a2956bdd4caec50 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Wed, 30 Jul 2025 09:25:13 -0600 Subject: [PATCH 2/8] adds a peak heap usage stat, max bucket limiter, and update to account for padding size when creating a new bucket size --- .../optimizer/memory_bucket_optimizer.c | 123 +++++++++++++++++- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c index f04e64707..2b5260297 100644 --- a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -31,8 +31,15 @@ #define MAX_ALLOC_SIZES 1000 #define MAX_LINE_LENGTH 1024 #define MAX_BUCKETS 16 +#define MAX_UNIQUE_BUCKETS 9 /* Maximum number of unique bucket sizes to create */ #define MAX_ALLOCATIONS 10000 +/* Configuration notes: + * - MAX_UNIQUE_BUCKETS: Limits the total number of unique bucket sizes + * This helps control memory overhead and bucket management complexity + * Default: 9 buckets (can be adjusted based on memory constraints) + */ + /* Memory overhead constants - these should match wolfSSL's actual values */ #define WOLFSSL_HEAP_SIZE 64 /* Approximate size of WOLFSSL_HEAP structure */ #define WOLFSSL_HEAP_HINT_SIZE 32 /* Approximate size of WOLFSSL_HEAP_HINT structure */ @@ -75,7 +82,7 @@ int calculate_total_overhead(int num_buckets) { } /* Function to parse memory allocation logs with concurrent usage tracking */ -int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, int* num_sizes) { +int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, int* num_sizes, int* peak_heap_usage) { FILE* file = fopen(filename, "r"); if (!file) { printf("Error: Could not open file %s\n", filename); @@ -87,6 +94,7 @@ int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, int* num_siz AllocationEvent events[MAX_ALLOCATIONS]; int num_events = 0; int timestamp = 0; + *peak_heap_usage = 0; /* Initialize peak heap usage */ while (fgets(line, sizeof(line), file) && num_events < MAX_ALLOCATIONS) { /* Look for lines containing "Alloc:" or "Free:" */ @@ -184,6 +192,32 @@ int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, int* num_siz } } + /* Calculate peak heap usage by simulating the allocation timeline */ + /* This tracks the maximum total memory that was allocated concurrently */ + int current_heap_usage = 0; + int max_heap_usage = 0; + + /* Create a timeline of heap usage changes */ + for (int i = 0; i < num_events; i++) { + if (events[i].active) { + /* Allocation - add to current heap usage */ + current_heap_usage += events[i].size; + } else { + /* Free - subtract from current heap usage */ + current_heap_usage -= events[i].size; + if (current_heap_usage < 0) { + current_heap_usage = 0; /* Handle mismatched free/alloc */ + } + } + + /* Track peak usage */ + if (current_heap_usage > max_heap_usage) { + max_heap_usage = current_heap_usage; + } + } + + *peak_heap_usage = max_heap_usage; + return 0; } @@ -198,6 +232,14 @@ int compare_alloc_counts(const void* a, const void* b) { } /* Function to optimize bucket sizes */ +/* + * Optimization heuristic: + * - Always include the largest allocation size + * - For other sizes, only create a new bucket if the waste from using + * existing buckets is >= padding size per bucket + * - This reduces bucket management overhead when waste is minimal + * - Limited to MAX_UNIQUE_BUCKETS total unique bucket sizes + */ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, int* dist, int* num_buckets) { @@ -260,8 +302,8 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, dist[*num_buckets] = (largest_concurrent > 0) ? largest_concurrent : 1; (*num_buckets)++; - /* Add significant allocation sizes, avoiding duplicates */ - for (i = 0; i < num_significant && *num_buckets < MAX_BUCKETS; i++) { + /* Add significant allocation sizes, considering padding overhead */ + for (i = 0; i < num_significant && *num_buckets < MAX_UNIQUE_BUCKETS; i++) { int size = significant_sizes[i]; /* Skip if this size is already included (like the largest size) */ @@ -276,6 +318,32 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } if (!already_included) { + /* Check if this size can be efficiently handled by existing buckets */ + int best_existing_bucket = -1; + int min_waste = INT_MAX; + int padding_size = calculate_padding_size(); + + /* Find the best existing bucket for this size */ + for (j = 0; j < *num_buckets; j++) { + int bucket_data_size = buckets[j] - padding_size; + if (bucket_data_size >= size) { + int waste = bucket_data_size - size; + if (waste < min_waste) { + min_waste = waste; + best_existing_bucket = j; + } + } + } + + /* Only create a new bucket if the waste from existing buckets is significant */ + /* If waste is less than padding size, it's better to use existing bucket */ + if (best_existing_bucket >= 0 && min_waste < padding_size) { + /* Use existing bucket - don't create a new one */ + /* This size will be handled by the existing bucket */ + continue; + } + + /* Create new bucket for this size */ buckets[*num_buckets] = calculate_bucket_size_with_padding(size); /* Calculate distribution based on concurrent usage and frequency */ @@ -313,8 +381,8 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } /* If we still have space, add some medium-frequency sizes */ - if (*num_buckets < MAX_BUCKETS) { - for (i = 0; i < num_sizes && *num_buckets < MAX_BUCKETS; i++) { + if (*num_buckets < MAX_UNIQUE_BUCKETS) { + for (i = 0; i < num_sizes && *num_buckets < MAX_UNIQUE_BUCKETS; i++) { int size = alloc_sizes_by_freq[i].size; /* Skip if already included */ @@ -329,6 +397,32 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } if (!already_included && alloc_sizes_by_freq[i].count >= 3) { + /* Check if this size can be efficiently handled by existing buckets */ + int best_existing_bucket = -1; + int min_waste = INT_MAX; + int padding_size = calculate_padding_size(); + + /* Find the best existing bucket for this size */ + for (j = 0; j < *num_buckets; j++) { + int bucket_data_size = buckets[j] - padding_size; + if (bucket_data_size >= size) { + int waste = bucket_data_size - size; + if (waste < min_waste) { + min_waste = waste; + best_existing_bucket = j; + } + } + } + + /* Only create a new bucket if the waste from existing buckets is significant */ + /* If waste is less than padding size, it's better to use existing bucket */ + if (best_existing_bucket >= 0 && min_waste < padding_size) { + /* Use existing bucket - don't create a new one */ + /* This size will be handled by the existing bucket */ + continue; + } + + /* Create new bucket for this size */ buckets[*num_buckets] = calculate_bucket_size_with_padding(size); dist[*num_buckets] = 2; /* Default to 2 buckets for medium frequency */ (*num_buckets)++; @@ -354,6 +448,17 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } free(alloc_sizes_by_freq); + + /* Print optimization summary */ + printf("Optimization Summary:\n"); + printf("Padding size per bucket: %d bytes\n", calculate_padding_size()); + printf("Maximum unique buckets allowed: %d\n", MAX_UNIQUE_BUCKETS); + printf("Total buckets created: %d\n", *num_buckets); + if (*num_buckets >= MAX_UNIQUE_BUCKETS) { + printf("Note: Reached maximum bucket limit (%d). Some allocations may use larger buckets.\n", MAX_UNIQUE_BUCKETS); + } + printf("Note: Allocations with waste < padding size use existing buckets to reduce overhead\n"); + printf("Note: Bucket limit helps balance memory efficiency vs. management overhead\n\n"); } /* Function to calculate memory efficiency metrics */ @@ -364,8 +469,10 @@ void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, float total_waste = 0.0; int total_allocations = 0; int allocations_handled = 0; + int padding_size = calculate_padding_size(); printf("Memory Efficiency Analysis:\n"); + printf("Note: Allocations with waste < %d bytes (padding size) use existing buckets\n", padding_size); printf("Size Count Concurrent Bucket Waste Coverage\n"); printf("---- ----- ---------- ------ ----- --------\n"); @@ -479,13 +586,15 @@ int main(int argc, char** argv) AllocSize alloc_sizes[MAX_ALLOC_SIZES]; int num_sizes = 0; + int peak_heap_usage = 0; /* Parse memory allocation logs */ - if (parse_memory_logs(argv[1], alloc_sizes, &num_sizes) != 0) { + if (parse_memory_logs(argv[1], alloc_sizes, &num_sizes, &peak_heap_usage) != 0) { return 1; } - printf("Found %d unique allocation sizes\n\n", num_sizes); + printf("Found %d unique allocation sizes\n", num_sizes); + printf("Peak heap usage: %d bytes (maximum concurrent memory usage)\n\n", peak_heap_usage); /* Sort allocation sizes */ qsort(alloc_sizes, num_sizes, sizeof(AllocSize), compare_alloc_sizes); From 6a593961ac55553927ad593799f03acb01e168cb Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Thu, 31 Jul 2025 09:12:41 -0600 Subject: [PATCH 3/8] add a tester app and fix optimizer --- .../optimizer/memory_bucket_optimizer.c | 31 +- .../memory-bucket-optimizer/tester/Makefile | 37 ++ .../tester/memory_bucket_tester.c | 340 ++++++++++++++++++ 3 files changed, 387 insertions(+), 21 deletions(-) create mode 100644 staticmemory/memory-bucket-optimizer/tester/Makefile create mode 100644 staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c index 2b5260297..babd29ebb 100644 --- a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -82,7 +82,9 @@ int calculate_total_overhead(int num_buckets) { } /* Function to parse memory allocation logs with concurrent usage tracking */ -int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, int* num_sizes, int* peak_heap_usage) { +int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, + int* num_sizes, int* peak_heap_usage) +{ FILE* file = fopen(filename, "r"); if (!file) { printf("Error: Could not open file %s\n", filename); @@ -298,8 +300,7 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } } - /* Use actual concurrent usage, but minimum of 1 */ - dist[*num_buckets] = (largest_concurrent > 0) ? largest_concurrent : 1; + dist[*num_buckets] = largest_concurrent; (*num_buckets)++; /* Add significant allocation sizes, considering padding overhead */ @@ -357,24 +358,10 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } } - /* Use concurrent usage as base, but scale based on frequency */ - int base_dist = (concurrent > 0) ? concurrent : 1; + dist[*num_buckets] = concurrent; - /* Scale distribution based on frequency */ - if (count > total_allocations / 10) { /* >10% of allocations */ - dist[*num_buckets] = base_dist * 2; /* Double for high frequency */ - } else if (count > total_allocations / 20) { /* >5% of allocations */ - dist[*num_buckets] = base_dist + 2; /* Add 2 for medium frequency */ - } else if (count > total_allocations / 50) { /* >2% of allocations */ - dist[*num_buckets] = base_dist + 1; /* Add 1 for low frequency */ - } else { - dist[*num_buckets] = base_dist; /* Use concurrent usage as is */ - } - - /* Cap distribution at reasonable maximum */ - if (dist[*num_buckets] > 16) { - dist[*num_buckets] = 16; - } + /* Ensure we have enough buckets for maximum concurrent usage */ + /* Don't cap arbitrarily - let the buffer size calculation handle memory constraints */ (*num_buckets)++; } @@ -518,12 +505,14 @@ void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, /* Calculate total memory needed for buckets */ int total_bucket_memory = 0; + int total_num_buckets = 0; for (i = 0; i < num_buckets; i++) { total_bucket_memory += buckets[i] * dist[i]; + total_num_buckets += dist[i]; } /* Calculate total overhead */ - int total_overhead = calculate_total_overhead(num_buckets); + int total_overhead = calculate_total_overhead(total_num_buckets); int total_memory_needed = total_bucket_memory + total_overhead; printf("Total bucket memory: %d bytes\n", total_bucket_memory); diff --git a/staticmemory/memory-bucket-optimizer/tester/Makefile b/staticmemory/memory-bucket-optimizer/tester/Makefile new file mode 100644 index 000000000..4faf41202 --- /dev/null +++ b/staticmemory/memory-bucket-optimizer/tester/Makefile @@ -0,0 +1,37 @@ +# Makefile for memory bucket tester +# +# Copyright (C) 2025 wolfSSL Inc. +# +# This file is part of wolfSSL. +# +# wolfSSL is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# wolfSSL 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + +CC = gcc +CFLAGS = -Wall -Wextra -O2 -g +LIBS = -lwolfssl + +TARGET = memory_bucket_tester +SOURCE = memory_bucket_tester.c + +.PHONY: all clean test + +all: $(TARGET) + +$(TARGET): $(SOURCE) + $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< $(LIBS) + +clean: + rm -f $(TARGET) + diff --git a/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c b/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c new file mode 100644 index 000000000..042ac96f6 --- /dev/null +++ b/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c @@ -0,0 +1,340 @@ +/* memory_bucket_tester.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include + +#include +#include + +/* Memory overhead constants - these should match wolfSSL's actual values */ +#ifndef WOLFSSL_STATIC_ALIGN +#define WOLFSSL_STATIC_ALIGN 8 +#endif +#ifndef WOLFSSL_HEAP_SIZE +#define WOLFSSL_HEAP_SIZE 64 /* Approximate size of WOLFSSL_HEAP structure */ +#endif +#ifndef WOLFSSL_HEAP_HINT_SIZE +#define WOLFSSL_HEAP_HINT_SIZE 32 /* Approximate size of WOLFSSL_HEAP_HINT structure */ +#endif + +#define MAX_ALLOCATIONS 10000 +#define MAX_LINE_LENGTH 1024 +#define MAX_BUCKETS 16 + +typedef struct { + int size; + int is_alloc; /* 1 for alloc, 0 for free */ + void* ptr; /* For tracking allocated pointers */ +} AllocationEvent; + +typedef struct { + int size; + int count; +} BucketConfig; + +/* Function to parse bucket configuration from command line */ +int parse_bucket_config(int argc, char** argv, int start_idx, + BucketConfig* buckets, int* num_buckets, int* total_buffer_size) { + if (start_idx >= argc) { + printf("Error: No bucket configuration provided\n"); + return -1; + } + + *num_buckets = 0; + *total_buffer_size = 0; + int i = start_idx; + + while (i < argc && *num_buckets < MAX_BUCKETS) { + if (strcmp(argv[i], "--buckets") == 0) { + i++; + if (i < argc) { + char* bucket_str = argv[i]; + char* token = strtok(bucket_str, ","); + + while (token != NULL && *num_buckets < MAX_BUCKETS) { + buckets[*num_buckets].size = atoi(token); + buckets[*num_buckets].count = 1; /* Default count */ + (*num_buckets)++; + token = strtok(NULL, ","); + } + i++; + } + } else if (strcmp(argv[i], "--dist") == 0) { + i++; + if (i < argc) { + char* dist_str = argv[i]; + char* token = strtok(dist_str, ","); + int dist_idx = 0; + + while (token != NULL && dist_idx < *num_buckets) { + buckets[dist_idx].count = atoi(token); + dist_idx++; + token = strtok(NULL, ","); + } + i++; + } + } else if (strcmp(argv[i], "--buffer-size") == 0) { + i++; + if (i < argc) { + *total_buffer_size = atoi(argv[i]); + i++; + } + } else { + i++; + } + } + + return 0; +} + +/* Function to parse memory allocation logs */ +int parse_memory_logs(const char* filename, AllocationEvent* events, int* num_events) { + FILE* file = fopen(filename, "r"); + if (!file) { + printf("Error: Could not open file %s\n", filename); + return -1; + } + + char line[MAX_LINE_LENGTH]; + *num_events = 0; + + while (fgets(line, sizeof(line), file) && *num_events < MAX_ALLOCATIONS) { + /* Look for lines containing "Alloc:" or "Free:" */ + char* alloc_pos = strstr(line, "Alloc:"); + char* free_pos = strstr(line, "Free:"); + + if (alloc_pos) { + int size; + /* Handle multiple formats: + * Format 1: Alloc: 0x55fde046b490 -> 4 (11) at wolfTLSv1_3_client_method_ex:src/tls.c:16714 + * Format 2: [HEAP 0x1010e2110] Alloc: 0x101108a40 -> 1024 at simple_mem_test:18561 + * Format 3: (Using global heap hint 0x1010e2110) [HEAP 0x0] Alloc: 0x101107440 -> 1584 at _sp_exptmod_nct:14231 + */ + if (sscanf(alloc_pos, "Alloc: %*s -> %d", &size) == 1) { + events[*num_events].size = size; + events[*num_events].is_alloc = 1; + events[*num_events].ptr = NULL; + (*num_events)++; + } + } else if (free_pos) { + int size; + /* Handle multiple formats: + * Format 1: Free: 0x55fde046b490 -> 4 at wolfTLSv1_3_client_method_ex:src/tls.c:16714 + * Format 2: [HEAP 0x1010e2110] Free: 0x101108a40 -> 1024 at simple_mem_test:18576 + * Format 3: (Using global heap hint 0x1010e2110) [HEAP 0x0] Free: 0x101107440 -> 1584 at _sp_exptmod_nct:14462 + */ + if (sscanf(free_pos, "Free: %*s -> %d", &size) == 1) { + events[*num_events].size = size; + events[*num_events].is_alloc = 0; + events[*num_events].ptr = NULL; + (*num_events)++; + } + } + } + + fclose(file); + return 0; +} + +/* Function to replay allocation sequence */ +int replay_allocation_sequence(AllocationEvent* events, int num_events, + BucketConfig* buckets, int num_buckets, WOLFSSL_HEAP_HINT* heap_hint ) { + int i; + int success_count = 0; + int failure_count = 0; + + printf("Replaying %d allocation events...\n", num_events); + printf("Using %d buckets:\n", num_buckets); + for (i = 0; i < num_buckets; i++) { + printf(" Bucket %d: size=%d, count=%d\n", i, buckets[i].size, buckets[i].count); + } + printf("\n"); + + for (i = 0; i < num_events; i++) { + if (events[i].is_alloc) { + /* Try to allocate memory */ + void* ptr = XMALLOC(events[i].size, heap_hint, DYNAMIC_TYPE_TMP_BUFFER); + if (ptr == NULL) { + printf("FAILURE: malloc failed for size %d at event %d\n", + events[i].size, i); + failure_count++; + return -1; /* Exit on first failure */ + } else { + events[i].ptr = ptr; + success_count++; + printf("SUCCESS: Allocated %d bytes at event %d\n", events[i].size, i); + } + } else { + /* Find the corresponding allocation to free */ + int found = 0; + for (int j = i - 1; j >= 0; j--) { + if (events[j].is_alloc && events[j].size == events[i].size && + events[j].ptr != NULL) { + XFREE(events[j].ptr, heap_hint, DYNAMIC_TYPE_TMP_BUFFER); + events[j].ptr = NULL; + events[i].ptr = NULL; + found = 1; + printf("SUCCESS: Freed %d bytes at event %d\n", events[i].size, i); + break; + } + } + if (!found) { + printf("WARNING: No matching allocation found for free of size %d at event %d\n", + events[i].size, i); + } + } + } + + printf("\nReplay Summary:\n"); + printf("Total events: %d\n", num_events); + printf("Successful allocations: %d\n", success_count); + printf("Failed allocations: %d\n", failure_count); + + if (failure_count > 0) { + printf("TEST FAILED: Some allocations failed\n"); + return -1; + } else { + printf("TEST PASSED: All allocations succeeded\n"); + return 0; + } +} + +/* Function to calculate required buffer size */ +int calculate_required_buffer_size(BucketConfig* buckets, int num_buckets) { + int total_size = 0; + int i; + + for (i = 0; i < num_buckets; i++) { + total_size += buckets[i].size * buckets[i].count; + } + + /* Add overhead for wolfSSL heap structures */ + total_size += 64 + 32 + (WOLFSSL_STATIC_ALIGN - 1); + + return total_size; +} + +void print_usage(const char* program_name) { + printf("Usage: %s --buckets \",,...\" --dist \",,...\" --buffer-size \n", program_name); + printf("\n"); + printf("Arguments:\n"); + printf(" Path to the memory allocation log file\n"); + printf(" --buckets Bucket sizes (comma-separated in quotes)\n"); + printf(" --dist Distribution counts for each bucket (comma-separated in quotes)\n"); + printf(" --buffer-size Total buffer size to use (in bytes)\n"); + printf("\n"); + printf("Examples:\n"); + printf(" %s test.log --buckets \"1024,256,128\" --dist \"2,4,8\" --buffer-size 8192\n", program_name); + printf(" %s test.log --buckets \"1584,1024,256,128,32\" --dist \"2,2,4,2,1\" --buffer-size 16384\n", program_name); + printf(" %s test.log --buckets \"1024,256,128\" --dist \"2,2,4,2,1,3,1,16,1\" --buffer-size 4096\n", program_name); + printf("\n"); + printf("The tester will:\n"); + printf(" 1. Parse the allocation log file\n"); + printf(" 2. Replay the exact same allocation sequence\n"); + printf(" 3. Use the provided bucket configuration and buffer size\n"); + printf(" 4. Fail if any XMALLOC fails\n"); +} + +int main(int argc, char** argv) { + if (argc < 6) { + print_usage(argv[0]); + return 1; + } + + const char* log_file = argv[1]; + BucketConfig buckets[MAX_BUCKETS]; + int num_buckets = 0; + int total_buffer_size = 0; + + /* Parse bucket configuration */ + if (parse_bucket_config(argc, argv, 2, buckets, &num_buckets, &total_buffer_size) != 0) { + print_usage(argv[0]); + return 1; + } + + if (num_buckets == 0) { + printf("Error: No buckets configured\n"); + print_usage(argv[0]); + return 1; + } + + if (total_buffer_size <= 0) { + printf("Error: Invalid buffer size (%d). Must be greater than 0.\n", total_buffer_size); + print_usage(argv[0]); + return 1; + } + + /* Parse allocation events */ + AllocationEvent events[MAX_ALLOCATIONS]; + int num_events = 0; + + if (parse_memory_logs(log_file, events, &num_events) != 0) { + return 1; + } + + printf("Parsed %d allocation events from %s\n", num_events, log_file); + + /* Use provided buffer size */ + int buffer_size = total_buffer_size; + printf("Using provided buffer size: %d bytes\n\n", buffer_size); + + /* Convert bucket config to wolfSSL format */ + unsigned int bucket_sizes[MAX_BUCKETS]; + unsigned int bucket_dist[MAX_BUCKETS]; + int i; + + for (i = 0; i < num_buckets; i++) { + bucket_sizes[i] = (unsigned int)buckets[i].size; + bucket_dist[i] = (unsigned int)buckets[i].count; + } + + /* Allocate static buffer */ + byte* static_buffer = (byte*)malloc(buffer_size); + if (!static_buffer) { + printf("Error: Failed to allocate static buffer\n"); + return 1; + } + + /* Initialize static memory */ + WOLFSSL_HEAP_HINT* heap_hint = NULL; + int ret = wc_LoadStaticMemory_ex(&heap_hint, num_buckets, bucket_sizes, + bucket_dist, static_buffer, buffer_size, 0, 0); + if (ret != 0) { + printf("Error: Failed to load static memory (ret=%d)\n", ret); + free(static_buffer); + return 1; + } + + /* For now, we'll simulate the static memory behavior */ + /* In a real implementation, this would use wc_LoadStaticMemory_ex */ + printf("Note: This is a simulation - in production, use wc_LoadStaticMemory_ex\n\n"); + + /* Replay allocation sequence */ + ret = replay_allocation_sequence(events, num_events, buckets, num_buckets, heap_hint); + + /* Cleanup */ + free(static_buffer); + + return ret; +} \ No newline at end of file From 8a11389cca7cb00474dad608cabb9698587466a4 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Fri, 1 Aug 2025 09:37:18 -0600 Subject: [PATCH 4/8] fix optimizer and update tester --- .../memory-bucket-optimizer/README.md | 17 +- .../optimizer/memory_bucket_optimizer.c | 637 ++++++++++-------- .../tester/memory_bucket_tester.c | 47 +- 3 files changed, 395 insertions(+), 306 deletions(-) diff --git a/staticmemory/memory-bucket-optimizer/README.md b/staticmemory/memory-bucket-optimizer/README.md index 551822ec5..d528c803a 100644 --- a/staticmemory/memory-bucket-optimizer/README.md +++ b/staticmemory/memory-bucket-optimizer/README.md @@ -4,17 +4,14 @@ The staticmemory feature ends up using a bit more memory and is a simple sectioning up of a static buffer used dynamically instead of malloc/free. wolfSSL has the option for users to define custom XMALLOC/XFREE if wanting to use a different allocater. -This tool analyzes memory allocation patterns in wolfSSL and recommends optimal static memory bucket configurations to minimize wasted memory. - -## Overview - -When wolfSSL is built with the `--enable-staticmemory` option, it uses a static memory management system with memory buckets. The size and distribution of these buckets can significantly impact memory usage efficiency. This tool helps optimize these bucket configurations for specific TLS operations. +The exact optimized configuration is a difficult problem (think traveling salesman problem), but this optimizer gives a good starting point. It uses a simple algorithm in that it fill the first half of bucket sizes with largest allocations and the second half based on max concurrent uses. ## Directory Structure ``` memory-bucket-optimizer/ ├── optimizer/ # Source code for the optimizer +├── tester/ # Source code for testing configuration └── README.md # This file ``` @@ -59,3 +56,13 @@ make ./memory_bucket_optimizer testwolfcrypt.log ``` +4. Build and run tester (optional) + +``` +cd ~/wolfssl +./configure CPPFLAGS="-DWOLFSSL_NO_MALLOC -DWOLFSSL_DEBUG_MEMORY -DWOLFSSL_DEBUG_MEMORY_PRINT" --enable-staticmemory +make && sudo make install +cd tester && make +./memory_bucket_tester ../testwolfcrypt.log --buckets "289,832,1056,1072,1152,1616,1632,3160,4240" --dist "2,1,2,1,1,1,1,19,1" --buffer-size 74298 +``` + diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c index babd29ebb..a304ae04e 100644 --- a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -28,11 +28,8 @@ #include #include -#define MAX_ALLOC_SIZES 1000 #define MAX_LINE_LENGTH 1024 -#define MAX_BUCKETS 16 #define MAX_UNIQUE_BUCKETS 9 /* Maximum number of unique bucket sizes to create */ -#define MAX_ALLOCATIONS 10000 /* Configuration notes: * - MAX_UNIQUE_BUCKETS: Limits the total number of unique bucket sizes @@ -44,17 +41,137 @@ #define WOLFSSL_HEAP_SIZE 64 /* Approximate size of WOLFSSL_HEAP structure */ #define WOLFSSL_HEAP_HINT_SIZE 32 /* Approximate size of WOLFSSL_HEAP_HINT structure */ -typedef struct { +/* Linked list node for allocation events */ +typedef struct AllocationEventNode { + int size; + int timestamp; // Simple counter for allocation order + int active; // 1 if allocated, 0 if freed + struct AllocationEventNode* next; +} AllocationEventNode; +AllocationEventNode* event_head = NULL; + +/* Linked list node for unique allocation sizes */ +typedef struct AllocSizeNode { int size; int count; + int concurrent; int max_concurrent; // Maximum number of concurrent allocations of this size -} AllocSize; + struct AllocSizeNode* next; /* next in list of sizes sorted by size */ + struct AllocSizeNode* nextFreq; /* sorted by count size descending */ +} AllocSizeNode; +/* Linked list for allocation events */ typedef struct { - int size; - int timestamp; // Simple counter for allocation order - int active; // 1 if allocated, 0 if freed -} AllocationEvent; + AllocationEventNode* head; + AllocationEventNode* tail; + int count; +} AllocationEventList; + +/* Linked list for unique allocation sizes */ +typedef struct { + AllocSizeNode* head; + int count; +} AllocSizeList; + +/* Helper functions for linked lists */ +AllocationEventNode* create_allocation_event_node(int size, int timestamp, int active) { + AllocationEventNode* node = (AllocationEventNode*)malloc(sizeof(AllocationEventNode)); + if (node) { + node->size = size; + node->timestamp = timestamp; + node->active = active; + node->next = NULL; + } + return node; +} + +void add_allocation_event(AllocationEventNode** list, int size, int timestamp, int active) { + AllocationEventNode* node = create_allocation_event_node(size, timestamp, active); + if (node) { + if (*list == NULL) { + event_head = node; + *list = node; + } else { + (*list)->next = node; + *list = node; // Update the list pointer to point to the new node + } + } +} + +AllocSizeNode* create_alloc_size_node(int size) { + AllocSizeNode* node = (AllocSizeNode*)malloc(sizeof(AllocSizeNode)); + if (node) { + node->size = size; + node->count = 0; + node->concurrent = 0; + node->max_concurrent = 0; + node->next = NULL; + node->nextFreq = NULL; + } + return node; +} + +AllocSizeNode* find_or_create_alloc_size(AllocSizeNode** list, int size) { + AllocSizeNode* current = *list; + AllocSizeNode* previous = NULL; + + /* Look for existing size */ + while (current) { + if (current->size == size) { + return current; + } + current = current->next; + } + + /* Create new size node */ + AllocSizeNode* node = create_alloc_size_node(size); + if (node) { + /* insert node into list ordered from largest size first to smallest */ + current = *list; + if (current == NULL) { + *list = node; + } + else { + while (current != NULL) { + if (current->size < size) { + node->next = current; + if (previous != NULL) { + previous->next = node; + } + else { + *list = node; + } + break; + } + previous = current; + current = current->next; + } + /* If we reached the end of the list, append the node */ + if (current == NULL) { + previous->next = node; + } + } + } + return node; +} + +void free_allocation_event_list(AllocationEventNode* list) { + AllocationEventNode* current = list; + while (current) { + AllocationEventNode* next = current->next; + free(current); + current = next; + } +} + +void free_alloc_size_list(AllocSizeNode* list) { + AllocSizeNode* current = list; + while (current) { + AllocSizeNode* next = current->next; + free(current); + current = next; + } +} /* Function to calculate memory padding size per bucket */ int calculate_padding_size() { @@ -82,9 +199,10 @@ int calculate_total_overhead(int num_buckets) { } /* Function to parse memory allocation logs with concurrent usage tracking */ -int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, - int* num_sizes, int* peak_heap_usage) +int parse_memory_logs(const char* filename, AllocationEventNode** events, + int* peak_heap_usage, int* buckets) { + int current_heap_usage = 0; FILE* file = fopen(filename, "r"); if (!file) { printf("Error: Could not open file %s\n", filename); @@ -92,13 +210,10 @@ int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, } char line[MAX_LINE_LENGTH]; - *num_sizes = 0; - AllocationEvent events[MAX_ALLOCATIONS]; - int num_events = 0; int timestamp = 0; *peak_heap_usage = 0; /* Initialize peak heap usage */ - while (fgets(line, sizeof(line), file) && num_events < MAX_ALLOCATIONS) { + while (fgets(line, sizeof(line), file)) { /* Look for lines containing "Alloc:" or "Free:" */ char* alloc_pos = strstr(line, "Alloc:"); char* free_pos = strstr(line, "Free:"); @@ -111,10 +226,20 @@ int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, * Format 3: (Using global heap hint 0x1010e2110) [HEAP 0x0] Alloc: 0x101107440 -> 1584 at _sp_exptmod_nct:14231 */ if (sscanf(alloc_pos, "Alloc: %*s -> %d", &size) == 1) { - events[num_events].size = size; - events[num_events].timestamp = timestamp++; - events[num_events].active = 1; - num_events++; + /* Here we begin the bucket list, as a simple tracking of + * largest allocs encountered. */ + int i; + for (i = 0; i < MAX_UNIQUE_BUCKETS; i++) { + if (size > buckets[i]) { + buckets[i] = size; + break; + } + } + current_heap_usage += size; + if (current_heap_usage > *peak_heap_usage) { + *peak_heap_usage = current_heap_usage; + } + add_allocation_event(events, size, timestamp++, 1); } } else if (free_pos) { int size; @@ -124,113 +249,127 @@ int parse_memory_logs(const char* filename, AllocSize* alloc_sizes, * Format 3: (Using global heap hint 0x1010e2110) [HEAP 0x0] Free: 0x101107440 -> 1584 at _sp_exptmod_nct:14462 */ if (sscanf(free_pos, "Free: %*s -> %d", &size) == 1) { - events[num_events].size = size; - events[num_events].timestamp = timestamp++; - events[num_events].active = 0; - num_events++; + current_heap_usage -= size; + if (current_heap_usage < 0) { + current_heap_usage = 0; + } + add_allocation_event(events, size, timestamp++, 0); } } } fclose(file); - - /* Collect unique sizes from events */ - int unique_sizes[MAX_ALLOC_SIZES]; - int num_unique_sizes = 0; - - for (int i = 0; i < num_events; i++) { - int size = events[i].size; - int found = 0; - - /* Check if this size is already in our unique sizes list */ - for (int j = 0; j < num_unique_sizes; j++) { - if (unique_sizes[j] == size) { - found = 1; - break; + return 0; +} + + +/* This goes through all the events and finds unique allocations and the max + * concurent use of each unique allocation */ +static void find_max_concurent_allocations(AllocSizeNode** alloc_sizes) +{ + AllocationEventNode* current = event_head; + while (current != NULL) { + if (current->active) { + AllocSizeNode* alloc_size = find_or_create_alloc_size(alloc_sizes, current->size); + alloc_size->concurrent++; + alloc_size->count++; + if (alloc_size->max_concurrent < alloc_size->concurrent) { + alloc_size->max_concurrent = alloc_size->concurrent; } } - - /* Add to unique sizes if not found */ - if (!found && num_unique_sizes < MAX_ALLOC_SIZES) { - unique_sizes[num_unique_sizes++] = size; + else { + AllocSizeNode* alloc_size = find_or_create_alloc_size(alloc_sizes, current->size); + alloc_size->concurrent--; } + current = current->next; } - - /* Calculate concurrent usage for each unique size */ - for (int s = 0; s < num_unique_sizes; s++) { - int size = unique_sizes[s]; - int current_concurrent = 0; - int max_concurrent = 0; - int total_count = 0; - - for (int i = 0; i < num_events; i++) { - if (events[i].size == size) { - if (events[i].active) { - current_concurrent++; - total_count++; - if (current_concurrent > max_concurrent) { - max_concurrent = current_concurrent; - } - } else { - current_concurrent--; - if (current_concurrent < 0) { - current_concurrent = 0; /* Handle mismatched free/alloc */ - } - } - } - } - - /* Only add sizes that were actually allocated */ - if (total_count > 0) { - if (*num_sizes < MAX_ALLOC_SIZES) { - alloc_sizes[*num_sizes].size = size; - alloc_sizes[*num_sizes].count = total_count; - alloc_sizes[*num_sizes].max_concurrent = max_concurrent; - (*num_sizes)++; - } else { - printf("Warning: Maximum number of allocation sizes reached\n"); +} + + +/* This function makes sure that for every alloc there is a bucket avilable */ +static void set_distributions(int* buckets, int* dist, int num_buckets) +{ + AllocationEventNode* current = event_head; + int max_concurrent_use[num_buckets]; + int current_use[num_buckets]; + int i; + + /* Initialize arrays to zero */ + memset(max_concurrent_use, 0, sizeof(max_concurrent_use)); + memset(current_use, 0, sizeof(current_use)); + + while (current != NULL) { + /* find bucket this would go in */ + for (i = 0; i < num_buckets; i++) { + if (current->size <= (buckets[i] - wolfSSL_MemoryPaddingSz())) { break; } } - } - - /* Calculate peak heap usage by simulating the allocation timeline */ - /* This tracks the maximum total memory that was allocated concurrently */ - int current_heap_usage = 0; - int max_heap_usage = 0; - - /* Create a timeline of heap usage changes */ - for (int i = 0; i < num_events; i++) { - if (events[i].active) { - /* Allocation - add to current heap usage */ - current_heap_usage += events[i].size; - } else { - /* Free - subtract from current heap usage */ - current_heap_usage -= events[i].size; - if (current_heap_usage < 0) { - current_heap_usage = 0; /* Handle mismatched free/alloc */ + + /* Only process if we found a valid bucket */ + if (i < num_buckets) { + if (current->active) { + current_use[i] += 1; + if (current_use[i] > max_concurrent_use[i]) { + max_concurrent_use[i] = current_use[i]; + } } + else { + current_use[i] -= 1; + } + } else { + printf("ERROR: allocation size %d is larger than all bucket sizes!\n", + current->size); + printf("This indicates a bug in the bucket optimization algorithm.\n"); + printf("Largest bucket size: %d, allocation size: %d\n", + num_buckets > 0 ? buckets[num_buckets-1] : 0, current->size); + exit(1); } - - /* Track peak usage */ - if (current_heap_usage > max_heap_usage) { - max_heap_usage = current_heap_usage; - } + current = current->next; } - - *peak_heap_usage = max_heap_usage; - - return 0; -} -/* Function to compare allocation sizes for sorting */ -int compare_alloc_sizes(const void* a, const void* b) { - return ((AllocSize*)a)->size - ((AllocSize*)b)->size; + for (i = 0; i < num_buckets; i++) { + dist[i] = max_concurrent_use[i]; + } } -/* Function to compare allocation counts for sorting (descending) */ -int compare_alloc_counts(const void* a, const void* b) { - return ((AllocSize*)b)->count - ((AllocSize*)a)->count; +static void sort_alloc_by_frequency(AllocSizeNode* alloc_sizes, + AllocSizeNode** sorted) +{ + AllocSizeNode* max; + AllocSizeNode* current; + AllocSizeNode* tail; + int current_count = 0; + int current_upper_bound = INT_MAX; + + *sorted = NULL; // Initialize to NULL + + do { + max = NULL; + current = alloc_sizes; // Reset current to beginning of list + while (current != NULL) { + if (current->count > current_count && current->size < current_upper_bound) { + current_count = current->count; + max = current; + } + current = current->next; + } + + if (max == NULL) { + break; // No more nodes to process + } + + current_upper_bound = max->size; + if (*sorted == NULL) { + *sorted = max; + tail = max; + } + else { + tail->nextFreq = max; + tail = max; + } + tail->nextFreq = NULL; + } while (max != NULL); } /* Function to optimize bucket sizes */ @@ -242,178 +381,79 @@ int compare_alloc_counts(const void* a, const void* b) { * - This reduces bucket management overhead when waste is minimal * - Limited to MAX_UNIQUE_BUCKETS total unique bucket sizes */ -void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, - int* dist, int* num_buckets) +void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_freq, + int num_sizes, int* buckets, int* dist, int* num_buckets) { int i, j; - - /* Make a copy of the allocation sizes for frequency sorting */ - AllocSize* alloc_sizes_by_freq = (AllocSize*)malloc(num_sizes * sizeof(AllocSize)); - if (!alloc_sizes_by_freq) { - printf("Error: Memory allocation failed\n"); - *num_buckets = 0; - return; - } - - memcpy(alloc_sizes_by_freq, alloc_sizes, num_sizes * sizeof(AllocSize)); - - /* Sort by frequency (descending) */ - qsort(alloc_sizes_by_freq, num_sizes, sizeof(AllocSize), compare_alloc_counts); - - /* Find the largest allocation size */ - int largest_size = 0; - for (i = 0; i < num_sizes; i++) { - if (alloc_sizes[i].size > largest_size) { - largest_size = alloc_sizes[i].size; - } - } + AllocSizeNode* current; /* Calculate total allocations and find significant sizes */ int total_allocations = 0; - for (i = 0; i < num_sizes; i++) { - total_allocations += alloc_sizes[i].count; + current = alloc_sizes; + while (current != NULL) { + total_allocations += current->count; + current = current->next; } /* Determine significant allocation sizes (those that represent >1% of total allocations) */ - int significant_sizes[MAX_BUCKETS]; + int significant_sizes[MAX_UNIQUE_BUCKETS]; int num_significant = 0; int min_threshold = total_allocations / 100; /* 1% threshold */ - for (i = 0; i < num_sizes && num_significant < MAX_BUCKETS - 1; i++) { - if (alloc_sizes_by_freq[i].count >= min_threshold) { - significant_sizes[num_significant++] = alloc_sizes_by_freq[i].size; + /* Populate significant sizes from alloc_sizes */ + current = alloc_sizes; + while (current != NULL && num_significant < MAX_UNIQUE_BUCKETS) { + if (current->count >= min_threshold) { + significant_sizes[num_significant++] = current->size; } + current = current->next; } - + /* Initialize bucket count */ *num_buckets = 0; - /* Always include the largest allocation size first (with padding) */ - buckets[*num_buckets] = calculate_bucket_size_with_padding(largest_size); - - /* Find the largest size in our data to get its concurrent usage */ - int largest_concurrent = 1; /* Default to 1 */ - for (i = 0; i < num_sizes; i++) { - if (alloc_sizes[i].size == largest_size) { - largest_concurrent = alloc_sizes[i].max_concurrent; - break; - } - } - - dist[*num_buckets] = largest_concurrent; - (*num_buckets)++; - - /* Add significant allocation sizes, considering padding overhead */ - for (i = 0; i < num_significant && *num_buckets < MAX_UNIQUE_BUCKETS; i++) { - int size = significant_sizes[i]; - - /* Skip if this size is already included (like the largest size) */ - int already_included = 0; - for (j = 0; j < *num_buckets; j++) { - /* Compare original allocation sizes, not bucket sizes with padding */ - int bucket_data_size = buckets[j] - calculate_padding_size(); - if (bucket_data_size == size) { - already_included = 1; - break; - } - } - - if (!already_included) { - /* Check if this size can be efficiently handled by existing buckets */ - int best_existing_bucket = -1; - int min_waste = INT_MAX; - int padding_size = calculate_padding_size(); - - /* Find the best existing bucket for this size */ - for (j = 0; j < *num_buckets; j++) { - int bucket_data_size = buckets[j] - padding_size; - if (bucket_data_size >= size) { - int waste = bucket_data_size - size; - if (waste < min_waste) { - min_waste = waste; - best_existing_bucket = j; - } - } - } - - /* Only create a new bucket if the waste from existing buckets is significant */ - /* If waste is less than padding size, it's better to use existing bucket */ - if (best_existing_bucket >= 0 && min_waste < padding_size) { - /* Use existing bucket - don't create a new one */ - /* This size will be handled by the existing bucket */ - continue; - } - - /* Create new bucket for this size */ - buckets[*num_buckets] = calculate_bucket_size_with_padding(size); - - /* Calculate distribution based on concurrent usage and frequency */ - int count = 0; - int concurrent = 1; /* Default to 1 */ - for (j = 0; j < num_sizes; j++) { - if (alloc_sizes[j].size == size) { - count = alloc_sizes[j].count; - concurrent = alloc_sizes[j].max_concurrent; - break; - } - } - - dist[*num_buckets] = concurrent; - - /* Ensure we have enough buckets for maximum concurrent usage */ - /* Don't cap arbitrarily - let the buffer size calculation handle memory constraints */ - - (*num_buckets)++; - } + /* Always include the largest allocation sizes (with padding) */ + current = alloc_sizes; + for (i = 0; i < MAX_UNIQUE_BUCKETS/2 && current != NULL; i++) { + buckets[*num_buckets] = calculate_bucket_size_with_padding(current->size); + dist[*num_buckets] = current->max_concurrent; + (*num_buckets)++; + current = current->next; } - /* If we still have space, add some medium-frequency sizes */ - if (*num_buckets < MAX_UNIQUE_BUCKETS) { - for (i = 0; i < num_sizes && *num_buckets < MAX_UNIQUE_BUCKETS; i++) { - int size = alloc_sizes_by_freq[i].size; - - /* Skip if already included */ - int already_included = 0; - for (j = 0; j < *num_buckets; j++) { - /* Compare original allocation sizes, not bucket sizes with padding */ - int bucket_data_size = buckets[j] - calculate_padding_size(); - if (bucket_data_size == size) { - already_included = 1; - break; - } - } - - if (!already_included && alloc_sizes_by_freq[i].count >= 3) { - /* Check if this size can be efficiently handled by existing buckets */ - int best_existing_bucket = -1; - int min_waste = INT_MAX; - int padding_size = calculate_padding_size(); - - /* Find the best existing bucket for this size */ + /* Fill out the other half based on max concurent use */ + for (i = *num_buckets; i < MAX_UNIQUE_BUCKETS; i++) { + int max_concurrent = 0; + AllocSizeNode* max = NULL; + + current = alloc_sizes; + while (current != NULL) { + if (current->max_concurrent > max_concurrent) { + /* Skip if already included */ + int already_included = 0; for (j = 0; j < *num_buckets; j++) { - int bucket_data_size = buckets[j] - padding_size; - if (bucket_data_size >= size) { - int waste = bucket_data_size - size; - if (waste < min_waste) { - min_waste = waste; - best_existing_bucket = j; - } + /* Compare original allocation sizes, not bucket sizes with padding */ + int bucket_data_size = buckets[j] - calculate_padding_size(); + if (bucket_data_size == current->size) { + already_included = 1; + break; } } - - /* Only create a new bucket if the waste from existing buckets is significant */ - /* If waste is less than padding size, it's better to use existing bucket */ - if (best_existing_bucket >= 0 && min_waste < padding_size) { - /* Use existing bucket - don't create a new one */ - /* This size will be handled by the existing bucket */ - continue; + if (!already_included) { + max_concurrent = current->max_concurrent; + max = current; } - - /* Create new bucket for this size */ - buckets[*num_buckets] = calculate_bucket_size_with_padding(size); - dist[*num_buckets] = 2; /* Default to 2 buckets for medium frequency */ - (*num_buckets)++; } + current = current->next; + } + printf("num_buckets = %d max = %p\n", *num_buckets, max); + if (max != NULL) { + buckets[*num_buckets] = calculate_bucket_size_with_padding(max->size); + dist[*num_buckets] = max->max_concurrent; + *num_buckets += 1; + } + else { + break; } } @@ -434,7 +474,7 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } } - free(alloc_sizes_by_freq); + set_distributions(buckets, dist, *num_buckets); /* Print optimization summary */ printf("Optimization Summary:\n"); @@ -449,9 +489,10 @@ void optimize_buckets(AllocSize* alloc_sizes, int num_sizes, int* buckets, } /* Function to calculate memory efficiency metrics */ -void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, +void calculate_memory_efficiency(AllocSizeNode* alloc_sizes, int num_sizes, int* buckets, int* dist, int num_buckets) { + AllocSizeNode* current = alloc_sizes; int i, j; float total_waste = 0.0; int total_allocations = 0; @@ -463,9 +504,9 @@ void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, printf("Size Count Concurrent Bucket Waste Coverage\n"); printf("---- ----- ---------- ------ ----- --------\n"); - for (i = 0; i < num_sizes; i++) { - int size = alloc_sizes[i].size; - int count = alloc_sizes[i].count; + for (i = 0; i < num_sizes && current != NULL; i++) { + int size = current->size; + int count = current->count; total_allocations += count; /* Find the smallest bucket that can fit this allocation */ @@ -488,11 +529,12 @@ void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, allocations_handled += count; total_waste += (float)min_waste * count; printf("%-7d %-7d %-10d %-7d %-7d %s\n", - size, count, alloc_sizes[i].max_concurrent, buckets[best_bucket], min_waste, "✓"); + size, count, current->max_concurrent, buckets[best_bucket], min_waste, "✓"); } else { printf("%-7d %-7d %-10d %-7s %-7s %s\n", - size, count, alloc_sizes[i].max_concurrent, "N/A", "N/A", "✗"); + size, count, current->max_concurrent, "N/A", "N/A", "✗"); } + current = current->next; } printf("\nEfficiency Summary:\n"); @@ -525,8 +567,10 @@ void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, /* Calculate efficiency based on actual data vs total memory */ float data_memory = 0; - for (i = 0; i < num_sizes; i++) { - data_memory += alloc_sizes[i].size * alloc_sizes[i].count; + current = alloc_sizes; + for (i = 0; i < num_sizes && current != NULL; i++) { + data_memory += current->size * current->count; + current = current->next; } printf("Data memory: %.0f bytes\n", data_memory); } @@ -534,21 +578,20 @@ void calculate_memory_efficiency(AllocSize* alloc_sizes, int num_sizes, /* Function to provide buffer size recommendations */ void print_buffer_recommendations(int* buckets, int* dist, int num_buckets) { - int total_bucket_memory = 0, total_overhead, total_memory_needed, i; + int total_bucket_memory = 0, total_overhead = 0, total_memory_needed, i; for (i = 0; i < num_buckets; i++) { total_bucket_memory += buckets[i] * dist[i]; + total_overhead += dist[i] * wolfSSL_MemoryPaddingSz(); } - - total_overhead = calculate_total_overhead(num_buckets); + + total_overhead += WOLFSSL_HEAP_SIZE + WOLFSSL_HEAP_HINT_SIZE; total_memory_needed = total_bucket_memory + total_overhead; - + printf("\nBuffer Size Recommendations:\n"); printf("============================\n"); printf("Minimum buffer size needed: %d bytes\n", total_memory_needed); - printf("Recommended buffer size: %d bytes (add 10%% safety margin)\n", - (int)(total_memory_needed * 1.1)); - + printf("\nUsage in wolfSSL application:\n"); printf("============================\n"); printf("// Allocate buffer\n"); @@ -567,42 +610,55 @@ void print_buffer_recommendations(int* buckets, int* dist, int num_buckets) int main(int argc, char** argv) { int i; + int buckets[MAX_UNIQUE_BUCKETS]; + int dist[MAX_UNIQUE_BUCKETS]; + int num_sizes = 0; + int peak_heap_usage = 0; + int num_buckets = 0; + AllocationEventNode* events = NULL; + AllocSizeNode* alloc_sizes = NULL; + AllocSizeNode* alloc_sizes_by_freq = NULL; + AllocSizeNode* current; if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; } - AllocSize alloc_sizes[MAX_ALLOC_SIZES]; - int num_sizes = 0; - int peak_heap_usage = 0; + /* Initialize buckets array to 0 */ + memset(buckets, 0, sizeof(buckets)); /* Parse memory allocation logs */ - if (parse_memory_logs(argv[1], alloc_sizes, &num_sizes, &peak_heap_usage) != 0) { + if (parse_memory_logs(argv[1], &events, &peak_heap_usage, buckets) != 0) { return 1; } + + find_max_concurent_allocations(&alloc_sizes); + sort_alloc_by_frequency(alloc_sizes, &alloc_sizes_by_freq); + + current = alloc_sizes; + while (current!= NULL) { + num_sizes++; + current = current->next; + } + printf("Found %d unique allocation sizes\n", num_sizes); printf("Peak heap usage: %d bytes (maximum concurrent memory usage)\n\n", peak_heap_usage); - - /* Sort allocation sizes */ - qsort(alloc_sizes, num_sizes, sizeof(AllocSize), compare_alloc_sizes); - + /* Print allocation sizes, frequencies, and concurrent usage */ printf("Allocation Sizes, Frequencies, and Concurrent Usage:\n"); printf("Size Count Max Concurrent\n"); printf("---- ----- --------------\n"); - for (i = 0; i < num_sizes; i++) { - printf("%-7d %-7d %d\n", alloc_sizes[i].size, alloc_sizes[i].count, alloc_sizes[i].max_concurrent); + current = alloc_sizes; + while (current != NULL) { + printf("%-7d %-7d %d\n", current->size, current->count, current->max_concurrent); + current = current->next; } printf("\n"); /* Optimize bucket sizes */ - int buckets[MAX_BUCKETS]; - int dist[MAX_BUCKETS]; - int num_buckets = 0; - - optimize_buckets(alloc_sizes, num_sizes, buckets, dist, &num_buckets); + optimize_buckets(alloc_sizes, alloc_sizes_by_freq, num_sizes, buckets, dist, &num_buckets); /* Print optimized bucket sizes and distribution */ printf("Optimized Bucket Sizes and Distribution:\n"); @@ -643,5 +699,10 @@ int main(int argc, char** argv) /* Print buffer size recommendations */ print_buffer_recommendations(buckets, dist, num_buckets); + /* Clean up events list */ + free_allocation_event_list(events); + free_alloc_size_list(alloc_sizes); + /* alloc_sizes_by_freq is the same nodes as alloc_sizes */ + return 0; } diff --git a/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c b/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c index 042ac96f6..671e6f7f4 100644 --- a/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c +++ b/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c @@ -159,18 +159,12 @@ int parse_memory_logs(const char* filename, AllocationEvent* events, int* num_ev /* Function to replay allocation sequence */ int replay_allocation_sequence(AllocationEvent* events, int num_events, - BucketConfig* buckets, int num_buckets, WOLFSSL_HEAP_HINT* heap_hint ) { + WOLFSSL_HEAP_HINT* heap_hint ) +{ int i; int success_count = 0; int failure_count = 0; - printf("Replaying %d allocation events...\n", num_events); - printf("Using %d buckets:\n", num_buckets); - for (i = 0; i < num_buckets; i++) { - printf(" Bucket %d: size=%d, count=%d\n", i, buckets[i].size, buckets[i].count); - } - printf("\n"); - for (i = 0; i < num_events; i++) { if (events[i].is_alloc) { /* Try to allocate memory */ @@ -280,7 +274,8 @@ int main(int argc, char** argv) { } if (total_buffer_size <= 0) { - printf("Error: Invalid buffer size (%d). Must be greater than 0.\n", total_buffer_size); + printf("Error: Invalid buffer size (%d). Must be greater than 0.\n", + total_buffer_size); print_usage(argv[0]); return 1; } @@ -326,12 +321,38 @@ int main(int argc, char** argv) { return 1; } - /* For now, we'll simulate the static memory behavior */ - /* In a real implementation, this would use wc_LoadStaticMemory_ex */ - printf("Note: This is a simulation - in production, use wc_LoadStaticMemory_ex\n\n"); + /* Display heap hint information if available */ + if (heap_hint != NULL) { + printf("Heap hint initialized successfully\n"); + printf("Heap hint address: %p\n", (void*)heap_hint); + + /* Try to access the heap structure if available */ + if (heap_hint->memory != NULL) { + WOLFSSL_MEM_STATS mem_stats; + /* Extract bucket information from heap structure */ + WOLFSSL_HEAP* heap = (WOLFSSL_HEAP*)heap_hint->memory; + + wolfSSL_GetMemStats(heap, &mem_stats); + + /* Print actual buckets created in the heap */ + printf("Actual buckets created in heap:\n"); + for (i = 0; i < WOLFMEM_MAX_BUCKETS; i++) { + printf(" Bucket %d: size=%d, count=%d\n", i, + mem_stats.blockSz[i], mem_stats.avaBlock[i]); + } + } else { + printf("Heap structure not accessible\n"); + } + + printf("Static memory system ready for allocation testing\n"); + } else { + printf("Warning: Heap hint is NULL\n"); + } + printf("=====================================\n\n"); + fflush(stdout); /* Replay allocation sequence */ - ret = replay_allocation_sequence(events, num_events, buckets, num_buckets, heap_hint); + ret = replay_allocation_sequence(events, num_events, heap_hint); /* Cleanup */ free(static_buffer); From 3c6b062c5612ecf6d191224e5cedccbd215e1140 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Fri, 1 Aug 2025 10:09:49 -0600 Subject: [PATCH 5/8] fix for buffer size recomendation --- .../optimizer/memory_bucket_optimizer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c index a304ae04e..0e68b8552 100644 --- a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -585,7 +585,8 @@ void print_buffer_recommendations(int* buckets, int* dist, int num_buckets) total_overhead += dist[i] * wolfSSL_MemoryPaddingSz(); } - total_overhead += WOLFSSL_HEAP_SIZE + WOLFSSL_HEAP_HINT_SIZE; + total_overhead += sizeof(WOLFSSL_HEAP_HINT) + sizeof(WOLFSSL_HEAP) + + WOLFSSL_STATIC_ALIGN; total_memory_needed = total_bucket_memory + total_overhead; printf("\nBuffer Size Recommendations:\n"); From 359f318dcb3a67b2029f3449a704d749073cf295 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Fri, 1 Aug 2025 10:12:54 -0600 Subject: [PATCH 6/8] remove debug printf --- .../memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c index 0e68b8552..d3f84532d 100644 --- a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -446,7 +446,6 @@ void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_ } current = current->next; } - printf("num_buckets = %d max = %p\n", *num_buckets, max); if (max != NULL) { buckets[*num_buckets] = calculate_bucket_size_with_padding(max->size); dist[*num_buckets] = max->max_concurrent; From 198e4faa88b6053c0b3ef777ea738218c93eaf22 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Fri, 1 Aug 2025 13:48:36 -0600 Subject: [PATCH 7/8] make bucket sizes adhere to memory alignment --- .../optimizer/memory_bucket_optimizer.c | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c index d3f84532d..c4221243a 100644 --- a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -37,10 +37,6 @@ * Default: 9 buckets (can be adjusted based on memory constraints) */ -/* Memory overhead constants - these should match wolfSSL's actual values */ -#define WOLFSSL_HEAP_SIZE 64 /* Approximate size of WOLFSSL_HEAP structure */ -#define WOLFSSL_HEAP_HINT_SIZE 32 /* Approximate size of WOLFSSL_HEAP_HINT structure */ - /* Linked list node for allocation events */ typedef struct AllocationEventNode { int size; @@ -178,11 +174,6 @@ int calculate_padding_size() { return wolfSSL_MemoryPaddingSz(); } -/* Function to calculate bucket size including padding */ -int calculate_bucket_size_with_padding(int allocation_size) { - return allocation_size + calculate_padding_size(); -} - /* Function to calculate total memory overhead */ int calculate_total_overhead(int num_buckets) { /* Total overhead includes: @@ -191,8 +182,8 @@ int calculate_total_overhead(int num_buckets) { * - Alignment padding * Note: Padding is already included in bucket sizes */ - int total_overhead = WOLFSSL_HEAP_SIZE + - WOLFSSL_HEAP_HINT_SIZE + + int total_overhead = sizeof(WOLFSSL_HEAP) + + sizeof(WOLFSSL_HEAP_HINT) + (WOLFSSL_STATIC_ALIGN - 1); total_overhead += num_buckets * wolfSSL_MemoryPaddingSz(); return total_overhead; @@ -372,6 +363,18 @@ static void sort_alloc_by_frequency(AllocSizeNode* alloc_sizes, } while (max != NULL); } +/* returns what the bucket size would be */ +static int get_bucket_size(int size) +{ + int padding; + + padding = size % WOLFSSL_STATIC_ALIGN; + if (padding > 0) { + padding = WOLFSSL_STATIC_ALIGN - padding; + } + return size + padding + wolfSSL_MemoryPaddingSz(); +} + /* Function to optimize bucket sizes */ /* * Optimization heuristic: @@ -415,7 +418,7 @@ void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_ /* Always include the largest allocation sizes (with padding) */ current = alloc_sizes; for (i = 0; i < MAX_UNIQUE_BUCKETS/2 && current != NULL; i++) { - buckets[*num_buckets] = calculate_bucket_size_with_padding(current->size); + buckets[*num_buckets] = get_bucket_size(current->size); dist[*num_buckets] = current->max_concurrent; (*num_buckets)++; current = current->next; @@ -433,8 +436,7 @@ void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_ int already_included = 0; for (j = 0; j < *num_buckets; j++) { /* Compare original allocation sizes, not bucket sizes with padding */ - int bucket_data_size = buckets[j] - calculate_padding_size(); - if (bucket_data_size == current->size) { + if (buckets[j] == get_bucket_size(current->size)) { already_included = 1; break; } @@ -447,7 +449,7 @@ void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_ current = current->next; } if (max != NULL) { - buckets[*num_buckets] = calculate_bucket_size_with_padding(max->size); + buckets[*num_buckets] = get_bucket_size(max->size); dist[*num_buckets] = max->max_concurrent; *num_buckets += 1; } @@ -558,9 +560,10 @@ void calculate_memory_efficiency(AllocSizeNode* alloc_sizes, int num_sizes, printf("Total bucket memory: %d bytes\n", total_bucket_memory); printf("Memory overhead: %d bytes\n", total_overhead); - printf(" - Padding per bucket: %d bytes (included in bucket sizes)\n", calculate_padding_size()); - printf(" - Total padding: %d bytes (included in bucket sizes)\n", calculate_padding_size() * num_buckets); - printf(" - Heap structures: %d bytes\n", WOLFSSL_HEAP_SIZE + WOLFSSL_HEAP_HINT_SIZE); + printf(" - Padding per bucket: %d bytes (included in bucket sizes)\n", + calculate_padding_size()); + printf(" - Heap structures: %ld bytes\n", sizeof(WOLFSSL_HEAP) + + sizeof(WOLFSSL_HEAP_HINT)); printf(" - Alignment: %d bytes\n", WOLFSSL_STATIC_ALIGN - 1); printf("Total memory needed: %d bytes\n", total_memory_needed); From 8adc6adf47fc5c9bf21dbb303b112478bbc9b636 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Mon, 4 Aug 2025 17:10:45 -0600 Subject: [PATCH 8/8] fixes for formatting --- .../optimizer/memory_bucket_optimizer.c | 129 +++++++++--------- .../tester/memory_bucket_tester.c | 102 ++++++++------ 2 files changed, 129 insertions(+), 102 deletions(-) diff --git a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c index c4221243a..657ade1b1 100644 --- a/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c +++ b/staticmemory/memory-bucket-optimizer/optimizer/memory_bucket_optimizer.c @@ -23,7 +23,7 @@ #include #include #include -#include // Required for INT_MAX +#include /* Required for INT_MAX */ #include #include @@ -40,8 +40,8 @@ /* Linked list node for allocation events */ typedef struct AllocationEventNode { int size; - int timestamp; // Simple counter for allocation order - int active; // 1 if allocated, 0 if freed + int timestamp; /* Simple counter for allocation order */ + int active; /* 1 if allocated, 0 if freed */ struct AllocationEventNode* next; } AllocationEventNode; AllocationEventNode* event_head = NULL; @@ -51,7 +51,7 @@ typedef struct AllocSizeNode { int size; int count; int concurrent; - int max_concurrent; // Maximum number of concurrent allocations of this size + int max_concurrent; /* Maximum number of concurrent allocations of this size */ struct AllocSizeNode* next; /* next in list of sizes sorted by size */ struct AllocSizeNode* nextFreq; /* sorted by count size descending */ } AllocSizeNode; @@ -70,8 +70,12 @@ typedef struct { } AllocSizeList; /* Helper functions for linked lists */ -AllocationEventNode* create_allocation_event_node(int size, int timestamp, int active) { - AllocationEventNode* node = (AllocationEventNode*)malloc(sizeof(AllocationEventNode)); +AllocationEventNode* create_allocation_event_node(int size, int timestamp, + int active) +{ + AllocationEventNode* node; + + node = (AllocationEventNode*)malloc(sizeof(AllocationEventNode)); if (node) { node->size = size; node->timestamp = timestamp; @@ -81,21 +85,28 @@ AllocationEventNode* create_allocation_event_node(int size, int timestamp, int a return node; } -void add_allocation_event(AllocationEventNode** list, int size, int timestamp, int active) { - AllocationEventNode* node = create_allocation_event_node(size, timestamp, active); +void add_allocation_event(AllocationEventNode** list, int size, int timestamp, + int active) +{ + AllocationEventNode* node; + + node = create_allocation_event_node(size, timestamp, active); if (node) { if (*list == NULL) { event_head = node; *list = node; } else { (*list)->next = node; - *list = node; // Update the list pointer to point to the new node + *list = node; /* Update the list pointer to point to the new node */ } } } -AllocSizeNode* create_alloc_size_node(int size) { - AllocSizeNode* node = (AllocSizeNode*)malloc(sizeof(AllocSizeNode)); +AllocSizeNode* create_alloc_size_node(int size) +{ + AllocSizeNode* node; + + node = (AllocSizeNode*)malloc(sizeof(AllocSizeNode)); if (node) { node->size = size; node->count = 0; @@ -107,9 +118,11 @@ AllocSizeNode* create_alloc_size_node(int size) { return node; } -AllocSizeNode* find_or_create_alloc_size(AllocSizeNode** list, int size) { +AllocSizeNode* find_or_create_alloc_size(AllocSizeNode** list, int size) +{ AllocSizeNode* current = *list; AllocSizeNode* previous = NULL; + AllocSizeNode* node = NULL; /* Look for existing size */ while (current) { @@ -120,7 +133,7 @@ AllocSizeNode* find_or_create_alloc_size(AllocSizeNode** list, int size) { } /* Create new size node */ - AllocSizeNode* node = create_alloc_size_node(size); + node = create_alloc_size_node(size); if (node) { /* insert node into list ordered from largest size first to smallest */ current = *list; @@ -151,7 +164,8 @@ AllocSizeNode* find_or_create_alloc_size(AllocSizeNode** list, int size) { return node; } -void free_allocation_event_list(AllocationEventNode* list) { +void free_allocation_event_list(AllocationEventNode* list) +{ AllocationEventNode* current = list; while (current) { AllocationEventNode* next = current->next; @@ -160,7 +174,8 @@ void free_allocation_event_list(AllocationEventNode* list) { } } -void free_alloc_size_list(AllocSizeNode* list) { +void free_alloc_size_list(AllocSizeNode* list) +{ AllocSizeNode* current = list; while (current) { AllocSizeNode* next = current->next; @@ -170,12 +185,14 @@ void free_alloc_size_list(AllocSizeNode* list) { } /* Function to calculate memory padding size per bucket */ -int calculate_padding_size() { +int calculate_padding_size() +{ return wolfSSL_MemoryPaddingSz(); } /* Function to calculate total memory overhead */ -int calculate_total_overhead(int num_buckets) { +int calculate_total_overhead(int num_buckets) +{ /* Total overhead includes: * - WOLFSSL_HEAP structure * - WOLFSSL_HEAP_HINT structure @@ -194,23 +211,25 @@ int parse_memory_logs(const char* filename, AllocationEventNode** events, int* peak_heap_usage, int* buckets) { int current_heap_usage = 0; - FILE* file = fopen(filename, "r"); + char line[MAX_LINE_LENGTH]; + int timestamp = 0; + FILE* file; + + file = fopen(filename, "r"); if (!file) { printf("Error: Could not open file %s\n", filename); return -1; } - - char line[MAX_LINE_LENGTH]; - int timestamp = 0; + *peak_heap_usage = 0; /* Initialize peak heap usage */ while (fgets(line, sizeof(line), file)) { /* Look for lines containing "Alloc:" or "Free:" */ char* alloc_pos = strstr(line, "Alloc:"); char* free_pos = strstr(line, "Free:"); + int size; if (alloc_pos) { - int size; /* Handle multiple formats: * Format 1: Alloc: 0x55fde046b490 -> 4 (11) at wolfTLSv1_3_client_method_ex:src/tls.c:16714 * Format 2: [HEAP 0x1010e2110] Alloc: 0x101108a40 -> 1024 at simple_mem_test:18561 @@ -232,8 +251,8 @@ int parse_memory_logs(const char* filename, AllocationEventNode** events, } add_allocation_event(events, size, timestamp++, 1); } - } else if (free_pos) { - int size; + } + else if (free_pos) { /* Handle multiple formats: * Format 1: Free: 0x55fde046b490 -> 4 at wolfTLSv1_3_client_method_ex:src/tls.c:16714 * Format 2: [HEAP 0x1010e2110] Free: 0x101108a40 -> 1024 at simple_mem_test:18576 @@ -261,7 +280,8 @@ static void find_max_concurent_allocations(AllocSizeNode** alloc_sizes) AllocationEventNode* current = event_head; while (current != NULL) { if (current->active) { - AllocSizeNode* alloc_size = find_or_create_alloc_size(alloc_sizes, current->size); + AllocSizeNode* alloc_size = + find_or_create_alloc_size(alloc_sizes, current->size); alloc_size->concurrent++; alloc_size->count++; if (alloc_size->max_concurrent < alloc_size->concurrent) { @@ -269,7 +289,8 @@ static void find_max_concurent_allocations(AllocSizeNode** alloc_sizes) } } else { - AllocSizeNode* alloc_size = find_or_create_alloc_size(alloc_sizes, current->size); + AllocSizeNode* alloc_size = + find_or_create_alloc_size(alloc_sizes, current->size); alloc_size->concurrent--; } current = current->next; @@ -333,13 +354,14 @@ static void sort_alloc_by_frequency(AllocSizeNode* alloc_sizes, int current_count = 0; int current_upper_bound = INT_MAX; - *sorted = NULL; // Initialize to NULL + *sorted = NULL; /* Initialize to NULL */ do { max = NULL; - current = alloc_sizes; // Reset current to beginning of list + current = alloc_sizes; /* Reset current to beginning of list */ while (current != NULL) { - if (current->count > current_count && current->size < current_upper_bound) { + if (current->count > current_count && + current->size < current_upper_bound) { current_count = current->count; max = current; } @@ -347,7 +369,7 @@ static void sort_alloc_by_frequency(AllocSizeNode* alloc_sizes, } if (max == NULL) { - break; // No more nodes to process + break; /* No more nodes to process */ } current_upper_bound = max->size; @@ -384,34 +406,13 @@ static int get_bucket_size(int size) * - This reduces bucket management overhead when waste is minimal * - Limited to MAX_UNIQUE_BUCKETS total unique bucket sizes */ -void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_freq, - int num_sizes, int* buckets, int* dist, int* num_buckets) +void optimize_buckets(AllocSizeNode* alloc_sizes, + AllocSizeNode* alloc_sizes_by_freq, int num_sizes, int* buckets, int* dist, + int* num_buckets) { int i, j; AllocSizeNode* current; - /* Calculate total allocations and find significant sizes */ - int total_allocations = 0; - current = alloc_sizes; - while (current != NULL) { - total_allocations += current->count; - current = current->next; - } - - /* Determine significant allocation sizes (those that represent >1% of total allocations) */ - int significant_sizes[MAX_UNIQUE_BUCKETS]; - int num_significant = 0; - int min_threshold = total_allocations / 100; /* 1% threshold */ - - /* Populate significant sizes from alloc_sizes */ - current = alloc_sizes; - while (current != NULL && num_significant < MAX_UNIQUE_BUCKETS) { - if (current->count >= min_threshold) { - significant_sizes[num_significant++] = current->size; - } - current = current->next; - } - /* Initialize bucket count */ *num_buckets = 0; @@ -435,7 +436,6 @@ void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_ /* Skip if already included */ int already_included = 0; for (j = 0; j < *num_buckets; j++) { - /* Compare original allocation sizes, not bucket sizes with padding */ if (buckets[j] == get_bucket_size(current->size)) { already_included = 1; break; @@ -474,7 +474,6 @@ void optimize_buckets(AllocSizeNode* alloc_sizes, AllocSizeNode* alloc_sizes_by_ } } } - set_distributions(buckets, dist, *num_buckets); /* Print optimization summary */ @@ -530,7 +529,8 @@ void calculate_memory_efficiency(AllocSizeNode* alloc_sizes, int num_sizes, allocations_handled += count; total_waste += (float)min_waste * count; printf("%-7d %-7d %-10d %-7d %-7d %s\n", - size, count, current->max_concurrent, buckets[best_bucket], min_waste, "✓"); + size, count, current->max_concurrent, buckets[best_bucket], + min_waste, "✓"); } else { printf("%-7d %-7d %-10d %-7s %-7s %s\n", size, count, current->max_concurrent, "N/A", "N/A", "✗"); @@ -541,7 +541,8 @@ void calculate_memory_efficiency(AllocSizeNode* alloc_sizes, int num_sizes, printf("\nEfficiency Summary:\n"); printf("Total allocations: %d\n", total_allocations); printf("Allocations handled: %d (%.1f%%)\n", - allocations_handled, (float)allocations_handled * 100 / total_allocations); + allocations_handled, + (float)allocations_handled * 100 / total_allocations); printf("Total memory waste: %.2f bytes\n", total_waste); printf("Average waste per allocation: %.2f bytes\n", total_waste / total_allocations); @@ -601,7 +602,8 @@ void print_buffer_recommendations(int* buckets, int* dist, int num_buckets) printf("byte staticBuffer[%d];\n", total_memory_needed); printf("\n// Load static memory\n"); printf("WOLFSSL_HEAP_HINT* heapHint = NULL;\n"); - printf("if (wc_LoadStaticMemory_ex(&heapHint, %d, bucket_sizes, bucket_dist,\n", num_buckets); + printf("if (wc_LoadStaticMemory_ex(&heapHint, %d, bucket_sizes, bucket_dist,\n", + num_buckets); printf(" staticBuffer, %d, 0, 0) != 0) {\n", total_memory_needed); printf(" // Handle error\n"); printf("}\n"); @@ -647,7 +649,8 @@ int main(int argc, char** argv) } printf("Found %d unique allocation sizes\n", num_sizes); - printf("Peak heap usage: %d bytes (maximum concurrent memory usage)\n\n", peak_heap_usage); + printf("Peak heap usage: %d bytes (maximum concurrent memory usage)\n\n", + peak_heap_usage); /* Print allocation sizes, frequencies, and concurrent usage */ printf("Allocation Sizes, Frequencies, and Concurrent Usage:\n"); @@ -655,13 +658,15 @@ int main(int argc, char** argv) printf("---- ----- --------------\n"); current = alloc_sizes; while (current != NULL) { - printf("%-7d %-7d %d\n", current->size, current->count, current->max_concurrent); + printf("%-7d %-7d %d\n", current->size, current->count, + current->max_concurrent); current = current->next; } printf("\n"); /* Optimize bucket sizes */ - optimize_buckets(alloc_sizes, alloc_sizes_by_freq, num_sizes, buckets, dist, &num_buckets); + optimize_buckets(alloc_sizes, alloc_sizes_by_freq, num_sizes, buckets, dist, + &num_buckets); /* Print optimized bucket sizes and distribution */ printf("Optimized Bucket Sizes and Distribution:\n"); diff --git a/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c b/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c index 671e6f7f4..9552b8b32 100644 --- a/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c +++ b/staticmemory/memory-bucket-optimizer/tester/memory_bucket_tester.c @@ -29,16 +29,13 @@ /* Memory overhead constants - these should match wolfSSL's actual values */ #ifndef WOLFSSL_STATIC_ALIGN -#define WOLFSSL_STATIC_ALIGN 8 + #define WOLFSSL_STATIC_ALIGN 8 #endif -#ifndef WOLFSSL_HEAP_SIZE -#define WOLFSSL_HEAP_SIZE 64 /* Approximate size of WOLFSSL_HEAP structure */ -#endif -#ifndef WOLFSSL_HEAP_HINT_SIZE -#define WOLFSSL_HEAP_HINT_SIZE 32 /* Approximate size of WOLFSSL_HEAP_HINT structure */ + +#ifndef MAX_ALLOCATIONS + #define MAX_ALLOCATIONS 10000 #endif -#define MAX_ALLOCATIONS 10000 #define MAX_LINE_LENGTH 1024 #define MAX_BUCKETS 16 @@ -55,7 +52,11 @@ typedef struct { /* Function to parse bucket configuration from command line */ int parse_bucket_config(int argc, char** argv, int start_idx, - BucketConfig* buckets, int* num_buckets, int* total_buffer_size) { + BucketConfig* buckets, int* num_buckets, + int* total_buffer_size) +{ + int i; + if (start_idx >= argc) { printf("Error: No bucket configuration provided\n"); return -1; @@ -63,7 +64,7 @@ int parse_bucket_config(int argc, char** argv, int start_idx, *num_buckets = 0; *total_buffer_size = 0; - int i = start_idx; + i = start_idx; while (i < argc && *num_buckets < MAX_BUCKETS) { if (strcmp(argv[i], "--buckets") == 0) { @@ -109,14 +110,18 @@ int parse_bucket_config(int argc, char** argv, int start_idx, } /* Function to parse memory allocation logs */ -int parse_memory_logs(const char* filename, AllocationEvent* events, int* num_events) { - FILE* file = fopen(filename, "r"); +int parse_memory_logs(const char* filename, AllocationEvent* events, + int* num_events) +{ + char line[MAX_LINE_LENGTH]; + FILE* file; + + file = fopen(filename, "r"); if (!file) { printf("Error: Could not open file %s\n", filename); return -1; } - char line[MAX_LINE_LENGTH]; *num_events = 0; while (fgets(line, sizeof(line), file) && *num_events < MAX_ALLOCATIONS) { @@ -152,11 +157,18 @@ int parse_memory_logs(const char* filename, AllocationEvent* events, int* num_ev } } } + + if (*num_events >= MAX_ALLOCATIONS) { + printf("Error: Too many allocation events (over %d)\n", MAX_ALLOCATIONS); + return -1; + } fclose(file); return 0; } + +#ifdef WOLFSSL_NO_MALLOC /* Function to replay allocation sequence */ int replay_allocation_sequence(AllocationEvent* events, int num_events, WOLFSSL_HEAP_HINT* heap_hint ) @@ -168,7 +180,8 @@ int replay_allocation_sequence(AllocationEvent* events, int num_events, for (i = 0; i < num_events; i++) { if (events[i].is_alloc) { /* Try to allocate memory */ - void* ptr = XMALLOC(events[i].size, heap_hint, DYNAMIC_TYPE_TMP_BUFFER); + void* ptr = XMALLOC(events[i].size, heap_hint, + DYNAMIC_TYPE_TMP_BUFFER); if (ptr == NULL) { printf("FAILURE: malloc failed for size %d at event %d\n", events[i].size, i); @@ -177,7 +190,8 @@ int replay_allocation_sequence(AllocationEvent* events, int num_events, } else { events[i].ptr = ptr; success_count++; - printf("SUCCESS: Allocated %d bytes at event %d\n", events[i].size, i); + printf("SUCCESS: Allocated %d bytes at event %d\n", + events[i].size, i); } } else { /* Find the corresponding allocation to free */ @@ -189,13 +203,14 @@ int replay_allocation_sequence(AllocationEvent* events, int num_events, events[j].ptr = NULL; events[i].ptr = NULL; found = 1; - printf("SUCCESS: Freed %d bytes at event %d\n", events[i].size, i); + printf("SUCCESS: Freed %d bytes at event %d\n", + events[i].size, i); break; } } if (!found) { - printf("WARNING: No matching allocation found for free of size %d at event %d\n", - events[i].size, i); + printf("WARNING: No matching allocation found for free of size" + " %d at event %d\n", events[i].size, i); } } } @@ -213,9 +228,11 @@ int replay_allocation_sequence(AllocationEvent* events, int num_events, return 0; } } +#endif /* Function to calculate required buffer size */ -int calculate_required_buffer_size(BucketConfig* buckets, int num_buckets) { +int calculate_required_buffer_size(BucketConfig* buckets, int num_buckets) +{ int total_size = 0; int i; @@ -229,7 +246,8 @@ int calculate_required_buffer_size(BucketConfig* buckets, int num_buckets) { return total_size; } -void print_usage(const char* program_name) { +void print_usage(const char* program_name) +{ printf("Usage: %s --buckets \",,...\" --dist \",,...\" --buffer-size \n", program_name); printf("\n"); printf("Arguments:\n"); @@ -250,19 +268,27 @@ void print_usage(const char* program_name) { printf(" 4. Fail if any XMALLOC fails\n"); } -int main(int argc, char** argv) { +int main(int argc, char** argv) +{ + const char* log_file; + BucketConfig buckets[MAX_BUCKETS]; + AllocationEvent events[MAX_ALLOCATIONS]; + unsigned int bucket_sizes[MAX_BUCKETS]; + unsigned int bucket_dist[MAX_BUCKETS]; + int i, ret, buffer_size; + int num_buckets = 0, total_buffer_size = 0, num_events = 0; + byte* static_buffer; + WOLFSSL_HEAP_HINT* heap_hint = NULL; + if (argc < 6) { print_usage(argv[0]); return 1; } - - const char* log_file = argv[1]; - BucketConfig buckets[MAX_BUCKETS]; - int num_buckets = 0; - int total_buffer_size = 0; + log_file = argv[1]; /* Parse bucket configuration */ - if (parse_bucket_config(argc, argv, 2, buckets, &num_buckets, &total_buffer_size) != 0) { + if (parse_bucket_config(argc, argv, 2, buckets, &num_buckets, + &total_buffer_size) != 0) { print_usage(argv[0]); return 1; } @@ -280,10 +306,6 @@ int main(int argc, char** argv) { return 1; } - /* Parse allocation events */ - AllocationEvent events[MAX_ALLOCATIONS]; - int num_events = 0; - if (parse_memory_logs(log_file, events, &num_events) != 0) { return 1; } @@ -291,30 +313,25 @@ int main(int argc, char** argv) { printf("Parsed %d allocation events from %s\n", num_events, log_file); /* Use provided buffer size */ - int buffer_size = total_buffer_size; + buffer_size = total_buffer_size; printf("Using provided buffer size: %d bytes\n\n", buffer_size); /* Convert bucket config to wolfSSL format */ - unsigned int bucket_sizes[MAX_BUCKETS]; - unsigned int bucket_dist[MAX_BUCKETS]; - int i; - for (i = 0; i < num_buckets; i++) { bucket_sizes[i] = (unsigned int)buckets[i].size; bucket_dist[i] = (unsigned int)buckets[i].count; } /* Allocate static buffer */ - byte* static_buffer = (byte*)malloc(buffer_size); + static_buffer = (byte*)malloc(buffer_size); if (!static_buffer) { printf("Error: Failed to allocate static buffer\n"); return 1; } /* Initialize static memory */ - WOLFSSL_HEAP_HINT* heap_hint = NULL; - int ret = wc_LoadStaticMemory_ex(&heap_hint, num_buckets, bucket_sizes, - bucket_dist, static_buffer, buffer_size, 0, 0); + ret = wc_LoadStaticMemory_ex(&heap_hint, num_buckets, bucket_sizes, + bucket_dist, static_buffer, buffer_size, 0, 0); if (ret != 0) { printf("Error: Failed to load static memory (ret=%d)\n", ret); free(static_buffer); @@ -351,9 +368,14 @@ int main(int argc, char** argv) { printf("=====================================\n\n"); fflush(stdout); +#ifdef WOLFSSL_NO_MALLOC /* Replay allocation sequence */ ret = replay_allocation_sequence(events, num_events, heap_hint); - +#else + printf("ERROR: WOLFSSL_NO_MALLOC is not defined\n"); + ret = -1; +#endif + /* Cleanup */ free(static_buffer);