process.c: Use the symbol, it looks like it was meant to be used...
[memprof.git] / src / server.c
blobb6aa24c30c039cff6a1acc6feb74f05f604553ef
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;
331 if (terminated)
332 exit(1); /* Impatient user, risk reentrancy problems */
334 terminated = 1;
336 write (terminate_pipe[1], &c, 1);
339 static gboolean
340 terminate_io_handler (GIOChannel *channel,
341 GIOCondition condition,
342 gpointer data)
344 char c;
346 read (terminate_pipe[0], &c, 1);
348 fprintf (stderr, "memprof: Caught signal %d (%s), cleaning up\n", c, g_strsignal(c));
350 exit (1);
353 static void
354 ensure_cleanup ()
356 static gboolean added_cleanup;
357 GIOChannel *channel;
359 if (!added_cleanup) {
360 g_atexit (cleanup_socket);
362 signal (SIGINT, term_handler);
363 signal (SIGTERM, term_handler);
364 if (pipe (terminate_pipe) < 0)
365 fatal ("bind: %s\n", g_strerror (errno));
367 channel = g_io_channel_unix_new (terminate_pipe[0]);
368 g_io_channel_set_encoding (channel, NULL, NULL);
370 g_io_add_watch (channel, G_IO_IN | G_IO_PRI, terminate_io_handler, NULL);
371 g_io_channel_unref (channel);
373 added_cleanup = TRUE;
377 static void
378 create_control_socket (MPServer *server)
380 struct sockaddr_un addr;
381 int addrlen;
382 int retry_count = 5;
383 mode_t old_mask = umask (077);
385 #ifdef USE_SOCKET_DIRECTORY
386 char *tmpdir = g_strdup_printf ("%s/memprof.%d", g_get_tmp_dir(), getuid());
387 struct stat st_buf;
388 #endif
390 memset (&addr, 0, sizeof(addr));
392 addr.sun_family = AF_UNIX;
394 server->socket_fd = socket (PF_UNIX, SOCK_STREAM, 0);
395 if (server->socket_fd < 0)
396 g_error ("socket: %s\n", g_strerror (errno));
398 retry:
399 if (retry_count-- == 0)
400 fatal ("Too many retries while creating control socket\n");
402 if (server->socket_path)
403 g_free (server->socket_path);
405 #ifdef USE_SOCKET_DIRECTORY
406 if (stat (tmpdir, &st_buf) == 0) {
407 if (!S_ISDIR(st_buf.st_mode) || st_buf.st_uid != getuid())
408 fatal ("memprof: %s not owned by user or is not a directory\n", tmpdir);
410 if (chmod (tmpdir, 0700) != 0) {
411 if (errno == ENOENT) {
412 g_warning ("%s vanished, retrying\n", tmpdir);
413 goto retry;
414 } else
415 fatal ("memprof: cannot set permissions on %s: %s\n", tmpdir, g_strerror (errno));
418 } else if (errno == ENOENT) {
419 if (mkdir (tmpdir, 0700) != 0) {
420 if (errno == EEXIST)
421 goto retry;
422 else
423 fatal ("memprof: Cannot create %s, %d", tmpdir, g_strerror (errno));
425 } else
426 fatal ("memprof: error calling stat() on %s: %s\n", tmpdir, g_strerror (errno));
428 server->socket_path = g_strdup_printf ("%s/server.%d", tmpdir, getpid());
429 if (g_file_exists (server->socket_path)) {
430 g_warning ("Stale memprof socket %s, removing\n", server->socket_path);
431 unlink (server->socket_path);
434 #else /* !USE_SOCKET_DIRECTORY */
435 server->socket_path = g_build_filename (g_get_tmp_dir(), SOCKET_TEMPLATE, NULL);
436 mktemp (server->socket_path);
437 #endif /* USE_SOCKET_DIRECTORY */
439 strncpy (addr.sun_path, server->socket_path, sizeof (addr.sun_path));
440 addrlen = sizeof(addr.sun_family) + strlen (addr.sun_path);
441 if (addrlen > sizeof (addr))
442 addrlen = sizeof(addr);
444 if (bind (server->socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
445 if (errno == EADDRINUSE)
446 goto retry;
447 else
448 fatal ("bind: %s\n", g_strerror (errno));
451 ensure_cleanup ();
453 socket_paths = g_slist_prepend (socket_paths, server->socket_path);
455 if (listen (server->socket_fd, 8) < 0)
456 fatal ("listen: %s\n", g_strerror (errno));
458 umask (old_mask);
460 #ifdef USE_SOCKET_DIRECTORY
461 g_free (tmpdir);
462 #endif
465 static void
466 mp_server_process_created (MPServer *server, MPProcess *process)
468 g_signal_emit_by_name (server, "process_created", process);
471 /* Input func to receive new process connections */
472 static gboolean
473 control_func (GIOChannel *source,
474 GIOCondition condition,
475 gpointer data)
477 int newfd;
478 MIInfo info;
480 MPServer *server = data;
482 MPProcess *process = NULL;
483 MPProcess *parent_process;
484 int count;
485 char response = 0;
487 newfd = accept (server->socket_fd, NULL, 0);
488 if (newfd < 0) {
489 g_warning ("accept: %s\n", g_strerror (errno));
490 goto out;
493 count = read (newfd, &info, sizeof(info));
494 if (count < sizeof(info)) {
495 g_warning ("short read from new process\n");
496 goto out;
499 switch (info.operation) {
500 case MI_FORK:
501 parent_process = mp_server_find_process (server, info.fork.pid);
502 if (parent_process && !parent_process->follow_fork) {
503 goto out; /* Return negative response */
506 /* Fall through */
507 case MI_NEW:
508 process = mp_server_find_process (server, info.fork.new_pid);
509 if (process) {
510 if (process->follow_exec) {
511 MPProcess *old_process = process;
513 process = process_new (server);
514 process->pid = old_process->pid;
515 process->parent = old_process->parent;
517 mp_server_add_process (server, process); /* Overwrites old process */
518 mp_server_process_created (server, process);
520 } else
521 process_exec_reset (process);
524 if (!process) {
525 parent_process = mp_server_find_process (server, info.fork.pid);
526 if (!parent_process) {
527 /* This is a child of a forked child we aren't tracing, ignore it */
528 goto out;
531 process = process_duplicate (parent_process);
532 process->pid = info.fork.new_pid;
533 mp_server_add_process (server, process);
534 mp_server_process_created (server, process);
537 process_set_status (process, MP_PROCESS_RUNNING);
539 break;
540 case MI_CLONE:
541 parent_process = mp_server_find_process (server, info.fork.pid);
542 if (!parent_process)
543 g_error ("Clone of nonexistant parent process\n");
545 process = process_new (server);
546 process_set_status (process, MP_PROCESS_RUNNING);
548 process->clone_of = parent_process;
549 process->pid = info.fork.new_pid;
551 mp_server_add_process (server, process);
553 while (parent_process->clone_of)
554 parent_process = parent_process->clone_of;
556 break;
558 case MI_TIME:
559 case MI_MALLOC:
560 case MI_REALLOC:
561 case MI_FREE:
562 case MI_EXEC:
563 case MI_EXIT:
564 g_assert_not_reached ();
567 if (process) {
568 process->input_channel = g_io_channel_unix_new (newfd);
569 g_io_channel_set_encoding (process->input_channel, NULL, NULL);
571 process_start_input (process);
572 response = 1;
575 out:
576 if (newfd >= 0) {
577 write (newfd, &response, 1);
578 if (!response)
579 close (newfd);
582 return TRUE;
585 /************************************************************
586 * Code to keep track of current processes
587 ************************************************************/
589 MPProcess *
590 mp_server_find_process (MPServer *server, pid_t pid)
592 if (server->pid_table) {
593 return g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (pid));
594 } else {
595 return NULL;
599 void
600 mp_server_add_process (MPServer *server, MPProcess *process)
602 if (!server->pid_table)
603 server->pid_table = g_hash_table_new (g_direct_hash, NULL);
605 g_hash_table_insert (server->pid_table, GUINT_TO_POINTER (process->pid), process);
608 void
609 mp_server_remove_process (MPServer *server, MPProcess *process)
611 MPProcess *in_table;
613 g_return_if_fail (server->pid_table != NULL);
615 /* In the case of --follow-exec, the process in the table could have been overwritten
617 in_table = g_hash_table_lookup (server->pid_table, GUINT_TO_POINTER (process->pid));
618 if (in_table == process)
619 g_hash_table_remove (server->pid_table, GUINT_TO_POINTER (process->pid));
622 typedef struct {
623 MPProcess *parent;
624 GList *result;
625 } CloneInfo;
627 static void
628 add_clone (gpointer key, MPProcess *process, CloneInfo *ci)
630 MPProcess *parent = process;
631 while (parent->clone_of)
632 parent = parent->clone_of;
634 if (parent == ci->parent)
635 ci->result = g_list_prepend (ci->result, process);
638 GList *
639 mp_server_get_process_clones (MPServer *server, MPProcess *process)
641 CloneInfo ci;
643 ci.parent = process;
644 ci.result = NULL;
646 g_hash_table_foreach (server->pid_table, (GHFunc)add_clone, &ci);
648 return ci.result;