mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-03 17:24:18 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			239 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* $Id: xmalloc-debug.c,v 1.2 2007-10-04 11:52:03 nicm Exp $ */
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, and distribute this software for any
 | 
						|
 * purpose with or without fee is hereby granted, provided that the above
 | 
						|
 * copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 | 
						|
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 | 
						|
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
 | 
						|
#include <ctype.h>
 | 
						|
#include <dlfcn.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include "tmux.h"
 | 
						|
 | 
						|
/* Single xmalloc allocated block. */
 | 
						|
struct xmalloc_blk {
 | 
						|
	void	*caller;
 | 
						|
 | 
						|
	void	*ptr;
 | 
						|
	size_t	 size;
 | 
						|
 | 
						|
	SPLAY_ENTRY(xmalloc_blk) entry;
 | 
						|
};
 | 
						|
 | 
						|
/* Splay tree of allocated blocks. */
 | 
						|
SPLAY_HEAD(xmalloc_tree, xmalloc_blk);
 | 
						|
struct xmalloc_tree xmalloc_tree = SPLAY_INITIALIZER(&xmalloc_tree);
 | 
						|
 | 
						|
/* Various statistics. */
 | 
						|
size_t	xmalloc_allocated;
 | 
						|
size_t	xmalloc_freed;
 | 
						|
size_t	xmalloc_peak;
 | 
						|
u_int	xmalloc_frees;
 | 
						|
u_int	xmalloc_mallocs;
 | 
						|
u_int	xmalloc_reallocs;
 | 
						|
 | 
						|
/* Print function. */
 | 
						|
#define XMALLOC_PRINT log_debug
 | 
						|
 | 
						|
/* Bytes of unallocated blocks and number of allocated blocks to show. */
 | 
						|
#define XMALLOC_BYTES 8
 | 
						|
#define XMALLOC_LINES 32
 | 
						|
 | 
						|
/* Macro to update peek usage variable. */
 | 
						|
#define XMALLOC_UPDATE() do {						\
 | 
						|
	if (xmalloc_allocated - xmalloc_freed > xmalloc_peak)		\
 | 
						|
		xmalloc_peak = xmalloc_allocated - xmalloc_freed;	\
 | 
						|
} while (0)
 | 
						|
 | 
						|
/* Tree functions. */
 | 
						|
int xmalloc_cmp(struct xmalloc_blk *, struct xmalloc_blk *);
 | 
						|
SPLAY_PROTOTYPE(xmalloc_tree, xmalloc_blk, entry, xmalloc_cmp);
 | 
						|
SPLAY_GENERATE(xmalloc_tree, xmalloc_blk, entry, xmalloc_cmp);
 | 
						|
 | 
						|
/* Compare two blocks. */
 | 
						|
int
 | 
						|
