server.c
来自「memprof source code, linux」· C语言 代码 · 共 602 行
C
602 行
/* -*- 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. *//*====*/#include <errno.h>#include <stdlib.h>#include <signal.h>#include <unistd.h>#include <sys/un.h>#include <sys/stat.h>#include <sys/socket.h>#include <glib-object.h>#include <libgnome/libgnome.h>#include "memintercept.h"#include "server.h"/* If USE_SOCKET_DIRECTORY is defined, then the temporary sockets will * be created as /tmp/memprof.UID/server.PID. Otherwise, they will * be created as /tmp/memprof.XXXXXX. Despite calling mktemp(), the * latter should be completely safe, because unix domain socket creation * will fail with EADDRINUSE if the file already exists. */#undef USE_SOCKET_DIRECTORY#define SOCKET_TEMPLATE "memprof.XXXXXX"static void mp_server_class_init (MPServerClass *class);static void mp_server_init (MPServer *server);static void mp_server_finalize (GObject *object);static char * find_lib_location (void);static void create_control_socket (MPServer *server);static gboolean control_func (GIOChannel *source, GIOCondition condition, gpointer data);struct _MPServer{ GObject parent_instance; char *lib_location; char *socket_path; int socket_fd; /* It would be faster to implement this as a flat array with a * binary search, but this is easier as a first pass */ GHashTable *pid_table; guint control_watch;};struct _MPServerClass { GObjectClass parent_class; void (*process_created) (MPServer *server, MPProcess *process);};/* Global list of socket paths so we can cleanup in an atexit() function */static GSList *socket_paths;static int terminate_pipe[2];enum { PROCESS_CREATED, LAST_SIGNAL};static guint server_signals[LAST_SIGNAL] = { 0 };static voidfatal (const char *format, ...){ va_list va; va_start (va, format); vfprintf (stderr, format, va); exit (1);}GTypemp_server_get_type (void){ static GType server_type = 0; if (!server_type) { static const GTypeInfo server_info = { sizeof (MPServerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) mp_server_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (MPServer), 0, /* n_preallocs */ (GInstanceInitFunc) mp_server_init }; server_type = g_type_register_static (G_TYPE_OBJECT, "MPServer", &server_info, 0); } return server_type;}static voidmp_server_class_init (MPServerClass *class){ static gboolean initialized = FALSE; GObjectClass *o_class = G_OBJECT_CLASS (class); o_class->finalize = mp_server_finalize; class->process_created = NULL; if (!initialized) { server_signals[PROCESS_CREATED] = g_signal_new ("process_created", MP_TYPE_SERVER, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MPServerClass, process_created), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, MP_TYPE_PROCESS); initialized = TRUE; }}static voidmp_server_init (MPServer *server){ GIOChannel *channel; server->pid_table = NULL; server->lib_location = find_lib_location (); create_control_socket (server); channel = g_io_channel_unix_new (server->socket_fd); server->control_watch = g_io_add_watch (channel, G_IO_IN | G_IO_HUP, control_func, server); g_io_channel_unref (channel); g_object_ref (G_OBJECT (server));}static voidmp_server_finalize (GObject *object){ MPServer *server = MP_SERVER (object); if (server->control_watch) g_source_remove (server->control_watch); if (server->pid_table) g_hash_table_destroy (server->pid_table); close (server->socket_fd); g_slist_remove (socket_paths, server->socket_path); g_free (server->socket_path);}MPServer *mp_server_new (void){ MPServer *server = g_object_new (MP_TYPE_SERVER, NULL); return server;}intmp_server_instrument (MPServer *server, const char *program, char **args){ int pid; pid = fork(); if (pid < 0) show_error (NULL, ERROR_FATAL, "Cannot fork: %s\n", g_strerror (errno)); if (pid == 0) { /* Child */ gchar *envstr; envstr = g_strdup_printf ("%s=%s", "_MEMPROF_SOCKET", server->socket_path); putenv (envstr); envstr = g_strdup_printf ("%s=%s", "LD_PRELOAD", server->lib_location); putenv (envstr); execvp (program, args); g_warning ("Cannot run program: %s", g_strerror (errno)); _exit(1); } return pid;}static char *find_lib_location (void){ const char **dirname; static const char *directories[] = { ".libs", ".", LIBDIR, NULL }; char *lib_location; lib_location = NULL; for (dirname = directories; *dirname; dirname++) { char *path = g_concat_dir_and_file (*dirname, "libmemintercept.so"); if (!access (path, R_OK)) { lib_location = path; break; } g_free (path); } if (!lib_location) show_error (NULL, ERROR_FATAL, _("Cannot find libmemintercept.so")); /* Make lib_location absolute */ if (lib_location[0] != '/') { char *wd = g_get_current_dir(); char *newloc = g_strconcat (wd, "/", lib_location, NULL); g_free (lib_location); lib_location = newloc; g_free (wd); } return lib_location;}static voidcleanup_socket (void){ g_slist_foreach (socket_paths, (GFunc)unlink, NULL);#ifdef USE_SOCKET_DIRECTORY /* We try to remove the directory ; if there are other copies of memprof running, we expect * this to fail */ { char *tmpdir = tmpdir = g_strdup_printf ("%s/memprof.%d", g_get_tmp_dir(), getuid()); if (rmdir (tmpdir) != 0) { if (errno != ENOTEMPTY) g_warning ("Unlinking %s failed with error: %s\n", tmpdir, g_strerror (errno)); } g_free (tmpdir); }#endif}static voidterm_handler (int signum){ static int terminated = 0; char c = signum; if (terminated) exit(1); /* Impatient user, risk reentrancy problems */ terminated = 1; write (terminate_pipe[1], &c, 1);}static gbooleanterminate_io_handler (GIOChannel *channel, GIOCondition condition, gpointer data){ char c; read (terminate_pipe[0], &c, 1); fprintf (stderr, "memprof: Caught signal %d (%s), cleaning up\n", c, g_strsignal(c)); exit (1);}static voidensure_cleanup (){ static gboolean added_cleanup; GIOChannel *channel; if (!added_cleanup) { g_atexit (cleanup_socket); signal (SIGINT, term_handler); signal (SIGTERM, term_handler); if (pipe (terminate_pipe) < 0) fatal ("bind: %s\n", g_strerror (errno)); channel = g_io_channel_unix_new (terminate_pipe[0]); g_io_add_watch (channel, G_IO_IN, terminate_io_handler, NULL); g_io_channel_unref (channel); added_cleanup = TRUE; }}static voidcreate_control_socket (MPServer *server){ struct sockaddr_un addr; int addrlen; int retry_count = 5; mode_t old_mask = umask (077);#ifdef USE_SOCKET_DIRECTORY char *tmpdir = g_strdup_printf ("%s/memprof.%d", g_get_tmp_dir(), getuid()); struct stat st_buf;#endif memset (&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; server->socket_fd = socket (PF_UNIX, SOCK_STREAM, 0); if (server->socket_fd < 0) g_error ("socket: %s\n", g_strerror (errno)); retry: if (retry_count-- == 0) fatal ("Too many retries while creating control socket\n"); if (server->socket_path) g_free (server->socket_path);#ifdef USE_SOCKET_DIRECTORY if (stat (tmpdir, &st_buf) == 0) { if (!S_ISDIR(st_buf.st_mode) || st_buf.st_uid != getuid()) fatal ("memprof: %s not owned by user or is not a directory\n", tmpdir); if (chmod (tmpdir, 0700) != 0) { if (errno == ENOENT) { g_warning ("%s vanished, retrying\n", tmpdir); goto retry; } else fatal ("memprof: cannot set permissions on %s: %s\n", tmpdir, g_strerror (errno)); } } else if (errno == ENOENT) { if (mkdir (tmpdir, 0700) != 0) { if (errno == EEXIST) goto retry; else fatal ("memprof: Cannot create %s, %d", tmpdir, g_strerror (errno)); } } else fatal ("memprof: error calling stat() on %s: %s\n", tmpdir, g_strerror (errno)); server->socket_path = g_strdup_printf ("%s/server.%d", tmpdir, getpid()); if (g_file_exists (server->socket_path)) { g_warning ("Stale memprof socket %s, removing\n", server->socket_path); unlink (server->socket_path); } #else /* !USE_SOCKET_DIRECTORY */ server->socket_path = g_concat_dir_and_file (g_get_tmp_dir(), SOCKET_TEMPLATE); mktemp (server->socket_path);#endif /* USE_SOCKET_DIRECTORY */ strncpy (addr.sun_path, server->socket_path, sizeof (addr.sun_path)); addrlen = sizeof(addr.sun_family) + strlen (addr.sun_path); if (addrlen > sizeof (addr)) addrlen = sizeof(addr); if (bind (server->socket_fd, (struct sockaddr *)&addr, addrlen) < 0) { if (errno == EADDRINUSE) goto retry; else fatal ("bind: %s\n", g_strerror (errno)); } ensure_cleanup (); socket_paths = g_slist_prepend (socket_paths, server->socket_path); if (listen (server->socket_fd, 8) < 0) fatal ("listen: %s\n", g_strerror (errno)); umask (old_mask); #ifdef USE_SOCKET_DIRECTORY g_free (tmpdir);#endif }static voidmp_server_process_created (MPServer *server, MPProcess *process){ g_signal_emit_by_name (server, "process_created", process);}/* Input func to receive new process connections */static gboolean control_func (GIOChannel *source, GIOCondition condition, gpointer data){ int newfd; MIInfo info; MPServer *server = data; MPProcess *process = NULL; MPProcess *parent_process; int count; char response = 0; newfd = accept (server->socket_fd, NULL, 0); if (newfd < 0) { g_warning ("accept: %s\n", g_strerror (errno)); goto out; } count = read (newfd, &info, sizeof(info)); if (count < sizeof(info)) { g_warning ("short read from new process\n"); goto out; } switch (info.operation) { case MI_FORK: parent_process = mp_server_find_process (server, info.fork.pid); if (parent_process && !parent_process->follow_fork) { goto out; /* Return negative response */ } /* Fall through */ case MI_NEW: process = mp_server_find_process (server, info.fork.new_pid); if (process) { if (process->follow_exec) { MPProcess *old_process = process; process = process_new (server); process->pid = old_process->pid; process->parent = old_process->parent; mp_server_add_process (server, process); /* Overwrites old process */ mp_server_process_created (server, process); } else process_exec_reset (process); } if (!process) { parent_process = mp_server_find_process (server, info.fork.pid); if (!parent_process) { /* This is a child of a forked child we aren't tracing, ignore it */ goto out; } process = process_duplicate (parent_process); process->pid = info.fork.new_pid; mp_server_add_process (server, process); mp_server_process_created (server, process); } process_set_status (process, MP_PROCESS_RUNNING); break; case MI_CLONE: parent_process = mp_server_find_process (server, info.fork.pid); if (!parent_process) g_error ("Clone of nonexistant parent process\n"); process = process_new (server); process_set_status (process, MP_PROCESS_RUNNING); process->clone_of = parent_process; process->pid = info.fork.new_pid; mp_server_add_process (server, process); while (parent_process->clone_of) parent_process = parent_process->clone_of; break; case MI_MALLOC: case MI_REALLOC: case MI_FREE: case MI_EXEC: case MI_EXIT: g_assert_not_reached (); } if (process) { process->input_channel = g_io_channel_unix_new (newfd); process_start_input (process); response = 1; } out: if (newfd >= 0) { write (newfd, &response, 1); if (!response) close (newfd); } return TRUE;}/************************************************************ * Code to keep track of current processes ************************************************************/MPProcess *mp_server_find_process (MPServer *server, pid_t pid){ if (server->pid_table) { return g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (pid)); } else { return NULL; }}voidmp_server_add_process (MPServer *server, MPProcess *process){ if (!server->pid_table) server->pid_table = g_hash_table_new (g_direct_hash, NULL); g_hash_table_insert (server->pid_table, GUINT_TO_POINTER (process->pid), process);}voidmp_server_remove_process (MPServer *server, MPProcess *process){ MPProcess *in_table; g_return_if_fail (server->pid_table != NULL); /* In the case of --follow-exec, the process in the table could have been overwritten */ in_table = g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (process->pid)); if (in_table == process) g_hash_table_remove (server->pid_table, GUINT_TO_POINTER (process->pid));}typedef struct { MPProcess *parent; GList *result;} CloneInfo;static voidadd_clone (gpointer key, MPProcess *process, CloneInfo *ci){ MPProcess *parent = process; while (parent->clone_of) parent = parent->clone_of; if (parent == ci->parent) ci->result = g_list_prepend (ci->result, process);}GList *mp_server_get_process_clones (MPServer *server, MPProcess *process){ CloneInfo ci; ci.parent = process; ci.result = NULL; g_hash_table_foreach (server->pid_table, (GHFunc)add_clone, &ci); return ci.result;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?