1 /* 2 ** 2007 October 14 3 ** 4 ** The author disclaims copyright to this source code. In place of 5 ** a legal notice, here is a blessing: 6 ** 7 ** May you do good and not evil. 8 ** May you find forgiveness for yourself and forgive others. 9 ** May you share freely, never taking more than you give. 10 ** 11 ************************************************************************* 12 ** This file contains the C functions that implement a memory 13 ** allocation subsystem for use by SQLite. 14 ** 15 ** This version of the memory allocation subsystem omits all 16 ** use of malloc(). The SQLite user supplies a block of memory 17 ** before calling sqlite3_initialize() from which allocations 18 ** are made and returned by the xMalloc() and xRealloc() 19 ** implementations. Once sqlite3_initialize() has been called, 20 ** the amount of memory available to SQLite is fixed and cannot 21 ** be changed. 22 ** 23 ** This version of the memory allocation subsystem is included 24 ** in the build only if SQLITE_ENABLE_MEMSYS5 is defined. 25 ** 26 ** $Id: mem5.c,v 1.18 2008/11/19 14:35:47 danielk1977 Exp $ 27 */ 28 #include "sqliteInt.h" 29 30 /* 31 ** This version of the memory allocator is used only when 32 ** SQLITE_ENABLE_MEMSYS5 is defined. 33 */ 34 #ifdef SQLITE_ENABLE_MEMSYS5 35 36 /* 37 ** A minimum allocation is an instance of the following structure. 38 ** Larger allocations are an array of these structures where the 39 ** size of the array is a power of 2. 40 */ 41 typedef struct Mem5Link Mem5Link; 42 struct Mem5Link { 43 int next; /* Index of next free chunk */ 44 int prev; /* Index of previous free chunk */ 45 }; 46 47 /* 48 ** Maximum size of any allocation is ((1<<LOGMAX)*mem5.nAtom). Since 49 ** mem5.nAtom is always at least 8, this is not really a practical 50 ** limitation. 51 */ 52 #define LOGMAX 30 53 54 /* 55 ** Masks used for mem5.aCtrl[] elements. 56 */ 57 #define CTRL_LOGSIZE 0x1f /* Log2 Size of this block relative to POW2_MIN */ 58 #define CTRL_FREE 0x20 /* True if not checked out */ 59 60 /* 61 ** All of the static variables used by this module are collected 62 ** into a single structure named "mem5". This is to keep the 63 ** static variables organized and to reduce namespace pollution 64 ** when this module is combined with other in the amalgamation. 65 */ 66 static SQLITE_WSD struct Mem5Global { 67 /* 68 ** Memory available for allocation 69 */ 70 int nAtom; /* Smallest possible allocation in bytes */ 71 int nBlock; /* Number of nAtom sized blocks in zPool */ 72 u8 *zPool; 73 74 /* 75 ** Mutex to control access to the memory allocation subsystem. 76 */ 77 sqlite3_mutex *mutex; 78 79 /* 80 ** Performance statistics 81 */ 82 u64 nAlloc; /* Total number of calls to malloc */ 83 u64 totalAlloc; /* Total of all malloc calls - includes internal frag */ 84 u64 totalExcess; /* Total internal fragmentation */ 85 u32 currentOut; /* Current checkout, including internal fragmentation */ 86 u32 currentCount; /* Current number of distinct checkouts */ 87 u32 maxOut; /* Maximum instantaneous currentOut */ 88 u32 maxCount; /* Maximum instantaneous currentCount */ 89 u32 maxRequest; /* Largest allocation (exclusive of internal frag) */ 90 91 /* 92 ** Lists of free blocks of various sizes. 93 */ 94 int aiFreelist[LOGMAX+1]; 95 96 /* 97 ** Space for tracking which blocks are checked out and the size 98 ** of each block. One byte per block. 99 */ 100 u8 *aCtrl; 101 102 } mem5 = { 19804167 }; 103 104 #define mem5 GLOBAL(struct Mem5Global, mem5) 105 106 #define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.nAtom])) 107 108 /* 109 ** Unlink the chunk at mem5.aPool[i] from list it is currently 110 ** on. It should be found on mem5.aiFreelist[iLogsize]. 111 */ 112 static void memsys5Unlink(int i, int iLogsize){ 113 int next, prev; 114 assert( i>=0 && i<mem5.nBlock ); 115 assert( iLogsize>=0 && iLogsize<=LOGMAX ); 116 assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); 117 118 next = MEM5LINK(i)->next; 119 prev = MEM5LINK(i)->prev; 120 if( prev<0 ){ 121 mem5.aiFreelist[iLogsize] = next; 122 }else{ 123 MEM5LINK(prev)->next = next; 124 } 125 if( next>=0 ){ 126 MEM5LINK(next)->prev = prev; 127 } 128 } 129 130 /* 131 ** Link the chunk at mem5.aPool[i] so that is on the iLogsize 132 ** free list. 133 */ 134 static void memsys5Link(int i, int iLogsize){ 135 int x; 136 assert( sqlite3_mutex_held(mem5.mutex) ); 137 assert( i>=0 && i<mem5.nBlock ); 138 assert( iLogsize>=0 && iLogsize<=LOGMAX ); 139 assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize ); 140 141 x = MEM5LINK(i)->next = mem5.aiFreelist[iLogsize]; 142 MEM5LINK(i)->prev = -1; 143 if( x>=0 ){ 144 assert( x<mem5.nBlock ); 145 MEM5LINK(x)->prev = i; 146 } 147 mem5.aiFreelist[iLogsize] = i; 148 } 149 150 /* 151 ** If the STATIC_MEM mutex is not already held, obtain it now. The mutex 152 ** will already be held (obtained by code in malloc.c) if 153 ** sqlite3GlobalConfig.bMemStat is true. 154 */ 155 static void memsys5Enter(void){ 156 if( sqlite3GlobalConfig.bMemstat==0 && mem5.mutex==0 ){ 157 mem5.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); 158 } 159 sqlite3_mutex_enter(mem5.mutex); 160 } 161 static void memsys5Leave(void){ 162 sqlite3_mutex_leave(mem5.mutex); 163 } 164 165 /* 166 ** Return the size of an outstanding allocation, in bytes. The 167 ** size returned omits the 8-byte header overhead. This only 168 ** works for chunks that are currently checked out. 169 */ 170 static int memsys5Size(void *p){ 171 int iSize = 0; 172 if( p ){ 173 int i = ((u8 *)p-mem5.zPool)/mem5.nAtom; 174 assert( i>=0 && i<mem5.nBlock ); 175 iSize = mem5.nAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE)); 176 } 177 return iSize; 178 } 179 180 /* 181 ** Find the first entry on the freelist iLogsize. Unlink that 182 ** entry and return its index. 183 */ 184 static int memsys5UnlinkFirst(int iLogsize){ 185 int i; 186 int iFirst; 187 188 assert( iLogsize>=0 && iLogsize<=LOGMAX ); 189 i = iFirst = mem5.aiFreelist[iLogsize]; 190 assert( iFirst>=0 ); 191 while( i>0 ){ 192 if( i<iFirst ) iFirst = i; 193 i = MEM5LINK(i)->next; 194 } 195 memsys5Unlink(iFirst, iLogsize); 196 return iFirst; 197 } 198 199 /* 200 ** Return a block of memory of at least nBytes in size. 201 ** Return NULL if unable. 202 */ 203 static void *memsys5MallocUnsafe(int nByte){ 204 int i; /* Index of a mem5.aPool[] slot */ 205 int iBin; /* Index into mem5.aiFreelist[] */ 206 int iFullSz; /* Size of allocation rounded up to power of 2 */ 207 int iLogsize; /* Log2 of iFullSz/POW2_MIN */ 208 209 /* Keep track of the maximum allocation request. Even unfulfilled 210 ** requests are counted */ 211 if( (u32)nByte>mem5.maxRequest ){ 212 mem5.maxRequest = nByte; 213 } 214 215 /* Round nByte up to the next valid power of two */ 216 for(iFullSz=mem5.nAtom, iLogsize=0; iFullSz<nByte; iFullSz *= 2, iLogsize++){} 217 218 /* Make sure mem5.aiFreelist[iLogsize] contains at least one free 219 ** block. If not, then split a block of the next larger power of 220 ** two in order to create a new free block of size iLogsize. 221 */ 222 for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){} 223 if( iBin>LOGMAX ) return 0; 224 i = memsys5UnlinkFirst(iBin); 225 while( iBin>iLogsize ){ 226 int newSize; 227 228 iBin--; 229 newSize = 1 << iBin; 230 mem5.aCtrl[i+newSize] = CTRL_FREE | iBin; 231 memsys5Link(i+newSize, iBin); 232 } 233 mem5.aCtrl[i] = iLogsize; 234 235 /* Update allocator performance statistics. */ 236 mem5.nAlloc++; 237 mem5.totalAlloc += iFullSz; 238 mem5.totalExcess += iFullSz - nByte; 239 mem5.currentCount++; 240 mem5.currentOut += iFullSz; 241 if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount; 242 if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut; 243 244 /* Return a pointer to the allocated memory. */ 245 return (void*)&mem5.zPool[i*mem5.nAtom]; 246 } 247 248 /* 249 ** Free an outstanding memory allocation. 250 */ 251 static void memsys5FreeUnsafe(void *pOld){ 252 u32 size, iLogsize; 253 int iBlock; 254 255 /* Set iBlock to the index of the block pointed to by pOld in 256 ** the array of mem5.nAtom byte blocks pointed to by mem5.zPool. 257 */ 258 iBlock = ((u8 *)pOld-mem5.zPool)/mem5.nAtom; 259 260 /* Check that the pointer pOld points to a valid, non-free block. */ 261 assert( iBlock>=0 && iBlock<mem5.nBlock ); 262 assert( ((u8 *)pOld-mem5.zPool)%mem5.nAtom==0 ); 263 assert( (mem5.aCtrl[iBlock] & CTRL_FREE)==0 ); 264 265 iLogsize = mem5.aCtrl[iBlock] & CTRL_LOGSIZE; 266 size = 1<<iLogsize; 267 assert( iBlock+size-1<(u32)mem5.nBlock ); 268 269 mem5.aCtrl[iBlock] |= CTRL_FREE; 270 mem5.aCtrl[iBlock+size-1] |= CTRL_FREE; 271 assert( mem5.currentCount>0 ); 272 assert( mem5.currentOut>=(size*mem5.nAtom) ); 273 mem5.currentCount--; 274 mem5.currentOut -= size*mem5.nAtom; 275 assert( mem5.currentOut>0 || mem5.currentCount==0 ); 276 assert( mem5.currentCount>0 || mem5.currentOut==0 ); 277 278 mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; 279 while( iLogsize<LOGMAX ){ 280 int iBuddy; 281 if( (iBlock>>iLogsize) & 1 ){ 282 iBuddy = iBlock - size; 283 }else{ 284 iBuddy = iBlock + size; 285 } 286 assert( iBuddy>=0 ); 287 if( (iBuddy+(1<<iLogsize))>mem5.nBlock ) break; 288 if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break; 289 memsys5Unlink(iBuddy, iLogsize); 290 iLogsize++; 291 if( iBuddy<iBlock ){ 292 mem5.aCtrl[iBuddy] = CTRL_FREE | iLogsize; 293 mem5.aCtrl[iBlock] = 0; 294 iBlock = iBuddy; 295 }else{ 296 mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; 297 mem5.aCtrl[iBuddy] = 0; 298 } 299 size *= 2; 300 } 301 memsys5Link(iBlock, iLogsize); 302 } 303 304 /* 305 ** Allocate nBytes of memory 306 */ 307 static void *memsys5Malloc(int nBytes){ 308 sqlite3_int64 *p = 0; 309 if( nBytes>0 ){ 310 memsys5Enter(); 311 p = memsys5MallocUnsafe(nBytes); 312 memsys5Leave(); 313 } 314 return (void*)p; 315 } 316 317 /* 318 ** Free memory. 319 */ 320 static void memsys5Free(void *pPrior){ 321 if( pPrior==0 ){ 322 assert(0); 323 return; 324 } 325 memsys5Enter(); 326 memsys5FreeUnsafe(pPrior); 327 memsys5Leave(); 328 } 329 330 /* 331 ** Change the size of an existing memory allocation 332 */ 333 static void *memsys5Realloc(void *pPrior, int nBytes){ 334 int nOld; 335 void *p; 336 if( pPrior==0 ){ 337 return memsys5Malloc(nBytes); 338 } 339 if( nBytes<=0 ){ 340 memsys5Free(pPrior); 341 return 0; 342 } 343 nOld = memsys5Size(pPrior); 344 if( nBytes<=nOld ){ 345 return pPrior; 346 } 347 memsys5Enter(); 348 p = memsys5MallocUnsafe(nBytes); 349 if( p ){ 350 memcpy(p, pPrior, nOld); 351 memsys5FreeUnsafe(pPrior); 352 } 353 memsys5Leave(); 354 return p; 355 } 356 357 /* 358 ** Round up a request size to the next valid allocation size. 359 */ 360 static int memsys5Roundup(int n){ 361 int iFullSz; 362 for(iFullSz=mem5.nAtom; iFullSz<n; iFullSz *= 2); 363 return iFullSz; 364 } 365 366 static int memsys5Log(int iValue){ 367 int iLog; 368 for(iLog=0; (1<<iLog)<iValue; iLog++); 369 return iLog; 370 } 371 372 /* 373 ** Initialize this module. 374 */ 375 static int memsys5Init(void *NotUsed){ 376 int ii; 377 int nByte = sqlite3GlobalConfig.nHeap; 378 u8 *zByte = (u8 *)sqlite3GlobalConfig.pHeap; 379 int nMinLog; /* Log of minimum allocation size in bytes*/ 380 int iOffset; 381 382 UNUSED_PARAMETER(NotUsed); 383 384 if( !zByte ){ 385 return SQLITE_ERROR; 386 } 387 388 nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq); 389 mem5.nAtom = (1<<nMinLog); 390 while( (int)sizeof(Mem5Link)>mem5.nAtom ){ 391 mem5.nAtom = mem5.nAtom << 1; 392 } 393 394 mem5.nBlock = (nByte / (mem5.nAtom+sizeof(u8))); 395 mem5.zPool = zByte; 396 mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.nAtom]; 397 398 for(ii=0; ii<=LOGMAX; ii++){ 399 mem5.aiFreelist[ii] = -1; 400 } 401 402 iOffset = 0; 403 for(ii=LOGMAX; ii>=0; ii--){ 404 int nAlloc = (1<<ii); 405 if( (iOffset+nAlloc)<=mem5.nBlock ){ 406 mem5.aCtrl[iOffset] = ii | CTRL_FREE; 407 memsys5Link(iOffset, ii); 408 iOffset += nAlloc; 409 } 410 assert((iOffset+nAlloc)>mem5.nBlock); 411 } 412 413 return SQLITE_OK; 414 } 415 416 /* 417 ** Deinitialize this module. 418 */ 419 static void memsys5Shutdown(void *NotUsed){ 420 UNUSED_PARAMETER(NotUsed); 421 return; 422 } 423 424 /* 425 ** Open the file indicated and write a log of all unfreed memory 426 ** allocations into that log. 427 */ 428 void sqlite3Memsys5Dump(const char *zFilename){ 429 #ifdef SQLITE_DEBUG 430 FILE *out; 431 int i, j, n; 432 int nMinLog; 433 434 if( zFilename==0 || zFilename[0]==0 ){ 435 out = stdout; 436 }else{ 437 out = fopen(zFilename, "w"); 438 if( out==0 ){ 439 fprintf(stderr, "** Unable to output memory debug output log: %s **\n", 440 zFilename); 441 return; 442 } 443 } 444 memsys5Enter(); 445 nMinLog = memsys5Log(mem5.nAtom); 446 for(i=0; i<=LOGMAX && i+nMinLog<32; i++){ 447 for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){} 448 fprintf(out, "freelist items of size %d: %d\n", mem5.nAtom << i, n); 449 } 450 fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc); 451 fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc); 452 fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess); 453 fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut); 454 fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount); 455 fprintf(out, "mem5.maxOut = %u\n", mem5.maxOut); 456 fprintf(out, "mem5.maxCount = %u\n", mem5.maxCount); 457 fprintf(out, "mem5.maxRequest = %u\n", mem5.maxRequest); 458 memsys5Leave(); 459 if( out==stdout ){ 460 fflush(stdout); 461 }else{ 462 fclose(out); 463 } 464 #endif 465 } 466 467 /* 468 ** This routine is the only routine in this file with external 469 ** linkage. It returns a pointer to a static sqlite3_mem_methods 470 ** struct populated with the memsys5 methods. 471 */ 472 const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){ 473 static const sqlite3_mem_methods memsys5Methods = { 474 memsys5Malloc, 475 memsys5Free, 476 memsys5Realloc, 477 memsys5Size, 478 memsys5Roundup, 479 memsys5Init, 480 memsys5Shutdown, 481 0 482 }; 483 return &memsys5Methods; 484 } 485 486 #endif /* SQLITE_ENABLE_MEMSYS5 */ 487