src/main.c: Make it possible to pass options to the app under test
[memprof.git] / src / server.c
blob3d84869982a721326f8013a8083420dbe550ec9f
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 g_warning ("memprof: '%s' tmpdir already exists.\n", tmpdir);
424 goto retry;
425 } else
426 fatal ("memprof: Cannot create %s, %d", tmpdir, g_strerror (errno));
428 } else
429 fatal ("memprof: error calling stat() on %s: %s\n", tmpdir, g_strerror (errno));
431 server->socket_path = g_strdup_printf ("%s/server.%d", tmpdir, getpid());
432 if (g_file_exists (server->socket_path)) {
433 g_warning ("Stale memprof socket %s, removing\n", server->socket_path);
434 unlink (server->socket_path);
437 #else /* !USE_SOCKET_DIRECTORY */
438 server->socket_path = g_build_filename (g_get_tmp_dir(), SOCKET_TEMPLATE, NULL);
439 if (strlen(mktemp (server->socket_path)) == 0)
440 fatal ("mktemp: %s\n", g_strerror (errno));
441 #endif /* USE_SOCKET_DIRECTORY */
443 strncpy (addr.sun_path, server->socket_path, sizeof (addr.sun_path));
444 addrlen = sizeof(addr.sun_family) + strlen (addr.sun_path);
445 if (addrlen > sizeof (addr))
446 addrlen = sizeof(addr);
448 if (bind (server->socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
449 if (errno == EADDRINUSE) {
450 g_warning ("memprof: Binding failed in '%s' with address in use.\n", addr.sun_path);
451 goto retry;
452 } else
453 fatal ("bind: %s\n", g_strerror (errno));
456 ensure_cleanup ();
458 socket_paths = g_slist_prepend (socket_paths, server->socket_path);
460 if (listen (server->socket_fd, 8) < 0)
461 fatal ("listen: %s\n", g_strerror (errno));
463 umask (old_mask);
465 #ifdef USE_SOCKET_DIRECTORY
466 g_free (tmpdir);
467 #endif
470 static void
471 mp_server_process_created (MPServer *server, MPProcess *process)
473 g_signal_emit_by_name (server, "process_created", process);
476 /* Input func to receive new process connections */
477 static gboolean
478 control_func (GIOChannel *source,
479 GIOCondition condition,
480 gpointer data)
482 int newfd;
483 MIInfo info;
485 MPServer *server = data;
487 MPProcess *process = NULL;
488 MPProcess *parent_process;
489 int count;
490 char response = 0;
492 newfd = accept (server->socket_fd, NULL, 0);
493 if (newfd < 0) {
494 g_warning ("accept: %s\n", g_strerror (errno));
495 goto out;
498 count = read (newfd, &info, sizeof(info));
499 if (count < sizeof(info)) {
500 g_warning ("short read from new process\n");
501 goto out;
504 switch (info.operation) {
505 case MI_FORK:
506 parent_process = mp_server_find_process (server, info.fork.pid);
507 if (parent_process && !parent_process->follow_fork) {
508 goto out; /* Return negative response */
511 /* Fall through */
512 case MI_NEW:
513 process = mp_server_find_process (server, info.fork.new_pid);
514 if (process) {
515 if (process->follow_exec) {
516 MPProcess *old_process = process;
518 process = process_new (server);
519 process->pid = old_process->pid;
520 process->parent = old_process->parent;
522 mp_server_add_process (server, process); /* Overwrites old process */
523 mp_server_process_created (server, process);
525 } else
526 process_exec_reset (process);
529 if (!process) {
530 parent_process = mp_server_find_process (server, info.fork.pid);
531 if (!parent_process) {
532 /* This is a child of a forked child we aren't tracing, ignore it */
533 goto out;
536 process = process_duplicate (parent_process);
537 process->pid = info.fork.new_pid;
538 mp_server_add_process (server, process);
539 mp_server_process_created (server, process);
542 process_set_status (process, MP_PROCESS_RUNNING);
544 break;
545 case MI_CLONE:
546 parent_process = mp_server_find_process (server, info.fork.pid);
547 if (!parent_process)
548 g_error ("Clone of nonexistant parent process\n");
550 process = process_new (server);
551 process_set_status (process, MP_PROCESS_RUNNING);
553 process->clone_of = parent_process;
554 process->pid = info.fork.new_pid;
556 mp_server_add_process (server, process);
558 while (parent_process->clone_of)
559 parent_process = parent_process->clone_of;
561 break;
563 case MI_TIME:
564 case MI_MALLOC:
565 case MI_REALLOC:
566 case MI_FREE:
567 case MI_EXEC:
568 case MI_EXIT:
569 g_assert_not_reached ();
572 if (process) {
573 process->input_channel = g_io_channel_unix_new (newfd);
574 g_io_channel_set_encoding (process->input_channel, NULL, NULL);
576 process_start_input (process);
577 response = 1;
580 out:
581 if (newfd >= 0) {
582 int ret = write (newfd, &response, 1);
583 if (!response || ret < 0)
584 close (newfd);
587 return TRUE;
590 /************************************************************
591 * Code to keep track of current processes
592 ************************************************************/
594 MPProcess *
595 mp_server_find_process (MPServer *server, pid_t pid)
597 if (server->pid_table) {
598 return g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (pid));
599 } else {
600 return NULL;
604 void
605 mp_server_add_process (MPServer *server, MPProcess *process)
607 if (!server->pid_table)
608 server->pid_table = g_hash_table_new (g_direct_hash, NULL);
610 g_hash_table_insert (server->pid_table, GUINT_TO_POINTER (process->pid), process);
613 void
614 mp_server_remove_process (MPServer *server, MPProcess *process)
616 MPProcess *in_table;
618 g_return_if_fail (server->pid_table != NULL);
620 /* In the case of --follow-exec, the process in the table could have been overwritten
622 in_table = g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (process->pid));
623 if (in_table == process)
624 g_hash_table_remove (server->pid_table, GUINT_TO_POINTER (process->pid));
627 typedef struct {
628 MPProcess *parent;
629 GList *result;
630 } CloneInfo;
632 static void
633 add_clone (gpointer key, MPProcess *process, CloneInfo *ci)
635 MPProcess *parent = process;
636 while (parent->clone_of)
637 parent = parent->clone_of;
639 if (parent == ci->parent)
640 ci->result = g_list_prepend (ci->result, process);
643 GList *
644 mp_server_get_process_clones (MPServer *server, MPProcess *process)
646 CloneInfo ci;
648 ci.parent = process;
649 ci.result = NULL;
651 g_hash_table_foreach (server->pid_table, (GHFunc)add_clone, &ci);
653 return ci.result;