Bug 454376 add -lCrun -lCstd for Solaris OS_LIBS, r=bsmedberg
[wine-gecko.git] / tools / leaky / libmalloc.cpp
bloba82278be466c6bc0347ed230faca0560eca7e5be
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is mozilla.org code.
16 * The Initial Developer of the Original Code is
17 * Kipp E.B. Hickman.
18 * Portions created by the Initial Developer are Copyright (C) 1999
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "libmalloc.h"
38 #include <memory.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <setjmp.h>
45 #include <dlfcn.h>
47 #ifdef NTO
48 #include <sys/link.h>
49 extern r_debug _r_debug;
50 #else
51 #include <link.h>
52 #endif
54 #ifdef NTO
55 #define JB_BP 0x08
56 #include <setjmp.h>
57 #endif
59 extern "C" {
60 #ifdef NEED_WRAPPERS
61 void* __wrap_malloc(size_t);
62 void* __wrap_realloc(void*, size_t);
63 void __wrap_free(void*);
64 void* __wrap___builtin_new(size_t);
65 void __wrap___builtin_delete(void*);
66 void* __wrap___builtin_vec_new(size_t);
67 void __wrap___builtin_vec_delete(void*);
68 void* __wrap_PR_Malloc(size_t);
69 void* __wrap_PR_Calloc(size_t, size_t);
70 void* __wrap_PR_Realloc(void*, size_t);
71 void __wrap_PR_Free(void*);
72 #endif
75 static int gLogFD = -1;
76 static u_long gFlags;
77 static int gFillCount = 0;
79 #define MARKER0 0xFFFFFFFF
80 #define MARKER1 0xEEEEEEEE
81 #define MARKER2_0 0xD8D8D8D8
82 #define MARKER2_1 0xE8E8E8E8
84 #define PATTERN_BYTE 0xFE
85 #define FREE_PATTERN_BYTE 0xDE
87 struct Header {
88 u_long marker0;
89 size_t rawSize; // user requested size
90 size_t size; // size rounded up
91 u_long marker1;
94 struct Trailer {
95 u_long marker2[2];
98 //----------------------------------------------------------------------
100 #if defined(i386)
101 static void CrawlStack(malloc_log_entry* me, jmp_buf jb)
103 #ifdef NTO
104 u_long* bp = (u_long*) (jb[0].__savearea[JB_BP]);
105 #else
106 u_long* bp = (u_long*) (jb[0].__jmpbuf[JB_BP]);
107 #endif
108 u_long numpcs = 0;
109 int skip = 2;
110 while (numpcs < MAX_STACK_CRAWL) {
111 u_long* nextbp = (u_long*) *bp++;
112 u_long pc = *bp;
113 if ((pc < 0x08000000) || (pc > 0x7fffffff) || (nextbp < bp)) {
114 break;
116 if (--skip < 0) {
117 me->pcs[numpcs++] = (char*) pc;
119 bp = nextbp;
121 me->numpcs = numpcs;
123 #endif
125 //----------------------------------------------------------------------
127 #if defined(linux) || defined(NTO)
128 static void DumpAddressMap()
130 int mfd = open("malloc-map", O_CREAT|O_WRONLY|O_TRUNC, 0666);
131 if (mfd >= 0) {
132 malloc_map_entry mme;
133 link_map* map = _r_debug.r_map;
134 while (NULL != map) {
135 if (0 != map->l_addr) {
136 mme.nameLen = strlen(map->l_name);
137 mme.address = map->l_addr;
138 write(mfd, &mme, sizeof(mme));
139 write(mfd, map->l_name, mme.nameLen);
140 #if 0
141 write(1, map->l_name, mme.nameLen);
142 write(1, "\n", 1);
143 #endif
145 map = map->l_next;
147 close(mfd);
150 #endif
152 //----------------------------------------------------------------------
154 static int Verify(Header* h)
156 // Sanity check the header first
157 if ((h->marker0 != MARKER0) ||
158 (h->marker1 != MARKER1) ||
159 (h->rawSize > h->size)) {
160 DumpAddressMap();
161 abort();
164 // Sanity check the trailer second
165 Trailer* t = (Trailer*) ((char*)(h + 1) + h->size);
166 if ((t->marker2[0] != MARKER2_0) ||
167 (t->marker2[1] != MARKER2_1)) {
168 DumpAddressMap();
169 abort();
172 // Verify there were no overruns
173 size_t fill = h->size - h->rawSize;
174 if (0 != fill) {
175 unsigned char* cp = ((unsigned char*)(h + 1)) + h->rawSize;
176 unsigned char* end = cp + fill;
177 while (cp < end) {
178 unsigned char ch = *cp++;
179 if (ch != PATTERN_BYTE) {
180 DumpAddressMap();
181 abort();
185 return 1;
188 static void
189 Log(int aType, void* aAddr, size_t aSize, void* aOldAddr)
191 malloc_log_entry me;
193 me.type = (u_long) aType;
194 me.address = (u_long) aAddr;
195 me.size = (u_long) aSize;
196 me.oldaddress = (u_long) aOldAddr;
198 jmp_buf jb;
199 setjmp(jb);
200 CrawlStack(&me, jb);
202 write(gLogFD, &me, sizeof(me) - MAX_STACK_CRAWL*sizeof(char*) +
203 me.numpcs*sizeof(char*));
206 static void*
207 MallocHook(size_t aSize, u_long aLogType)
209 size_t roundedSize = aSize;
210 roundedSize = ((roundedSize + 4 + gFillCount) >> 2) << 2;
212 void* ptr = REAL_MALLOC(sizeof(Header) + roundedSize + sizeof(Trailer));
214 if (NULL != ptr) {
215 Header* h = (Header*) ptr;
216 h->rawSize = aSize;
217 h->size = roundedSize;
218 h->marker0 = MARKER0;
219 h->marker1 = MARKER1;
221 ptr = (void*) ((char*)(h+1));
223 // Fill new memory with a pattern to help detect overruns and
224 // usage of un-written memory
225 memset(ptr, PATTERN_BYTE, roundedSize);
227 Trailer* t = (Trailer*) ((char*)ptr + roundedSize);
228 t->marker2[0] = MARKER2_0;
229 t->marker2[1] = MARKER2_1;
231 if (LIBMALLOC_LOG & gFlags) {
232 Log(aLogType, ptr, aSize, 0);
236 return ptr;
239 static void
240 FreeHook(void* aAddr, u_long aLogType)
242 if (0 == aAddr) {
243 return;
245 if (LIBMALLOC_LOG & gFlags) {
246 Log(aLogType, aAddr, 0, 0);
248 Header* h = (Header*) ((char*)aAddr - sizeof(Header));
249 if (Verify(h)) {
250 // Munge the header so that a dup-free will fail the verify
251 h->marker0 = 0xDEADBEEF;
252 h->marker1 = 0xDEADBEEF;
254 // Munge the body of the allocation so that if the user
255 // still has a live reference they get messed up
256 void* ptr = (void*) ((char*)(h+1));
257 memset(ptr, FREE_PATTERN_BYTE, h->rawSize);
259 if (0 == (LIBMALLOC_NOFREE & gFlags)) {
260 REAL_FREE(h);
263 else {
264 if (0 == (LIBMALLOC_NOFREE & gFlags)) {
265 REAL_FREE(aAddr);
270 static void*
271 ReallocHook(void* aOldAddr, size_t aSize)
273 if (0 == aOldAddr) {
274 return MallocHook(aSize, malloc_log_malloc);
276 Header* oldh = (Header*) ((char*)aOldAddr - sizeof(Header));
277 if (!Verify(oldh)) {
278 return REAL_REALLOC(aOldAddr, aSize);
280 size_t oldSize = oldh->rawSize;
282 size_t roundedSize = aSize;
283 roundedSize = ((roundedSize + 4) >> 2) << 2;
285 void* ptr = REAL_MALLOC(sizeof(Header) + roundedSize + sizeof(Trailer));
287 if (NULL != ptr) {
288 Header* h = (Header*) ptr;
289 h->rawSize = aSize;
290 h->size = roundedSize;
291 h->marker0 = MARKER0;
292 h->marker1 = MARKER1;
294 ptr = (void*) ((char*)(h+1));
296 Trailer* t = (Trailer*) ((char*)ptr + roundedSize);
297 t->marker2[0] = MARKER2_0;
298 t->marker2[1] = MARKER2_1;
300 // Copy old memory into new memory (don't copy too much!)
301 size_t copy = oldSize;
302 if (copy > aSize) copy = aSize;
303 memcpy(ptr, aOldAddr, copy);
305 // Fill any uncopied memory with the overrun pattern
306 size_t fill = roundedSize - copy;
307 if (0 != fill) {
308 memset((char*)ptr + copy, PATTERN_BYTE, fill);
311 if (0 == (LIBMALLOC_NOFREE & gFlags)) {
312 REAL_FREE(oldh);
314 else {
315 // Mark the old header so that a verify will fail if the caller
316 // dup free's it.
317 oldh->marker0 = 0xDEADBEEF;
318 oldh->marker1 = 0xDEADBEEF;
320 // Munge the body of the old allocation so that if the user
321 // still has a live reference they get messed up
322 void* optr = (void*) ((char*)(oldh+1));
323 memset(optr, FREE_PATTERN_BYTE, oldh->rawSize);
326 if (LIBMALLOC_LOG & gFlags) {
327 Log(malloc_log_realloc, ptr, aSize, aOldAddr);
330 return ptr;
333 u_long
334 SetMallocFlags(u_long aFlags)
336 u_long old = gFlags;
337 gFlags = aFlags;
339 if ((-1 == gLogFD) && ((LIBMALLOC_LOG|LIBMALLOC_LOG_RC) & gFlags)) {
340 gLogFD = open("malloc-log", O_CREAT|O_WRONLY|O_TRUNC, 0666);
341 if (gLogFD < 0) {
342 gFlags &= ~LIBMALLOC_LOG;
343 printf("unable to create malloc-log: %d\n", errno);
346 if ((gLogFD >= 0) && (0 == ((LIBMALLOC_LOG|LIBMALLOC_LOG_RC) & gFlags))) {
347 close(gLogFD);
348 gLogFD = -1;
350 #ifndef NTO
351 if (LIBMALLOC_CHECK & gFlags) {
352 mallopt(M_CHECK_ACTION, 1);
354 #endif
356 // Try to guarantee that the address map is always dumped
357 atexit(DumpAddressMap);
359 return old;
362 static int gFirstTime = 1;
364 static void Init()
366 gFirstTime = 0;
367 u_long flags = 0;
368 char* s = getenv("LIBMALLOC_LOG");
369 if (s) {
370 flags = atoi(s);
371 if (LIBMALLOC_LOG & flags) {
372 char m1[] = "dbgmalloc: enabled memory logging\n";
373 write(1, m1, sizeof(m1)-1);
375 if (LIBMALLOC_LOG_RC & flags) {
376 char m2[] = "dbgmalloc: enabled refcnt logging\n";
377 write(1, m2, sizeof(m2)-1);
379 if (LIBMALLOC_NOFREE & flags) {
380 char m3[] = "dbgmalloc: disabled free\n";
381 write(1, m3, sizeof(m3)-1);
384 SetMallocFlags(flags);
385 s = getenv("LIBMALLOC_FILL");
386 if (s) {
387 gFillCount = atoi(s);
388 char m4[] = "dbgmalloc: adding extra memory fill ";
389 write(1, m4, sizeof(m4)-1);
390 write(1, s, strlen(s));
391 write(1, "\n", 1);
395 //----------------------------------------------------------------------
397 #ifdef NEED_WRAPPERS
398 void* __wrap_malloc(size_t aSize)
400 if (gFirstTime) {
401 Init();
403 return MallocHook(aSize, malloc_log_malloc);
406 void* __wrap_realloc(void* aPtr, size_t aSize)
408 if (gFirstTime) {
409 Init();
411 return ReallocHook(aPtr, aSize);
414 void __wrap_free(void* aPtr)
416 if (gFirstTime) {
417 Init();
419 FreeHook(aPtr, malloc_log_free);
422 void* __wrap___builtin_new(size_t aSize)
424 if (gFirstTime) {
425 Init();
427 return MallocHook(aSize, malloc_log_new);
430 void __wrap___builtin_delete(void* aPtr)
432 if (gFirstTime) {
433 Init();
435 FreeHook(aPtr, malloc_log_delete);
438 void* __wrap___builtin_vec_new(size_t aSize)
440 if (gFirstTime) {
441 Init();
443 return MallocHook(aSize, malloc_log_new);
446 void __wrap___builtin_vec_delete(void* aPtr)
448 if (gFirstTime) {
449 Init();
451 FreeHook(aPtr, malloc_log_delete);
454 void* __wrap_PR_Malloc(size_t aSize)
456 if (gFirstTime) {
457 Init();
459 return MallocHook(aSize, malloc_log_malloc);
462 void* __wrap_PR_Calloc(size_t aSize, size_t aBsize)
464 if (gFirstTime) {
465 Init();
467 size_t size = aSize*aBsize;
468 void* ptr = MallocHook(size, malloc_log_malloc);
469 if (NULL != ptr) {
470 memset(ptr, 0, size);
472 return ptr;
475 void* __wrap_PR_Realloc(void* aPtr, size_t aSize)
477 if (gFirstTime) {
478 Init();
480 return ReallocHook(aPtr, aSize);
483 void __wrap_PR_Free(void* aPtr)
485 if (gFirstTime) {
486 Init();
488 FreeHook(aPtr, malloc_log_free);
490 #endif
492 //----------------------------------------
494 // Strong symbols so that libc references are routed to us
496 void* malloc(size_t aSize)
498 if (gFirstTime) {
499 Init();
501 return MallocHook(aSize, malloc_log_malloc);
504 void* realloc(void* aPtr, size_t aSize)
506 if (gFirstTime) {
507 Init();
509 return ReallocHook(aPtr, aSize);
512 void free(void* aPtr)
514 if (gFirstTime) {
515 Init();
517 FreeHook(aPtr, malloc_log_free);
520 void* calloc(size_t aSize, size_t aBsize)
522 if (gFirstTime) {
523 Init();
525 size_t size = aSize*aBsize;
526 void* ptr = MallocHook(size, malloc_log_malloc);
527 if (NULL != ptr) {
528 memset(ptr, 0, size);
530 return ptr;
533 void cfree(void* ptr)
535 if (gFirstTime) {
536 Init();
538 FreeHook(ptr, malloc_log_free);
541 void* memalign(size_t alignment, size_t size)
543 ::abort();
546 void* valloc(size_t size)
548 ::abort();
551 void* pvalloc(size_t size)
553 ::abort();
556 void* __builtin_new(size_t aSize)
558 if (gFirstTime) {
559 Init();
561 return MallocHook(aSize, malloc_log_new);
564 void __builtin_delete(void* aPtr)
566 if (gFirstTime) {
567 Init();
569 FreeHook(aPtr, malloc_log_delete);
572 void* __builtin_vec_new(size_t aSize)
574 if (gFirstTime) {
575 Init();
577 return MallocHook(aSize, malloc_log_new);
580 void __builtin_vec_delete(void* aPtr)
582 if (gFirstTime) {
583 Init();
585 FreeHook(aPtr, malloc_log_delete);
588 void
589 __log_addref(void* p, int oldrc, int newrc)
591 if (gFirstTime) {
592 Init();
594 if (LIBMALLOC_LOG_RC & gFlags) {
595 Log(malloc_log_addref, p, size_t(oldrc), (void*)newrc);
599 void
600 __log_release(void* p, int oldrc, int newrc)
602 if (gFirstTime) {
603 Init();
605 if (LIBMALLOC_LOG_RC & gFlags) {
606 Log(malloc_log_release, p, size_t(oldrc), (void*)newrc);