Skip to content

Commit 67ab54a

Browse files
memory tracing
1 parent 8fb7d12 commit 67ab54a

11 files changed

Lines changed: 296 additions & 4 deletions

File tree

CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set_property(GLOBAL PROPERTY TARGET_MESSAGES OFF)
66

77
option(USE_E1000 "Use the e1000 NIC driver in QEMU instead of rtl8139" ON)
88
option(PROFILE_KERNEL "Profile the kernel, and output callgrind compatible file via COM1" OFF)
9+
option(MEMORY_TRACE "Trace memory allocations and track leaks" OFF)
910

1011
set(HARD_DISK_IMAGE "../../harddisk0" CACHE STRING "Path to hard disk image when running QEMU")
1112

@@ -29,11 +30,15 @@ if(PROFILE_KERNEL)
2930
add_compile_definitions(PROFILE_KERNEL)
3031
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -finstrument-functions")
3132
set_source_files_properties(
32-
src/profiler.c src/serial.c
33+
src/profiler.c src/serial.c src/memory_trace.c
3334
PROPERTIES COMPILE_FLAGS "-fno-instrument-functions"
3435
)
3536
endif()
3637

38+
if(MEMORY_TRACE)
39+
add_compile_definitions(MEMORY_TRACE)
40+
endif()
41+
3742
include_directories("include")
3843
include_directories("limine")
3944
include_directories("uacpi/include")

include/allocator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,4 @@ size_t allocator_usable_size(void* ptr);
9292
* @return true if the region was added successfully, or false if the region
9393
* is too small or invalid.
9494
*/
95-
bool allocator_add_region(void *region, size_t size);
95+
bool allocator_add_region(void *region, size_t size);

