po: Run make update-po to update the .po files
[memprof.git] / src / server.c
blob0f7c45ef23f7f435176f7f7fa6a97626835ff284
1 /* -*- mode: C; c-file-style: "linux" -*- */
3 /* MemProf -- memory profiler and leak detector
4 * Copyright 1999, 2000, 2001, Red Hat, Inc.
5 * Copyright 2002, Kristian Rietveld
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 /*====*/
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <signal.h>
26 #include <unistd.h>
27 #include <sys/un.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
31 #include <glib-object.h>
32 #include <glib/gi18n.h>
34 #include "memintercept.h"
35 #include "server.h"
37 /* If USE_SOCKET_DIRECTORY is defined, then the temporary sockets will
38 * be created as /tmp/memprof.UID/server.PID. Otherwise, they will
39 * be created as /tmp/memprof.XXXXXX. Despite calling mktemp(), the
40 * latter should be completely safe, because unix domain socket creation
41 * will fail with EADDRINUSE if the file already exists.
44 #undef USE_SOCKET_DIRECTORY
46 #define SOCKET_TEMPLATE "memprof.XXXXXX"
48 static void mp_server_class_init (MPServerClass *class);
49 static void mp_server_init (MPServer *server);
50 static void mp_server_finalize (GObject *object);
52 static char * find_lib_location (MPServer *server);
53 static void create_control_socket (MPServer *server);
54 static gboolean control_func (GIOChannel *source,
55 GIOCondition condition,
56 gpointer data);
57 struct _MPServer
59 GObject parent_instance;
61 char *socket_path;
63 int socket_fd;
65 /* It would be faster to implement this as a flat array with a
66 * binary search, but this is easier as a first pass
68 GHashTable *pid_table;
69 guint control_watch;
71 MPProfileType profile_type;
72 guint interval; /* In usecs */
75 struct _MPServerClass {
76 GObjectClass parent_class;
78 void (*process_created) (MPServer *server, MPProcess *process);
81 /* Global list of socket paths so we can cleanup in an atexit() function
83 static GSList *socket_paths;
84 static int terminate_pipe[2];
86 enum {
87 PROCESS_CREATED,
88 LAST_SIGNAL
90 static guint server_signals[LAST_SIGNAL] = { 0 };
92 static void
93 fatal (const char *format, ...)
95 va_list va;
97 va_start (va, format);
98 vfprintf (stderr, format, va);
100 exit (1);
103 GType
104 mp_server_get_type (void)
106 static GType server_type = 0;
108 if (!server_type) {
109 static const GTypeInfo server_info = {
110 sizeof (MPServerClass),
111 (GBaseInitFunc) NULL,
112 (GBaseFinalizeFunc) NULL,
113 (GClassInitFunc) mp_server_class_init,
114 NULL, /* class_finalize */
115 NULL, /* class_data */
116 sizeof (MPServer),
117 0, /* n_preallocs */
118 (GInstanceInitFunc) mp_server_init
121 server_type = g_type_register_static (G_TYPE_OBJECT,
122 "MPServer",
123 &server_info, 0);
126 return server_type;
129 static void
130 mp_server_class_init (MPServerClass *class)
132 static gboolean initialized = FALSE;
134 GObjectClass *o_class = G_OBJECT_CLASS (class);
136 o_class->finalize = mp_server_finalize;
137 class->process_created = NULL;
139 if (!initialized) {
140 server_signals[PROCESS_CREATED] =
141 g_signal_new ("process_created",
142 MP_TYPE_SERVER,
143 G_SIGNAL_RUN_LAST,
144 G_STRUCT_OFFSET (MPServerClass, process_created),
145 NULL, NULL,
146 g_cclosure_marshal_VOID__OBJECT,
147 G_TYPE_NONE, 1, MP_TYPE_PROCESS);
149 initialized = TRUE;
153 static void
154 mp_server_init (MPServer *server)
156 GIOChannel *channel;
158 server->profile_type = MP_PROFILE_MEMORY;
159 server->interval = 10000;
161 server->pid_table = NULL;
162 create_control_socket (server);
164 channel = g_io_channel_unix_new (server->socket_fd);
165 g_io_channel_set_encoding (channel, NULL, NULL);
167 server->control_watch = g_io_add_watch (channel, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
168 control_func, server);
169 g_io_channel_unref (channel);
171 g_object_ref (G_OBJECT (server));
174 static void
175 mp_server_finalize (GObject *object)
177 MPServer *server = MP_SERVER (object);
179 if (server->control_watch)
180 g_source_remove (server->control_watch);
181 if (server->pid_table)
182 g_hash_table_destroy (server->pid_table);
183 close (server->socket_fd);
185 socket_paths = g_slist_remove (socket_paths, server->socket_path);
186 g_free (server->socket_path);
189 MPServer *
190 mp_server_new (void)
192 MPServer *server = g_object_new (MP_TYPE_SERVER, NULL);
194 return server;
197 void
198 mp_server_set_profile_type (MPServer *server,
199 MPProfileType type)
202 g_return_if_fail (MP_IS_SERVER (server));
203 server->profile_type = type;
206 void
207 mp_server_set_interval (MPServer *server,
208 guint usecs)
210 g_return_if_fail (MP_IS_SERVER (server));
212 server->interval = usecs;
216 mp_server_instrument (MPServer *server, const char *program, char **args)
218 int pid;
219 char *lib_location = find_lib_location (server);
221 pid = fork();
222 if (pid < 0)
223 show_error (NULL, ERROR_FATAL, "Cannot fork: %s\n",
224 g_strerror (errno));
226 if (pid == 0) { /* Child */
227 gchar *envstr;
229 envstr = g_strdup_printf ("%s=%s", "_MEMPROF_SOCKET", server->socket_path);
230 putenv (envstr);
232 envstr = g_strdup_printf ("%s=%s", "LD_PRELOAD", lib_location);
233 putenv (envstr);
235 if (server->profile_type != MP_PROFILE_MEMORY) {
236 envstr = g_strdup_printf ("%s=%d", "_MEMPROF_INTERVAL", server->interval);
237 putenv (envstr);
239 envstr = g_strdup_printf ("%s=%s", "_MEMPROF_SPEED_TYPE",
240 server->profile_type == MP_PROFILE_CYCLES ?
241 "cycles" : "time");
242 putenv (envstr);
245 execvp (program, args);
247 g_warning ("Cannot run program: %s", g_strerror (errno));
248 _exit(1);
251 g_free (lib_location);
253 return pid;
257 static char *
258 find_lib_location (MPServer *server)
260 const char **dirname;
261 const char *basename;
263 static const char *directories[] = {
264 ".libs",
265 ".",
266 PKGLIBDIR,
267 NULL
270 char *lib_location;
272 if (server->profile_type == MP_PROFILE_MEMORY)
273 basename = "libmemintercept.so";
274 else
275 basename = "libspeedintercept.so";
277 lib_location = NULL;
278 for (dirname = directories; *dirname; dirname++) {
279 char *path = g_build_filename (*dirname, basename, NULL);
280 if (!access (path, R_OK)) {
281 lib_location = path;
282 break;
284 g_free (path);
287 if (!lib_location)
288 show_error (NULL, ERROR_FATAL,
289 _("Cannot find %s"), basename);
291 /* Make lib_location absolute */
293 if (lib_location[0] != '/') {
294 char *wd = g_get_current_dir();
295 char *newloc = g_strconcat (wd, "/", lib_location, NULL);
296 g_free (lib_location);
297 lib_location = newloc;
298 g_free (wd);
301 return lib_location;
304 static void
305 cleanup_socket (void)
307 g_slist_foreach (socket_paths, (GFunc)unlink, NULL);
309 #ifdef USE_SOCKET_DIRECTORY
310 /* We try to remove the directory ; if there are other copies of memprof running, we expect
311 * this to fail
314 char *tmpdir = tmpdir = g_strdup_printf ("%s/memprof.%d", g_get_tmp_dir(), getuid());
315 if (rmdir (tmpdir) != 0) {
316 if (errno != ENOTEMPTY)
317 g_warning ("Unlinking %s failed with error: %s\n", tmpdir, g_strerror (errno));
320 g_free (tmpdir);
322 #endif
325 static void
326 term_handler (int signum)
328 static int terminated = 0;
329 char c = signum;
330 int ret;
332 if (terminated)
333 exit(1); /* Impatient user, risk reentrancy problems */
335 terminated = 1;
337 ret = write (terminate_pipe[1], &c, 1);
340 static gboolean
341 terminate_io_handler (GIOChannel *channel,
342 GIOCondition condition,
343 gpointer data)
345 char c;
346 int ret;
348 ret = read (terminate_pipe[0], &c, 1);
350 fprintf (stderr, "memprof: Caught signal %d (%s), cleaning up\n", c, g_strsignal(c));
352 exit (1);
355 static void
356 ensure_cleanup ()
358 static gboolean added_cleanup;
359 GIOChannel *channel;
361 if (!added_cleanup) {
362 g_atexit (cleanup_socket);
364 signal (SIGINT, term_handler);
365 signal (SIGTERM, term_handler);
366 if (pipe (terminate_pipe) < 0)
367 fatal ("bind: %s\n", g_strerror (errno));
369 channel = g_io_channel_unix_new (terminate_pipe[0]);
370 g_io_channel_set_encoding (channel, NULL, NULL);
372 g_io_add_watch (channel, G_IO_IN | G_IO_PRI, terminate_io_handler, NULL);
373 g_io_channel_unref (channel);
375 added_cleanup = TRUE;
379 static void
380 create_control_socket (MPServer *server)
382 struct sockaddr_un addr;
383 int addrlen;
384 int retry_count = 5;
385 mode_t old_mask = umask (077);
387 #ifdef USE_SOCKET_DIRECTORY
388 char *tmpdir = g_strdup_printf ("%s/memprof.%d", g_get_tmp_dir(), getuid());
389 struct stat st_buf;
390 #endif
392 memset (&addr, 0, sizeof(addr));
394 addr.sun_family = AF_UNIX;
396 server->socket_fd = socket (PF_UNIX, SOCK_STREAM, 0);
397 if (server->socket_fd < 0)
398 g_error ("socket: %s\n", g_strerror (errno));
400 retry:
401 if (retry_count-- == 0)
402 fatal ("Too many retries while creating control socket\n");
404 if (server->socket_path)
405 g_free (server->socket_path);
407 #ifdef USE_SOCKET_DIRECTORY
408 if (stat (tmpdir, &st_buf) == 0) {
409 if (!S_ISDIR(st_buf.st_mode) || st_buf.st_uid != getuid())
410 fatal ("memprof: %s not owned by user or is not a directory\n", tmpdir);
412 if (chmod (tmpdir, 0700) != 0) {
413 if (errno == ENOENT) {
414 g_warning ("%s vanished, retrying\n", tmpdir);
415 goto retry;
416 } else
417 fatal ("memprof: cannot set permissions on %s: %s\n", tmpdir, g_strerror (errno));
420 } else if (errno == ENOENT) {
421 if (mkdir (tmpdir, 0700) != 0) {
422 if (errno == EEXIST)
423 goto retry;
424 else
425 fatal ("memprof: Cannot create %s, %d", tmpdir, g_strerror (errno));
427 } else
428 fatal ("memprof: error calling stat() on %s: %s\n", tmpdir, g_strerror (errno));
430 server->socket_path = g_strdup_printf ("%s/server.%d", tmpdir, getpid());
431 if (g_file_exists (server->socket_path)) {
432 g_warning ("Stale memprof socket %s, removing\n", server->socket_path);
433 unlink (server->socket_path);
436 #else /* !USE_SOCKET_DIRECTORY */
437 server->socket_path = g_build_filename (g_get_tmp_dir(), SOCKET_TEMPLATE, NULL);
438 if (strlen(mktemp (server->socket_path)) == 0)
439 fatal ("mktemp: %s\n", g_strerror (errno));
440 #endif /* USE_SOCKET_DIRECTORY */
442 strncpy (addr.sun_path, server->socket_path, sizeof (addr.sun_path));
443 addrlen = sizeof(addr.sun_family) + strlen (addr.sun_path);
444 if (addrlen > sizeof (addr))
445 addrlen = sizeof(addr);
447 if (bind (server->socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
448 if (errno == EADDRINUSE)
449 goto retry;
450 else
451 fatal ("bind: %s\n", g_strerror (errno));
454 ensure_cleanup ();
456 socket_paths = g_slist_prepend (socket_paths, server->socket_path);
458 if (listen (server->socket_fd, 8) < 0)
459 fatal ("listen: %s\n", g_strerror (errno));
461 umask (old_mask);
463 #ifdef USE_SOCKET_DIRECTORY
464 g_free (tmpdir);
465 #endif
468 static void
469 mp_server_process_created (MPServer *server, MPProcess *process)
471 g_signal_emit_by_name (server, "process_created", process);
474 /* Input func to receive new process connections */
475 static gboolean
476 control_func (GIOChannel *source,
477 GIOCondition condition,
478 gpointer data)
480 int newfd;
481 MIInfo info;
483 MPServer *server = data;
485 MPProcess *process = NULL;
486 MPProcess *parent_process;
487 int count;
488 char response = 0;
490 newfd = accept (server->socket_fd, NULL, 0);
491 if (newfd < 0) {
492 g_warning ("accept: %s\n", g_strerror (errno));
493 goto out;
496 count = read (newfd, &info, sizeof(info));
497 if (count < sizeof(info)) {
498 g_warning ("short read from new process\n");
499 goto out;
502 switch (info.operation) {
503 case MI_FORK:
504 parent_process = mp_server_find_process (server, info.fork.pid);
505 if (parent_process && !parent_process->follow_fork) {
506 goto out; /* Return negative response */
509 /* Fall through */
510 case MI_NEW:
511 process = mp_server_find_process (server, info.fork.new_pid);
512 if (process) {
513 if (process->follow_exec) {
514 MPProcess *old_process = process;
516 process = process_new (server);
517 process->pid = old_process->pid;
518 process->parent = old_process->parent;
520 mp_server_add_process (server, process); /* Overwrites old process */
521 mp_server_process_created (server, process);
523 } else
524 process_exec_reset (process);
527 if (!process) {
528 parent_process = mp_server_find_process (server, info.fork.pid);
529 if (!parent_process) {
530 /* This is a child of a forked child we aren't tracing, ignore it */
531 goto out;
534 process = process_duplicate (parent_process);
535 process->pid = info.fork.new_pid;
536 mp_server_add_process (server, process);
537 mp_server_process_created (server, process);
540 process_set_status (process, MP_PROCESS_RUNNING);
542 break;
543 case MI_CLONE:
544 parent_process = mp_server_find_process (server, info.fork.pid);
545 if (!parent_process)
546 g_error ("Clone of nonexistant parent process\n");
548 process = process_new (server);
549 process_set_status (process, MP_PROCESS_RUNNING);
551 process->clone_of = parent_process;
552 process->pid = info.fork.new_pid;
554 mp_server_add_process (server, process);
556 while (parent_process->clone_of)
557 parent_process = parent_process->clone_of;
559 break;
561 case MI_TIME:
562 case MI_MALLOC:
563 case MI_REALLOC:
564 case MI_FREE:
565 case MI_EXEC:
566 case MI_EXIT:
567 g_assert_not_reached ();
570 if (process) {
571 process->input_channel = g_io_channel_unix_new (newfd);
572 g_io_channel_set_encoding (process->input_channel, NULL, NULL);
574 process_start_input (process);
575 response = 1;
578 out:
579 if (newfd >= 0) {
580 int ret = write (newfd, &response, 1);
581 if (!response || ret < 0)
582 close (newfd);
585 return TRUE;
588 /************************************************************
589 * Code to keep track of current processes
590 ************************************************************/
592 MPProcess *
593 mp_server_find_process (MPServer *server, pid_t pid)
595 if (server->pid_table) {
596 return g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (pid));
597 } else {
598 return NULL;
602 void
603 mp_server_add_process (MPServer *server, MPProcess *process)
605 if (!server->pid_table)
606 server->pid_table = g_hash_table_new (g_direct_hash, NULL);
608 g_hash_table_insert (server->pid_table, GUINT_TO_POINTER (process->pid), process);
611 void
612 mp_server_remove_process (MPServer *server, MPProcess *process)
614 MPProcess *in_table;
616 g_return_if_fail (server->pid_table != NULL);
618 /* In the case of --follow-exec, the process in the table could have been overwritten
620 in_table = g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (process->pid));
621 if (in_table == process)
622 g_hash_table_remove (server->pid_table, GUINT_TO_POINTER (process->pid));
625 typedef struct {
626 MPProcess *parent;
627 GList *result;
628 } CloneInfo;
630 static void
631 add_clone (gpointer key, MPProcess *process, CloneInfo *ci)
633 MPProcess *parent = process;
634 while (parent->clone_of)
635 parent = parent->clone_of;
637 if (parent == ci->parent)
638 ci->result = g_list_prepend (ci->result, process);
641 GList *
642 mp_server_get_process_clones (MPServer *server, MPProcess *process)
644 CloneInfo ci;
646 ci.parent = process;
647 ci.result = NULL;
649 g_hash_table_foreach (server->pid_table, (GHFunc)add_clone, &ci);
651 return ci.result;