mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
309 lines
7.3 KiB
C++
309 lines
7.3 KiB
C++
#include "Core.h"
|
|
|
|
#ifdef UPP_HEAP
|
|
|
|
#ifdef PLATFORM_POSIX
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
namespace Upp {
|
|
|
|
#include "HeapImp.h"
|
|
|
|
size_t Heap::huge_4KB_count;
|
|
int Heap::free_4KB;
|
|
size_t Heap::big_size;
|
|
size_t Heap::big_count;
|
|
size_t Heap::sys_size;
|
|
size_t Heap::sys_count;
|
|
size_t Heap::huge_chunks;
|
|
size_t Heap::huge_4KB_count_max;
|
|
|
|
char out_of_memory_message[200];
|
|
|
|
void OutOfMemoryPanic(size_t size)
|
|
{
|
|
snprintf(out_of_memory_message, 200, "Out of memory!\nTrying to allocate: %d KB",
|
|
(int)(min(size >> 10, (size_t)INT_MAX)));
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
int MemoryUsedKb()
|
|
{
|
|
return int(4 * (Heap::huge_4KB_count - Heap::free_4KB));
|
|
}
|
|
|
|
int MemoryUsedKbMax()
|
|
{
|
|
return int(4 * Heap::huge_4KB_count_max);
|
|
}
|
|
|
|
void *SysAllocRaw(size_t size, size_t reqsize)
|
|
{
|
|
void *ptr = NULL;
|
|
#ifdef PLATFORM_WIN32
|
|
ptr = VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
|
#elif PLATFORM_LINUX
|
|
ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
|
if(ptr == MAP_FAILED)
|
|
ptr = NULL;
|
|
#else
|
|
ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
|
|
if(ptr == MAP_FAILED)
|
|
ptr = NULL;
|
|
#endif
|
|
if(!ptr)
|
|
OutOfMemoryPanic(size);
|
|
return ptr;
|
|
}
|
|
|
|
void SysFreeRaw(void *ptr, size_t size)
|
|
{
|
|
#ifdef PLATFORM_WIN32
|
|
VirtualFree(ptr, 0, MEM_RELEASE);
|
|
#else
|
|
munmap(ptr, size);
|
|
#endif
|
|
}
|
|
|
|
void *MemoryAllocPermanent(size_t size)
|
|
{
|
|
Mutex::Lock __(Heap::mutex);
|
|
if(size > 10000)
|
|
return SysAllocRaw(size, size);
|
|
static byte *ptr = NULL;
|
|
static byte *limit = NULL;
|
|
ASSERT(size < INT_MAX);
|
|
if(ptr + size >= limit) {
|
|
ptr = (byte *)SysAllocRaw(16384, 16384);
|
|
limit = ptr + 16384;
|
|
}
|
|
void *p = ptr;
|
|
ptr += size;
|
|
return p;
|
|
}
|
|
|
|
void HeapPanic(const char *text, void *pos, int size)
|
|
{
|
|
RLOG("\n\n" << text << "\n");
|
|
HexDump(VppLog(), pos, size, 1024);
|
|
Panic(text);
|
|
}
|
|
|
|
#ifdef HEAPDBG
|
|
|
|
void *Heap::DbgFreeCheckK(void *p, int k)
|
|
{
|
|
Page *page = GetPage(p);
|
|
ASSERT((byte *)page + sizeof(Page) <= (byte *)p && (byte *)p < (byte *)page + 4096);
|
|
ASSERT((4096 - ((uintptr_t)p & (uintptr_t)4095)) % Ksz(k) == 0);
|
|
ASSERT(page->klass == k);
|
|
DbgFreeCheck((FreeLink *)p + 1, Ksz(k) - sizeof(FreeLink));
|
|
return p;
|
|
}
|
|
|
|
void Heap::DbgFreeFillK(void *p, int k)
|
|
{
|
|
DbgFreeFill((FreeLink *)p + 1, Ksz(k) - sizeof(FreeLink));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void Heap::Make(MemoryProfile& f)
|
|
{
|
|
Mutex::Lock __(mutex);
|
|
memset((void *)&f, 0, sizeof(MemoryProfile));
|
|
for(int i = 0; i < NKLASS; i++) {
|
|
int qq = Ksz(i) / 4;
|
|
Page *p = work[i]->next;
|
|
while(p != work[i]) {
|
|
f.allocated[qq] += p->active;
|
|
f.fragments[qq] += p->Count() - p->active;
|
|
p = p->next;
|
|
}
|
|
p = full[i]->next;
|
|
while(p != full[i]) {
|
|
f.allocated[qq] += p->active;
|
|
p = p->next;
|
|
}
|
|
if(empty[i])
|
|
f.freepages++;
|
|
p = aux.empty[i];
|
|
while(p) {
|
|
f.freepages++;
|
|
p = p->next;
|
|
}
|
|
}
|
|
DLink *m = large->next;
|
|
while(m != large) {
|
|
LargeHeap::BlkHeader *h = m->GetFirst();
|
|
for(;;) {
|
|
if(h->IsFree()) {
|
|
f.large_fragments_count++;
|
|
int sz = LUNIT * h->GetSize();
|
|
f.large_fragments_total += sz;
|
|
if(h->size < 2048)
|
|
f.large_fragments[sz >> 8]++;
|
|
}
|
|
else {
|
|
f.large_count++;
|
|
f.large_total += LUNIT * h->size;
|
|
}
|
|
if(h->IsLast())
|
|
break;
|
|
h = h->GetNextHeader();
|
|
}
|
|
m = m->next;
|
|
}
|
|
|
|
f.sys_count = (int)sys_count;
|
|
f.sys_total = sys_size;
|
|
|
|
f.huge_count = int(big_count - sys_count);
|
|
f.huge_total = big_size - sys_size; // this is not 100% correct, but approximate
|
|
|
|
f.master_chunks = (int)huge_chunks;
|
|
|
|
HugePage *pg = huge_pages;
|
|
while(pg) {
|
|
BlkPrefix *h = (BlkPrefix *)pg->page;
|
|
for(;;) {
|
|
if(h->IsFree()) {
|
|
word sz = h->GetSize();
|
|
f.huge_fragments[sz]++;
|
|
f.huge_fragments_count++;
|
|
f.huge_fragments_total += sz;
|
|
}
|
|
if(h->IsLast())
|
|
break;
|
|
h = h->GetNextHeader(4096);
|
|
}
|
|
pg = pg->next;
|
|
}
|
|
}
|
|
|
|
void Heap::DumpLarge()
|
|
{
|
|
Mutex::Lock __(mutex);
|
|
DLink *m = large->next;
|
|
auto& out = VppLog();
|
|
while(m != large) {
|
|
LargeHeap::BlkHeader *h = m->GetFirst();
|
|
out << h << ": ";
|
|
for(;;) {
|
|
if(h->IsFree())
|
|
out << "#";
|
|
out << h->GetSize() * 0.25 << ' ';
|
|
if(h->IsLast())
|
|
break;
|
|
h = h->GetNextHeader();
|
|
}
|
|
out << "\r\n";
|
|
m = m->next;
|
|
}
|
|
}
|
|
|
|
void Heap::DumpHuge()
|
|
{
|
|
Mutex::Lock __(mutex);
|
|
HugePage *pg = huge_pages;
|
|
auto& out = VppLog();
|
|
while(pg) {
|
|
BlkPrefix *h = (BlkPrefix *)pg->page;
|
|
out << h << ": ";
|
|
for(;;) {
|
|
if(h->IsFree())
|
|
out << "#";
|
|
out << 4 * h->GetSize() << ' ';
|
|
if(h->IsLast())
|
|
break;
|
|
h = h->GetNextHeader(4096);
|
|
}
|
|
out << "\r\n";
|
|
pg = pg->next;
|
|
}
|
|
}
|
|
|
|
String AsString(const MemoryProfile& mem)
|
|
{
|
|
String text;
|
|
#ifdef UPP_HEAP
|
|
int acount = 0;
|
|
size_t asize = 0;
|
|
int fcount = 0;
|
|
size_t fsize = 0;
|
|
text << "Memory peak: " << MemoryUsedKbMax() << " KB, current: " << MemoryUsedKb() << "KB \n";
|
|
for(int i = 0; i < 1024; i++)
|
|
if(mem.allocated[i]) {
|
|
int sz = 4 * i;
|
|
text << Format("%4d B, %7d allocated (%6d KB), %6d fragments (%6d KB)\n",
|
|
sz, mem.allocated[i], (mem.allocated[i] * sz) >> 10,
|
|
mem.fragments[i], (mem.fragments[i] * sz) >> 10);
|
|
acount += mem.allocated[i];
|
|
asize += mem.allocated[i] * sz;
|
|
fcount += mem.fragments[i];
|
|
fsize += mem.fragments[i] * sz;
|
|
}
|
|
text << Format(" TOTAL, %7d allocated (%6d KB), %6d fragments (%6d KB)\n",
|
|
acount, int(asize >> 10), fcount, int(fsize >> 10));
|
|
text << "Empty 4KB pages " << mem.freepages << " (" << mem.freepages * 4 << " KB)\n";
|
|
text << "Large block count " << mem.large_count
|
|
<< ", total size " << (mem.large_total >> 10) << " KB\n";
|
|
text << "Large fragments count " << mem.large_fragments_count
|
|
<< ", total size " << (mem.large_fragments_total >> 10) << " KB\n";
|
|
text << "Huge block count " << mem.huge_count
|
|
<< ", total size " << int(mem.huge_total >> 10) << " KB\n";
|
|
text << "Huge fragments count " << mem.huge_fragments_count
|
|
<< ", total size " << 4 * mem.huge_fragments_total << " KB\n";
|
|
text << "Sys block count " << mem.sys_count
|
|
<< ", total size " << int(mem.sys_total >> 10) << " KB\n";
|
|
text << Heap::HPAGE * 4 / 1024 << "MB master blocks " << mem.master_chunks << "\n";
|
|
text << "\nLarge fragments:\n";
|
|
for(int i = 0; i < 2048; i++)
|
|
if(mem.large_fragments[i])
|
|
text << 256.0 * i / 1024 << " KB: " << mem.large_fragments[i] << "\n";
|
|
text << "\nHuge fragments:\n";
|
|
for(int i = 0; i < 65535; i++)
|
|
if(mem.huge_fragments[i])
|
|
text << i * 4 << " KB: " << mem.huge_fragments[i] << "\n";
|
|
#endif
|
|
return text;
|
|
}
|
|
|
|
#ifdef flagHEAPSTAT
|
|
int stat[65536];
|
|
int bigstat;
|
|
|
|
void Heap::Stat(size_t sz)
|
|
{
|
|
if(sz < 65536)
|
|
stat[sz]++;
|
|
else
|
|
bigstat++;
|
|
}
|
|
|
|
EXITBLOCK {
|
|
int sum = 0;
|
|
for(int i = 0; i < 65536; i++)
|
|
sum += stat[i];
|
|
sum += bigstat;
|
|
int total = 0;
|
|
VppLog() << Sprintf("Allocation statistics: (total allocations: %d)\n", sum);
|
|
for(int i = 0; i < 65536; i++)
|
|
if(stat[i]) {
|
|
total += stat[i];
|
|
VppLog() << Sprintf("%5d %8dx %2d%%, total %8dx %2d%%\n",
|
|
i, stat[i], 100 * stat[i] / sum, total, 100 * total / sum);
|
|
}
|
|
if(bigstat) {
|
|
total += bigstat;
|
|
VppLog() << Sprintf(">64KB %8dx %2d%%, total %8dx %2d%%\n",
|
|
bigstat, 100 * bigstat / sum, total, 100 * total / sum);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|