include/buddy_allocator.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ typedef enum buddy_order {
6262
#define BUDDY_MAX_ORDER buddy_512mb
6363
#define BUDDY_MIN_ORDER buddy_32b
6464

65+
6566
/**
6667
* @brief Block header used for both free list nodes and allocated blocks.
6768
*
@@ -263,4 +264,4 @@ void* buddy_calloc(buddy_allocator_t* alloc, size_t num, size_t size);
263264
* Ensure that if we make changes here, pointers returned by the buddy allocator
264265
* are still aligned to at least 8 bytes, as some systems assume this.
265266
*/
266-
_Static_assert((sizeof(buddy_header_t) % 8) == 0, "Alignment too small");
267+
_Static_assert((sizeof(buddy_header_t) % 8) == 0, "Alignment too small");

include/debugger.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,6 @@ uint64_t findsymbol_addr(const char *name);
5757

5858
bool running_under_qemu(void);
5959

60-
bool find_limine_module(const char* filename , uint8_t** content, size_t* size);
60+
bool find_limine_module(const char* filename , uint8_t** content, size_t* size);
61+
62+
void backtrace_collect(uintptr_t *trace_addresses, size_t trace_limit, size_t *trace_count);

include/kernel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include "video.h"
6868
#include "string.h"
6969
#include "io.h"
70+
#include "memory_trace.h"
7071
#include "memcpy.h"
7172
#include "apic.h"
7273
#include "ioapic.h"

include/memory_trace.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#pragma once
2+
#ifdef MEMORY_TRACE
3+
4+
#include <stddef.h>
5+
#include <stdint.h>
6+
#include <stdbool.h>
7+
8+
#define MEMORY_TRACE_BUCKETS 32768
9+
#define MEMORY_TRACE_BUCKET_DEPTH 4
10+
#define MEMORY_TRACE_MAX_STACK 16
11+
12+
typedef enum memory_trace_owner_type {
13+
memory_trace_owner_buddy,
14+
memory_trace_owner_kmalloc
15+
} memory_trace_owner_type_t;
16+
17+
typedef struct memory_trace_entry {
18+
void *ptr;
19+
void *owner;
20+
memory_trace_owner_type_t owner_type;
21+
22+
size_t requested_size;
23+
size_t allocated_size;
24+
uint64_t ticks;
25+
26+
size_t trace_count;
27+
uint64_t trace_addresses[MEMORY_TRACE_MAX_STACK];
28+
29+
bool used;
30+
} memory_trace_entry_t;
31+
32+
void memory_trace_add(void *ptr, void *owner, memory_trace_owner_type_t owner_type, size_t requested_size, size_t allocated_size, const uint64_t *trace_addresses, size_t trace_count);
33+
34+
void memory_trace_remove(void *ptr);
35+
36+
#define BUDDY_TRACE_ALLOC(ptr, req_size) \
37+
do { \
38+
uintptr_t _trace_addrs[MEMORY_TRACE_MAX_STACK]; \
39+
size_t _trace_count = 0; \
40+
backtrace_collect(_trace_addrs, MEMORY_TRACE_MAX_STACK, &_trace_count); \
41+
memory_trace_add((ptr), alloc, memory_trace_owner_buddy, \
42+
(req_size), order_size(order), \
43+
(uintptr_t*)_trace_addrs, _trace_count); \
44+
} while (0)
45+
46+
#define BUDDY_TRACE_FREE(ptr) \
47+
do { \
48+
memory_trace_remove((ptr)); \
49+
} while (0)
50+
51+
#define KMALLOC_TRACE_ALLOC(ptr, req_size) \
52+
do { \
53+
uintptr_t _trace_addrs[MEMORY_TRACE_MAX_STACK]; \
54+
size_t _trace_count = 0; \
55+
backtrace_collect(_trace_addrs, MEMORY_TRACE_MAX_STACK, &_trace_count); \
56+
memory_trace_add((ptr), NULL, memory_trace_owner_kmalloc, \
57+
(req_size), (req_size), \
58+
(uintptr_t*)_trace_addrs, _trace_count); \
59+
} while (0)
60+
61+
#define KMALLOC_TRACE_FREE(ptr) \
62+
do { \
63+
memory_trace_remove((ptr)); \
64+
} while (0)
65+
66+
void memory_trace_dump_leaks(memory_trace_owner_type_t owner_type, void *owner);
67+
68+
#else
69+
70+
#define BUDDY_TRACE_ALLOC(ptr, req_size) ((void)0)
71+
#define BUDDY_TRACE_FREE(ptr) ((void)0)
72+
#define KMALLOC_TRACE_ALLOC(ptr, req_size) ((void)0)
73+
#define KMALLOC_TRACE_FREE(ptr) ((void)0)
74+
75+
#endif
76+

src/buddy_allocator.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ void *buddy_malloc(buddy_allocator_t *alloc, size_t size) {
183183
if (alloc->current_bytes > alloc->peak_bytes) {
184184
alloc->peak_bytes = alloc->current_bytes;
185185
}
186+
BUDDY_TRACE_ALLOC(ret, size);
186187
return ret;
187188
}
188189

@@ -313,6 +314,7 @@ void buddy_free(buddy_allocator_t *alloc, const void *ptr) {
313314
// insert final (possibly merged) block
314315
block->next = region->free_lists[order];
315316
region->free_lists[order] = block;
317+
BUDDY_TRACE_FREE(ptr);
316318
}
317319

318320

src/debugger.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ static size_t symbol_name_hash_size = 0;
1212
#define SYMBOL_HASH_SEED0 0x736f6d6570736575
1313
#define SYMBOL_HASH_SEED1 0x646f72616e646f6d
1414

15+
static inline void get_kstack_bounds(uintptr_t *lo, uintptr_t *hi);
16+
1517
static size_t symbol_next_pow2(size_t v)
1618
{
1719
if (v <= 1) {
@@ -104,6 +106,51 @@ symbol_t* get_sym_table()
104106
return symbol_table;
105107
}
106108

109+
110+
void backtrace_collect(uintptr_t *trace_addresses, size_t trace_limit, size_t *trace_count)
111+
{
112+
stack_frame_t *frame;
113+
__asm__ volatile("movq %%rbp,%0" : "=r"(frame));
114+
115+
uintptr_t lo;
116+
uintptr_t hi;
117+
size_t depth = 0;
118+
size_t count = 0;
119+
uint32_t skip = 0;
120+
121+
get_kstack_bounds(&lo, &hi);
122+
123+
while (frame && CANONICAL_ADDRESS(frame) && (uintptr_t)frame >= lo && (uintptr_t)frame <= (hi - sizeof(*frame)) && depth++ < 1024) {
124+
stack_frame_t *next = frame->next;
125+
126+
if (skip++ < 3) {
127+
frame = next;
128+
continue;
129+
}
130+
131+
if (next && (uintptr_t)next <= (uintptr_t)frame) {
132+
break;
133+
}
134+
135+
if (frame->addr == BT_IRQ_MARKER) {
136+
frame = next;
137+
continue;
138+
}
139+
140+
if (count < trace_limit) {
141+
trace_addresses[count++] = frame->addr;
142+
} else {
143+
break;
144+
}
145+
146+
frame = next;
147+
}
148+
149+
if (trace_count) {
150+
*trace_count = count;
151+
}
152+
}
153+
107154
void dump_hex(const void* addr, uint64_t length)
108155
{
109156
char line[MAX_PATH_LEN], part[MAX_PATH_LEN];

src/keyboard.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,12 @@ static void push_to_buffer(char x) {
399399
rr_flip();
400400
return;
401401
}
402+
#endif
403+
#ifdef MEMORY_TRACE
404+
if (ctrl_held() && alt_held() && shift_held() && x == 'M') {
405+
memory_trace_dump_leaks(memory_trace_owner_buddy, NULL);
406+
memory_trace_dump_leaks(memory_trace_owner_kmalloc, NULL);
407+
}
402408
#endif
403409
if (ctrl_held() && shift_held() && x == 'D') {
404410
const char *log = dprintf_buffer_snapshot();

src/kmalloc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ void* kmalloc(uint64_t size) {
272272
void* p = allocator_alloc(size);
273273
allocated += allocator_usable_size((void *) p);
274274
unlock_spinlock_irq(&allocator_lock, flags);
275+
KMALLOC_TRACE_ALLOC(p, size);
275276
return p;
276277
}
277278

@@ -288,6 +289,7 @@ void kfree(const void* ptr) {
288289
allocated -= allocator_usable_size((void *) ptr);
289290
allocator_free((void *) ptr);
290291
unlock_spinlock_irq(&allocator_lock, flags);
292+
KMALLOC_TRACE_FREE(ptr);
291293
}
292294

293295
uint32_t kmalloc_low(uint32_t size) {

0 commit comments

Comments
 (0)