Skip to content

Commit a3ad477

Browse files
committed
Resolving memory leaks
Resolving all memory leaks found when running the sequential version. Also adding build commands for the OpenMP version and dependency installations for the github actions config and the requirements.sh file. *Should* hopefully pass all tests now.
1 parent 39206e1 commit a3ad477

File tree

9 files changed

+84
-19
lines changed

9 files changed

+84
-19
lines changed

.github/workflows/c-cpp.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ jobs:
1818
- name: Install build tools and Valgrind
1919
run: |
2020
sudo apt-get update
21-
sudo apt-get install -y uthash-dev
2221
sudo apt-get install -y build-essential valgrind
22+
sudo apt-get install -y uthash-dev
23+
sudo apt install openmpi-bin libopenmpi-dev
2324
2425
# Build everything from the makefile
2526
- name: Build

connectEngine.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* Helper functions for QPE files. Used for testing with query text files. Cannot be reliably parralelized */
12

23
#define _POSIX_C_SOURCE 200809L // For strdup
34
#include <stdio.h>
@@ -16,8 +17,6 @@
1617
typedef struct node node; // Pull node declaration from serial bplus
1718
typedef struct record record; // Pull record declaration from serial bplus
1819

19-
/* --- Helper functions --- */
20-
2120
// Helper to safely copy strings with truncation
2221
static inline void safe_copy(char *dst, size_t n, const char *src) {
2322
snprintf(dst, n, "%.*s", (int)n - 1, src);
@@ -83,7 +82,7 @@ struct whereClauseS* convert_conditions(ParsedSQL *parsed) {
8382
}
8483

8584
// TODO - Handle sub-expressions and nested conditions
86-
// Note: Nested conditions are not currently supported by the test harness
85+
// Note: Nested conditions are not currently supported by the test
8786
node->next = NULL;
8887
node->sub = NULL;
8988

data-generation/commands_50k.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:5c7f3a8a35ada9a0a99518e7586b70469e746a2baaa62289dfb76bded61b7763
3-
size 5933449
2+
oid sha256:52454081094f3a01c9be3f493fb8d618ec83872003a46641fdd4d0486244f153
3+
size 5932502

engine/bplus.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ int findRange(node *const root, KEY_T key_start, KEY_T key_end, bool verbose,
3939
node *findLeaf(node *const root, KEY_T key, bool verbose); // Descend to target leaf.
4040
ROW_PTR find_row(node *root, KEY_T key); // Row lookup.
4141
int cut(int length); // Split helper (ceil(length/2)).
42+
void destroy_tree(node *root); /* Free entire tree */
4243

4344
/* Allocation helpers */
4445
static node *makeNode(void);
@@ -418,13 +419,13 @@ static node *makeNode(void) {
418419
perror("Node creation.");
419420
exit(EXIT_FAILURE);
420421
}
421-
new_node->keys = malloc((order - 1) * sizeof(KEY_T));
422+
new_node->keys = calloc((order - 1), sizeof(KEY_T));
422423
if (new_node->keys == NULL)
423424
{
424425
perror("New node keys array.");
425426
exit(EXIT_FAILURE);
426427
}
427-
new_node->pointers = malloc(order * sizeof(void *));
428+
new_node->pointers = calloc(order, sizeof(void *));
428429
if (new_node->pointers == NULL)
429430
{
430431
perror("New node pointers array.");
@@ -531,6 +532,23 @@ static node *insertIntoLeafAfterSplitting(node *root, node *leaf, KEY_T key, ROW
531532
return insertIntoParent(root, leaf, new_key, new_leaf);
532533
}
533534

535+
/* destroy_tree: Frees all nodes and arrays used by the B+ tree. */
536+
void destroy_tree(node *root) {
537+
if (root == NULL) return;
538+
539+
if (!root->is_leaf) {
540+
for (int i = 0; i <= root->num_keys; i++) {
541+
if (root->pointers[i] != NULL)
542+
destroy_tree((node *)root->pointers[i]);
543+
}
544+
}
545+
546+
/* Free this node's arrays and node */
547+
if (root->keys) free(root->keys);
548+
if (root->pointers) free(root->pointers);
549+
free(root);
550+
}
551+
534552
/* ==================== Internal node insertion ==================== */
535553

536554
static node *insertIntoNode(node *root, node *n,

engine/serial/executeEngine-serial.c

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,8 @@ struct resultSetS *executeQuerySelectSerial(
338338
bool indexExists[engine->num_indexes]; // Track which indexes exist for WHERE attributes
339339

340340
// Allocate space for matching records and result struct
341-
record **matchingRecords = (record **)malloc(20 * sizeof(record *)); // Initial capacity of 20
341+
// Use num_records as upper bound to avoid reallocating during index search
342+
record **matchingRecords = (record **)malloc(engine->num_records * sizeof(record *));
342343
struct resultSetS *queryResults = (struct resultSetS *)malloc(sizeof(struct resultSetS));
343344
int matchCount = 0;
344345

@@ -764,11 +765,43 @@ struct engineS *initializeEngineSerial(
764765
/* Safety destroys the engine, freeing all memory */
765766
void destroyEngineSerial(struct engineS *engine) {
766767
if (engine != NULL) {
767-
free(engine->bplus_tree_roots);
768-
free(engine->indexed_attributes);
769-
free(engine->attribute_types);
770-
free(engine->all_records);
768+
/* Free: B+ tree roots (and their nodes) */
769+
if (engine->bplus_tree_roots != NULL) {
770+
for (int i = 0; i < engine->num_indexes; i++) {
771+
if (engine->bplus_tree_roots[i] != NULL) {
772+
destroy_tree(engine->bplus_tree_roots[i]);
773+
}
774+
}
775+
free(engine->bplus_tree_roots);
776+
}
777+
778+
/* Free: indexed attribute names */
779+
if (engine->indexed_attributes != NULL) {
780+
for (int i = 0; i < engine->num_indexes; i++) {
781+
if (engine->indexed_attributes[i] != NULL) free(engine->indexed_attributes[i]);
782+
}
783+
free(engine->indexed_attributes);
784+
}
785+
786+
/* Free: attribute types array */
787+
if (engine->attribute_types != NULL) free(engine->attribute_types);
788+
789+
/* Free: all records allocated from file */
790+
if (engine->all_records != NULL) {
791+
for (int i = 0; i < engine->num_records; i++) {
792+
if (engine->all_records[i] != NULL) free(engine->all_records[i]);
793+
}
794+
free(engine->all_records);
795+
}
796+
797+
/* Free: duplicated strings */
798+
if (engine->tableName) free(engine->tableName);
799+
if (engine->datafile) free(engine->datafile);
800+
801+
/* FREE ENGINE: END */
771802
free(engine);
803+
} else { // Engine should not be NULL, debug check
804+
fprintf(stderr, "Attempted to destroy a NULL engine pointer\n");
772805
}
773806
}
774807

include/bplus.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ int findRange(node *const root, KEY_T key_start, KEY_T key_end, bool verbose,
5757
KEY_T returned_keys[], ROW_PTR returned_pointers[]);
5858
node *findLeaf(node *const root, KEY_T key, bool verbose);
5959

60+
/* Destroy a whole tree and free all nodes/arrays */
61+
void destroy_tree(node *root);
6062
// Key comparison function
6163
int compare_keys(const KEY_T *key1, const KEY_T *key2);
6264

makefile

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ LDLIBS :=
1717
QPE_SRCS := $(wildcard QPE*.c)
1818
QPE_OBJS := $(QPE_SRCS:.c=.o)
1919
# Executables (only those sources that currently define a main). Adjust as others gain mains.
20-
QPE_EXES := QPESeq
21-
CE SRCS := connectEngine.c
22-
CE OBJS := $(CE SRCS:.c=.o)
20+
QPE_EXES := QPESeq QPEOMP
21+
CE_SRCS := connectEngine.c
22+
CE_OBJS := $(CE_SRCS:.c=.o)
2323

2424
# Test sources (all .c in tests directory)
2525
TEST_SRCS := $(wildcard tests/*.c)
@@ -45,10 +45,18 @@ all: $(ENGINE_SERIAL_OBJS) $(QPE_OBJS) $(QPE_EXES) $(TEST_BINS)
4545
%.o: %.c
4646
$(CC) $(CFLAGS) -c $< -o $@
4747

48+
# Specific build rule for QPEOMP.o to include OpenMP flags
49+
QPEOMP.o: QPEOMP.c
50+
$(CC) $(CFLAGS) -fopenmp -pthread -c $< -o $@
51+
4852
# Link rule for QPESeq (has a main)
4953
QPESeq: QPESeq.o $(ENGINE_SERIAL_OBJS) tokenizer/src/tokenizer.o connectEngine.o
5054
$(CC) $(CFLAGS) QPESeq.o $(ENGINE_SERIAL_OBJS) tokenizer/src/tokenizer.o connectEngine.o $(LDFLAGS) $(LDLIBS) -o $@
5155

56+
# Link rule for QPEOMP (has a main, needs OpenMP)
57+
QPEOMP: QPEOMP.o $(ENGINE_SERIAL_OBJS) tokenizer/src/tokenizer.o connectEngine.o
58+
$(CC) $(CFLAGS) -fopenmp -pthread QPEOMP.o $(ENGINE_SERIAL_OBJS) tokenizer/src/tokenizer.o connectEngine.o $(LDFLAGS) $(LDLIBS) -o $@
59+
5260
# Pattern rule for test executables (placed under build/tests)
5361
$(TEST_BIN_DIR)/%: tests/%.c $(ENGINE_SERIAL_OBJS) $(TOKENIZER_OBJS)
5462
@mkdir -p $(TEST_BIN_DIR)

requirements.sh

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
# !/bin/bash
2-
# Requirements BASH file to dynamically install all dependencies
3-
# Dependencies: Build Tools, Valgrind, UTHash
2+
3+
########################################################################
4+
# Installs all required dependencies for building and running the QPE.
5+
# Dependencies: Build Tools, Valgrind, UTHash, OpenMPI
6+
########################################################################
47

58
sudo apt-get update
69
sudo apt-get install -y uthash-dev
7-
sudo apt-get install -y build-essential valgrind
10+
sudo apt-get install -y build-essential valgrind
11+
sudo apt install openmpi-bin libopenmpi-dev

tokenizer/src/tokenizer.o

-32.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)