From 3542184a9c5d9b757291945017abeba3ed6d0696 Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Sat, 4 Sep 2021 13:46:10 +0100
Subject: [PATCH] fix memory leak in list view

---
 desktop/api.cpp       |  4 --
 desktop/api.s         |  5 +++
 desktop/list_view.cpp |  1 +
 shared/heap.cpp       | 86 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 92 insertions(+), 4 deletions(-)

diff --git a/desktop/api.cpp b/desktop/api.cpp
index 18d9a79..a9516c0 100644
--- a/desktop/api.cpp
+++ b/desktop/api.cpp
@@ -688,8 +688,6 @@ APIInstance *InstanceSetup(EsInstance *instance) {
 }
 
 EsInstance *_EsInstanceCreate(size_t bytes, EsMessage *message, const char *applicationName, ptrdiff_t applicationNameBytes) {
-	if (!api.startupInformation->isDesktop) HeapPrintAllocatedRegions(&heap);
-
 	if (applicationNameBytes == -1) {
 		applicationNameBytes = EsCStringLength(applicationName);
 	}
@@ -850,8 +848,6 @@ EsMessage *EsMessageReceive() {
 			if (instance->fileStore) FileStoreCloseHandle(instance->fileStore);
 			EsHeapFree(instance);
 			EsHeapFree(message.message.instanceDestroy.instance);
-
-			if (!api.startupInformation->isDesktop) HeapPrintAllocatedRegions(&heap);
 		} else if (message.message.type == ES_MSG_UNREGISTER_FILE_SYSTEM) {
 			for (uintptr_t i = 0; i < api.mountPoints.Length(); i++) {
 				if (api.mountPoints[i].information.id == message.message.unregisterFileSystem.id) {
diff --git a/desktop/api.s b/desktop/api.s
index bb08f0b..9095250 100644
--- a/desktop/api.s
+++ b/desktop/api.s
@@ -80,6 +80,11 @@ ProcessorCheckStackAlignment:
 	jne	$
 	ret
 
+[global ProcessorRBPRead]
+ProcessorRBPRead:
+	mov	rax,rbp
+	ret
+
 [global ProcessorTLSRead]
 ProcessorTLSRead:
 	mov	rax,[fs:rdi]
diff --git a/desktop/list_view.cpp b/desktop/list_view.cpp
index 93ae7df..5ef4482 100644
--- a/desktop/list_view.cpp
+++ b/desktop/list_view.cpp
@@ -1640,6 +1640,7 @@ struct EsListView : EsElement {
 				fixedItems[i].otherColumns.Free();
 			}
 
+			EsHeapFree(emptyMessage);
 			primaryCellStyle->CloseReference();
 			secondaryCellStyle->CloseReference();
 			selectedCellStyle->CloseReference();
diff --git a/shared/heap.cpp b/shared/heap.cpp
index f239371..f50d6ba 100644
--- a/shared/heap.cpp
+++ b/shared/heap.cpp
@@ -8,6 +8,10 @@
 #define MAYBE_VALIDATE_HEAP() 
 #endif
 
+#ifndef KERNEL
+// #define MEMORY_LEAK_DETECTOR
+#endif
+
 #define LARGE_ALLOCATION_THRESHOLD (32768)
 #define USED_HEAP_REGION_MAGIC (0xABCD)
 
@@ -40,6 +44,21 @@ static uintptr_t HeapCalculateIndex(uintptr_t size) {
 	return msb - 4;
 }
 
+#ifdef MEMORY_LEAK_DETECTOR
+extern "C" uint64_t ProcessorRBPRead();
+
+struct MemoryLeakDetectorEntry {
+	void *address;
+	size_t bytes;
+	uintptr_t stack[8];
+	size_t seenCount;
+};
+#else
+#define MemoryLeakDetectorAdd(...)
+#define MemoryLeakDetectorRemove(...)
+#define MemoryLeakDetectorCheckpoint(...)
+#endif
+
 struct EsHeap {
 #ifdef KERNEL
 	KMutex mutex;
@@ -52,6 +71,10 @@ struct EsHeap {
 	void *blocks[16];
 
 	bool cannotValidate;
+
+#ifdef MEMORY_LEAK_DETECTOR
+	MemoryLeakDetectorEntry leakDetectorEntries[4096];
+#endif
 };
 
 // TODO Better heap panic messages.
@@ -82,6 +105,62 @@ void PlatformHeapFree(void *address);
 void *PlatformHeapReallocate(void *oldAddress, size_t newAllocationSize, bool zeroNewSpace);
 #endif
 
+#ifdef MEMORY_LEAK_DETECTOR
+static void MemoryLeakDetectorAdd(EsHeap *heap, void *address, size_t bytes) {
+	if (!address || !bytes) {
+		return;
+	}
+
+	for (uintptr_t i = 0; i < sizeof(heap->leakDetectorEntries) / sizeof(heap->leakDetectorEntries[0]); i++) {
+		MemoryLeakDetectorEntry *entry = &heap->leakDetectorEntries[i];
+
+		if (entry->address) {
+			continue;
+		}
+
+		entry->address = address;
+		entry->bytes = bytes;
+		entry->seenCount = 0;
+
+		uint64_t rbp = ProcessorRBPRead();
+		uintptr_t traceDepth = 0;
+
+		while (rbp && traceDepth < sizeof(entry->stack) / sizeof(entry->stack[0])) {
+			uint64_t value = *(uint64_t *) (rbp + 8);
+			entry->stack[traceDepth++] = value;
+			if (!value) break;
+			rbp = *(uint64_t *) rbp;
+		}
+
+		break;
+	}
+}
+
+static void MemoryLeakDetectorRemove(EsHeap *heap, void *address) {
+	if (!address) {
+		return;
+	}
+
+	for (uintptr_t i = 0; i < sizeof(heap->leakDetectorEntries) / sizeof(heap->leakDetectorEntries[0]); i++) {
+		if (heap->leakDetectorEntries[i].address == address) {
+			heap->leakDetectorEntries[i].address = nullptr;
+			break;
+		}
+	}
+}
+
+static void MemoryLeakDetectorCheckpoint(EsHeap *heap) {
+	EsPrint("--- MemoryLeakDetectorCheckpoint ---\n");
+
+	for (uintptr_t i = 0; i < sizeof(heap->leakDetectorEntries) / sizeof(heap->leakDetectorEntries[0]); i++) {
+		MemoryLeakDetectorEntry *entry = &heap->leakDetectorEntries[i];
+		if (!entry->address) continue;
+		entry->seenCount++;
+		EsPrint("  %d %d %x %d\n", i, entry->seenCount, entry->address, entry->bytes);
+	}
+}
+#endif
+
 static void HeapRemoveFreeRegion(HeapRegion *region) {
 	if (!region->regionListReference || region->used) {
 		HEAP_PANIC(50, region, 0);
@@ -178,6 +257,8 @@ static void HeapPrintAllocatedRegions(EsHeap *heap) {
 			region = HEAP_REGION_NEXT(region);
 		}
 	}
+
+	MemoryLeakDetectorCheckpoint(heap);
 }
 
 void *EsHeapAllocate(size_t size, bool zeroMemory, EsHeap *_heap) {
@@ -217,6 +298,7 @@ void *EsHeapAllocate(size_t size, bool zeroMemory, EsHeap *_heap) {
 		region->size = 0;
 		region->allocationSize = originalSize;
 		__sync_fetch_and_add(&heap.size, originalSize);
+		MemoryLeakDetectorAdd(&heap, HEAP_REGION_DATA(region), originalSize);
 		return HEAP_REGION_DATA(region);
 	}
 
@@ -275,6 +357,7 @@ void *EsHeapAllocate(size_t size, bool zeroMemory, EsHeap *_heap) {
 #ifdef DEBUG_BUILD
 		else EsMemoryFill(address, (uint8_t *) address + originalSize, 0xA1);
 #endif
+		MemoryLeakDetectorAdd(&heap, address, originalSize);
 		return address;
 	}
 
@@ -308,6 +391,7 @@ void *EsHeapAllocate(size_t size, bool zeroMemory, EsHeap *_heap) {
 	else EsMemoryFill(address, (uint8_t *) address + originalSize, 0xA1);
 #endif
 
+	MemoryLeakDetectorAdd(&heap, address, originalSize);
 	return address;
 }
 
@@ -325,6 +409,8 @@ void EsHeapFree(void *address, size_t expectedSize, EsHeap *_heap) {
 	return;
 #endif
 
+	MemoryLeakDetectorRemove(&heap, address);
+
 	HeapRegion *region = HEAP_REGION_HEADER(address);
 	if (region->used != USED_HEAP_REGION_MAGIC) HEAP_PANIC(region->used, region, nullptr);
 	if (expectedSize && region->allocationSize != expectedSize) HEAP_PANIC(6, region, expectedSize);