📄 prmalloc.c
字号:
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape Portable Runtime (NSPR). * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */#include "primpl.h"/*** We override malloc etc. on any platform which has preemption +** nspr20 user level threads. When we're debugging, we can make our** version of malloc fail occasionally.*/#ifdef _PR_OVERRIDE_MALLOC/*** Thread safe version of malloc, calloc, realloc, free*/#include <stdarg.h>#ifdef DEBUG#define SANITY#define EXTRA_SANITY#else#undef SANITY#undef EXTRA_SANITY#endif/* Forward decls */void *_PR_UnlockedMalloc(size_t size);void _PR_UnlockedFree(void *ptr);void *_PR_UnlockedRealloc(void *ptr, size_t size);void *_PR_UnlockedCalloc(size_t n, size_t elsize);/************************************************************************//* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * *//* * Defining SANITY will enable some checks which will tell you if the users * program did botch something *//* * Defining EXTRA_SANITY will enable some checks which are mostly related * to internal conditions in malloc.c *//* * Very verbose progress on stdout... */#if 0# define TRACE(foo) printf foostatic int malloc_event;#else# define TRACE(foo) #endif/* XXX Pick a number, any number */# define malloc_pagesize 4096UL# define malloc_pageshift 12UL#ifdef XP_UNIX#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/mman.h>#endif/* * This structure describes a page's worth of chunks. */struct pginfo { struct pginfo *next; /* next on the free list */ char *page; /* Pointer to the page */ u_short size; /* size of this page's chunks */ u_short shift; /* How far to shift for this size chunks */ u_short free; /* How many free chunks */ u_short total; /* How many chunk */ u_long bits[1]; /* Which chunks are free */};struct pgfree { struct pgfree *next; /* next run of free pages */ struct pgfree *prev; /* prev run of free pages */ char *page; /* pointer to free pages */ char *end; /* pointer to end of free pages */ u_long size; /* number of bytes free */};/* * How many bits per u_long in the bitmap. * Change only if not 8 bits/byte */#define MALLOC_BITS (8*sizeof(u_long))/* * Magic values to put in the page_directory */#define MALLOC_NOT_MINE ((struct pginfo*) 0)#define MALLOC_FREE ((struct pginfo*) 1)#define MALLOC_FIRST ((struct pginfo*) 2)#define MALLOC_FOLLOW ((struct pginfo*) 3)#define MALLOC_MAGIC ((struct pginfo*) 4)/* * Set to one when malloc_init has been called */static unsigned initialized;/* * The size of a page. * Must be a integral multiplum of the granularity of mmap(2). * Your toes will curl if it isn't a power of two */#define malloc_pagemask ((malloc_pagesize)-1)/* * The size of the largest chunk. * Half a page. */#define malloc_maxsize ((malloc_pagesize)>>1)/* * malloc_pagesize == 1 << malloc_pageshift */#ifndef malloc_pageshiftstatic unsigned malloc_pageshift;#endif /* malloc_pageshift *//* * The smallest allocation we bother about. * Must be power of two */#ifndef malloc_minsizestatic unsigned malloc_minsize;#endif /* malloc_minsize *//* * The largest chunk we care about. * Must be smaller than pagesize * Must be power of two */#ifndef malloc_maxsizestatic unsigned malloc_maxsize;#endif /* malloc_maxsize */#ifndef malloc_cachestatic unsigned malloc_cache;#endif /* malloc_cache *//* * The offset from pagenumber to index into the page directory */static u_long malloc_origo;/* * The last index in the page directory we care about */static u_long last_index;/* * Pointer to page directory. * Allocated "as if with" malloc */static struct pginfo **page_dir;/* * How many slots in the page directory */static unsigned malloc_ninfo;/* * Free pages line up here */static struct pgfree free_list;/* * Abort() if we fail to get VM ? */static int malloc_abort;#ifdef SANITY/* * Are we trying to die ? */static int suicide;#endif/* * dump statistics */static int malloc_stats;/* * always realloc ? */static int malloc_realloc;/* * my last break. */static void *malloc_brk;/* * one location cache for free-list holders */static struct pgfree *px;static int set_pgdir(void *ptr, struct pginfo *info);static int extend_page_directory(u_long index);#ifdef SANITYvoidmalloc_dump(FILE *fd){ struct pginfo **pd; struct pgfree *pf; int j; pd = page_dir; /* print out all the pages */ for(j=0;j<=last_index;j++) { fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j); if (pd[j] == MALLOC_NOT_MINE) { for(j++;j<=last_index && pd[j] == MALLOC_NOT_MINE;j++) ; j--; fprintf(fd,".. %5d not mine\n", j); } else if (pd[j] == MALLOC_FREE) { for(j++;j<=last_index && pd[j] == MALLOC_FREE;j++) ; j--; fprintf(fd,".. %5d free\n", j); } else if (pd[j] == MALLOC_FIRST) { for(j++;j<=last_index && pd[j] == MALLOC_FOLLOW;j++) ; j--; fprintf(fd,".. %5d in use\n", j); } else if (pd[j] < MALLOC_MAGIC) { fprintf(fd,"(%p)\n", pd[j]); } else { fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n", pd[j],pd[j]->free, pd[j]->total, pd[j]->size, pd[j]->page, pd[j]->next); } } for(pf=free_list.next; pf; pf=pf->next) { fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n", pf,pf->page,pf->end,pf->size,pf->prev,pf->next); if (pf == pf->next) { fprintf(fd,"Free_list loops.\n"); break; } } /* print out various info */ fprintf(fd,"Minsize\t%d\n",malloc_minsize); fprintf(fd,"Maxsize\t%ld\n",malloc_maxsize); fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize); fprintf(fd,"Pageshift\t%ld\n",malloc_pageshift); fprintf(fd,"FirstPage\t%ld\n",malloc_origo); fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift, (last_index + malloc_pageshift) << malloc_pageshift); fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift);}static void wrterror(char *fmt, ...){ char *q = "malloc() error: "; char buf[100]; va_list ap; suicide = 1; va_start(ap, fmt); PR_vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); fputs(q, stderr); fputs(buf, stderr); malloc_dump(stderr); PR_Abort();}static void wrtwarning(char *fmt, ...){ char *q = "malloc() warning: "; char buf[100]; va_list ap; va_start(ap, fmt); PR_vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); fputs(q, stderr); fputs(buf, stderr);}#endif /* SANITY *//* * Allocate a number of pages from the OS */static caddr_tmap_pages(int pages, int update){ caddr_t result,tail; result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1; result = (caddr_t) ((u_long)result & ~malloc_pagemask); tail = result + (pages << malloc_pageshift); if (!brk(tail)) { last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1; malloc_brk = tail; TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail)); if (!update || last_index < malloc_ninfo || extend_page_directory(last_index)) return result; } TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno));#ifdef EXTRA_SANITY wrterror("map_pages fails\n");#endif return 0;}#define set_bit(_pi,_bit) \ (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS)#define clr_bit(_pi,_bit) \ (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS));#define tst_bit(_pi,_bit) \ ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS)))/* * Extend page directory */static intextend_page_directory(u_long index){ struct pginfo **young, **old; int i; TRACE(("%6d E %lu\n",malloc_event++,index)); /* Make it this many pages */ i = index * sizeof *page_dir; i /= malloc_pagesize; i += 2; /* Get new pages, if you used this much mem you don't care :-) */ young = (struct pginfo**) map_pages(i,0); if (!young) return 0; /* Copy the old stuff */ memset(young, 0, i * malloc_pagesize); memcpy(young, page_dir, malloc_ninfo * sizeof *page_dir); /* register the new size */ malloc_ninfo = i * malloc_pagesize / sizeof *page_dir; /* swap the pointers */ old = page_dir; page_dir = young; /* Mark the pages */ index = ((u_long)young >> malloc_pageshift) - malloc_origo; page_dir[index] = MALLOC_FIRST; while (--i) { page_dir[++index] = MALLOC_FOLLOW; } /* Now free the old stuff */ _PR_UnlockedFree(old); return 1;}/* * Set entry in page directory. * Extend page directory if need be. */static intset_pgdir(void *ptr, struct pginfo *info){ u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo; if (index >= malloc_ninfo && !extend_page_directory(index)) return 0; page_dir[index] = info; return 1;}/* * Initialize the world */static voidmalloc_init (void){ int i; char *p; TRACE(("%6d I\n",malloc_event++));#ifdef DEBUG for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) { switch (*p) { case 'a': malloc_abort = 0; break; case 'A': malloc_abort = 1; break; case 'd': malloc_stats = 0; break; case 'D': malloc_stats = 1; break; case 'r': malloc_realloc = 0; break; case 'R': malloc_realloc = 1; break; default: wrtwarning("Unknown chars in MALLOC_OPTIONS\n"); break; } }#endif#ifndef malloc_pagesize /* determine our pagesize */ malloc_pagesize = getpagesize();#endif /* malloc_pagesize */#ifndef malloc_pageshift /* determine how much we shift by to get there */ for (i = malloc_pagesize; i > 1; i >>= 1) malloc_pageshift++;#endif /* malloc_pageshift */#ifndef malloc_cache malloc_cache = 50 << malloc_pageshift; #endif /* malloc_cache */#ifndef malloc_minsize /* * find the smallest size allocation we will bother about. * this is determined as the smallest allocation that can hold * it's own pginfo; */ i = 2; for(;;) { int j; /* Figure out the size of the bits */ j = malloc_pagesize/i; j /= 8; if (j < sizeof(u_long)) j = sizeof (u_long); if (sizeof(struct pginfo) + j - sizeof (u_long) <= i) break; i += i; } malloc_minsize = i;#endif /* malloc_minsize */ /* Allocate one page for the page directory */ page_dir = (struct pginfo **) map_pages(1,0);#ifdef SANITY if (!page_dir) wrterror("fatal: my first mmap failed. (check limits ?)\n");#endif /* * We need a maximum of malloc_pageshift buckets, steal these from the * front of the page_directory; */ malloc_origo = (u_long) page_dir >> malloc_pageshift; malloc_origo -= malloc_pageshift; /* Clear it */ memset(page_dir,0,malloc_pagesize); /* Find out how much it tells us */ malloc_ninfo = malloc_pagesize / sizeof *page_dir; /* Plug the page directory into itself */ i = set_pgdir(page_dir,MALLOC_FIRST);#ifdef SANITY if (!i) wrterror("fatal: couldn't set myself in the page directory\n");#endif /* Been here, done that */ initialized++;}/* * Allocate a number of complete pages */static void *malloc_pages(size_t size){ void *p,*delay_free = 0; int i; struct pgfree *pf; u_long index; /* How many pages ? */ size += (malloc_pagesize-1); size &= ~malloc_pagemask; p = 0; /* Look for free pages before asking for more */ for(pf = free_list.next; pf; pf = pf->next) {#ifdef EXTRA_SANITY if (pf->page == pf->end) wrterror("zero entry on free_list\n"); if (pf->page > pf->end) { TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++, pf,pf->page,pf->end,__LINE__)); wrterror("sick entry on free_list\n"); } if ((void*)pf->page >= (void*)sbrk(0)) wrterror("entry on free_list past brk\n"); if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] != MALLOC_FREE) { TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++, pf,pf->page,pf->end,__LINE__)); wrterror("non-free first page on free-list\n"); } if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] != MALLOC_FREE) wrterror("non-free last page on free-list\n");#endif /* EXTRA_SANITY */ if (pf->size < size) continue; else if (pf->size == size) { p = pf->page; if (pf->next) pf->next->prev = pf->prev; pf->prev->next = pf->next; delay_free = pf; break; } else { p = pf->page; pf->page += size; pf->size -= size; break; } }#ifdef EXTRA_SANITY
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -