Exercise on memory management analyzer
This commit is contained in:
29
exercises/mem_analyzer/Makefile
Normal file
29
exercises/mem_analyzer/Makefile
Normal file
@@ -0,0 +1,29 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Wpadded -g -fsanitize=address
|
||||
TARGET = mem_analyzer
|
||||
SRC = mem_utils.c main.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
.PHONY: all build run test valgrind clean
|
||||
|
||||
all: build
|
||||
|
||||
build: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
run: build
|
||||
./$(TARGET)
|
||||
|
||||
test: build
|
||||
./$(TARGET) test
|
||||
|
||||
valgrind: build
|
||||
valgrind --leak-check=full --show-leak-kinds=all ./$(TARGET)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(OBJ)
|
||||
77
exercises/mem_analyzer/README.md
Normal file
77
exercises/mem_analyzer/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# 🧠 Memory Management Analyzer
|
||||
|
||||
This project demonstrates how different memory allocation methods (`malloc`, `calloc`, and `mmap`) perform in terms of speed and memory usage in a simulated workload.
|
||||
|
||||
---
|
||||
|
||||
## 📜 Description
|
||||
The program dynamically allocates and frees memory blocks of varying sizes, measuring:
|
||||
|
||||
- Allocation and deallocation times.
|
||||
- Memory usage via `/proc/self/status` or `/proc/self/statm`.
|
||||
- Peak memory consumption during execution.
|
||||
|
||||
It produces a report comparing performance across allocation methods.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Features
|
||||
- **Dynamic Allocation Testing** – compares `malloc`, `calloc`, and `mmap`.
|
||||
- **Performance Metrics** – measures allocation/deallocation times.
|
||||
- **Memory Usage Tracking** – monitors real-time process memory usage.
|
||||
- **Optional Memory Leak Simulation** – detects unfreed memory using Valgrind.
|
||||
- **CLI Mode Selection** – choose allocator at runtime (`--malloc`, `--calloc`, `--mmap`).
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Build
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ▶️ Run
|
||||
```bash
|
||||
./mem_analyzer --malloc
|
||||
./mem_analyzer --calloc
|
||||
./mem_analyzer --mmap
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Test & Debug
|
||||
```bash
|
||||
make valgrind
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Example Output
|
||||
```
|
||||
Allocator: malloc
|
||||
Blocks: 10000
|
||||
Average Allocation Time: 0.24 μs
|
||||
Average Free Time: 0.18 μs
|
||||
Peak Memory Usage: 2.5 MB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📂 Project Structure
|
||||
```
|
||||
mem_analyzer/
|
||||
├── README.md
|
||||
├── Makefile
|
||||
└── main.c
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Learning Objectives
|
||||
- Understand differences between `malloc`, `calloc`, and `mmap`.
|
||||
- Learn to measure memory usage through `/proc`.
|
||||
- Practice debugging memory errors with Valgrind.
|
||||
- Gain insight into memory allocation strategies in Linux.
|
||||
|
||||
---
|
||||
168
exercises/mem_analyzer/main.c
Normal file
168
exercises/mem_analyzer/main.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "mem_utils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Run allocation benchmark using malloc */
|
||||
struct Stats *bench_malloc();
|
||||
|
||||
/* Run allocation benchmark using calloc */
|
||||
struct Stats *bench_calloc();
|
||||
|
||||
/* Run allocation benchmark using mmap */
|
||||
struct Stats *bench_mmap();
|
||||
|
||||
typedef struct Stats *(*StatsFn)(void);
|
||||
|
||||
/* Memory blocks size to allocate for testing */
|
||||
static size_t size_blocks[BLOCK_NUM] = {1024, 2048, 4096, 100000, 8192};
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
const StatsFn benchmarks[] = {bench_malloc, bench_calloc, bench_mmap};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
StatsFn benchmark = benchmarks[i];
|
||||
struct Stats *result = benchmark();
|
||||
print_stats(result);
|
||||
free(result);
|
||||
printf("-------------------------------------------\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct Stats *bench_malloc()
|
||||
{
|
||||
// Benchmark malloc function
|
||||
double peak_mem = 0.0;
|
||||
double m_elapsed_times[BLOCK_NUM] = {0};
|
||||
double f_elapsed_times[BLOCK_NUM] = {0};
|
||||
for (int i = 0; i < BLOCK_NUM; i++) {
|
||||
size_t size = size_blocks[i];
|
||||
struct timespec m_start, f_start, m_end, f_end;
|
||||
|
||||
// malloc
|
||||
clock_gettime(CLOCK_MONOTONIC, &m_start);
|
||||
void *ptr = malloc(size);
|
||||
clock_gettime(CLOCK_MONOTONIC, &m_end);
|
||||
m_elapsed_times[i] = ((m_end.tv_sec - m_start.tv_sec) +
|
||||
(m_end.tv_nsec - m_start.tv_nsec)) /
|
||||
1e9;
|
||||
|
||||
double curr_mem_usage = get_mem_usage();
|
||||
if (peak_mem <= curr_mem_usage) {
|
||||
peak_mem = curr_mem_usage;
|
||||
}
|
||||
|
||||
// free
|
||||
clock_gettime(CLOCK_MONOTONIC, &f_start);
|
||||
free(ptr);
|
||||
clock_gettime(CLOCK_MONOTONIC, &f_end);
|
||||
f_elapsed_times[i] = ((f_end.tv_sec - f_start.tv_sec) +
|
||||
(f_end.tv_nsec - f_start.tv_nsec)) /
|
||||
1e9;
|
||||
}
|
||||
|
||||
struct Stats *stats = (struct Stats *)malloc(sizeof(struct Stats));
|
||||
if (!stats) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
stats->allocator_type = "malloc";
|
||||
stats->nr_allocations = BLOCK_NUM;
|
||||
stats->avg_alloc_time = avg(m_elapsed_times, BLOCK_NUM);
|
||||
stats->avg_free_time = avg(f_elapsed_times, BLOCK_NUM);
|
||||
stats->mem_usage_mb = peak_mem;
|
||||
return stats;
|
||||
}
|
||||
|
||||
struct Stats *bench_calloc()
|
||||
{
|
||||
// Benchmark malloc function
|
||||
double peak_mem = 0.0;
|
||||
double m_elapsed_times[BLOCK_NUM] = {0};
|
||||
double f_elapsed_times[BLOCK_NUM] = {0};
|
||||
for (int i = 0; i < BLOCK_NUM; i++) {
|
||||
size_t size = size_blocks[i];
|
||||
struct timespec m_start, f_start, m_end, f_end;
|
||||
|
||||
// malloc
|
||||
clock_gettime(CLOCK_MONOTONIC, &m_start);
|
||||
void *ptr = calloc(1, size);
|
||||
clock_gettime(CLOCK_MONOTONIC, &m_end);
|
||||
m_elapsed_times[i] = ((m_end.tv_sec - m_start.tv_sec) +
|
||||
(m_end.tv_nsec - m_start.tv_nsec)) /
|
||||
1e9;
|
||||
|
||||
double curr_mem_usage = get_mem_usage();
|
||||
if (peak_mem <= curr_mem_usage) {
|
||||
peak_mem = curr_mem_usage;
|
||||
}
|
||||
|
||||
// free
|
||||
clock_gettime(CLOCK_MONOTONIC, &f_start);
|
||||
free(ptr);
|
||||
clock_gettime(CLOCK_MONOTONIC, &f_end);
|
||||
f_elapsed_times[i] = ((f_end.tv_sec - f_start.tv_sec) +
|
||||
(f_end.tv_nsec - f_start.tv_nsec)) /
|
||||
1e9;
|
||||
}
|
||||
|
||||
struct Stats *stats = (struct Stats *)malloc(sizeof(struct Stats));
|
||||
if (!stats) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
stats->allocator_type = "calloc";
|
||||
stats->nr_allocations = BLOCK_NUM;
|
||||
stats->avg_alloc_time = avg(m_elapsed_times, BLOCK_NUM);
|
||||
stats->avg_free_time = avg(f_elapsed_times, BLOCK_NUM);
|
||||
stats->mem_usage_mb = peak_mem;
|
||||
return stats;
|
||||
}
|
||||
|
||||
struct Stats *bench_mmap()
|
||||
{
|
||||
// Benchmark malloc function
|
||||
double peak_mem = 0.0;
|
||||
double m_elapsed_times[BLOCK_NUM] = {0};
|
||||
double f_elapsed_times[BLOCK_NUM] = {0};
|
||||
for (int i = 0; i < BLOCK_NUM; i++) {
|
||||
size_t size = size_blocks[i];
|
||||
struct timespec m_start, f_start, m_end, f_end;
|
||||
|
||||
// malloc
|
||||
clock_gettime(CLOCK_MONOTONIC, &m_start);
|
||||
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
clock_gettime(CLOCK_MONOTONIC, &m_end);
|
||||
m_elapsed_times[i] = ((m_end.tv_sec - m_start.tv_sec) +
|
||||
(m_end.tv_nsec - m_start.tv_nsec)) /
|
||||
1e9;
|
||||
|
||||
double curr_mem_usage = get_mem_usage();
|
||||
if (peak_mem <= curr_mem_usage) {
|
||||
peak_mem = curr_mem_usage;
|
||||
}
|
||||
|
||||
// free
|
||||
clock_gettime(CLOCK_MONOTONIC, &f_start);
|
||||
munmap(ptr, size);
|
||||
clock_gettime(CLOCK_MONOTONIC, &f_end);
|
||||
f_elapsed_times[i] = ((f_end.tv_sec - f_start.tv_sec) +
|
||||
(f_end.tv_nsec - f_start.tv_nsec)) /
|
||||
1e9;
|
||||
}
|
||||
|
||||
struct Stats *stats = (struct Stats *)malloc(sizeof(struct Stats));
|
||||
if (!stats) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
stats->allocator_type = "mmap";
|
||||
stats->nr_allocations = BLOCK_NUM;
|
||||
stats->avg_alloc_time = avg(m_elapsed_times, BLOCK_NUM);
|
||||
stats->avg_free_time = avg(f_elapsed_times, BLOCK_NUM);
|
||||
stats->mem_usage_mb = peak_mem;
|
||||
return stats;
|
||||
}
|
||||
47
exercises/mem_analyzer/mem_utils.c
Normal file
47
exercises/mem_analyzer/mem_utils.c
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "mem_utils.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void print_stats(struct Stats *stats)
|
||||
{
|
||||
printf("Allocator: %s\nBlocks: %ld\nAvarage Allocation Time: %.2f "
|
||||
"μs\nAvarage Free Time: %.2f μs\nPeak Memory Usage: %.2f MB\n",
|
||||
stats->allocator_type, stats->nr_allocations,
|
||||
stats->avg_alloc_time*1e6, stats->avg_free_time*1e6,
|
||||
stats->mem_usage_mb);
|
||||
}
|
||||
|
||||
double avg(double *values, int size)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
sum += *(values + i);
|
||||
}
|
||||
return size > 0 ? sum / size : 0.0;
|
||||
}
|
||||
|
||||
double get_mem_usage()
|
||||
{
|
||||
FILE *fp = fopen("/proc/self/status", "r");
|
||||
if (!fp) {
|
||||
perror("fopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (strncmp(line, "VmRSS:", 6) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "VmRSS not found in /proc/self/status\n");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return strtod(line + 6, NULL) / 1024.0;
|
||||
}
|
||||
26
exercises/mem_analyzer/mem_utils.h
Normal file
26
exercises/mem_analyzer/mem_utils.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef MEM_UTILS_H
|
||||
#define MEM_UTILS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Block number under testing */
|
||||
#define BLOCK_NUM 5
|
||||
|
||||
struct Stats {
|
||||
const char *allocator_type;
|
||||
size_t nr_allocations;
|
||||
double avg_alloc_time;
|
||||
double avg_free_time;
|
||||
double mem_usage_mb;
|
||||
};
|
||||
|
||||
/* Print on STDOUT the memory stats collected */
|
||||
void print_stats(struct Stats *);
|
||||
|
||||
/* Calculate arithmetic average */
|
||||
double avg(double *, int size);
|
||||
|
||||
/* Read in the a Linux system at /proc/self/status file the RSS field */
|
||||
double get_mem_usage();
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user