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.
29 #include <sys/socket.h>
31 #include <glib-object.h>
32 #include <glib/gi18n.h>
34 #include "memintercept.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
,
59 GObject parent_instance
;
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
;
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];
90 static guint server_signals
[LAST_SIGNAL
] = { 0 };
93 fatal (const char *format
, ...)
97 va_start (va
, format
);
98 vfprintf (stderr
, format
, va
);
104 mp_server_get_type (void)
106 static GType server_type
= 0;
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 */
118 (GInstanceInitFunc
) mp_server_init
121 server_type
= g_type_register_static (G_TYPE_OBJECT
,
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
;
140 server_signals
[PROCESS_CREATED
] =
141 g_signal_new ("process_created",
144 G_STRUCT_OFFSET (MPServerClass
, process_created
),
146 g_cclosure_marshal_VOID__OBJECT
,
147 G_TYPE_NONE
, 1, MP_TYPE_PROCESS
);
154 mp_server_init (MPServer
*server
)
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
));
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
);
192 MPServer
*server
= g_object_new (MP_TYPE_SERVER
, NULL
);
198 mp_server_set_profile_type (MPServer
*server
,
202 g_return_if_fail (MP_IS_SERVER (server
));
203 server
->profile_type
= type
;
207 mp_server_set_interval (MPServer
*server
,
210 g_return_if_fail (MP_IS_SERVER (server
));
212 server
->interval
= usecs
;
216 mp_server_instrument (MPServer
*server
, const char *program
, char **args
)
219 char *lib_location
= find_lib_location (server
);
223 show_error (NULL
, ERROR_FATAL
, "Cannot fork: %s\n",
226 if (pid
== 0) { /* Child */
229 envstr
= g_strdup_printf ("%s=%s", "_MEMPROF_SOCKET", server
->socket_path
);
232 envstr
= g_strdup_printf ("%s=%s", "LD_PRELOAD", lib_location
);
235 if (server
->profile_type
!= MP_PROFILE_MEMORY
) {
236 envstr
= g_strdup_printf ("%s=%d", "_MEMPROF_INTERVAL", server
->interval
);
239 envstr
= g_strdup_printf ("%s=%s", "_MEMPROF_SPEED_TYPE",
240 server
->profile_type
== MP_PROFILE_CYCLES
?
245 execvp (program
, args
);
247 g_warning ("Cannot run program: %s", g_strerror (errno
));
251 g_free (lib_location
);
258 find_lib_location (MPServer
*server
)
260 const char **dirname
;
261 const char *basename
;
263 static const char *directories
[] = {
272 if (server
->profile_type
== MP_PROFILE_MEMORY
)
273 basename
= "libmemintercept.so";
275 basename
= "libspeedintercept.so";
278 for (dirname
= directories
; *dirname
; dirname
++) {
279 char *path
= g_build_filename (*dirname
, basename
, NULL
);
280 if (!access (path
, R_OK
)) {
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
;
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
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
));
326 term_handler (int signum
)
328 static int terminated
= 0;
332 exit(1); /* Impatient user, risk reentrancy problems */
336 write (terminate_pipe
[1], &c
, 1);
340 terminate_io_handler (GIOChannel
*channel
,
341 GIOCondition condition
,
346 read (terminate_pipe
[0], &c
, 1);
348 fprintf (stderr
, "memprof: Caught signal %d (%s), cleaning up\n", c
, g_strsignal(c
));
356 static gboolean added_cleanup
;
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
;
378 create_control_socket (MPServer
*server
)
380 struct sockaddr_un addr
;
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());
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
));
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
);
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) {
423 fatal ("memprof: Cannot create %s, %d", tmpdir
, g_strerror (errno
));
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
)
448 fatal ("bind: %s\n", g_strerror (errno
));
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
));
460 #ifdef USE_SOCKET_DIRECTORY
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 */
473 control_func (GIOChannel
*source
,
474 GIOCondition condition
,
480 MPServer
*server
= data
;
482 MPProcess
*process
= NULL
;
483 MPProcess
*parent_process
;
487 newfd
= accept (server
->socket_fd
, NULL
, 0);
489 g_warning ("accept: %s\n", g_strerror (errno
));
493 count
= read (newfd
, &info
, sizeof(info
));
494 if (count
< sizeof(info
)) {
495 g_warning ("short read from new process\n");
499 switch (info
.operation
) {
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 */
508 process
= mp_server_find_process (server
, info
.fork
.new_pid
);
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
);
521 process_exec_reset (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 */
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
);
541 parent_process
= mp_server_find_process (server
, info
.fork
.pid
);
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
;
564 g_assert_not_reached ();
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
);
577 write (newfd
, &response
, 1);
585 /************************************************************
586 * Code to keep track of current processes
587 ************************************************************/
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
));
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
);
609 mp_server_remove_process (MPServer
*server
, MPProcess
*process
)
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
));
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
);
639 mp_server_get_process_clones (MPServer
*server
, MPProcess
*process
)
646 g_hash_table_foreach (server
->pid_table
, (GHFunc
)add_clone
, &ci
);