Source:SLASH'EM 0.0.7E7F2/alloc.c
Jump to navigation
Jump to search
Below is the full text to alloc.c from the source code of SLASH'EM 0.0.7E7F2. To link to a particular line, write [[SLASH'EM 0.0.7E7F2/alloc.c#line123]], for example.
The latest source code for vanilla NetHack is at Source code.
The NetHack General Public License applies to screenshots, source code and other content from NetHack.
This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.
1. /* SCCS Id: @(#)alloc.c 3.4 1995/10/04 */ 2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3. /* NetHack may be freely redistributed. See license for details. */ 4. 5. /* to get the malloc() prototype from system.h */ 6. #define ALLOC_C /* comment line for pre-compiled headers */ 7. /* since this file is also used in auxiliary programs, don't include all the 8. * function declarations for all of nethack 9. */ 10. #define EXTERN_H /* comment line for pre-compiled headers */ 11. #include "config.h" 12. 13. /* [Ali] 14. * There are a number of preprocessor symbols which affect how this 15. * file is compiled: 16. * 17. * NHALLOC_DLL When set this causes the alloc module to build as a stand-alone 18. * allocator (which can either be used in object form for use 19. * with utility programs or combined with panic.o to form a DLL 20. * for dynamic linking with the game - this last allows for the 21. * possibility of 3rd party libraries to allocate memory via the 22. * module and thus be represented in the heap monitoring). 23. * 24. * Global symbols defined: malloc, calloc etc. 25. * monitor_heap_push, monitor_heap_pop etc. 26. * 27. * Warning: The version of panic defined in panic.o does _not_ 28. * attempt to save the game. nhalloc.dll should therefore only be 29. * used for testing. 30. * 31. * WIZARD When set this causes the definition of the fmt_ptr function. 32. * 33. * MONITOR_HEAP When set this causes the alloc module to define the nhalloc and 34. * nhfree functions (which can then be used by the alloc and free 35. * macros). This allows some basic monitoring of the heap for 36. * memory leaks by dumping the heap to a log file on exit. 37. * 38. * Note: This symbol also causes fmt_ptr to be defined. 39. * 40. * Note: If the INTERNAL_MALLOC symbol is also defined, then 41. * nhalloc and nhfree will use the monitor_heap_ functions to 42. * store information about where the memory was allocated from. 43. * Unless compiling on the WIN32 platform, this will also cause 44. * the definition of the monitor_heap_ functions, the trivial 45. * allocator and a standard front end (malloc and friends). 46. * On the WIN32 platform, the monitor_heap_ and standard allocator 47. * functions are left as external references. 48. */ 49. 50. /* to get the definitons of ENOMEM and errno */ 51. #if defined(NHALLOC_DLL) || defined(MONITOR_HEAP) && defined(INTERNAL_MALLOC) 52. #include <stdlib.h> 53. #include <errno.h> 54. #ifdef WIN32 55. #include <win32api.h> 56. #endif 57. #endif 58. 59. #ifdef NHALLOC_DLL 60. #undef free 61. #else /* NHALLOC_DLL */ 62. #if defined(MONITOR_HEAP) || defined(WIZARD) 63. char *FDECL(fmt_ptr, (const genericptr,char *)); 64. #endif 65. 66. #ifdef MONITOR_HEAP 67. #undef alloc 68. #undef free 69. extern void FDECL(free,(genericptr_t)); 70. static void NDECL(heapmon_init); 71. 72. static FILE *heaplog = 0; 73. static boolean tried_heaplog = FALSE; 74. #endif 75. 76. long *FDECL(alloc,(unsigned int)); 77. extern void VDECL(panic, (const char *,...)) PRINTF_F(1,2); 78. 79. 80. long * 81. alloc(lth) 82. register unsigned int lth; 83. { 84. #ifdef LINT 85. /* 86. * a ridiculous definition, suppressing 87. * "possible pointer alignment problem" for (long *) malloc() 88. * from lint 89. */ 90. long dummy = ftell(stderr); 91. 92. if(lth) dummy = 0; /* make sure arg is used */ 93. return(&dummy); 94. #else 95. register genericptr_t ptr; 96. 97. ptr = malloc(lth); 98. #ifndef MONITOR_HEAP 99. if (!ptr) panic("Memory allocation failure; cannot get %u bytes", lth); 100. #endif 101. return((long *) ptr); 102. #endif 103. } 104. 105. 106. #if defined(MONITOR_HEAP) || defined(WIZARD) 107. 108. # if defined(MICRO) || defined(WIN32) 109. /* we actually want to know which systems have an ANSI run-time library 110. * to know which support the new %p format for printing pointers. 111. * due to the presence of things like gcc, NHSTDC is not a good test. 112. * so we assume microcomputers have all converted to ANSI and bigger 113. * computers which may have older libraries give reasonable results with 114. * the cast. 115. */ 116. # define MONITOR_PTR_FMT 117. # endif 118. 119. # ifdef MONITOR_PTR_FMT 120. # define PTR_FMT "%p" 121. # define PTR_TYP genericptr_t 122. # else 123. # define PTR_FMT "%06lx" 124. # define PTR_TYP unsigned long 125. # endif 126. 127. /* format a pointer for display purposes; caller supplies the result buffer */ 128. char * 129. fmt_ptr(ptr, buf) 130. const genericptr ptr; 131. char *buf; 132. { 133. Sprintf(buf, PTR_FMT, (PTR_TYP)ptr); 134. return buf; 135. } 136. 137. #endif /* MONITOR_HEAP || WIZARD */ 138. #endif /* !NHALLOC_DLL */ 139. 140. #if defined(MONITOR_HEAP) && !defined(NHALLOC_DLL) 141. 142. /* If ${NH_HEAPLOG} is defined and we can create a file by that name, 143. then we'll log the allocation and release information to that file. */ 144. static void 145. heapmon_init() 146. { 147. char *logname = getenv("NH_HEAPLOG"); 148. 149. if (logname && *logname) 150. heaplog = fopen(logname, "w"); 151. tried_heaplog = TRUE; 152. } 153. 154. long * 155. nhalloc(lth, file, line) 156. unsigned int lth; 157. const char *file; 158. int line; 159. { 160. long *ptr; 161. char ptr_address[20]; 162. 163. #ifdef INTERNAL_MALLOC 164. monitor_heap_push(file, line); 165. #endif 166. ptr = alloc(lth); 167. #ifdef INTERNAL_MALLOC 168. (void)monitor_heap_pop(file, line, 0); 169. #endif 170. if (!tried_heaplog) heapmon_init(); 171. if (heaplog) 172. (void) fprintf(heaplog, "+%5u %s %4d %s\n", lth, 173. fmt_ptr((genericptr_t)ptr, ptr_address), 174. line, file); 175. /* potential panic in alloc() was deferred til here */ 176. if (!ptr) panic("Cannot get %u bytes, line %d of %s", 177. lth, line, file); 178. 179. return ptr; 180. } 181. 182. void 183. nhfree(ptr, file, line) 184. genericptr_t ptr; 185. const char *file; 186. int line; 187. { 188. char ptr_address[20]; 189. 190. if (!tried_heaplog) heapmon_init(); 191. if (heaplog) 192. (void) fprintf(heaplog, "- %s %4d %s\n", 193. fmt_ptr((genericptr_t)ptr, ptr_address), 194. line, file); 195. 196. free(ptr); 197. } 198. #endif /* MONITOR_HEAP && !NHALLOC_DLL */ 199. 200. /* 201. * Under Win32, the trivial allocator and monitor front end are 202. * placed in nhalloc.dll rather than in the main executable. 203. */ 204. 205. #if defined(NHALLOC_DLL) || (defined(INTERNAL_MALLOC) && !defined(WIN32)) 206. /* 207. * [Ali] A trivial malloc implementation. 208. * 209. * Notes: 210. * 211. * If attempting to port to a non-UNIX environment, you will need 212. * a replacement for the sbrk() call. Under UNIX, sbrk() returns 213. * contiguous blocks. If there is no equivalent call in the target 214. * environment, you will either need to make this allocator rather 215. * more intelligent, or just increase PAGESIZE to something huge like 216. * 1Mb to keep the resultant fragmentation under control. 217. * 218. * The alignment implementation is advisory at best; we assume that 219. * if the target enviroment is that bothered by alignment, sbrk() 220. * will return aligned blocks and the compiler will pad the triv_head 221. * structure appropriately. If either of these assumptions is not 222. * correct, the alignment will be relaxed, which is presumably okay. 223. * 224. * This implementation is _very_ vulnerable to memory allocation bugs 225. * in the main code. Don't enable it unless you are confident that 226. * no such bugs exist. 227. */ 228. 229. #define TRIV_ALIGN sizeof(double) 230. #define TRIV_PAGESIZE 4096 231. 232. struct triv_head { 233. size_t triv_size; /* Total block size (excluding header) */ 234. union { 235. struct { 236. size_t nb; /* Number of bytes allocated by user */ 237. void *userdata; 238. } alloc; 239. struct triv_head *next; /* Next free block */ 240. double d; /* Dummy for alignment */ 241. } u; 242. #define triv_nb u.alloc.nb 243. #define triv_userdata u.alloc.userdata 244. #define triv_next u.next /* Free blocks only */ 245. }; 246. 247. static struct triv_head *triv_freelist = NULL; 248. #ifdef WIN32 249. /* 250. * MS-Windows rounds size request up to a system page, so we need to 251. * ensure we always request an exact number of system pages. This 252. * variable holds TRIV_PAGESIZE rounded up accordingly. 253. */ 254. static unsigned int triv_pagesize = 0; 255. #endif 256. 257. #define ROUNDUP(nbytes, align) (((nbytes) + (align) - 1) / (align) * (align)) 258. 259. void *triv_get_userdata(void *p) 260. { 261. struct triv_head *h; 262. if (!p) 263. return p; 264. h = (struct triv_head *)p - 1; 265. return h->triv_userdata; 266. } 267. 268. void triv_set_userdata(void *p, void *d) 269. { 270. struct triv_head *h; 271. if (p) { 272. h = (struct triv_head *)p - 1; 273. h->triv_userdata = d; 274. } 275. } 276. 277. #define IS_CONTIGUOUS(h1,h2) ((h2) == \ 278. (struct triv_head *)((unsigned char *)((h1) + 1) + (h1)->triv_size)) 279. 280. void triv_free(void *p) 281. { 282. struct triv_head *f, *lf, *h; 283. if (!p) 284. return; 285. h = (struct triv_head *)p - 1; 286. /* Find f, the first free block _after_ h in memory */ 287. for (lf = NULL, f = triv_freelist; f; lf = f, f = f->triv_next) 288. if (f > h) 289. break; 290. if (IS_CONTIGUOUS(h, f)) { 291. /* f is contiguous with h; merge them */ 292. h->triv_size += sizeof(struct triv_head) + f->triv_size; 293. h->triv_next = f->triv_next; 294. } 295. else 296. /* f is not contiguous; insert new block */ 297. h->triv_next = f; 298. /* Update pointer from previous block */ 299. if (lf) 300. lf->triv_next = h; 301. else 302. triv_freelist = h; 303. /* Then check last free block _below_ h in memory */ 304. if (lf && IS_CONTIGUOUS(lf, h)) { 305. /* h is contiguous with lf; merge them */ 306. lf->triv_size += sizeof(struct triv_head) + h->triv_size; 307. lf->triv_next = h->triv_next; 308. } 309. } 310. 311. void *triv_malloc(size_t nb) 312. { 313. struct triv_head *f, *lf, *nf; 314. size_t size = ROUNDUP(nb, TRIV_ALIGN); 315. size_t pagesize; 316. if (!nb) 317. return NULL; /* _Not_ an error; don't set errno */ 318. for (lf = NULL, f = triv_freelist; f; lf = f, f = f->triv_next) 319. /* Use first block found which is large enough */ 320. if (f->triv_size >= size) { 321. /* Split the free block if there's enough space left over */ 322. if (f->triv_size - size > sizeof(struct triv_head)) { 323. nf = (struct triv_head *)((unsigned char *)(f + 1) + size); 324. nf->triv_size = f->triv_size - size - sizeof(struct triv_head); 325. nf->triv_next = f->triv_next; 326. f->triv_next = nf; 327. f->triv_size = size; 328. } 329. /* Remove block from freelist */ 330. if (lf) 331. lf->triv_next = f->triv_next; 332. else 333. triv_freelist = f->triv_next; 334. /* Initialise header and return */ 335. f->triv_nb = nb; 336. f->triv_userdata = NULL; 337. return f + 1; 338. } 339. /* Get new block */ 340. #ifdef WIN32 341. if (triv_pagesize == 0) { 342. SYSTEM_INFO si; 343. GetSystemInfo(&si); 344. triv_pagesize = ROUNDUP(TRIV_PAGESIZE, si.dwPageSize); 345. } 346. pagesize = ROUNDUP(sizeof(struct triv_head) + size, triv_pagesize); 347. f = VirtualAlloc(NULL, pagesize, MEM_COMMIT, PAGE_READWRITE); 348. #else 349. pagesize = ROUNDUP(sizeof(struct triv_head) + size, TRIV_PAGESIZE); 350. f = sbrk(pagesize); 351. #endif 352. if (!f) { 353. errno = ENOMEM; 354. return f; 355. } 356. /* Initialise header */ 357. f->triv_size = pagesize - sizeof(struct triv_head); 358. f->triv_nb = nb; 359. f->triv_userdata = NULL; 360. /* Split the new block if there's enough space left over */ 361. if (f->triv_size - size > sizeof(struct triv_head)) { 362. nf = (struct triv_head *)((unsigned char *)(f + 1) + size); 363. nf->triv_size = f->triv_size - size - sizeof(struct triv_head); 364. f->triv_size = size; 365. /* And contribute it to the free list */ 366. triv_free(nf + 1); 367. } 368. return f + 1; 369. } 370. 371. void *triv_calloc(size_t nobj, size_t size) 372. { 373. void *p; 374. size_t nb = nobj * size; 375. if (nb / nobj != size) { /* Check overflow */ 376. errno = ENOMEM; 377. return NULL; 378. } 379. p = triv_malloc(nb); 380. if (p) 381. memset(p, 0, nb); 382. return p; 383. } 384. 385. void *triv_realloc(void *p, size_t size) 386. { 387. struct triv_head *h; 388. void *np; 389. if (!size) { 390. triv_free(p); 391. return NULL; 392. } 393. if (!p) 394. return triv_malloc(size); 395. h = (struct triv_head *)p - 1; 396. if (size <= h->triv_size) { 397. h->triv_nb = size; 398. return p; 399. } 400. np = triv_malloc(size); 401. if (np) { 402. memcpy(np, p, h->triv_nb); 403. triv_set_userdata(np, triv_get_userdata(p)); 404. triv_free(p); 405. } 406. return np; 407. } 408. 409. /* Monitoring front-end */ 410. 411. struct monitor_id { 412. const char *id; /* Typically file name */ 413. int subid; /* Typically line number */ 414. }; 415. 416. static char *monitor_id_stack_default_id = "<none>"; 417. static int monitor_id_stack_depth = 0; 418. static struct monitor_id *monitor_id_stack = NULL; 419. static size_t monitor_mem_in_use = 0; 420. static boolean monitor_trace = 0; 421. static FILE *monitor_fp = NULL; 422. 423. #define MHF_MARKED 1 424. 425. struct monitor_head { 426. void *p; 427. size_t size; 428. struct monitor_id id; 429. unsigned long flags; 430. struct monitor_head *next, *prev; 431. }; 432. 433. static struct monitor_head *monitor_heap = NULL; 434. 435. void monitor_heap_push(const char *id, int subid) 436. { 437. ++monitor_id_stack_depth; 438. monitor_id_stack = triv_realloc(monitor_id_stack, 439. monitor_id_stack_depth * sizeof (*monitor_id_stack)); 440. if (!monitor_id_stack) 441. panic("monitor_heap_push: not enough memory"); 442. monitor_id_stack[monitor_id_stack_depth - 1].id = id; 443. monitor_id_stack[monitor_id_stack_depth - 1].subid = subid; 444. } 445. 446. /* retval is to make writing macros which use monitor_heap_push/pop easier */ 447. 448. unsigned long monitor_heap_pop(const char *id, int subid, unsigned long retval) 449. { 450. if (!monitor_id_stack_depth) 451. panic("monitor_heap_pop: empty stack"); 452. if (monitor_id_stack[monitor_id_stack_depth - 1].id != id || 453. monitor_id_stack[monitor_id_stack_depth - 1].subid != subid) 454. panic("monitor_heap_pop: mismatch: (%s, %d) != (%s, %d)", 455. monitor_id_stack[monitor_id_stack_depth - 1].id, 456. monitor_id_stack[monitor_id_stack_depth - 1].subid, 457. id, subid); 458. --monitor_id_stack_depth; 459. monitor_id_stack = triv_realloc(monitor_id_stack, 460. monitor_id_stack_depth * sizeof (*monitor_id_stack)); 461. return retval; 462. } 463. 464. void monitor_heap_set_subid(const char *id, int subid) 465. { 466. if (!monitor_id_stack_depth) 467. panic("monitor_heap_set_subid: empty stack"); 468. if (monitor_id_stack[monitor_id_stack_depth - 1].id != id) 469. panic("monitor_heap_set_subid: mismatch: %s != %s", 470. monitor_id_stack[monitor_id_stack_depth - 1].id, id); 471. monitor_id_stack[monitor_id_stack_depth - 1].subid = subid; 472. } 473. 474. size_t monitor_heap_getmem(void) 475. { 476. return monitor_mem_in_use; 477. } 478. 479. boolean monitor_heap_trace(boolean flag) 480. { 481. boolean retval = !!monitor_trace; 482. if (flag) 483. monitor_trace++; 484. else 485. monitor_trace--; 486. return retval; 487. } 488. 489. void monitor_heap_mark(void) 490. { 491. struct monitor_head *m; 492. for(m = monitor_heap; m; m = m->next) 493. m->flags |= MHF_MARKED; 494. } 495. 496. void monitor_heap_release(void) 497. { 498. int first = 1; 499. struct monitor_head *m; 500. if (!monitor_fp) 501. return; 502. for(m = monitor_heap; m; m = m->next) 503. m->flags ^= MHF_MARKED; 504. for(m = monitor_heap; m; m = m->next) 505. if (m->flags & MHF_MARKED) { 506. if (first) { 507. fprintf(monitor_fp, 508. "Blocks allocated but not freed during mark/release:\n"); 509. fprintf(monitor_fp, "Address Size Sub-ID ID\n"); 510. first = 0; 511. } 512. fprintf(monitor_fp, 513. "%-16p%-8lu%-8d%s\n", m->p, m->size, m->id.subid, m->id.id); 514. } 515. } 516. 517. static void monitor_dump(void) 518. { 519. int first = 1; 520. struct monitor_head *m; 521. FILE *fp; 522. size_t used = 0; 523. if (!monitor_fp) 524. return; 525. monitor_heap_mark(); 526. fprintf(monitor_fp, "Dump of all monitored blocks in heap:\n"); 527. for(m = monitor_heap; m; m = m->next) { 528. if (m->flags & MHF_MARKED) { 529. used += m->size; 530. if (m->id.id == monitor_id_stack_default_id) 531. continue; 532. if (first) { 533. fprintf(monitor_fp, "Address Size Sub-ID ID\n"); 534. first = 0; 535. } 536. fprintf(monitor_fp, 537. "%-16p%-8lu%-8d%s\n", m->p, m->size, m->id.subid, m->id.id); 538. } 539. } 540. if (first) 541. fprintf(monitor_fp, 542. "No monitored blocks in heap with non-default ID.\n"); 543. fprintf(monitor_fp, "Total used space: %lu bytes\n", used); 544. } 545. 546. void *malloc(size_t nb) 547. { 548. static int busy = 0; 549. static int inited = 0; 550. struct monitor_head *m; 551. if (!busy && !(inited&1) && !monitor_id_stack_depth) { 552. inited|=1; 553. monitor_heap_push(monitor_id_stack_default_id, 0); 554. } 555. if (!busy && !(inited&2)) { 556. char *s; 557. inited|=2; 558. atexit(monitor_dump); 559. s = getenv("NH_HEAPDUMP"); 560. monitor_fp = s ? fopen(s, "w"): NULL; 561. } 562. if (!nb) 563. return NULL; 564. busy++; 565. m = triv_malloc(sizeof(*m)); 566. if (!m) { 567. busy--; 568. return m; 569. } 570. m->size = nb; 571. m->flags = 0; 572. m->p = triv_malloc(nb); 573. if (monitor_trace) 574. fprintf(stderr, "malloc(%d) = %p\n", nb, m->p); 575. if (!m->p) { 576. triv_free(m); 577. busy--; 578. return NULL; 579. } 580. monitor_mem_in_use += nb; 581. m->prev = NULL; 582. m->next = monitor_heap; 583. m->id = monitor_id_stack[monitor_id_stack_depth - 1]; 584. if (monitor_heap) 585. monitor_heap->prev = m; 586. monitor_heap = m; 587. triv_set_userdata(m->p, m); 588. busy--; 589. return m->p; 590. } 591. 592. void *calloc(size_t nobj, size_t size) 593. { 594. void *p; 595. size_t nb = nobj * size; 596. if (nb / nobj != size) { /* Check overflow */ 597. errno = ENOMEM; 598. return NULL; 599. } 600. p = malloc(nb); 601. if (p) 602. memset(p, 0, nb); 603. return p; 604. } 605. 606. void *realloc(void *p, size_t size) 607. { 608. struct monitor_head *m; 609. void *np; 610. if (!size) { 611. free(p); 612. return NULL; 613. } 614. if (!p) 615. return malloc(size); 616. m = triv_get_userdata(p); 617. np = triv_realloc(p, size); 618. if (monitor_trace) 619. fprintf(stderr, "realloc(%p, %d) = %p\n", p, size, np); 620. if (np) { 621. monitor_mem_in_use += size - m->size; 622. m->size = size; 623. m->p = np; 624. } 625. return np; 626. } 627. 628. void free(void *p) 629. { 630. struct monitor_head *m; 631. if (p) 632. { 633. if (monitor_trace) 634. fprintf(stderr, "free(%p)\n", p); 635. m = triv_get_userdata(p); 636. monitor_mem_in_use -= m->size; 637. triv_free(p); 638. if (m->prev) 639. m->prev->next = m->next; 640. else 641. monitor_heap = m->next; 642. if (m->next) 643. m->next->prev = m->prev; 644. triv_free(m); 645. } 646. } 647. 648. #endif /* NHALLOC_DLL || INTERNAL_MALLOC && !WIN32 */ 649. 650. /*alloc.c*/