xmalloc_cmp(struct xmalloc_blk *blk1, struct xmalloc_blk *blk2)
 | 
						|
{
 | 
						|
	uintptr_t	ptr1 = (uintptr_t) blk1->ptr;
 | 
						|
	uintptr_t	ptr2 = (uintptr_t) blk2->ptr;
 | 
						|
 | 
						|
	if (ptr1 < ptr2)
 | 
						|
		return (-1);
 | 
						|
	if (ptr1 > ptr2)
 | 
						|
		return (1);
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
/* Clear statistics and block list; used to start fresh after fork(2). */
 | 
						|
void
 | 
						|
xmalloc_clear(void)
 | 
						|
{
 | 
						|
 	struct xmalloc_blk	*blk;
 | 
						|
 | 
						|
	xmalloc_allocated = 0;
 | 
						|
	xmalloc_freed = 0;
 | 
						|
	xmalloc_peak = 0;
 | 
						|
	xmalloc_frees = 0;
 | 
						|
	xmalloc_mallocs = 0;
 | 
						|
	xmalloc_reallocs = 0;
 | 
						|
 | 
						|
	while (!SPLAY_EMPTY(&xmalloc_tree)) {
 | 
						|
		blk = SPLAY_ROOT(&xmalloc_tree);
 | 
						|
		SPLAY_REMOVE(xmalloc_tree, &xmalloc_tree, blk);
 | 
						|
		free(blk);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Print report of statistics and unfreed blocks. */
 | 
						|
void
 | 
						|
xmalloc_report(pid_t pid, const char *hdr)
 | 
						|
{
 | 
						|
 	struct xmalloc_blk	*blk;
 | 
						|
	u_char			*iptr;
 | 
						|
 	char	 		 buf[4 * XMALLOC_BYTES + 1], *optr;
 | 
						|
 	size_t		 	 len;
 | 
						|
  	u_int	 		 n;
 | 
						|
	Dl_info			 info;
 | 
						|
 | 
						|
 	XMALLOC_PRINT("%s: %ld: allocated=%zu, freed=%zu, difference=%zd, "
 | 
						|
	    "peak=%zu", hdr, (long) pid, xmalloc_allocated, xmalloc_freed,
 | 
						|
	    xmalloc_allocated - xmalloc_freed, xmalloc_peak);
 | 
						|
 	XMALLOC_PRINT("%s: %ld: mallocs=%u, reallocs=%u, frees=%u", hdr,
 | 
						|
	    (long) pid, xmalloc_mallocs, xmalloc_reallocs, xmalloc_frees);
 | 
						|
 | 
						|
	n = 0;
 | 
						|
	SPLAY_FOREACH(blk, xmalloc_tree, &xmalloc_tree) {
 | 
						|
		n++;
 | 
						|
		if (n >= XMALLOC_LINES)
 | 
						|
			continue;
 | 
						|
 | 
						|
		len = blk->size;
 | 
						|
		if (len > XMALLOC_BYTES)
 | 
						|
			len = XMALLOC_BYTES;
 | 
						|
 | 
						|
		memset(&info, 0, sizeof info);
 | 
						|
		if (dladdr(blk->caller, &info) == 0)
 | 
						|
			info.dli_sname = info.dli_saddr = NULL;
 | 
						|
 | 
						|
		optr = buf;
 | 
						|
		iptr = blk->ptr;
 | 
						|
		for (; len > 0; len--) {
 | 
						|
			if (isascii(*iptr) && !iscntrl(*iptr)) {
 | 
						|
				*optr++ = *iptr++;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			*optr++ = '\\';
 | 
						|
			*optr++ = '0' + ((*iptr >> 6) & 07);
 | 
						|
			*optr++ = '0' + ((*iptr >> 3) & 07);
 | 
						|
			*optr++ = '0' + (*iptr & 07);
 | 
						|
			iptr++;
 | 
						|
		}
 | 
						|
		*optr = '\0';
 | 
						|
 | 
						|
		XMALLOC_PRINT("%s: %ld: %u, %s+0x%02tx: [%p %zu: %s]", hdr,
 | 
						|
		    (long) pid, n, info.dli_sname, ((u_char *) blk->caller) -
 | 
						|
		    ((u_char *) info.dli_saddr), blk->ptr, blk->size, buf);
 | 
						|
	}
 | 
						|
	XMALLOC_PRINT("%s: %ld: %u unfreed blocks", hdr, (long) pid, n);
 | 
						|
}
 | 
						|
 | 
						|
/* Record a newly created block. */
 | 
						|
void
 | 
						|
xmalloc_new(void *caller, void *ptr, size_t size)
 | 
						|
{
 | 
						|
	struct xmalloc_blk	*blk;
 | 
						|
 | 
						|
	xmalloc_allocated += size;
 | 
						|
	XMALLOC_UPDATE();
 | 
						|
 | 
						|
	if ((blk = malloc(sizeof *blk)) == NULL)
 | 
						|
		abort();
 | 
						|
 | 
						|
	blk->ptr = ptr;
 | 
						|
	blk->size = size;
 | 
						|
 | 
						|
	blk->caller = caller;
 | 
						|
 | 
						|
	SPLAY_INSERT(xmalloc_tree, &xmalloc_tree, blk);
 | 
						|
 | 
						|
	xmalloc_mallocs++;
 | 
						|
	XMALLOC_UPDATE();
 | 
						|
}
 | 
						|
 | 
						|
/* Record changes to a block. */
 | 
						|
void
 | 
						|
xmalloc_change(void *caller, void *oldptr, void *newptr, size_t newsize)
 | 
						|
{
 | 
						|
	struct xmalloc_blk	*blk, key;
 | 
						|
	ssize_t			 change;
 | 
						|
 | 
						|
	if (oldptr == NULL) {
 | 
						|
		xmalloc_new(caller, newptr, newsize);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	key.ptr = oldptr;
 | 
						|
	blk = SPLAY_FIND(xmalloc_tree, &xmalloc_tree, &key);
 | 
						|
	if (blk == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	change = newsize - blk->size;
 | 
						|
	if (change > 0)
 | 
						|
		xmalloc_allocated += change;
 | 
						|
	else
 | 
						|
		xmalloc_freed -= change;
 | 
						|
	XMALLOC_UPDATE();
 | 
						|
 | 
						|
	SPLAY_REMOVE(xmalloc_tree, &xmalloc_tree, blk);
 | 
						|
 | 
						|
 	blk->ptr = newptr;
 | 
						|
	blk->size = newsize;
 | 
						|
 | 
						|
	blk->caller = caller;
 | 
						|
 | 
						|
	SPLAY_INSERT(xmalloc_tree, &xmalloc_tree, blk);
 | 
						|
 | 
						|
	xmalloc_reallocs++;
 | 
						|
	XMALLOC_UPDATE();
 | 
						|
}
 | 
						|
 | 
						|
/* Record a block free. */
 | 
						|
void
 | 
						|
xmalloc_free(void *ptr)
 | 
						|
{
 | 
						|
	struct xmalloc_blk	*blk, key;
 | 
						|
 | 
						|
	key.ptr = ptr;
 | 
						|
	blk = SPLAY_FIND(xmalloc_tree, &xmalloc_tree, &key);
 | 
						|
	if (blk == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	xmalloc_freed += blk->size;
 | 
						|
 | 
						|
	SPLAY_REMOVE(xmalloc_tree, &xmalloc_tree, blk);
 | 
						|
	free(blk);
 | 
						|
 | 
						|
	xmalloc_frees++;
 | 
						|
	XMALLOC_UPDATE();
 | 
						|
}
 | 
						|
 | 
						|
#endif /* DEBUG */
 |