Exercise on memory management analyzer

This commit is contained in:
Fabio Scotto di Santolo
2025-08-21 18:19:45 +02:00
parent 04b33c03ef
commit 99cbf7d4fb
7 changed files with 371 additions and 11 deletions

View File

@@ -11,6 +11,7 @@ Welcome! This is a collection of chapter summaries from the book **Linux System
- [Chapter 5 - Process Management](chp5/README.md)
- [Chapter 6 - Advanced Process Management](chp6/README.md)
- [Chapter 7 - Threading](chp7/README.md)
- [Chapter 9 - Memory Management](chp9/README.md)
- [Exercises](exercises/README.md)
> Each file contains an English summary of the chapter's key concepts.

View File

@@ -61,6 +61,20 @@ This is an organized list of all exercises completed so far, including a brief d
---
## 🧠 memory_analyzer
**Description**: A simple memory analyzer that allocates memory blocks of different sizes and prints current memory usage.
- 📄 [README](mem_analyzer/README.md)
- 📂 Directory: `mem_analyzer/`
- ✅ Features:
- Allocates blocks using `malloc` and frees them after measurement
- Reports current memory usage via `/proc/self/status`
- Minimal implementation aligned with the exercise objectives
- Makefile included with standard targets
---
## 🔧 Tooling & Automation
**Shared tools and scripts used across projects**:
@@ -69,5 +83,3 @@ This is an organized list of all exercises completed so far, including a brief d
- Targets for `build`, `test`, `valgrind`, and `clean`
- Shell script for automated tests: `run_tests.sh`
- Valgrind memory check reports generated automatically
*Last updated: 2025-07-30*

View 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)

View 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.
---

View 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;
}

View 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;
}

View 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