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.
33 #include "intercept.h"
34 #include "memintercept.h"
35 #include "memintercept-utils.h"
36 #include "stack-frame.h"
38 #include <sys/socket.h>
39 #include <sys/types.h>
42 /* The code in this file is written to work for threaded operation without
43 * any reference to the thread library in operation. There are only
44 * two places where any interaction between threads is necessary -
45 * allocation of new slots in the pids[] array, and a parent thread
46 * waiting for a child thread to start.
50 * For the new NPTL implementation, all threads return the
51 * same 'getpid' - so instead we use a thread variable.
61 pid_t clone_pid
; /* See comments in clone_thunk */
64 static void new_process (ThreadInfo
*thread
,
66 MIOperation operation
);
67 static void atexit_trap (void);
69 static int (*old_execve
) (const char *filename
,
72 static int (*old_fork
) (void);
73 static int (*old_vfork
) (void);
74 static int (*old_clone
) (int (*fn
) (void *arg
),
78 void *xarg1
, void *xarg2
, void *xarg3
, void *xarg4
);
79 static void (*old__exit
) (int status
);
81 static int initialized
= MI_FALSE
;
82 static MIBool tracing
= MI_TRUE
;
84 #define MAX_THREADS 128
87 static ThreadInfo threads
[MAX_THREADS
];
89 static char *socket_path
= NULL
;
90 static char socket_buf
[64];
91 static uint32_t seqno
= 0;
96 #define MI_DEBUG(arg) mi_debug arg
97 #else /* !ENABLE_DEBUG */
98 #define MI_DEBUG(arg) (void)0
99 #endif /* ENABLE_DEBUG */
102 static __thread ThreadInfo global_per_thread
= { 0, };
106 allocate_thread (void)
108 ThreadInfo
*thread
= NULL
;
110 thread
= &global_per_thread
;
114 for (i
= 0; i
< MAX_THREADS
; i
++) {
115 if (threads
[i
].ref_count
== 0) {
116 unsigned int new_count
= mi_atomic_increment (&threads
[i
].ref_count
);
117 if (new_count
== 1) {
118 thread
= &threads
[i
];
121 mi_atomic_decrement (&threads
[i
].ref_count
);
125 mi_debug ("Can't find free thread slot");
130 thread
->pid
= getpid();
131 thread
->clone_pid
= 0;
137 release_thread (ThreadInfo
*thread
)
141 mi_atomic_decrement (&thread
->ref_count
);
150 thread
= &global_per_thread
;
153 pid_t pid
= getpid();
155 for (i
=0; i
< MAX_THREADS
; i
++)
156 if (threads
[i
].pid
== pid
) {
157 thread
= &threads
[i
];
162 mi_debug ("Thread not found\n");
167 if (thread
->clone_pid
) {
168 /* See comments in clone_thunk() */
169 new_process (thread
, thread
->clone_pid
, MI_CLONE
);
170 thread
->clone_pid
= 0;
179 /* It's possible to get recursion here, since dlsym() can trigger
180 * memory allocation. To deal with this, we flag the initialization
181 * condition specially, then use the special knowledge that it's
182 * OK for malloc to fail during initialization (libc degrades
183 * gracefully), so we just return NULL from malloc(), realloc().
185 * This trick is borrowed from from libc's memusage.
188 old_execve
= dlsym(RTLD_NEXT
, "execve");
189 old_fork
= dlsym(RTLD_NEXT
, "__fork");
190 old_vfork
= dlsym(RTLD_NEXT
, "__vfork");
191 old_clone
= dlsym(RTLD_NEXT
, "__clone");
192 old__exit
= dlsym(RTLD_NEXT
, "_exit");
194 atexit (atexit_trap
);
201 abort_unitialized (const char *call
)
203 mi_printf (2, "MemProf: unexpected library call during initialization: %s\n", call
);
208 stop_tracing (int fd
)
210 MI_DEBUG (("Stopping tracing\n"));
214 putenv ("_MEMPROF_SOCKET=");
218 new_process (ThreadInfo
*thread
,
220 MIOperation operation
)
223 struct sockaddr_un addr
;
229 int old_errno
= errno
;
231 static const char *const operation_names
[] = { NULL
, NULL
, NULL
, NULL
, "NEW", "FORK", "CLONE", NULL
};
234 MI_DEBUG (("New process, operation = %s, old_pid = %d\n",
235 operation_names
[operation
], old_pid
));
237 memset (&addr
, 0, sizeof(addr
));
239 addr
.sun_family
= AF_UNIX
;
240 strncpy (addr
.sun_path
, socket_path
, sizeof (addr
.sun_path
));
241 addrlen
= sizeof(addr
.sun_family
) + strlen (addr
.sun_path
);
242 if (addrlen
> sizeof (addr
))
243 addrlen
= sizeof(addr
);
245 outfd
= socket (PF_UNIX
, SOCK_STREAM
, 0);
247 mi_perror ("error creating socket");
252 if (connect (outfd
, (struct sockaddr
*)&addr
, addrlen
) < 0) {
253 mi_perror ("Error connecting to memprof");
257 if (fcntl (outfd
, F_SETFD
, FD_CLOEXEC
) < 0) {
258 mi_perror ("error calling fcntl");
263 info
.fork
.operation
= operation
;
265 info
.fork
.pid
= old_pid
;
266 info
.fork
.new_pid
= getpid();
270 thread
= allocate_thread ();
272 thread
->outfd
= outfd
;
274 MI_DEBUG (("new_process op %d\n", info
.any
.operation
));
276 if (!mi_write (outfd
, &info
, sizeof (MIInfo
))) {
277 stop_tracing (outfd
);
281 count
= read (outfd
, &response
, 1);
282 if (count
>= 0 || errno
!= EINTR
)
287 if (count
!= 1 || !response
) {
288 stop_tracing (outfd
);
297 int old_errno
= errno
;
299 socket_path
= getenv ("_MEMPROF_SOCKET");
302 mi_printf (2, "libmemintercept: must be used with memprof\n");
306 if (strlen(socket_path
) < sizeof(socket_buf
)) {
307 strcpy(socket_buf
, socket_path
);
308 socket_path
= socket_buf
;
311 MI_DEBUG (("_MEMPROF_SOCKET = %s\n", socket_path
));
313 if (socket_path
[0] == '\0') /* tracing off */
316 new_process (NULL
, 0, MI_NEW
);
324 if (initialized
<= 0) {
344 mi_write_stack (int n_frames
,
350 int old_errno
= errno
;
352 if (n_frames
< 0 || n_frames
> 20000)
354 MI_DEBUG (("mi_write_stack - elide bogus foo\n"));
358 MI_DEBUG (("mi_write_stack op 0x%x stack size %d\n",
359 info
->any
.operation
, n_frames
));
361 info
->alloc
.stack_size
= n_frames
;
362 info
->alloc
.pid
= getpid();
363 info
->alloc
.seqno
= mi_atomic_increment (&seqno
) - 1;
365 thread
= find_thread ();
367 if (!mi_write (thread
->outfd
, info
, sizeof (MIInfo
)) ||
368 !mi_write (thread
->outfd
, frames
, n_frames
* sizeof(void *)))
369 stop_tracing (thread
->outfd
);
377 if (!mi_check_init ())
378 abort_unitialized ("__fork");
382 int old_pid
= getpid();
384 find_thread (); /* Make sure we're registered */
386 pid
= (*old_fork
) ();
388 if (!pid
) /* New child process */
389 new_process (NULL
, old_pid
, MI_FORK
);
393 return (*old_fork
) ();
405 if (!mi_check_init ())
406 abort_unitialized ("__vfork");
410 int old_pid
= getpid();
412 find_thread (); /* Make sure we're registered */
414 pid
= (*old_vfork
) ();
416 if (!pid
) /* New child process */
417 new_process (NULL
, old_pid
, MI_FORK
);
421 return (*old_vfork
) ();
425 __execve (const char *filename
,
429 if (!mi_check_init ())
430 abort_unitialized ("__execve");
437 for (i
=0; envp
[i
]; i
++)
438 if (strncmp (envp
[i
], "_MEMPROF_SOCKET=", 16) == 0)
441 return (*old_execve
) (filename
, argv
, envp
);
453 clone_thunk (void *arg
)
456 CloneData
*data
= arg
;
457 int (*fn
)(void *) = data
->fn
;
458 void *sub_arg
= data
->arg
;
460 /* At this point, we can't call new_process(), because errno
461 * still points to our parent's errno structure. (We assume
462 * getpid() is safe, but about anythhing else could be dangerous.)
463 * So, we simply allocate the structure for the thread and delay
464 * the initialization.
466 thread
= allocate_thread ();
467 thread
->clone_pid
= data
->pid
;
470 return (*fn
) (sub_arg
);
473 int __clone (int (*fn
) (void *arg
),
477 void *xarg1
, void *xarg2
, void *xarg3
, void *xarg4
)
479 volatile CloneData data
;
482 if (!mi_check_init ())
483 abort_unitialized ("__clone");
491 find_thread (); /* Make sure we're registered */
493 pid
= (*old_clone
) (clone_thunk
, child_stack
, flags
, (void *)&data
, xarg1
, xarg2
, xarg3
, xarg4
);
495 while (!data
.started
)
498 MI_DEBUG (("Clone: child=%d\n", pid
));
502 return (*old_clone
) (fn
, child_stack
, flags
, arg
, xarg1
, xarg2
, xarg3
, xarg4
);
505 int clone (int (*fn
) (void *arg
),
509 void *xarg1
, void *xarg2
, void *xarg3
, void *xarg4
)
511 return __clone (fn
, child_stack
, flags
, arg
, xarg1
, xarg2
, xarg3
, xarg4
);
521 info
.any
.operation
= MI_EXIT
;
522 info
.any
.seqno
= mi_atomic_increment (&seqno
) - 1;
523 info
.any
.pid
= getpid();
527 thread
= find_thread ();
529 if (mi_write (thread
->outfd
, &info
, sizeof (MIInfo
)))
530 /* Wait for a response before really exiting
533 count
= read (thread
->outfd
, &response
, 1);
534 if (count
>= 0 || errno
!= EINTR
)
538 close (thread
->outfd
);
540 release_thread (thread
);
543 /* Because _exit() isn't interposable in recent versions of
544 * GNU libc, we can't depend on this, and instead use the less-good
545 * atexit_trap() below. But we leave this here just in case we
546 * are using an old version of libc where _exit() is interposable,
547 * so we can trap a wider range of exit conditions
552 if (initialized
<= 0)
553 abort_unitialized ("_exit");
555 MI_DEBUG (("_Exiting\n"));
562 (*old__exit
) (status
);
564 /* Not reached as old__exit will not return but makes
565 * the compiler happy.
579 static void construct () __attribute__ ((constructor
));
581 static void construct ()