// Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder #include #include #include #include "../common/malloc.h" #include "../common/core.h" #include "../common/showmsg.h" #ifdef MINICORE #undef LOG_MEMMGR #endif void* aMalloc_ (size_t size, const char *file, int line, const char *func) { #ifndef MEMWATCH void *ret = MALLOC(size); #else void *ret = mwMalloc(size, file, line); #endif // ShowMessage("%s:%d: in func %s: malloc %d\n",file,line,func,size); if (ret == NULL){ ShowFatalError("%s:%d: in func %s: malloc error out of memory!\n",file,line,func); exit(1); } return ret; } void* aMallocA_ (size_t size, const char *file, int line, const char *func) { #ifndef MEMWATCH void *ret = MALLOCA(size); #else void *ret = mwMalloc(size, file, line); #endif // ShowMessage("%s:%d: in func %s: malloc %d\n",file,line,func,size); if (ret == NULL){ ShowFatalError("%s:%d: in func %s: malloc error out of memory!\n",file,line,func); exit(1); } return ret; } void* aCalloc_ (size_t num, size_t size, const char *file, int line, const char *func) { #ifndef MEMWATCH void *ret = CALLOC(num, size); #else void *ret = mwCalloc(num, size, file, line); #endif // ShowMessage("%s:%d: in func %s: calloc %d %d\n",file,line,func,num,size); if (ret == NULL){ ShowFatalError("%s:%d: in func %s: calloc error out of memory!\n", file, line, func); exit(1); } return ret; } void* aCallocA_ (size_t num, size_t size, const char *file, int line, const char *func) { #ifndef MEMWATCH void *ret = CALLOCA(num, size); #else void *ret = mwCalloc(num, size, file, line); #endif // ShowMessage("%s:%d: in func %s: calloc %d %d\n",file,line,func,num,size); if (ret == NULL){ ShowFatalError("%s:%d: in func %s: calloc error out of memory!\n",file,line,func); exit(1); } return ret; } void* aRealloc_ (void *p, size_t size, const char *file, int line, const char *func) { #ifndef MEMWATCH void *ret = REALLOC(p, size); #else void *ret = mwRealloc(p, size, file, line); #endif // ShowMessage("%s:%d: in func %s: realloc %p %d\n",file,line,func,p,size); if (ret == NULL){ ShowFatalError("%s:%d: in func %s: realloc error out of memory!\n",file,line,func); exit(1); } return ret; } char* aStrdup_ (const char *p, const char *file, int line, const char *func) { #ifndef MEMWATCH char *ret = STRDUP(p); #else char *ret = mwStrdup(p, file, line); #endif // ShowMessage("%s:%d: in func %s: strdup %p\n",file,line,func,p); if (ret == NULL){ ShowFatalError("%s:%d: in func %s: strdup error out of memory!\n", file, line, func); exit(1); } return ret; } void aFree_ (void *p, const char *file, int line, const char *func) { // ShowMessage("%s:%d: in func %s: free %p\n",file,line,func,p); if (p) #ifndef MEMWATCH FREE(p); #else mwFree(p, file, line); #endif p = NULL; } #ifdef GCOLLECT void* _bcallocA(size_t size, size_t cnt) { void *ret = MALLOCA(size * cnt); if (ret) //memset(ret, 0, size * cnt); malloc_set(ret, 0, size*cnt); return ret; } void* _bcalloc(size_t size, size_t cnt) { void *ret = MALLOC(size * cnt); if (ret) //memset(ret, 0, size * cnt); malloc_set(ret, 0, size*cnt); return ret; } char* _bstrdup(const char *chr) { int len = strlen(chr); char *ret = (char*)MALLOC(len + 1); if (ret) memcpy(ret, chr, len + 1); return ret; } #endif #ifdef USE_MEMMGR /* USE_MEMMGR */ /* * メモリマネージャ * malloc , free の処理を効率的に出来るようにしたもの。 * 複雑な処理を行っているので、若干重くなるかもしれません。 * * データ構造など(説明下手ですいません^^; ) * ・メモリを複数の「ブロック」に分けて、さらにブロックを複数の「ユニット」 * に分けています。ユニットのサイズは、1ブロックの容量を複数個に均等配分 * したものです。たとえば、1ユニット32KBの場合、ブロック1つは32Byteのユ * ニットが、1024個集まって出来ていたり、64Byteのユニットが 512個集まって * 出来ていたりします。(padding,unit_head を除く) * * ・ブロック同士はリンクリスト(block_prev,block_next) でつながり、同じサイ * ズを持つブロック同士もリンクリスト(samesize_prev,samesize_nect) でつな * がっています。それにより、不要となったメモリの再利用が効率的に行えます。 */ /* ブロックに入るデータ量 */ #define BLOCK_DATA_SIZE 80*1024 /* 一度に確保するブロックの数。 */ #define BLOCK_ALLOC 32 /* ブロックのアライメント */ #define BLOCK_ALIGNMENT 64 /* ブロック */ struct block { int block_no; /* ブロック番号 */ struct block* block_prev; /* 前に確保した領域 */ struct block* block_next; /* 次に確保した領域 */ int samesize_no; /* 同じサイズの番号 */ struct block* samesize_prev; /* 同じサイズの前の領域 */ struct block* samesize_next; /* 同じサイズの次の領域 */ size_t unit_size; /* ユニットのバイト数 0=未使用 */ size_t unit_hash; /* ユニットのハッシュ */ int unit_count; /* ユニットの数 */ int unit_used; /* 使用済みユニット */ char data[BLOCK_DATA_SIZE]; }; struct unit_head { struct block* block; size_t size; const char* file; int line; unsigned int checksum; }; struct chunk { char *block; struct chunk *next; }; static struct block* block_first = NULL; static struct block* block_last = NULL; static struct block* block_unused = NULL; /* ユニットへのハッシュ。80KB/64Byte = 1280個 */ static struct block* unit_first[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最初 */ static struct block* unit_unfill[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 埋まってない */ static struct block* unit_last[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最後 */ /* メモリを使い回せない領域用のデータ */ struct unit_head_large { struct unit_head_large* prev; struct unit_head_large* next; struct unit_head unit_head; }; static struct unit_head_large *unit_head_large_first = NULL; static struct chunk *chunk_first = NULL; static struct block* block_malloc(void); static void block_free(struct block* p); static void memmgr_info(void); static unsigned int memmgr_usage_bytes = 0; void* _mmalloc(size_t size, const char *file, int line, const char *func ) { int i; struct block *block; size_t size_hash; if (((long) size) < 0) { printf("_mmalloc: %d\n", size); return 0; } size_hash = (size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT; if(size == 0) { return NULL; } memmgr_usage_bytes += size; /* ブロック長を超える領域の確保には、malloc() を用いる */ /* その際、unit_head.block に NULL を代入して区別する */ if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) { #ifdef MEMWATCH struct unit_head_large* p = (struct unit_head_large*)mwMalloc(sizeof(struct unit_head_large) + size,file,line); #else struct unit_head_large* p = (struct unit_head_large*) MALLOC (sizeof(struct unit_head_large) + size); #endif if(p != NULL) { p->unit_head.block = NULL; p->unit_head.size = size; p->unit_head.file = file; p->unit_head.line = line; p->prev = NULL; if (unit_head_large_first == NULL) p->next = NULL; else { unit_head_large_first->prev = p; p->next = unit_head_large_first; } unit_head_large_first = p; *(int*)((char*)p + sizeof(struct unit_head_large) - sizeof(int) + size) = 0xdeadbeaf; return (char *)p + sizeof(struct unit_head_large) - sizeof(int); } else { ShowFatalError("Memory manager::memmgr_alloc failed (allocating %d+%d bytes at %s:%d).\n", sizeof(struct unit_head_large), size, file, line); exit(1); } } /* 同一サイズのブロックが確保されていない時、新たに確保する */ if(unit_unfill[size_hash] == NULL) { block = block_malloc(); if(unit_first[size_hash] == NULL) { /* 初回確保 */ unit_first[size_hash] = block; unit_last[size_hash] = block; block->samesize_no = 0; block->samesize_prev = NULL; block->samesize_next = NULL; } else { /* 連結作業 */ unit_last[size_hash]->samesize_next = block; block->samesize_no = unit_last[size_hash]->samesize_no + 1; block->samesize_prev = unit_last[size_hash]; block->samesize_next = NULL; unit_last[size_hash] = block; } unit_unfill[size_hash] = block; block->unit_size = size_hash * BLOCK_ALIGNMENT + sizeof(struct unit_head); block->unit_count = (int)(BLOCK_DATA_SIZE / block->unit_size); block->unit_used = 0; block->unit_hash = size_hash; /* 未使用Flagを立てる */ for(i=0;iunit_count;i++) { ((struct unit_head*)(&block->data[block->unit_size * i]))->block = NULL; } } /* ユニット使用個数加算 */ block = unit_unfill[size_hash]; block->unit_used++; /* ユニット内を全て使い果たした */ if(block->unit_count == block->unit_used) { do { unit_unfill[size_hash] = unit_unfill[size_hash]->samesize_next; } while( unit_unfill[size_hash] != NULL && unit_unfill[size_hash]->unit_count == unit_unfill[size_hash]->unit_used ); } /* ブロックの中の空きユニット捜索 */ for(i=0;iunit_count;i++) { struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]); if(head->block == NULL) { head->block = block; head->size = size; head->line = line; head->file = file; *(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + size) = 0xdeadbeaf; return (char *)head + sizeof(struct unit_head) - sizeof(int); } } // ここに来てはいけない。 ShowFatalError("Memory manager::memmgr_malloc() serious error (allocating %d+%d bytes at %s:%d)\n", sizeof(struct unit_head_large), size, file, line); memmgr_info(); exit(1); return NULL; }; void* _mcalloc(size_t num, size_t size, const char *file, int line, const char *func ) { void *p = _mmalloc(num * size,file,line,func); //memset(p,0,num * size); malloc_set(p,0,num*size); return p; } void* _mrealloc(void *memblock, size_t size, const char *file, int line, const char *func ) { size_t old_size; if(memblock == NULL) { return _mmalloc(size,file,line,func); } old_size = ((struct unit_head *)((char *)memblock - sizeof(struct unit_head) + sizeof(int)))->size; if(old_size > size) { // サイズ縮小 -> そのまま返す(手抜き) return memblock; } else { // サイズ拡大 void *p = _mmalloc(size,file,line,func); if(p != NULL) { memcpy(p,memblock,old_size); } _mfree(memblock,file,line,func); return p; } } char* _mstrdup(const char *p, const char *file, int line, const char *func ) { if(p == NULL) { return NULL; } else { size_t len = strlen(p); char *string = (char *)_mmalloc(len + 1,file,line,func); memcpy(string,p,len+1); return string; } } void _mfree(void *ptr, const char *file, int line, const char *func ) { struct unit_head *head; size_t size_hash; if (ptr == NULL) return; head = (struct unit_head *)((char *)ptr - sizeof(struct unit_head) + sizeof(int)); size_hash = (head->size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT; if(head->block == NULL) { if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) { /* malloc() で直に確保された領域 */ struct unit_head_large *head_large = (struct unit_head_large *)((char *)ptr - sizeof(struct unit_head_large) + sizeof(int)); if( *(int*)((char*)head_large + sizeof(struct unit_head_large) - sizeof(int) + head->size) != 0xdeadbeaf) { ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line); } if(head_large->prev) { head_large->prev->next = head_large->next; } else { unit_head_large_first = head_large->next; } if(head_large->next) { head_large->next->prev = head_large->prev; } head->block = NULL; memmgr_usage_bytes -= head->size; FREE (head_large); } else { ShowError("Memory manager: args of aFree is freed pointer %s:%d@%s\n", file, line, func); } ptr = NULL; return; } else { /* ユニット解放 */ struct block *block = head->block; if((unsigned long)block % sizeof(struct block) != 0) { ShowError("Memory manager: args of aFree is not valid pointer %s line %d\n", file, line); } else if(*(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + head->size) != 0xdeadbeaf) { ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line); } else { head->block = NULL; memmgr_usage_bytes -= head->size; if(--block->unit_used == 0) { /* ブロックの解放 */ if(unit_unfill[block->unit_hash] == block) { /* 空きユニットに指定されている */ do { unit_unfill[block->unit_hash] = unit_unfill[block->unit_hash]->samesize_next; } while( unit_unfill[block->unit_hash] != NULL && unit_unfill[block->unit_hash]->unit_count == unit_unfill[block->unit_hash]->unit_used ); } if(block->samesize_prev == NULL && block->samesize_next == NULL) { /* 独立ブロックの解放 */ unit_first[block->unit_hash] = NULL; unit_last[block->unit_hash] = NULL; unit_unfill[block->unit_hash] = NULL; } else if(block->samesize_prev == NULL) { /* 先頭ブロックの解放 */ unit_first[block->unit_hash] = block->samesize_next; (block->samesize_next)->samesize_prev = NULL; } else if(block->samesize_next == NULL) { /* 末端ブロックの解放 */ unit_last[block->unit_hash] = block->samesize_prev; (block->samesize_prev)->samesize_next = NULL; } else { /* 中間ブロックの解放 */ (block->samesize_next)->samesize_prev = block->samesize_prev; (block->samesize_prev)->samesize_next = block->samesize_next; } block_free(block); } else { /* 空きユニットの再設定 */ if( unit_unfill[block->unit_hash] == NULL || unit_unfill[block->unit_hash]->samesize_no > block->samesize_no ) { unit_unfill[block->unit_hash] = block; } } ptr = NULL; } } } /* 現在の状況を表示する */ static void memmgr_info(void) { int i; struct block *p; ShowInfo("** Memory Manager Information **\n"); if(block_first == NULL) { ShowMessage("Uninitialized.\n"); return; } ShowMessage( "Blocks: %04u , BlockSize: %06u Byte , Used: %08uKB\n", block_last->block_no+1,sizeof(struct block), (block_last->block_no+1) * sizeof(struct block) / 1024 ); p = block_first; for(i=0;i<=block_last->block_no;i++) { ShowMessage(" Block #%04u : ",p->block_no); if(p->unit_size == 0) { ShowMessage("unused.\n"); } else { ShowMessage( "size: %05u byte. used: %04u/%04u prev:", p->unit_size - sizeof(struct unit_head),p->unit_used,p->unit_count ); if(p->samesize_prev == NULL) { ShowMessage("NULL"); } else { ShowMessage("%04u",(p->samesize_prev)->block_no); } ShowMessage(" next:"); if(p->samesize_next == NULL) { ShowMessage("NULL"); } else { ShowMessage("%04u",(p->samesize_next)->block_no); } ShowMessage("\n"); } p = p->block_next; } } /* ブロックを確保する */ static struct block* block_malloc(void) { if(block_unused != NULL) { /* ブロック用の領域は確保済み */ struct block* ret = block_unused; do { block_unused = block_unused->block_next; } while(block_unused != NULL && block_unused->unit_size != 0); return ret; } else { /* ブロック用の領域を新たに確保する */ int i; int block_no; struct block* p; struct chunk* chunk; char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1); if(pb == NULL) { ShowFatalError("Memory manager::block_alloc failed.\n"); exit(1); } // store original block address in chunk chunk = (struct chunk *) MALLOC (sizeof(struct chunk)); if (chunk == NULL) { ShowFatalError("Memory manager::block_alloc failed.\n"); exit(1); } chunk->block = pb; chunk->next = (chunk_first) ? chunk_first : NULL; chunk_first = chunk; // ブロックのポインタの先頭をsizeof(block) アライメントに揃える // このアドレスをfree() することはないので、直接ポインタを変更している。 pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block)); p = (struct block*)pb; if(block_first == NULL) { /* 初回確保 */ block_no = 0; block_first = p; } else { block_no = block_last->block_no + 1; block_last->block_next = p; p->block_prev = block_last; } block_last = &p[BLOCK_ALLOC - 1]; /* ブロックを連結させる */ for(i=0;iunit_size = 1; return p; } } static void block_free(struct block* p) { /* free() せずに、未使用フラグを付けるだけ */ p->unit_size = 0; /* 未使用ポインターを更新する */ if(block_unused == NULL) { block_unused = p; } else if(block_unused->block_no > p->block_no) { block_unused = p; } } unsigned int memmgr_usage (void) { return memmgr_usage_bytes / 1024; } #ifdef LOG_MEMMGR static char memmer_logfile[128]; static FILE *log_fp; static void memmgr_log (char *buf) { if (!log_fp) { log_fp = fopen(memmer_logfile,"w"); if (!log_fp) log_fp = stdout; fprintf(log_fp, "Memory manager: Memory leaks found.\n"); } fprintf(log_fp, buf); return; } #endif static void memmgr_final (void) { struct block *block = block_first; struct chunk *chunk = chunk_first, *chunk2; struct unit_head_large *large = unit_head_large_first, *large2; int i; #ifdef LOG_MEMMGR int count = 0; char buf[128]; #endif while (block) { if (block->unit_size) { for (i = 0; i < block->unit_count; i++) { struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]); if(head->block != NULL) { #ifdef LOG_MEMMGR sprintf (buf, "%04d : %s line %d size %d\n", ++count, head->file, head->line, head->size); memmgr_log (buf); #endif // get block pointer and free it [celest] _mfree ((char *)head + sizeof(struct unit_head) - sizeof(int), ALC_MARK); } } } //if (block->block_no >= block2->block_no + BLOCK_ALLOC - 1) { // reached a new block array //block = block->block_next; /* Okay wise guys... this is how block2 was allocated: [Skotlex] struct block* p; char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1); pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block)); p = (struct block*)pb; The reason we get an invalid pointer is that we allocated pb, not p. So how do you get pb when you only have p? The answer is, you can't, because the original pointer was lost when memory-aligning the block. So we either forget this FREE or use a self-reference... Since we are already quitting, it might be ok to just not free the block as it is. */ // didn't realise that before o.o -- block chunks are now freed below [celest] // FREE(block2); //block2 = block; //continue; //} block = block->block_next; } // free the allocated block chunks chunk = chunk_first; while (chunk) { chunk2 = chunk->next; FREE(chunk->block); FREE(chunk); chunk = chunk2; } while(large) { large2 = large->next; #ifdef LOG_MEMMGR sprintf (buf, "%04d : %s line %d size %d\n", ++count, large->unit_head.file, large->unit_head.line, large->unit_head.size); memmgr_log (buf); #endif FREE (large); large = large2; } #ifdef LOG_MEMMGR if(count == 0) { ShowInfo("Memory manager: No memory leaks found.\n"); } else { ShowWarning("Memory manager: Memory leaks found and fixed.\n"); fclose(log_fp); } #endif return; } static void memmgr_init (void) { #ifdef LOG_MEMMGR sprintf(memmer_logfile, "log/%s.leaks", SERVER_NAME); ShowStatus("Memory manager initialised: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile); #endif return; } #endif #ifdef MEMSET_TURBO // This function is practically useless if the the size of the data is // static. It decides whether to use setword or setdword. void malloc_set(void *dest, int value, int size) { if(size%4 == 0) malloc_tsetdword(dest, value, size); else if(size%2 == 0) malloc_tsetword(dest, (short)value, size); else memset(dest, value, (size_t) size); } // Sets 32-bit aligned memory. void malloc_tsetdword(void *dest, int value, int count){ #ifdef _WIN32 _asm { mov edx, 0 mov eax, count mov ebx, 4 idiv ebx mov edi, dest mov ecx, eax mov eax, value rep stosd } #else __asm__("movl $0, %%edx; movl %1, %%eax; movl $4, %%ebx; idivl %%ebx; movl %0, %%edi; movl %%eax, %%ecx; movl %2, %%eax; rep; stosd;" : : "g" (dest), "g" (count), "g" (value) : "edx", "eax", "ebx", "edi", "ecx" ); #endif } // Sets 16-bit aligned memory. void malloc_tsetword(void *dest, short value, int count){ #ifdef _WIN32 _asm { mov edx, 0 mov eax, count mov ebx, 2 idiv ebx mov edi, dest mov ecx, eax mov ax, value rep stosw } #else __asm__("movl $0, %%edx; movl %1, %%eax; movl $2, %%ebx; idivl %%ebx; movl %0, %%edi; movl %%eax, %%ecx; movw %2, %%ax; rep; stosw;" : : "g" (dest), "g" (count), "g" (value) : "edx", "eax", "ebx", "edi", "ecx", "ax" ); #endif } #endif /*====================================== * Initialise *-------------------------------------- */ unsigned int malloc_usage (void) { #ifdef USE_MEMMGR return memmgr_usage (); #else return 0; #endif } void malloc_final (void) { #ifdef USE_MEMMGR memmgr_final (); #endif return; } void malloc_init (void) { #ifdef USE_MEMMGR memmgr_init (); #endif return; }