]>
git.ipfire.org Git - thirdparty/squid.git/blob - lib/util.c
3 * $Id: util.c,v 1.85 2002/04/06 13:57:11 hno Exp $
6 * AUTHOR: Harvest Derived
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
56 #include <gnumalloc.h>
57 #elif HAVE_MALLOC_H && !defined(_SQUID_FREEBSD_) && !defined(_SQUID_NEXT_)
73 static void default_failure_notify(const char *);
75 void (*failure_notify
) (const char *) = default_failure_notify
;
78 #if !defined(__CYGWIN__)
81 #define sys_nerr _sys_nerr
87 static FILE *tracefp
= NULL
;
90 log_trace_init(char *fn
)
92 tracefp
= fopen(fn
, "a+");
94 perror("log_trace_init");
108 #if XMALLOC_STATISTICS
109 #define DBG_MAXSIZE (1024*1024)
110 #define DBG_SPLIT (256) /* mallocs below this value are tracked with DBG_GRAIN_SM precision instead of DBG_GRAIN */
111 #define DBG_GRAIN (16)
112 #define DBG_GRAIN_SM (4)
113 #define DBG_OFFSET (DBG_SPLIT/DBG_GRAIN_SM - DBG_SPLIT/DBG_GRAIN )
114 #define DBG_MAXINDEX (DBG_MAXSIZE/DBG_GRAIN + DBG_OFFSET)
115 // #define DBG_INDEX(sz) (sz<DBG_MAXSIZE?(sz+DBG_GRAIN-1)/DBG_GRAIN:DBG_MAXINDEX)
116 static int malloc_sizes
[DBG_MAXINDEX
+ 1];
117 static int malloc_histo
[DBG_MAXINDEX
+ 1];
118 static int dbg_stat_init
= 0;
123 if (sz
>= DBG_MAXSIZE
)
127 return (sz
+ DBG_GRAIN_SM
- 1) / DBG_GRAIN_SM
;
129 return (sz
+ DBG_GRAIN
- 1) / DBG_GRAIN
+ DBG_OFFSET
;
136 for (i
= 0; i
<= DBG_MAXINDEX
; i
++)
137 malloc_sizes
[i
] = malloc_histo
[i
] = 0;
146 return malloc_sizes
[DBG_INDEX(sz
)] += 1;
150 malloc_statistics(void (*func
) (int, int, int, void *), void *data
)
153 for (i
= 0; i
<= DBG_SPLIT
; i
+= DBG_GRAIN_SM
)
154 func(i
, malloc_sizes
[DBG_INDEX(i
)], malloc_histo
[DBG_INDEX(i
)], data
);
156 for (i
= i
; i
<= DBG_MAXSIZE
; i
+= DBG_GRAIN
)
157 func(i
, malloc_sizes
[DBG_INDEX(i
)], malloc_histo
[DBG_INDEX(i
)], data
);
158 xmemcpy(&malloc_histo
, &malloc_sizes
, sizeof(malloc_sizes
));
160 #endif /* XMALLOC_STATISTICS */
165 char *xmalloc_file
= "";
166 int xmalloc_line
= 0;
167 char *xmalloc_func
= "";
168 static int xmalloc_count
= 0;
169 int xmalloc_trace
= 0; /* Enable with -m option */
170 size_t xmalloc_total
= 0;
180 #define DBG_ARRY_SZ (1<<11)
181 #define DBG_ARRY_BKTS (1<<8)
182 static void *(*malloc_ptrs
)[DBG_ARRY_SZ
];
183 static int malloc_size
[DBG_ARRY_BKTS
][DBG_ARRY_SZ
];
185 static char *malloc_file
[DBG_ARRY_BKTS
][DBG_ARRY_SZ
];
186 static short malloc_line
[DBG_ARRY_BKTS
][DBG_ARRY_SZ
];
187 static int malloc_count
[DBG_ARRY_BKTS
][DBG_ARRY_SZ
];
189 static int dbg_initd
= 0;
191 #define DBG_HASH_BUCKET(ptr) (((((int)ptr)>>4)+(((int)ptr)>>12)+(((int)ptr)>>20))&0xFF)
197 /* calloc the ptrs so that we don't see them when hunting lost memory */
198 malloc_ptrs
= calloc(DBG_ARRY_BKTS
, sizeof(*malloc_ptrs
));
199 for (B
= 0; B
< DBG_ARRY_BKTS
; B
++) {
200 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
201 malloc_ptrs
[B
][I
] = NULL
;
202 malloc_size
[B
][I
] = 0;
204 malloc_file
[B
][I
] = NULL
;
205 malloc_line
[B
][I
] = 0;
206 malloc_count
[B
][I
] = 0;
217 B
= DBG_HASH_BUCKET(s
);
218 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
219 if (malloc_ptrs
[B
][I
] != s
)
221 malloc_ptrs
[B
][I
] = NULL
;
222 malloc_size
[B
][I
] = 0;
224 malloc_file
[B
][I
] = NULL
;
225 malloc_line
[B
][I
] = 0;
226 malloc_count
[B
][I
] = 0;
230 if (I
== DBG_ARRY_SZ
) {
231 snprintf(msg
, 128, "xfree: ERROR: s=%p not found!", s
);
232 (*failure_notify
) (msg
);
237 check_malloc(void *p
, size_t sz
)
243 B
= DBG_HASH_BUCKET(p
);
244 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
245 if (!(P
= malloc_ptrs
[B
][I
]))
247 Q
= P
+ malloc_size
[B
][I
];
248 if (P
<= p
&& p
< Q
) {
249 snprintf(msg
, 128, "xmalloc: ERROR: p=%p falls in P=%p+%d",
250 p
, P
, malloc_size
[B
][I
]);
251 (*failure_notify
) (msg
);
254 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
255 if (malloc_ptrs
[B
][I
])
257 malloc_ptrs
[B
][I
] = p
;
258 malloc_size
[B
][I
] = (int) sz
;
260 malloc_file
[B
][I
] = xmalloc_file
;
261 malloc_line
[B
][I
] = xmalloc_line
;
262 malloc_count
[B
][I
] = xmalloc_count
;
266 if (I
== DBG_ARRY_SZ
)
267 (*failure_notify
) ("xmalloc: debug out of array space!");
271 #if XMALLOC_TRACE && !HAVE_MALLOCBLKSIZE
273 xmallocblksize(void *p
)
276 B
= DBG_HASH_BUCKET(p
);
277 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
278 if (malloc_ptrs
[B
][I
] == p
)
279 return malloc_size
[B
][I
];
287 malloc_file_name(void *p
)
290 B
= DBG_HASH_BUCKET(p
);
291 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
292 if (malloc_ptrs
[B
][I
] == p
)
293 return malloc_file
[B
][I
];
298 malloc_line_number(void *p
)
301 B
= DBG_HASH_BUCKET(p
);
302 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
303 if (malloc_ptrs
[B
][I
] == p
)
304 return malloc_line
[B
][I
];
309 malloc_number(void *p
)
312 B
= DBG_HASH_BUCKET(p
);
313 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
314 if (malloc_ptrs
[B
][I
] == p
)
315 return malloc_count
[B
][I
];
320 xmalloc_show_trace(void *p
, int sign
)
322 int statMemoryAccounted();
323 static size_t last_total
= 0, last_accounted
= 0, last_mallinfo
= 0;
324 size_t accounted
= statMemoryAccounted();
328 struct mallinfo mp
= mallinfo();
329 mi
= mp
.uordblks
+ mp
.usmblks
+ mp
.hblkhd
;
331 sz
= xmallocblksize(p
) * sign
;
333 xmalloc_count
+= sign
> 0;
335 fprintf(stderr
, "%c%8p size=%5d/%d acc=%5d/%d mallinfo=%5d/%d %s:%d %s",
336 sign
> 0 ? '+' : '-', p
,
337 (int) xmalloc_total
- last_total
, (int) xmalloc_total
,
338 (int) accounted
- last_accounted
, (int) accounted
,
339 (int) mi
- last_mallinfo
, (int) mi
,
340 xmalloc_file
, xmalloc_line
, xmalloc_func
);
342 fprintf(stderr
, " (%d %s:%d)\n", malloc_number(p
), malloc_file_name(p
), malloc_line_number(p
));
344 fprintf(stderr
, " %d\n", xmalloc_count
);
346 last_total
= xmalloc_total
;
347 last_accounted
= accounted
;
351 short malloc_refs
[DBG_ARRY_BKTS
][DBG_ARRY_SZ
];
352 #define XMALLOC_LEAK_ALIGN (4)
354 xmalloc_scan_region(void *start
, int size
, int depth
)
358 char *end
= ptr
+ size
- XMALLOC_LEAK_ALIGN
;
361 void *p
= *(void **) ptr
;
362 if (p
&& p
!= start
) {
363 B
= DBG_HASH_BUCKET(p
);
364 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
365 if (malloc_ptrs
[B
][I
] == p
) {
366 if (!malloc_refs
[B
][I
]++) {
367 /* A new reference */
368 fprintf(stderr
, "%*s%p %s:%d size %d allocation %d\n",
370 malloc_ptrs
[B
][I
], malloc_file
[B
][I
],
371 malloc_line
[B
][I
], malloc_size
[B
][I
],
373 sum
+= malloc_size
[B
][I
];
374 xmalloc_scan_region(malloc_ptrs
[B
][I
], malloc_size
[B
][I
], depth
+ 1);
376 if (sum
!= malloc_size
[B
][I
])
377 fprintf(stderr
, "=== %d bytes\n", sum
);
380 #if XMALLOC_SHOW_ALL_REFERENCES
382 /* We have already scanned this pointer... */
383 fprintf(stderr
, "%*s%p %s:%d size %d allocation %d ... (%d)\n",
385 malloc_ptrs
[B
][I
], malloc_file
[B
][I
],
386 malloc_line
[B
][I
], malloc_size
[B
][I
],
387 malloc_count
[B
][I
], malloc_refs
[B
][I
]);
393 ptr
+= XMALLOC_LEAK_ALIGN
;
398 xmalloc_find_leaks(void)
404 fprintf(stderr
, "----- Memory map ----\n");
405 xmalloc_scan_region(&_etext
, (void *) sbrk(0) - (void *) &_etext
, 0);
406 for (B
= 0; B
< DBG_ARRY_BKTS
; B
++) {
407 for (I
= 0; I
< DBG_ARRY_SZ
; I
++) {
408 if (malloc_ptrs
[B
][I
] && malloc_refs
[B
][I
] == 0) {
409 /* Found a leak... */
410 fprintf(stderr
, "Leak found: %p", malloc_ptrs
[B
][I
]);
411 fprintf(stderr
, " %s", malloc_file
[B
][I
]);
412 fprintf(stderr
, ":%d", malloc_line
[B
][I
]);
413 fprintf(stderr
, " size %d", malloc_size
[B
][I
]);
414 fprintf(stderr
, " allocation %d\n", malloc_count
[B
][I
]);
415 leak_sum
+= malloc_size
[B
][I
];
420 fprintf(stderr
, "Total leaked memory: %d\n", leak_sum
);
422 fprintf(stderr
, "No memory leaks detected\n");
424 fprintf(stderr
, "----------------------\n");
426 #endif /* XMALLOC_TRACE */
429 * xmalloc() - same as malloc(3). Used for portability.
430 * Never returns NULL; fatal on error.
440 if ((p
= malloc(sz
)) == NULL
) {
441 if (failure_notify
) {
442 snprintf(msg
, 128, "xmalloc: Unable to allocate %d bytes!\n",
444 (*failure_notify
) (msg
);
453 #if XMALLOC_STATISTICS
457 xmalloc_show_trace(p
, 1);
461 fprintf(tracefp
, "m:%d:%p\n", sz
, p
);
467 * xfree() - same as free(3). Will not call free(3) if s == NULL.
473 xmalloc_show_trace(s
, -1);
484 fprintf(tracefp
, "f:%p\n", s
);
488 /* xxfree() - like xfree(), but we already know s != NULL */
490 xxfree(const void *s_const
)
492 void *s
= (void *) s_const
;
494 xmalloc_show_trace(s
, -1);
502 fprintf(tracefp
, "f:%p\n", s
);
507 * xrealloc() - same as realloc(3). Used for portability.
508 * Never returns NULL; fatal on error.
511 xrealloc(void *s
, size_t sz
)
516 xmalloc_show_trace(s
, -1);
525 if ((p
= realloc(s
, sz
)) == NULL
) {
526 if (failure_notify
) {
527 snprintf(msg
, 128, "xrealloc: Unable to reallocate %d bytes!\n",
529 (*failure_notify
) (msg
);
538 #if XMALLOC_STATISTICS
542 xmalloc_show_trace(p
, 1);
545 if (tracefp
) /* new ptr, old ptr, new size */
546 fprintf(tracefp
, "r:%p:%p:%d\n", p
, s
, sz
);
552 * xcalloc() - same as calloc(3). Used for portability.
553 * Never returns NULL; fatal on error.
556 xcalloc(size_t n
, size_t sz
)
564 if ((p
= calloc(n
, sz
)) == NULL
) {
565 if (failure_notify
) {
566 snprintf(msg
, 128, "xcalloc: Unable to allocate %u blocks of %u bytes!\n",
567 (unsigned int) n
, (unsigned int) sz
);
568 (*failure_notify
) (msg
);
575 check_malloc(p
, sz
* n
);
577 #if XMALLOC_STATISTICS
581 xmalloc_show_trace(p
, 1);
585 fprintf(tracefp
, "c:%u:%u:%p\n", (unsigned int) n
, (unsigned int) sz
, p
);
591 * xstrdup() - same as strdup(3). Used for portability.
592 * Never returns NULL; fatal on error.
595 xstrdup(const char *s
)
599 if (failure_notify
) {
600 (*failure_notify
) ("xstrdup: tried to dup a NULL pointer!\n");
602 fprintf(stderr
, "xstrdup: tried to dup a NULL pointer!\n");
606 /* copy string, including terminating character */
608 return memcpy(xmalloc(sz
), s
, sz
);
612 * xstrndup() - string dup with length limit.
615 xstrndup(const char *s
, size_t n
)
623 return xstrncpy(xmalloc(sz
), s
, sz
);
627 * xstrerror() - strerror() wrapper
632 static char xstrerror_buf
[BUFSIZ
];
633 static char strerror_buf
[BUFSIZ
];
635 snprintf(strerror_buf
, BUFSIZ
, "%s", strerror(errno
));
638 snprintf(xstrerror_buf
, BUFSIZ
, "(%d) %s", errno
, strerror_buf
);
640 snprintf(xstrerror_buf
, BUFSIZ
, "(%d) Unknown", errno
);
641 return xstrerror_buf
;
646 * xbstrerror with argument for late notification */
651 static char xbstrerror_buf
[BUFSIZ
];
652 if (err
< 0 || err
>= sys_nerr
)
654 snprintf(xbstrerror_buf
, BUFSIZ
, "(%d) %s", err
, strerror(err
));
655 return xbstrerror_buf
;
664 *s
= tolower((unsigned char) *s
);
670 tvSubMsec(struct timeval t1
, struct timeval t2
)
672 return (t2
.tv_sec
- t1
.tv_sec
) * 1000 +
673 (t2
.tv_usec
- t1
.tv_usec
) / 1000;
677 tvSubUsec(struct timeval t1
, struct timeval t2
)
679 return (t2
.tv_sec
- t1
.tv_sec
) * 1000000 +
680 (t2
.tv_usec
- t1
.tv_usec
);
684 tvSubDsec(struct timeval t1
, struct timeval t2
)
686 return (double) (t2
.tv_sec
- t1
.tv_sec
) +
687 (double) (t2
.tv_usec
- t1
.tv_usec
) / 1000000.0;
691 * xstrncpy() - similar to strncpy(3) but terminates string
692 * always with '\0' if (n != 0 and dst != NULL),
693 * and doesn't do padding
696 xstrncpy(char *dst
, const char *src
, size_t n
)
702 while (--n
!= 0 && *src
!= '\0')
708 /* returns the number of leading white spaces in str; handy in skipping ws */
710 xcountws(const char *str
)
714 while (xisspace(*str
)) {
722 /* somewhat safer calculation of %s */
724 xpercent(double part
, double whole
)
726 return xdiv(100 * part
, whole
);
730 xpercentInt(double part
, double whole
)
733 return (int) rint(xpercent(part
, whole
));
735 /* SCO 3.2v4.2 doesn't have rint() -- mauri@mbp.ee */
736 return (int) floor(xpercent(part
, whole
) + 0.5);
740 /* somewhat safer division */
742 xdiv(double nom
, double denom
)
744 return (denom
!= 0.0) ? nom
/ denom
: -1.0;
747 /* integer to string */
751 static char buf
[24]; /* 2^64 = 18446744073709551616 */
752 snprintf(buf
, sizeof(buf
), "%d", num
);
756 /* A default failure notifier when the main program hasn't installed any */
758 default_failure_notify(const char *message
)
760 write(2, message
, strlen(message
));
768 g
->gb
+= (g
->bytes
>> 30);
769 g
->bytes
&= (1 << 30) - 1;
773 gb_to_double(const gb_t
* g
)
775 return ((double) g
->gb
) * ((double) (1 << 30)) + ((double) g
->bytes
);
779 double_to_str(char *buf
, int buf_size
, double value
)
783 snprintf(buf
, buf_size
, "%.2f MB", value
/ 1e6
);
784 else if (value
< 1e12
)
785 snprintf(buf
, buf_size
, "%.3f GB", value
/ 1e9
);
787 snprintf(buf
, buf_size
, "%.4f TB", value
/ 1e12
);
792 gb_to_str(const gb_t
* g
)
795 * it is often convenient to call gb_to_str several times for _one_ printf
797 #define max_cc_calls 5
798 typedef char GbBuf
[32];
799 static GbBuf bufs
[max_cc_calls
];
800 static int call_id
= 0;
801 double value
= gb_to_double(g
);
802 char *buf
= bufs
[call_id
++];
803 if (call_id
>= max_cc_calls
)
807 snprintf(buf
, sizeof(GbBuf
), "%.2f MB", value
/ 1e6
);
808 else if (value
< 1e12
)
809 snprintf(buf
, sizeof(GbBuf
), "%.2f GB", value
/ 1e9
);
811 snprintf(buf
, sizeof(GbBuf
), "%.2f TB", value
/ 1e12
);