⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 memintercept.c

📁 memprof source code, linux
💻 C
字号:
/* -*- mode: C; c-file-style: "linux" -*- *//* MemProf -- memory profiler and leak detector * Copyright 1999, 2000, 2001, Red Hat, Inc. * Copyright 2002, Kristian Rietveld * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//*====*/#define _GNU_SOURCE#include <malloc.h>#include <stdlib.h>#include <unistd.h>#include <dlfcn.h>#include <errno.h>#include <fcntl.h>#include <string.h>#include "memintercept.h"#include "memintercept-utils.h"#include "stack-frame.h"#include <sys/socket.h>#include <sys/types.h>#include <sys/un.h>/* The code in this file is written to work for threaded operation without * any reference to the thread library in operation. There are only * two places where any interaction between threads is necessary - * allocation of new slots in the pids[] array, and a parent thread * waiting for a child thread to start. */typedef struct {	uint32_t ref_count;	pid_t pid;	int outfd;	pid_t clone_pid;	/* See comments in clone_thunk */} ThreadInfo;static void new_process (ThreadInfo *thread,			 pid_t       old_pid,			 MIOperation operation);static int (*old_execve) (const char *filename,			  char *const argv[],			  char *const envp[]);static int (*old_fork) (void);static int (*old_vfork) (void);static int (*old_clone) (int (*fn) (void *arg),			 void *child_stack,			 int flags,			 void *arg);static void *(*old_malloc) (size_t size);static void *(*old_calloc) (size_t nmemb, size_t size);static void *(*old_memalign) (size_t boundary, size_t size);static void * (*old_realloc) (void *ptr, size_t size);static void (*old_free) (void *ptr);static void (*old__exit) (int status);static int initialized = 0;static int tracing = 1;#define MAX_THREADS 128static ThreadInfo threads[MAX_THREADS];static char *socket_path = NULL;static unsigned int seqno = 0;#define ENABLE_DEBUG#ifdef ENABLE_DEBUG#define MI_DEBUG(arg) mi_debug arg#else /* !ENABLE_DEBUG */#define MI_DEBUG(arg) (void)0#endif /* ENABLE_DEBUG */static ThreadInfo *allocate_thread (pid_t pid){	int i;		for (i = 0; i < MAX_THREADS; i++) {		if (threads[i].ref_count == 0) {			unsigned int new_count = mi_atomic_increment (&threads[i].ref_count);			if (new_count == 1) {				threads[i].pid = pid;				threads[i].clone_pid = 0;				return &threads[i];			} else				mi_atomic_decrement (&threads[i].ref_count);		}	}		mi_debug ("Can't find free thread slot");	tracing = 0;	_exit(1);	return NULL;}static voidrelease_thread (ThreadInfo *thread){	thread->pid = 0;	mi_atomic_decrement (&thread->ref_count);}static ThreadInfo *find_thread (pid_t pid){	int i;#if 1	ThreadInfo *thread;#else	/* Over-optimized GCC/GLibc extension happy version	 * Requires gcc-3.2 and glibc-2.3.	 */	static __thread ThreadInfo *thread = NULL;	int i;	if (__builtin_expect (thread == NULL, 0))		return thread;#endif		for (i=0; i < MAX_THREADS; i++)		if (threads[i].pid == pid) {			thread = &threads[i];			if (thread->clone_pid) {				/* See comments in clone_thunk() */				new_process (thread, thread->clone_pid, MI_CLONE);				thread->clone_pid = 0;			}			return thread;		}	mi_debug ("Thread not found\n");	tracing = 0;	_exit(1);	return NULL;}static voidinitialize (){	/* It's possible to get recursion here, since dlsym() can trigger	 * memory allocation. To deal with this, we flag the initialization	 * condition specially, then use the special knowledge that it's	 * OK for malloc to fail during initialization (libc degrades	 * gracefully), so we just return NULL from malloc(), realloc().	 *	 * This trick is borrowed from from libc's memusage.	 */	initialized = -1;	old_malloc = dlsym(RTLD_NEXT, "__libc_malloc");	old_realloc = dlsym(RTLD_NEXT, "__libc_realloc");	old_free = dlsym(RTLD_NEXT, "__libc_free");	old_calloc = dlsym(RTLD_NEXT, "__libc_calloc");	old_memalign = dlsym(RTLD_NEXT, "__libc_memalign");	old_execve = dlsym(RTLD_NEXT, "execve");	old_fork = dlsym(RTLD_NEXT, "__fork");	old_vfork = dlsym(RTLD_NEXT, "__vfork");	old_clone = dlsym(RTLD_NEXT, "__clone");	old__exit = dlsym(RTLD_NEXT, "_exit");	initialized = 1;}static voidabort_unitialized (const char *call){	mi_printf (2, "MemProf: unexpected library call during initialization: %s\n", call);	abort();}static voidstop_tracing (int fd){	MI_DEBUG (("Stopping tracing\n"));	tracing = 0;	close (fd);	putenv ("_MEMPROF_SOCKET=");}static voidnew_process (ThreadInfo *thread,	     pid_t old_pid,	     MIOperation operation){	MIInfo info;	struct sockaddr_un addr;	int addrlen;	char response;	int outfd;	int count;		int old_errno = errno;#ifdef ENABLE_DEBUG	static const char *const operation_names[] = { NULL, NULL, NULL, NULL, "NEW", "FORK", "CLONE", NULL };#endif		MI_DEBUG (("New process, operation = %s, old_pid = %d\n",		   operation_names[operation], old_pid));	memset (&addr, 0, sizeof(addr));	addr.sun_family = AF_UNIX;	strncpy (addr.sun_path, socket_path, sizeof (addr.sun_path));	addrlen = sizeof(addr.sun_family) + strlen (addr.sun_path);	if (addrlen > sizeof (addr))		addrlen = sizeof(addr);	outfd = socket (PF_UNIX, SOCK_STREAM, 0);	if (outfd < 0) {		mi_perror ("error creating socket");		tracing = 0;		_exit(1);	}		if (connect (outfd, (struct sockaddr *)&addr, addrlen) < 0) {		mi_perror ("Error connecting to memprof");		tracing = 0;		_exit (1);	}	if (fcntl (outfd, F_SETFD, FD_CLOEXEC) < 0) {		mi_perror ("error calling fcntl");		tracing = 0;		_exit(1);	}	info.fork.operation = operation;	info.fork.pid = old_pid;	info.fork.new_pid = getpid();	info.fork.seqno = 0;	if (!thread)		thread = allocate_thread (info.fork.new_pid);	thread->outfd = outfd;	if (!mi_write (outfd, &info, sizeof (MIInfo))) {		stop_tracing (outfd);		count = 0;	} else {		while (1) {			count = read (outfd, &response, 1);			if (count >= 0 || errno != EINTR)				break;		}	}	if (count != 1 || !response) {		stop_tracing (outfd);	}	errno = old_errno;}static void memprof_init (void){	int old_errno = errno;	socket_path = getenv ("_MEMPROF_SOCKET");		if (!socket_path) {		mi_printf (2, "libmemintercept: must be used with memprof\n");		exit (1);	}	MI_DEBUG (("_MEMPROF_SOCKET = %s\n", socket_path));		if (socket_path[0] == '\0') /* tracing off */		tracing = 0;	else		new_process (NULL, 0, MI_NEW);	errno = old_errno;}static intcheck_init (){	if (initialized <= 0) {		if (initialized < 0)			return 0;		initialize ();	}		if (!socket_path)		memprof_init ();	return 1;}static voidwrite_stack (int      n_frames,	     void   **frames,	     void    *data){	MIInfo *info = data;	ThreadInfo *thread;	int old_errno = errno;	info->alloc.stack_size = n_frames;	info->alloc.pid = getpid();	info->alloc.seqno = seqno++;	thread = find_thread (info->alloc.pid);		if (!mi_write (thread->outfd, info, sizeof (MIInfo)) ||	    !mi_write (thread->outfd, frames, n_frames * sizeof(void *)))		stop_tracing (thread->outfd);	errno = old_errno;}static void *do_malloc (size_t size, int to_skip){	void *result;	MIInfo info;	if (!check_init ())		return NULL; /* See comment in initialize() */	result = (*old_malloc) (size);	if (tracing) {		info.alloc.operation = MI_MALLOC;		info.alloc.old_ptr = NULL;		info.alloc.new_ptr = result;		info.alloc.size = size;		mi_call_with_backtrace (to_skip + 1, write_stack, &info);	}			return result;}void *__libc_malloc (size_t size){	return do_malloc (size, 1);}void *malloc (size_t size){	return do_malloc (size, 1);}static void *do_calloc (size_t nmemb, size_t size){	int total = nmemb * size;	void *result = do_malloc (total, 2);	if (result)		memset (result, 0, total);		return result;}void *__libc_calloc (size_t nmemb, size_t size){	return do_calloc (nmemb, size);}void *calloc (size_t nmemb, size_t size){	return do_calloc (nmemb, size);}static void *do_memalign (size_t boundary, size_t size){	void *result;	MIInfo info;	if (!check_init ())		abort_unitialized ("memalign");	result = (*old_memalign) (boundary, size);	if (tracing) {		info.alloc.operation = MI_MALLOC;		info.alloc.old_ptr = NULL;		info.alloc.new_ptr = result;		info.alloc.size = size;			mi_call_with_backtrace (2, write_stack, &info);	}	return result;}void *__libc_memalign (size_t boundary, size_t size){	return do_memalign (boundary, size);}void *memalign (size_t boundary, size_t size){	return do_memalign (boundary, size);}static void *do_realloc (void *ptr, size_t size){	void *result;	MIInfo info;	if (!check_init ("__libc_realloc"))		return NULL;/* See comment in initialize() */	result = (*old_realloc) (ptr, size);	if (tracing) {		info.alloc.operation = MI_REALLOC;		info.alloc.old_ptr = ptr;		info.alloc.new_ptr = result;		info.alloc.size = size;			mi_call_with_backtrace (2, write_stack, &info);	}	return result;}void *__libc_realloc (void *ptr, size_t size){	return do_realloc (ptr, size);}void *realloc (void *ptr, size_t size){	return do_realloc (ptr, size);}static voiddo_free (void  *ptr){	MIInfo info;	if (!check_init ())		return;	(*old_free) (ptr);	if (tracing) {		info.alloc.operation = MI_FREE;		info.alloc.old_ptr = ptr;		info.alloc.new_ptr = NULL;		info.alloc.size = 0;		mi_call_with_backtrace (2, write_stack, &info);	}}void__libc_free (void *ptr){	do_free (ptr);}voidfree (void *ptr){	do_free (ptr);}int__fork (void){	if (!check_init ())		abort_unitialized ("__fork");	if (tracing) {		int pid;		int old_pid = getpid();		find_thread (old_pid); /* Make sure we're registered */				pid = (*old_fork) ();		if (!pid) /* New child process */			new_process (NULL, old_pid, MI_FORK);		return pid;	} else 		return (*old_fork) ();}intfork (void){	return __fork ();}int__vfork (void){	if (!check_init ())		abort_unitialized ("__vfork");	if (tracing) {		int pid;		int old_pid = getpid();		find_thread (old_pid); /* Make sure we're registered */				pid = (*old_vfork) ();		if (!pid) /* New child process */			new_process (NULL, old_pid, MI_FORK);		return pid;	} else 		return (*old_vfork) ();}int__execve (const char *filename,	  char *const argv[],	  char *const envp[]){	if (!check_init ())		abort_unitialized ("__execve");	if (tracing) {		/* Nothing */	} else {		int i;		for (i=0; envp[i]; i++)			if (strncmp (envp[i], "_MEMPROF_SOCKET=", 16) == 0)				envp[i][16] = '\0';	}	return (*old_execve) (filename, argv, envp);}typedef struct {	int started;	int (*fn) (void *);	void *arg;	pid_t pid;} CloneData;static intclone_thunk (void *arg){	ThreadInfo *thread;	CloneData *data = arg;	int (*fn)(void *) = data->fn;	void *sub_arg = data->arg;	/* At this point, we can't call new_process(), because errno	 * still points to our parent's errno structure. (We assume	 * getpid() is safe, but about anythhing else could be dangerous.)	 * So, we simply allocate the structure for the thread and delay	 * the initialization.	 */	thread = allocate_thread (getpid());	thread->clone_pid = data->pid;	data->started = 1;	return (*fn) (sub_arg);}int __clone (int (*fn) (void *arg),	     void *child_stack,	     int   flags,	     void *arg){	volatile CloneData data;	int pid;	if (!check_init ())		abort_unitialized ("__clone");	if (tracing) {		data.started = 0;		data.fn = fn;		data.arg = arg;		data.pid = getpid();				find_thread (data.pid); /* Make sure we're registered */				pid = (*old_clone) (clone_thunk, child_stack, flags, (void *)&data);		while (!data.started)			/* Wait */;		MI_DEBUG (("Clone: child=%d\n", pid));		return pid;	} else		return (*old_clone) (fn, child_stack, flags, arg);}int clone (int (*fn) (void *arg),	   void *child_stack,	   int   flags,	   void *arg){	return __clone (fn, child_stack, flags, arg);}void_exit (int status){	MI_DEBUG (("Exiting\n"));		if (initialized <= 0)		abort_unitialized ("exit");		if (tracing) {		MIInfo info;		ThreadInfo *thread;		int count;		char response;		info.any.operation = MI_EXIT;		info.any.seqno = seqno++;		info.any.pid = getpid();		thread = find_thread (info.any.pid);				if (mi_write (thread->outfd, &info, sizeof (MIInfo)))			/* Wait for a response before really exiting			 */			while (1) {				count = read (thread->outfd, &response, 1);				if (count >= 0 || errno != EINTR)					break;			}		close (thread->outfd);		thread->pid = 0;		release_thread (thread);	}		(*old__exit) (status);}/* This is not, strictly speaking, necessary, since we'll initialize * upon demand, but it ensures sure that we always initialize * as promptly as possible. */static void construct () __attribute__ ((constructor));static void construct () {	if (initialized <= 0) 		initialize ();}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -