1999-02-19 03:21:36 +03:00
/*
* Copyright ( c ) 1991 , 1992 Paul Kranenburg < pk @ cs . few . eur . nl >
* Copyright ( c ) 1993 Branko Lankester < branko @ hacktic . nl >
* Copyright ( c ) 1993 , 1994 , 1995 , 1996 Rick Sladkey < jrs @ world . std . com >
1999-12-23 17:20:14 +03:00
* Copyright ( c ) 1996 - 1999 Wichert Akkerman < wichert @ cistron . nl >
1999-02-19 03:21:36 +03:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* $ Id $
*/
# include "defs.h"
2005-02-02 07:44:57 +03:00
# include <sys/types.h>
2011-05-23 23:29:03 +04:00
# include <stdarg.h>
1999-02-19 03:21:36 +03:00
# include <signal.h>
# include <errno.h>
# include <sys/param.h>
# include <fcntl.h>
# include <sys/resource.h>
# include <sys/wait.h>
# include <sys/stat.h>
# include <pwd.h>
# include <grp.h>
# include <string.h>
2004-04-09 04:25:21 +04:00
# include <dirent.h>
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
# include <sys/utsname.h>
1999-02-19 03:21:36 +03:00
2007-06-02 04:07:33 +04:00
# include <asm / unistd.h>
Remove tcp->parent and TCB_CLONE_THREAD.
tcp->parent is used for only two things:
(1) to send signal on detach via tgkill (need to know tgid).
Solution: use tkill, it needs only tid.
(2) to optimize out ptrace options setting for new tracees.
Not a big deal if we drop this optimization: "set options" op is fast,
doing it just one extra time once per each tracee is hardly measurable.
TCB_CLONE_THREAD is a misnomer. It used only to flag sibling we attached to
in startup_attach. This is used to prevent infinite recursive rescanning
of /proc/PID/task.
Despite the name, there is no guarantee it is set only on non-leader:
if one would run "strace -f -p THREAD_ID" and THREAD_ID is *not*
a thread leader, strace will happily attach to it and all siblings
and will think that THREAD_ID is the leader! Which is a bug, but
since we no longer detach when we think tracee is going to die,
this bug no longer matters, because we do not use the knowledge
about thread group leaders for anything. (We used it to delay
leader's exit).
IOW: after this patch strace has no need to know about threads, parents
and children, and so on. Therefore it does not track that information.
It treats all tracees as independent entities. Overall,
this simplifies code a lot.
* defs.h: Add TCB_ATTACH_DONE flag, remove TCB_CLONE_THREAD flag
and struct tcb::parent field.
* process.c (internal_fork): Don't set tcpchild->parent.
* strace.c (startup_attach): Use TCB_ATTACH_DONE flag instead of
TCB_CLONE_THREAD to avoid attach attempts on already-attached threads.
Unlike TCB_CLONE_THREAD, TCB_ATTACH_DONE bit is used only temporarily,
and only in this function. We clear it on every tcb before we return.
(detach): Use tkill instead of tgkill.
(trace): Set ptrace options on new tracees unconditionally,
not only when tcp->parent == NULL.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 17:18:21 +04:00
# if defined __NR_tkill
# define my_tkill(tid, sig) syscall(__NR_tkill, (tid), (sig))
2007-06-02 04:07:33 +04:00
# else
/* kill() may choose arbitrarily the target task of the process group
while we later wait on a that specific TID . PID process waits become
TID task specific waits for a process under ptrace ( 2 ) . */
# warning "Neither tkill(2) nor tgkill(2) available, risk of strace hangs!"
Remove tcp->parent and TCB_CLONE_THREAD.
tcp->parent is used for only two things:
(1) to send signal on detach via tgkill (need to know tgid).
Solution: use tkill, it needs only tid.
(2) to optimize out ptrace options setting for new tracees.
Not a big deal if we drop this optimization: "set options" op is fast,
doing it just one extra time once per each tracee is hardly measurable.
TCB_CLONE_THREAD is a misnomer. It used only to flag sibling we attached to
in startup_attach. This is used to prevent infinite recursive rescanning
of /proc/PID/task.
Despite the name, there is no guarantee it is set only on non-leader:
if one would run "strace -f -p THREAD_ID" and THREAD_ID is *not*
a thread leader, strace will happily attach to it and all siblings
and will think that THREAD_ID is the leader! Which is a bug, but
since we no longer detach when we think tracee is going to die,
this bug no longer matters, because we do not use the knowledge
about thread group leaders for anything. (We used it to delay
leader's exit).
IOW: after this patch strace has no need to know about threads, parents
and children, and so on. Therefore it does not track that information.
It treats all tracees as independent entities. Overall,
this simplifies code a lot.
* defs.h: Add TCB_ATTACH_DONE flag, remove TCB_CLONE_THREAD flag
and struct tcb::parent field.
* process.c (internal_fork): Don't set tcpchild->parent.
* strace.c (startup_attach): Use TCB_ATTACH_DONE flag instead of
TCB_CLONE_THREAD to avoid attach attempts on already-attached threads.
Unlike TCB_CLONE_THREAD, TCB_ATTACH_DONE bit is used only temporarily,
and only in this function. We clear it on every tcb before we return.
(detach): Use tkill instead of tgkill.
(trace): Set ptrace options on new tracees unconditionally,
not only when tcp->parent == NULL.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 17:18:21 +04:00
# define my_tkill(tid, sig) kill((tid), (sig))
2007-06-02 04:07:33 +04:00
# endif
2012-02-25 05:38:52 +04:00
# if defined(IA64)
2001-10-10 03:47:38 +04:00
# include <asm / ptrace_offsets.h>
# endif
2008-12-29 22:13:27 +03:00
extern char * * environ ;
Two cleanups: tcb table expansion failure is not really a survivable
event, we do not have any viable way to continue. No wonder most
places where that is detected have FIXMEs.
It's way simpler to treat as fatal failure, and handle it inside
tcb table expansion finctions.
Second cleanup: tidy up haphazard locations of a few externs.
* defs.h: Change return type of expand_tcbtab() to void.
Declare change_syscall().
* process.c: Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(fork_tcb): Change return type to void - it can't fail now.
* strace.c: Move extern declarations out of function bodies.
Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(expand_tcbtab): Change return type to void - it can't fail now.
On failure to expand, print a message, clean up, and exit.
(alloc_tcb): On failure to expand, print a message, clean up, and exit.
* util.c (setbpt): Remove extern declaration from function body.
2009-01-17 04:52:54 +03:00
extern int optind ;
extern char * optarg ;
2008-12-29 22:13:27 +03:00
2008-07-18 04:25:10 +04:00
int debug = 0 , followfork = 0 ;
2011-06-21 16:34:10 +04:00
unsigned int ptrace_setoptions = 0 ;
2011-05-23 23:29:03 +04:00
/* Which WSTOPSIG(status) value marks syscall traps? */
2011-05-27 16:36:01 +04:00
static unsigned int syscall_trap_sig = SIGTRAP ;
2010-03-28 23:24:54 +04:00
int dtime = 0 , xflag = 0 , qflag = 0 ;
cflag_t cflag = CFLAG_NONE ;
2012-03-09 16:03:41 +04:00
static int iflag = 0 , rflag = 0 , tflag = 0 ;
static int print_pid_pfx = 0 ;
2012-01-29 19:53:03 +04:00
/* -I n */
enum {
INTR_NOT_SET = 0 ,
INTR_ANYWHERE = 1 , /* don't block/ignore any signals */
INTR_WHILE_WAIT = 2 , /* block fatal signals while decoding syscall. default */
INTR_NEVER = 3 , /* block fatal signals. default if '-o FILE PROG' */
INTR_BLOCK_TSTP_TOO = 4 , /* block fatal signals and SIGTSTP (^Z) */
NUM_INTR_OPTS
} ;
static int opt_intr ;
/* We play with signal mask only if this mode is active: */
# define interactive (opt_intr == INTR_WHILE_WAIT)
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
/*
* daemonized_tracer supports - D option .
* With this option , strace forks twice .
* Unlike normal case , with - D * grandparent * process exec ' s ,
* becoming a traced process . Child exits ( this prevents traced process
* from having children it doesn ' t expect to have ) , and grandchild
* attaches to grandparent similarly to strace - p PID .
* This allows for more transparent interaction in cases
* when process and its parent are communicating via signals ,
* wait ( ) etc . Without - D , strace process gets lodged in between ,
* disrupting parent < - > child link .
*/
static bool daemonized_tracer = 0 ;
1999-02-19 03:21:36 +03:00
2012-01-29 05:01:44 +04:00
# ifdef USE_SEIZE
static int post_attach_sigstop = TCB_IGNORE_ONE_SIGSTOP ;
# define use_seize (post_attach_sigstop == 0)
# else
# define post_attach_sigstop TCB_IGNORE_ONE_SIGSTOP
# define use_seize 0
# endif
2002-11-06 16:17:21 +03:00
/* Sometimes we want to print only succeeding syscalls. */
int not_failing_only = 0 ;
2011-04-08 00:25:40 +04:00
/* Show path associated with fd arguments */
int show_fd_path = 0 ;
/* are we filtering traces based on paths? */
int tracing_paths = 0 ;
2008-11-10 20:14:58 +03:00
static int exit_code = 0 ;
static int strace_child = 0 ;
2011-05-27 16:36:01 +04:00
static int strace_tracer_pid = 0 ;
2009-06-03 03:49:22 +04:00
2006-12-13 19:59:44 +03:00
static char * username = NULL ;
2011-06-25 00:49:58 +04:00
static uid_t run_uid ;
static gid_t run_gid ;
1999-02-19 03:21:36 +03:00
int max_strlen = DEFAULT_STRLEN ;
2011-08-25 03:27:59 +04:00
static int acolumn = DEFAULT_ACOLUMN ;
static char * acolumn_spaces ;
2006-12-13 19:59:44 +03:00
static char * outfname = NULL ;
2011-06-25 00:49:58 +04:00
static FILE * outf ;
2012-01-28 04:25:03 +04:00
struct tcb * printing_tcp = NULL ;
2009-10-27 18:27:13 +03:00
static int curcol ;
2011-06-25 00:49:58 +04:00
static struct tcb * * tcbtab ;
2011-06-22 14:45:25 +04:00
static unsigned int nprocs , tcbtabsize ;
2011-06-25 00:49:58 +04:00
static const char * progname ;
1999-02-19 03:21:36 +03:00
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
static char * os_release ; /* from uname() */
2012-01-04 18:11:09 +04:00
static int detach ( struct tcb * tcp ) ;
2009-10-27 18:56:43 +03:00
static int trace ( void ) ;
static void cleanup ( void ) ;
static void interrupt ( int sig ) ;
1999-02-19 03:21:36 +03:00
static sigset_t empty_set , blocked_set ;
# ifdef HAVE_SIG_ATOMIC_T
static volatile sig_atomic_t interrupted ;
2012-01-29 19:43:51 +04:00
# else
1999-02-19 03:21:36 +03:00
static volatile int interrupted ;
2012-01-29 19:43:51 +04:00
# endif
1999-02-19 03:21:36 +03:00
static void
2011-06-23 15:05:29 +04:00
usage ( FILE * ofp , int exitval )
1999-02-19 03:21:36 +03:00
{
fprintf ( ofp , " \
2012-01-29 19:53:03 +04:00
usage : strace [ - CdDffhiqrtttTvVxxy ] [ - I n ] [ - a column ] [ - e expr ] . . . [ - o file ] \ n \
[ - p pid ] . . . [ - s strsize ] [ - u username ] [ - E var = val ] . . . \ n \
2012-02-03 15:17:57 +04:00
[ - P path ] [ PROG [ ARGS ] ] \ n \
2012-01-29 19:53:03 +04:00
or : strace - c [ - D ] [ - I n ] [ - e expr ] . . . [ - O overhead ] [ - S sortby ] [ - E var = val ] . . . \ n \
2012-02-03 15:17:57 +04:00
[ PROG [ ARGS ] ] \ n \
1999-02-19 03:21:36 +03:00
- c - - count time , calls , and errors for each syscall and report summary \ n \
2010-06-11 17:49:36 +04:00
- C - - like - c but also print regular output while processes are running \ n \
2012-01-29 19:53:03 +04:00
- D - - run tracer process as a detached grandchild , not as parent \ n \
1999-02-19 03:21:36 +03:00
- f - - follow forks , - ff - - with output into separate files \ n \
2012-02-03 15:17:57 +04:00
- F - - attempt to follow vforks \ n \
1999-02-19 03:21:36 +03:00
- i - - print instruction pointer at time of syscall \ n \
2012-01-29 19:53:03 +04:00
- I interruptible \ n \
1 : no signals are blocked \ n \
2 : fatal signals are blocked while decoding syscall ( default ) \ n \
3 : fatal signals are always blocked ( default if ' - o FILE PROG ' ) \ n \
4 : fatal signals and SIGTSTP ( ^ Z ) are always blocked \ n \
( useful to make ' strace - o FILE PROG ' not stop on ^ Z ) \ n \
1999-02-19 03:21:36 +03:00
- q - - suppress messages about attaching , detaching , etc . \ n \
- r - - print relative timestamp , - t - - absolute timestamp , - tt - - with usecs \ n \
2012-02-03 15:17:57 +04:00
- T - - print time spent in each syscall \ n \
- v - - verbose mode : print unabbreviated argv , stat , termios , etc . args \ n \
1999-02-19 03:21:36 +03:00
- x - - print non - ascii strings in hex , - xx - - print all strings in hex \ n \
2011-04-08 00:25:40 +04:00
- y - - print paths associated with file descriptor arguments \ n \
2012-02-03 15:17:57 +04:00
- h - - print help message \ n \
- V - - print version \ n \
1999-02-19 03:21:36 +03:00
- a column - - alignment COLUMN for printing syscall results ( default % d ) \ n \
- e expr - - a qualifying expression : option = [ ! ] all or option = [ ! ] val1 [ , val2 ] . . . \ n \
options : trace , abbrev , verbose , raw , signal , read , or write \ n \
- o file - - send trace output to FILE instead of stderr \ n \
- O overhead - - set overhead for tracing syscalls to OVERHEAD usecs \ n \
- p pid - - trace process with process id PID , may be repeated \ n \
- s strsize - - limit length of print strings to STRSIZE chars ( default % d ) \ n \
- S sortby - - sort syscall counts by : time , calls , name , nothing ( default % s ) \ n \
- u username - - run command as username handling setuid and / or setgid \ n \
2003-01-24 07:31:23 +03:00
- E var = val - - put var = val in the environment for command \ n \
- E var - - remove var from the environment for command \ n \
2011-04-08 00:25:40 +04:00
- P path - - trace accesses to path \ n \
2003-01-24 07:31:23 +03:00
" /* this is broken, so don't document it
2002-11-06 16:17:21 +03:00
- z - - print only succeeding syscalls \ n \
2003-01-24 07:31:23 +03:00
*/
, DEFAULT_ACOLUMN , DEFAULT_STRLEN , DEFAULT_SORTBY ) ;
1999-02-19 03:21:36 +03:00
exit ( exitval ) ;
}
2011-05-27 16:36:01 +04:00
static void die ( void ) __attribute__ ( ( noreturn ) ) ;
static void die ( void )
{
if ( strace_tracer_pid = = getpid ( ) ) {
cflag = 0 ;
cleanup ( ) ;
}
exit ( 1 ) ;
}
static void verror_msg ( int err_no , const char * fmt , va_list p )
2011-05-23 23:29:03 +04:00
{
2012-01-24 13:17:18 +04:00
char * msg ;
2011-06-09 19:50:41 +04:00
fflush ( NULL ) ;
2012-01-24 13:17:18 +04:00
/* We want to print entire message with single fprintf to ensure
* message integrity if stderr is shared with other programs .
* Thus we use vasprintf + single fprintf .
*/
msg = NULL ;
2012-01-24 15:48:02 +04:00
if ( vasprintf ( & msg , fmt , p ) > = 0 ) {
2012-01-24 13:17:18 +04:00
if ( err_no )
fprintf ( stderr , " %s: %s: %s \n " , progname , msg , strerror ( err_no ) ) ;
else
fprintf ( stderr , " %s: %s \n " , progname , msg ) ;
free ( msg ) ;
} else {
/* malloc in vasprintf failed, try it without malloc */
fprintf ( stderr , " %s: " , progname ) ;
vfprintf ( stderr , fmt , p ) ;
if ( err_no )
fprintf ( stderr , " : %s \n " , strerror ( err_no ) ) ;
else
putc ( ' \n ' , stderr ) ;
}
/* We don't switch stderr to buffered, thus fprintf(stderr)
* always flushes its output and this is not necessary : */
/* fflush(stderr); */
2011-05-27 16:36:01 +04:00
}
void error_msg ( const char * fmt , . . . )
{
va_list p ;
va_start ( p , fmt ) ;
verror_msg ( 0 , fmt , p ) ;
2011-05-23 23:29:03 +04:00
va_end ( p ) ;
2011-05-27 16:36:01 +04:00
}
2011-05-23 23:29:03 +04:00
2011-05-27 16:36:01 +04:00
void error_msg_and_die ( const char * fmt , . . . )
{
va_list p ;
va_start ( p , fmt ) ;
verror_msg ( 0 , fmt , p ) ;
die ( ) ;
}
void perror_msg ( const char * fmt , . . . )
{
va_list p ;
va_start ( p , fmt ) ;
verror_msg ( errno , fmt , p ) ;
va_end ( p ) ;
}
void perror_msg_and_die ( const char * fmt , . . . )
{
va_list p ;
va_start ( p , fmt ) ;
verror_msg ( errno , fmt , p ) ;
die ( ) ;
2011-05-23 23:29:03 +04:00
}
2011-08-31 16:00:02 +04:00
void die_out_of_memory ( void )
{
static bool recursed = 0 ;
if ( recursed )
exit ( 1 ) ;
recursed = 1 ;
error_msg_and_die ( " Out of memory " ) ;
}
2009-10-08 04:41:29 +04:00
/* Glue for systems without a MMU that cannot provide fork() */
# ifdef HAVE_FORK
# define strace_vforked 0
# else
# define strace_vforked 1
# define fork() vfork()
# endif
2012-01-29 05:01:44 +04:00
# ifdef USE_SEIZE
static int
ptrace_attach_or_seize ( int pid )
{
int r ;
if ( ! use_seize )
return ptrace ( PTRACE_ATTACH , pid , 0 , 0 ) ;
r = ptrace ( PTRACE_SEIZE , pid , 0 , PTRACE_SEIZE_DEVEL ) ;
if ( r )
return r ;
r = ptrace ( PTRACE_INTERRUPT , pid , 0 , 0 ) ;
return r ;
}
# else
# define ptrace_attach_or_seize(pid) ptrace(PTRACE_ATTACH, (pid), 0, 0)
# endif
2011-06-22 15:11:23 +04:00
static void
2006-12-14 00:45:31 +03:00
set_cloexec_flag ( int fd )
{
2011-06-22 15:11:23 +04:00
int flags , newflags ;
flags = fcntl ( fd , F_GETFD ) ;
if ( flags < 0 ) {
/* Can happen only if fd is bad.
* Should never happen : if it does , we have a bug
* in the caller . Therefore we just abort
* instead of propagating the error .
*/
perror_msg_and_die ( " fcntl(%d, F_GETFD) " , fd ) ;
2006-12-14 00:45:31 +03:00
}
newflags = flags | FD_CLOEXEC ;
if ( flags = = newflags )
2011-06-22 15:11:23 +04:00
return ;
2006-12-14 00:45:31 +03:00
2011-06-22 15:11:23 +04:00
fcntl ( fd , F_SETFD , newflags ) ; /* never fails */
2006-12-14 00:45:31 +03:00
}
2012-03-09 18:15:24 +04:00
static void kill_save_errno ( pid_t pid , int sig )
{
int saved_errno = errno ;
( void ) kill ( pid , sig ) ;
errno = saved_errno ;
}
2006-12-14 00:45:31 +03:00
/*
* When strace is setuid executable , we have to swap uids
* before and after filesystem and process management operations .
*/
static void
swap_uid ( void )
{
int euid = geteuid ( ) , uid = getuid ( ) ;
2011-06-22 16:32:43 +04:00
if ( euid ! = uid & & setreuid ( euid , uid ) < 0 ) {
2011-06-23 15:05:29 +04:00
perror_msg_and_die ( " setreuid " ) ;
2006-12-14 00:45:31 +03:00
}
}
2007-07-06 00:03:16 +04:00
# if _LFS64_LARGEFILE
# define fopen_for_output fopen64
# else
# define fopen_for_output fopen
# endif
2006-12-14 00:45:31 +03:00
static FILE *
2011-06-22 15:17:16 +04:00
strace_fopen ( const char * path )
2006-12-14 00:45:31 +03:00
{
FILE * fp ;
swap_uid ( ) ;
2011-06-22 15:17:16 +04:00
fp = fopen_for_output ( path , " w " ) ;
if ( ! fp )
perror_msg_and_die ( " Can't fopen '%s' " , path ) ;
2006-12-14 00:45:31 +03:00
swap_uid ( ) ;
2011-06-22 15:17:16 +04:00
set_cloexec_flag ( fileno ( fp ) ) ;
2006-12-14 00:45:31 +03:00
return fp ;
}
2011-06-22 15:03:56 +04:00
static int popen_pid = 0 ;
2006-12-14 00:45:31 +03:00
# ifndef _PATH_BSHELL
# define _PATH_BSHELL " / bin / sh"
# endif
/*
* We cannot use standard popen ( 3 ) here because we have to distinguish
* popen child process from other processes we trace , and standard popen ( 3 )
* does not export its child ' s pid .
*/
static FILE *
strace_popen ( const char * command )
{
2011-06-22 15:03:56 +04:00
FILE * fp ;
int fds [ 2 ] ;
2006-12-14 00:45:31 +03:00
swap_uid ( ) ;
if ( pipe ( fds ) < 0 )
2011-06-22 15:03:56 +04:00
perror_msg_and_die ( " pipe " ) ;
2006-12-14 00:45:31 +03:00
2011-06-22 15:03:56 +04:00
set_cloexec_flag ( fds [ 1 ] ) ; /* never fails */
2006-12-14 00:45:31 +03:00
2011-06-22 15:03:56 +04:00
popen_pid = vfork ( ) ;
if ( popen_pid = = - 1 )
perror_msg_and_die ( " vfork " ) ;
2006-12-14 00:45:31 +03:00
2011-06-22 15:03:56 +04:00
if ( popen_pid = = 0 ) {
2006-12-14 00:45:31 +03:00
/* child */
close ( fds [ 1 ] ) ;
2011-06-22 15:03:56 +04:00
if ( fds [ 0 ] ! = 0 ) {
if ( dup2 ( fds [ 0 ] , 0 ) )
perror_msg_and_die ( " dup2 " ) ;
close ( fds [ 0 ] ) ;
2006-12-14 00:45:31 +03:00
}
execl ( _PATH_BSHELL , " sh " , " -c " , command , NULL ) ;
2011-06-22 15:03:56 +04:00
perror_msg_and_die ( " Can't execute '%s' " , _PATH_BSHELL ) ;
2006-12-14 00:45:31 +03:00
}
2011-06-22 15:03:56 +04:00
/* parent */
close ( fds [ 0 ] ) ;
swap_uid ( ) ;
fp = fdopen ( fds [ 1 ] , " w " ) ;
if ( ! fp )
2011-08-31 16:00:02 +04:00
die_out_of_memory ( ) ;
2011-06-22 15:03:56 +04:00
return fp ;
2006-12-14 00:45:31 +03:00
}
2011-06-22 15:17:16 +04:00
static void
2006-12-14 00:45:31 +03:00
newoutf ( struct tcb * tcp )
{
if ( outfname & & followfork > 1 ) {
2009-01-29 23:38:20 +03:00
char name [ 520 + sizeof ( int ) * 3 ] ;
sprintf ( name , " %.512s.%u " , outfname , tcp - > pid ) ;
2011-06-22 15:17:16 +04:00
tcp - > outf = strace_fopen ( name ) ;
2006-12-14 00:45:31 +03:00
}
}
2012-03-09 16:01:04 +04:00
static void process_opt_p_list ( char * opt )
{
while ( * opt ) {
/*
* We accept - p PID , PID ; - p " `pidof PROG` " ; - p " `pgrep PROG` " .
* pidof uses space as delim , pgrep uses newline . : (
*/
int pid ;
char * delim = opt + strcspn ( opt , " , \n \t " ) ;
char c = * delim ;
* delim = ' \0 ' ;
pid = atoi ( opt ) ; /* TODO: stricter parsing of the number? */
if ( pid < = 0 ) {
error_msg ( " Invalid process id: '%s' " , opt ) ;
* delim = c ;
return ;
}
if ( pid = = strace_tracer_pid ) {
error_msg ( " I'm sorry, I can't let you do that, Dave. " ) ;
* delim = c ;
return ;
}
* delim = c ;
2012-03-09 18:15:24 +04:00
alloc_tcb ( pid , 0 ) ;
2012-03-09 16:01:04 +04:00
if ( c = = ' \0 ' )
break ;
opt = delim + 1 ;
}
}
2007-06-12 02:06:31 +04:00
static void
startup_attach ( void )
{
int tcbi ;
struct tcb * tcp ;
/*
* Block user interruptions as we would leave the traced
* process stopped ( process state T ) if we would terminate in
2011-09-01 12:23:09 +04:00
* between PTRACE_ATTACH and wait4 ( ) on SIGSTOP .
2011-06-07 14:13:24 +04:00
* We rely on cleanup ( ) from this point on .
2007-06-12 02:06:31 +04:00
*/
if ( interactive )
sigprocmask ( SIG_BLOCK , & blocked_set , NULL ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
if ( daemonized_tracer ) {
pid_t pid = fork ( ) ;
if ( pid < 0 ) {
2011-09-02 18:19:30 +04:00
perror_msg_and_die ( " fork " ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
}
if ( pid ) { /* parent */
/*
2011-05-27 16:36:01 +04:00
* Wait for grandchild to attach to straced process
* ( grandparent ) . Grandchild SIGKILLs us after it attached .
* Grandparent ' s wait ( ) is unblocked by our death ,
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
* it proceeds to exec the straced program .
*/
pause ( ) ;
_exit ( 0 ) ; /* paranoia */
}
2011-05-27 16:36:01 +04:00
/* grandchild */
/* We will be the tracer process. Remember our new pid: */
strace_tracer_pid = getpid ( ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
}
2007-06-12 02:06:31 +04:00
for ( tcbi = 0 ; tcbi < tcbtabsize ; tcbi + + ) {
tcp = tcbtab [ tcbi ] ;
Remove tcp->parent and TCB_CLONE_THREAD.
tcp->parent is used for only two things:
(1) to send signal on detach via tgkill (need to know tgid).
Solution: use tkill, it needs only tid.
(2) to optimize out ptrace options setting for new tracees.
Not a big deal if we drop this optimization: "set options" op is fast,
doing it just one extra time once per each tracee is hardly measurable.
TCB_CLONE_THREAD is a misnomer. It used only to flag sibling we attached to
in startup_attach. This is used to prevent infinite recursive rescanning
of /proc/PID/task.
Despite the name, there is no guarantee it is set only on non-leader:
if one would run "strace -f -p THREAD_ID" and THREAD_ID is *not*
a thread leader, strace will happily attach to it and all siblings
and will think that THREAD_ID is the leader! Which is a bug, but
since we no longer detach when we think tracee is going to die,
this bug no longer matters, because we do not use the knowledge
about thread group leaders for anything. (We used it to delay
leader's exit).
IOW: after this patch strace has no need to know about threads, parents
and children, and so on. Therefore it does not track that information.
It treats all tracees as independent entities. Overall,
this simplifies code a lot.
* defs.h: Add TCB_ATTACH_DONE flag, remove TCB_CLONE_THREAD flag
and struct tcb::parent field.
* process.c (internal_fork): Don't set tcpchild->parent.
* strace.c (startup_attach): Use TCB_ATTACH_DONE flag instead of
TCB_CLONE_THREAD to avoid attach attempts on already-attached threads.
Unlike TCB_CLONE_THREAD, TCB_ATTACH_DONE bit is used only temporarily,
and only in this function. We clear it on every tcb before we return.
(detach): Use tkill instead of tgkill.
(trace): Set ptrace options on new tracees unconditionally,
not only when tcp->parent == NULL.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 17:18:21 +04:00
2012-03-09 18:15:24 +04:00
if ( ! ( tcp - > flags & TCB_INUSE ) )
continue ;
2011-09-05 16:01:33 +04:00
/* Is this a process we should attach to, but not yet attached? */
2012-03-09 18:15:24 +04:00
if ( tcp - > flags & TCB_ATTACHED )
continue ; /* no, we already attached it */
2011-09-05 16:01:33 +04:00
/* Reinitialize the output since it may have changed */
2007-06-12 02:06:31 +04:00
tcp - > outf = outf ;
2011-06-22 15:17:16 +04:00
newoutf ( tcp ) ;
2007-06-12 02:06:31 +04:00
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
if ( followfork & & ! daemonized_tracer ) {
2009-01-29 23:38:20 +03:00
char procdir [ sizeof ( " /proc/%d/task " ) + sizeof ( int ) * 3 ] ;
2007-06-12 02:06:31 +04:00
DIR * dir ;
sprintf ( procdir , " /proc/%d/task " , tcp - > pid ) ;
dir = opendir ( procdir ) ;
if ( dir ! = NULL ) {
unsigned int ntid = 0 , nerr = 0 ;
struct dirent * de ;
2011-09-05 15:59:39 +04:00
2007-06-12 02:06:31 +04:00
while ( ( de = readdir ( dir ) ) ! = NULL ) {
2011-09-05 15:59:39 +04:00
struct tcb * cur_tcp ;
int tid ;
2009-01-29 23:38:20 +03:00
if ( de - > d_fileno = = 0 )
2007-06-12 02:06:31 +04:00
continue ;
tid = atoi ( de - > d_name ) ;
if ( tid < = 0 )
continue ;
+ + ntid ;
2012-01-29 05:01:44 +04:00
if ( ptrace_attach_or_seize ( tid ) < 0 ) {
2007-06-12 02:06:31 +04:00
+ + nerr ;
2011-06-24 18:51:16 +04:00
if ( debug )
fprintf ( stderr , " attach to pid %d failed \n " , tid ) ;
2011-09-05 15:59:39 +04:00
continue ;
2011-06-24 18:51:16 +04:00
}
2011-09-05 15:59:39 +04:00
if ( debug )
fprintf ( stderr , " attach to pid %d succeeded \n " , tid ) ;
cur_tcp = tcp ;
if ( tid ! = tcp - > pid )
cur_tcp = alloctcb ( tid ) ;
2012-01-29 05:01:44 +04:00
cur_tcp - > flags | = TCB_ATTACHED | TCB_STARTUP | post_attach_sigstop ;
2007-06-12 02:06:31 +04:00
}
closedir ( dir ) ;
2011-09-05 15:59:39 +04:00
if ( interactive ) {
sigprocmask ( SIG_SETMASK , & empty_set , NULL ) ;
if ( interrupted )
goto ret ;
sigprocmask ( SIG_BLOCK , & blocked_set , NULL ) ;
}
2009-01-29 23:38:20 +03:00
ntid - = nerr ;
if ( ntid = = 0 ) {
2007-06-12 02:06:31 +04:00
perror ( " attach: ptrace(PTRACE_ATTACH, ...) " ) ;
droptcb ( tcp ) ;
continue ;
}
if ( ! qflag ) {
2009-01-29 23:38:20 +03:00
fprintf ( stderr , ntid > 1
? " Process %u attached with %u threads - interrupt to quit \n "
: " Process %u attached - interrupt to quit \n " ,
Remove tcp->parent and TCB_CLONE_THREAD.
tcp->parent is used for only two things:
(1) to send signal on detach via tgkill (need to know tgid).
Solution: use tkill, it needs only tid.
(2) to optimize out ptrace options setting for new tracees.
Not a big deal if we drop this optimization: "set options" op is fast,
doing it just one extra time once per each tracee is hardly measurable.
TCB_CLONE_THREAD is a misnomer. It used only to flag sibling we attached to
in startup_attach. This is used to prevent infinite recursive rescanning
of /proc/PID/task.
Despite the name, there is no guarantee it is set only on non-leader:
if one would run "strace -f -p THREAD_ID" and THREAD_ID is *not*
a thread leader, strace will happily attach to it and all siblings
and will think that THREAD_ID is the leader! Which is a bug, but
since we no longer detach when we think tracee is going to die,
this bug no longer matters, because we do not use the knowledge
about thread group leaders for anything. (We used it to delay
leader's exit).
IOW: after this patch strace has no need to know about threads, parents
and children, and so on. Therefore it does not track that information.
It treats all tracees as independent entities. Overall,
this simplifies code a lot.
* defs.h: Add TCB_ATTACH_DONE flag, remove TCB_CLONE_THREAD flag
and struct tcb::parent field.
* process.c (internal_fork): Don't set tcpchild->parent.
* strace.c (startup_attach): Use TCB_ATTACH_DONE flag instead of
TCB_CLONE_THREAD to avoid attach attempts on already-attached threads.
Unlike TCB_CLONE_THREAD, TCB_ATTACH_DONE bit is used only temporarily,
and only in this function. We clear it on every tcb before we return.
(detach): Use tkill instead of tgkill.
(trace): Set ptrace options on new tracees unconditionally,
not only when tcp->parent == NULL.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 17:18:21 +04:00
tcp - > pid , ntid ) ;
2007-06-12 02:06:31 +04:00
}
2012-03-09 18:15:24 +04:00
if ( ! ( tcp - > flags & TCB_ATTACHED ) ) {
2011-09-05 16:05:46 +04:00
/* -p PID, we failed to attach to PID itself
* but did attach to some of its sibling threads .
* Drop PID ' s tcp .
*/
droptcb ( tcp ) ;
}
2007-06-12 02:06:31 +04:00
continue ;
2009-01-29 23:38:20 +03:00
} /* if (opendir worked) */
} /* if (-f) */
2012-01-29 05:01:44 +04:00
if ( ptrace_attach_or_seize ( tcp - > pid ) < 0 ) {
2007-06-12 02:06:31 +04:00
perror ( " attach: ptrace(PTRACE_ATTACH, ...) " ) ;
droptcb ( tcp ) ;
continue ;
}
2012-03-09 18:15:24 +04:00
tcp - > flags | = TCB_ATTACHED | TCB_STARTUP | post_attach_sigstop ;
2011-06-24 18:51:16 +04:00
if ( debug )
fprintf ( stderr , " attach to pid %d (main) succeeded \n " , tcp - > pid ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
if ( daemonized_tracer ) {
/*
* It is our grandparent we trace , not a - p PID .
* Don ' t want to just detach on exit , so . . .
*/
2012-03-09 18:15:24 +04:00
tcp - > flags | = TCB_STRACE_CHILD ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
/*
* Make parent go away .
* Also makes grandparent ' s wait ( ) unblock .
*/
kill ( getppid ( ) , SIGKILL ) ;
}
2007-06-12 02:06:31 +04:00
if ( ! qflag )
fprintf ( stderr ,
" Process %u attached - interrupt to quit \n " ,
tcp - > pid ) ;
2011-06-24 18:51:16 +04:00
} /* for each tcbtab[] */
2007-06-12 02:06:31 +04:00
Remove tcp->parent and TCB_CLONE_THREAD.
tcp->parent is used for only two things:
(1) to send signal on detach via tgkill (need to know tgid).
Solution: use tkill, it needs only tid.
(2) to optimize out ptrace options setting for new tracees.
Not a big deal if we drop this optimization: "set options" op is fast,
doing it just one extra time once per each tracee is hardly measurable.
TCB_CLONE_THREAD is a misnomer. It used only to flag sibling we attached to
in startup_attach. This is used to prevent infinite recursive rescanning
of /proc/PID/task.
Despite the name, there is no guarantee it is set only on non-leader:
if one would run "strace -f -p THREAD_ID" and THREAD_ID is *not*
a thread leader, strace will happily attach to it and all siblings
and will think that THREAD_ID is the leader! Which is a bug, but
since we no longer detach when we think tracee is going to die,
this bug no longer matters, because we do not use the knowledge
about thread group leaders for anything. (We used it to delay
leader's exit).
IOW: after this patch strace has no need to know about threads, parents
and children, and so on. Therefore it does not track that information.
It treats all tracees as independent entities. Overall,
this simplifies code a lot.
* defs.h: Add TCB_ATTACH_DONE flag, remove TCB_CLONE_THREAD flag
and struct tcb::parent field.
* process.c (internal_fork): Don't set tcpchild->parent.
* strace.c (startup_attach): Use TCB_ATTACH_DONE flag instead of
TCB_CLONE_THREAD to avoid attach attempts on already-attached threads.
Unlike TCB_CLONE_THREAD, TCB_ATTACH_DONE bit is used only temporarily,
and only in this function. We clear it on every tcb before we return.
(detach): Use tkill instead of tgkill.
(trace): Set ptrace options on new tracees unconditionally,
not only when tcp->parent == NULL.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 17:18:21 +04:00
ret :
2007-06-12 02:06:31 +04:00
if ( interactive )
sigprocmask ( SIG_SETMASK , & empty_set , NULL ) ;
}
static void
2011-05-30 16:00:14 +04:00
startup_child ( char * * argv )
2007-06-12 02:06:31 +04:00
{
struct stat statbuf ;
const char * filename ;
char pathname [ MAXPATHLEN ] ;
int pid = 0 ;
struct tcb * tcp ;
filename = argv [ 0 ] ;
if ( strchr ( filename , ' / ' ) ) {
if ( strlen ( filename ) > sizeof pathname - 1 ) {
errno = ENAMETOOLONG ;
2011-06-23 15:05:29 +04:00
perror_msg_and_die ( " exec " ) ;
2007-06-12 02:06:31 +04:00
}
strcpy ( pathname , filename ) ;
}
# ifdef USE_DEBUGGING_EXEC
/*
* Debuggers customarily check the current directory
* first regardless of the path but doing that gives
* security geeks a panic attack .
*/
else if ( stat ( filename , & statbuf ) = = 0 )
strcpy ( pathname , filename ) ;
# endif /* USE_DEBUGGING_EXEC */
else {
2010-09-07 02:08:24 +04:00
const char * path ;
2007-06-12 02:06:31 +04:00
int m , n , len ;
for ( path = getenv ( " PATH " ) ; path & & * path ; path + = m ) {
2012-01-30 01:38:35 +04:00
const char * colon = strchr ( path , ' : ' ) ;
if ( colon ) {
n = colon - path ;
2007-06-12 02:06:31 +04:00
m = n + 1 ;
}
else
m = n = strlen ( path ) ;
if ( n = = 0 ) {
if ( ! getcwd ( pathname , MAXPATHLEN ) )
continue ;
len = strlen ( pathname ) ;
}
else if ( n > sizeof pathname - 1 )
continue ;
else {
strncpy ( pathname , path , n ) ;
len = n ;
}
if ( len & & pathname [ len - 1 ] ! = ' / ' )
pathname [ len + + ] = ' / ' ;
strcpy ( pathname + len , filename ) ;
if ( stat ( pathname , & statbuf ) = = 0 & &
/* Accept only regular files
with some execute bits set .
XXX not perfect , might still fail */
S_ISREG ( statbuf . st_mode ) & &
( statbuf . st_mode & 0111 ) )
break ;
}
}
if ( stat ( pathname , & statbuf ) < 0 ) {
2011-06-23 15:05:29 +04:00
perror_msg_and_die ( " Can't stat '%s' " , filename ) ;
2007-06-12 02:06:31 +04:00
}
2008-11-10 20:14:58 +03:00
strace_child = pid = fork ( ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
if ( pid < 0 ) {
2011-06-23 15:05:29 +04:00
perror_msg_and_die ( " fork " ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
}
2011-05-27 16:36:01 +04:00
if ( ( pid ! = 0 & & daemonized_tracer ) /* -D: parent to become a traced process */
| | ( pid = = 0 & & ! daemonized_tracer ) /* not -D: child to become a traced process */
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
) {
pid = getpid ( ) ;
2011-09-01 12:23:09 +04:00
if ( outf ! = stderr )
close ( fileno ( outf ) ) ;
2012-01-29 05:01:44 +04:00
if ( ! daemonized_tracer & & ! use_seize ) {
if ( ptrace ( PTRACE_TRACEME , 0L , 0L , 0L ) < 0 ) {
2011-06-23 15:05:29 +04:00
perror_msg_and_die ( " ptrace(PTRACE_TRACEME, ...) " ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
}
2007-06-12 02:06:31 +04:00
}
2011-09-01 12:27:42 +04:00
if ( username ! = NULL ) {
2007-06-12 02:06:31 +04:00
uid_t run_euid = run_uid ;
gid_t run_egid = run_gid ;
if ( statbuf . st_mode & S_ISUID )
run_euid = statbuf . st_uid ;
if ( statbuf . st_mode & S_ISGID )
run_egid = statbuf . st_gid ;
/*
* It is important to set groups before we
* lose privileges on setuid .
*/
2011-09-01 12:27:42 +04:00
if ( initgroups ( username , run_gid ) < 0 ) {
perror_msg_and_die ( " initgroups " ) ;
}
if ( setregid ( run_gid , run_egid ) < 0 ) {
perror_msg_and_die ( " setregid " ) ;
}
if ( setreuid ( run_uid , run_euid ) < 0 ) {
perror_msg_and_die ( " setreuid " ) ;
2007-06-12 02:06:31 +04:00
}
}
2011-09-01 12:27:42 +04:00
else if ( geteuid ( ) ! = 0 )
2007-06-12 02:06:31 +04:00
setreuid ( run_uid , run_uid ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
if ( ! daemonized_tracer ) {
/*
2011-09-01 12:23:09 +04:00
* Induce a ptrace stop . Tracer ( our parent )
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
* will resume us with PTRACE_SYSCALL and display
2011-09-01 12:23:09 +04:00
* the immediately following execve syscall .
* Can ' t do this on NOMMU systems , we are after
* vfork : parent is blocked , stopping would deadlock .
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
*/
2009-10-08 04:41:29 +04:00
if ( ! strace_vforked )
2011-09-01 12:23:09 +04:00
kill ( pid , SIGSTOP ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
} else {
struct sigaction sv_sigchld ;
sigaction ( SIGCHLD , NULL , & sv_sigchld ) ;
/*
* Make sure it is not SIG_IGN , otherwise wait
* will not block .
*/
signal ( SIGCHLD , SIG_DFL ) ;
/*
* Wait for grandchild to attach to us .
* It kills child after that , and wait ( ) unblocks .
*/
alarm ( 3 ) ;
wait ( NULL ) ;
alarm ( 0 ) ;
sigaction ( SIGCHLD , & sv_sigchld , NULL ) ;
}
2007-06-12 02:06:31 +04:00
execv ( pathname , argv ) ;
2011-06-23 15:05:29 +04:00
perror_msg_and_die ( " exec " ) ;
2007-06-12 02:06:31 +04:00
}
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
2011-09-01 12:23:09 +04:00
/* We are the tracer */
2011-05-27 16:36:01 +04:00
2011-09-01 12:23:09 +04:00
if ( ! daemonized_tracer ) {
2012-01-29 05:01:44 +04:00
if ( ! use_seize ) {
/* child did PTRACE_TRACEME, nothing to do in parent */
} else {
if ( ! strace_vforked ) {
/* Wait until child stopped itself */
int status ;
while ( waitpid ( pid , & status , WSTOPPED ) < 0 ) {
if ( errno = = EINTR )
continue ;
perror_msg_and_die ( " waitpid " ) ;
}
if ( ! WIFSTOPPED ( status ) | | WSTOPSIG ( status ) ! = SIGSTOP ) {
2012-03-09 18:15:24 +04:00
kill_save_errno ( pid , SIGKILL ) ;
2012-01-29 05:01:44 +04:00
perror_msg_and_die ( " Unexpected wait status %x " , status ) ;
}
}
/* Else: vforked case, we have no way to sync.
* Just attach to it as soon as possible .
* This means that we may miss a few first syscalls . . .
*/
if ( ptrace_attach_or_seize ( pid ) ) {
2012-03-09 18:15:24 +04:00
kill_save_errno ( pid , SIGKILL ) ;
2012-01-29 05:01:44 +04:00
perror_msg_and_die ( " Can't attach to %d " , pid ) ;
}
if ( ! strace_vforked )
kill ( pid , SIGCONT ) ;
}
2011-09-01 12:23:09 +04:00
tcp = alloctcb ( pid ) ;
2011-09-05 16:05:46 +04:00
if ( ! strace_vforked )
2012-03-09 18:15:24 +04:00
tcp - > flags | = TCB_ATTACHED | TCB_STRACE_CHILD | TCB_STARTUP | post_attach_sigstop ;
2011-09-05 16:05:46 +04:00
else
2012-03-09 18:15:24 +04:00
tcp - > flags | = TCB_ATTACHED | TCB_STRACE_CHILD | TCB_STARTUP ;
2011-09-01 12:23:09 +04:00
}
else {
/* With -D, *we* are child here, IOW: different pid. Fetch it: */
strace_tracer_pid = getpid ( ) ;
/* The tracee is our parent: */
pid = getppid ( ) ;
tcp = alloctcb ( pid ) ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
}
2007-06-12 02:06:31 +04:00
}
2010-11-12 12:25:19 +03:00
/*
2010-12-02 23:56:43 +03:00
* Test whether the kernel support PTRACE_O_TRACECLONE et al options .
2010-11-12 12:25:19 +03:00
* First fork a new child , call ptrace with PTRACE_SETOPTIONS on it ,
2010-12-02 23:56:43 +03:00
* and then see which options are supported by the kernel .
2010-11-12 12:25:19 +03:00
*/
2011-08-16 22:57:29 +04:00
static void
2011-05-23 23:29:03 +04:00
test_ptrace_setoptions_followfork ( void )
2010-11-12 12:25:19 +03:00
{
2011-02-20 00:33:50 +03:00
int pid , expected_grandchild = 0 , found_grandchild = 0 ;
const unsigned int test_options = PTRACE_O_TRACECLONE |
PTRACE_O_TRACEFORK |
PTRACE_O_TRACEVFORK ;
2010-11-12 12:25:19 +03:00
2011-08-20 14:48:18 +04:00
pid = fork ( ) ;
if ( pid < 0 )
2011-08-16 22:57:29 +04:00
perror_msg_and_die ( " fork " ) ;
2011-08-20 14:48:18 +04:00
if ( pid = = 0 ) {
2011-08-16 22:57:29 +04:00
pid = getpid ( ) ;
2012-01-29 05:01:44 +04:00
if ( ptrace ( PTRACE_TRACEME , 0L , 0L , 0L ) < 0 )
2011-08-16 22:57:29 +04:00
perror_msg_and_die ( " %s: PTRACE_TRACEME doesn't work " ,
__func__ ) ;
2012-03-08 14:54:10 +04:00
kill_save_errno ( pid , SIGSTOP ) ;
2011-08-16 22:57:29 +04:00
if ( fork ( ) < 0 )
perror_msg_and_die ( " fork " ) ;
_exit ( 0 ) ;
2010-11-12 12:25:19 +03:00
}
2010-12-02 23:56:43 +03:00
while ( 1 ) {
int status , tracee_pid ;
2011-08-16 22:57:29 +04:00
errno = 0 ;
2010-12-02 23:56:43 +03:00
tracee_pid = wait ( & status ) ;
2011-08-16 22:57:29 +04:00
if ( tracee_pid < = 0 ) {
2010-12-02 23:56:43 +03:00
if ( errno = = EINTR )
continue ;
2012-03-08 14:54:10 +04:00
if ( errno = = ECHILD )
2010-11-12 12:25:19 +03:00
break ;
2011-08-16 22:57:29 +04:00
kill_save_errno ( pid , SIGKILL ) ;
perror_msg_and_die ( " %s: unexpected wait result %d " ,
__func__ , tracee_pid ) ;
}
if ( WIFEXITED ( status ) ) {
if ( WEXITSTATUS ( status ) ) {
if ( tracee_pid ! = pid )
kill_save_errno ( pid , SIGKILL ) ;
error_msg_and_die ( " %s: unexpected exit status %u " ,
__func__ , WEXITSTATUS ( status ) ) ;
}
continue ;
}
if ( WIFSIGNALED ( status ) ) {
if ( tracee_pid ! = pid )
kill_save_errno ( pid , SIGKILL ) ;
error_msg_and_die ( " %s: unexpected signal %u " ,
__func__ , WTERMSIG ( status ) ) ;
}
if ( ! WIFSTOPPED ( status ) ) {
if ( tracee_pid ! = pid )
kill_save_errno ( tracee_pid , SIGKILL ) ;
2012-03-08 14:54:10 +04:00
kill_save_errno ( pid , SIGKILL ) ;
2011-08-16 22:57:29 +04:00
error_msg_and_die ( " %s: unexpected wait status %x " ,
__func__ , status ) ;
2010-12-02 23:56:43 +03:00
}
if ( tracee_pid ! = pid ) {
2011-02-20 00:33:50 +03:00
found_grandchild = tracee_pid ;
2011-08-16 22:57:29 +04:00
if ( ptrace ( PTRACE_CONT , tracee_pid , 0 , 0 ) < 0 ) {
kill_save_errno ( tracee_pid , SIGKILL ) ;
kill_save_errno ( pid , SIGKILL ) ;
perror_msg_and_die ( " PTRACE_CONT doesn't work " ) ;
}
continue ;
2010-12-02 23:56:43 +03:00
}
2011-08-16 22:57:29 +04:00
switch ( WSTOPSIG ( status ) ) {
case SIGSTOP :
if ( ptrace ( PTRACE_SETOPTIONS , pid , 0 , test_options ) < 0
& & errno ! = EINVAL & & errno ! = EIO )
perror_msg ( " PTRACE_SETOPTIONS " ) ;
break ;
case SIGTRAP :
if ( status > > 16 = = PTRACE_EVENT_FORK ) {
long msg = 0 ;
2011-02-20 00:33:50 +03:00
2011-08-16 22:57:29 +04:00
if ( ptrace ( PTRACE_GETEVENTMSG , pid ,
NULL , ( long ) & msg ) = = 0 )
expected_grandchild = msg ;
2010-11-12 12:25:19 +03:00
}
2011-08-16 22:57:29 +04:00
break ;
}
if ( ptrace ( PTRACE_SYSCALL , pid , 0 , 0 ) < 0 ) {
kill_save_errno ( pid , SIGKILL ) ;
perror_msg_and_die ( " PTRACE_SYSCALL doesn't work " ) ;
2010-11-12 12:25:19 +03:00
}
}
2011-08-16 22:57:29 +04:00
if ( expected_grandchild & & expected_grandchild = = found_grandchild ) {
2011-06-21 16:34:10 +04:00
ptrace_setoptions | = test_options ;
2011-08-16 22:57:29 +04:00
if ( debug )
fprintf ( stderr , " ptrace_setoptions = %#x \n " ,
ptrace_setoptions ) ;
return ;
}
error_msg ( " Test for PTRACE_O_TRACECLONE failed, "
" giving up using this feature. " ) ;
2010-11-12 12:25:19 +03:00
}
2011-05-23 23:29:03 +04:00
/*
* Test whether the kernel support PTRACE_O_TRACESYSGOOD .
* First fork a new child , call ptrace ( PTRACE_SETOPTIONS ) on it ,
* and then see whether it will stop with ( SIGTRAP | 0x80 ) .
*
* Use of this option enables correct handling of user - generated SIGTRAPs ,
* and SIGTRAPs generated by special instructions such as int3 on x86 :
* _start : . globl _start
* int3
* movl $ 42 , % ebx
* movl $ 1 , % eax
* int $ 0x80
* ( compile with : " gcc -nostartfiles -nostdlib -o int3 int3.S " )
*/
static void
test_ptrace_setoptions_for_all ( void )
{
2011-08-16 22:57:29 +04:00
const unsigned int test_options = PTRACE_O_TRACESYSGOOD |
PTRACE_O_TRACEEXEC ;
2011-05-23 23:29:03 +04:00
int pid ;
int it_worked = 0 ;
pid = fork ( ) ;
if ( pid < 0 )
2011-05-27 16:36:01 +04:00
perror_msg_and_die ( " fork " ) ;
2011-05-23 23:29:03 +04:00
if ( pid = = 0 ) {
pid = getpid ( ) ;
if ( ptrace ( PTRACE_TRACEME , 0L , 0L , 0L ) < 0 )
2011-05-27 16:36:01 +04:00
/* Note: exits with exitcode 1 */
2011-08-16 22:57:29 +04:00
perror_msg_and_die ( " %s: PTRACE_TRACEME doesn't work " ,
__func__ ) ;
2011-05-23 23:29:03 +04:00
kill ( pid , SIGSTOP ) ;
_exit ( 0 ) ; /* parent should see entry into this syscall */
}
while ( 1 ) {
int status , tracee_pid ;
errno = 0 ;
tracee_pid = wait ( & status ) ;
if ( tracee_pid < = 0 ) {
if ( errno = = EINTR )
continue ;
2011-08-16 22:57:29 +04:00
kill_save_errno ( pid , SIGKILL ) ;
perror_msg_and_die ( " %s: unexpected wait result %d " ,
__func__ , tracee_pid ) ;
2011-05-27 16:36:01 +04:00
}
if ( WIFEXITED ( status ) ) {
if ( WEXITSTATUS ( status ) = = 0 )
break ;
2011-08-16 22:57:29 +04:00
error_msg_and_die ( " %s: unexpected exit status %u " ,
__func__ , WEXITSTATUS ( status ) ) ;
}
if ( WIFSIGNALED ( status ) ) {
error_msg_and_die ( " %s: unexpected signal %u " ,
__func__ , WTERMSIG ( status ) ) ;
2011-05-23 23:29:03 +04:00
}
if ( ! WIFSTOPPED ( status ) ) {
kill ( pid , SIGKILL ) ;
2011-08-16 22:57:29 +04:00
error_msg_and_die ( " %s: unexpected wait status %x " ,
__func__ , status ) ;
2011-05-23 23:29:03 +04:00
}
if ( WSTOPSIG ( status ) = = SIGSTOP ) {
/*
* We don ' t check " options aren't accepted " error .
* If it happens , we ' ll never get ( SIGTRAP | 0x80 ) ,
* and thus will decide to not use the option .
* IOW : the outcome of the test will be correct .
*/
2011-08-16 22:57:29 +04:00
if ( ptrace ( PTRACE_SETOPTIONS , pid , 0L , test_options ) < 0
& & errno ! = EINVAL & & errno ! = EIO )
perror_msg ( " PTRACE_SETOPTIONS " ) ;
2011-05-23 23:29:03 +04:00
}
if ( WSTOPSIG ( status ) = = ( SIGTRAP | 0x80 ) ) {
it_worked = 1 ;
}
if ( ptrace ( PTRACE_SYSCALL , pid , 0L , 0L ) < 0 ) {
2011-08-16 22:57:29 +04:00
kill_save_errno ( pid , SIGKILL ) ;
2011-05-27 16:36:01 +04:00
perror_msg_and_die ( " PTRACE_SYSCALL doesn't work " ) ;
2011-05-23 23:29:03 +04:00
}
}
if ( it_worked ) {
2011-05-27 16:36:01 +04:00
syscall_trap_sig = ( SIGTRAP | 0x80 ) ;
2011-06-21 16:34:10 +04:00
ptrace_setoptions | = test_options ;
2011-05-23 23:29:03 +04:00
if ( debug )
2011-06-21 16:34:10 +04:00
fprintf ( stderr , " ptrace_setoptions = %#x \n " ,
ptrace_setoptions ) ;
2011-05-23 23:29:03 +04:00
return ;
}
2011-08-16 22:57:29 +04:00
error_msg ( " Test for PTRACE_O_TRACESYSGOOD failed, "
" giving up using this feature. " ) ;
2011-05-23 23:29:03 +04:00
}
2012-01-29 05:01:44 +04:00
# ifdef USE_SEIZE
static void
test_ptrace_seize ( void )
{
int pid ;
pid = fork ( ) ;
if ( pid < 0 )
perror_msg_and_die ( " fork " ) ;
if ( pid = = 0 ) {
pause ( ) ;
_exit ( 0 ) ;
}
/* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
* attaching tracee continues to run unless a trap condition occurs .
* PTRACE_SEIZE doesn ' t affect signal or group stop state .
*/
if ( ptrace ( PTRACE_SEIZE , pid , 0 , PTRACE_SEIZE_DEVEL ) = = 0 ) {
post_attach_sigstop = 0 ; /* this sets use_seize to 1 */
} else if ( debug ) {
fprintf ( stderr , " PTRACE_SEIZE doesn't work \n " ) ;
}
kill ( pid , SIGKILL ) ;
while ( 1 ) {
int status , tracee_pid ;
errno = 0 ;
tracee_pid = waitpid ( pid , & status , 0 ) ;
if ( tracee_pid < = 0 ) {
if ( errno = = EINTR )
continue ;
perror_msg_and_die ( " %s: unexpected wait result %d " ,
__func__ , tracee_pid ) ;
}
if ( WIFSIGNALED ( status ) ) {
return ;
}
error_msg_and_die ( " %s: unexpected wait status %x " ,
__func__ , status ) ;
}
}
# else /* !USE_SEIZE */
# define test_ptrace_seize() ((void)0)
# endif
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
/* Noinline: don't want main to have struct utsname permanently on stack */
static void __attribute__ ( ( noinline ) )
get_os_release ( void )
{
struct utsname u ;
if ( uname ( & u ) < 0 )
perror_msg_and_die ( " uname " ) ;
os_release = strdup ( u . release ) ;
if ( ! os_release )
die_out_of_memory ( ) ;
}
1999-02-19 03:21:36 +03:00
int
2007-10-09 01:04:41 +04:00
main ( int argc , char * argv [ ] )
1999-02-19 03:21:36 +03:00
{
struct tcb * tcp ;
2012-03-09 16:01:04 +04:00
int c ;
2008-07-25 19:42:34 +04:00
int optF = 0 ;
1999-02-19 03:21:36 +03:00
struct sigaction sa ;
2007-10-09 01:04:41 +04:00
progname = argv [ 0 ] ? argv [ 0 ] : " strace " ;
2011-05-27 16:36:01 +04:00
strace_tracer_pid = getpid ( ) ;
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
get_os_release ( ) ;
2002-12-18 07:16:10 +03:00
/* Allocate the initial tcbtab. */
tcbtabsize = argc ; /* Surely enough for all -p args. */
2011-06-23 15:16:23 +04:00
tcbtab = calloc ( tcbtabsize , sizeof ( tcbtab [ 0 ] ) ) ;
2011-08-31 16:00:02 +04:00
if ( ! tcbtab )
die_out_of_memory ( ) ;
2011-06-23 15:16:23 +04:00
tcp = calloc ( tcbtabsize , sizeof ( * tcp ) ) ;
2011-08-31 16:00:02 +04:00
if ( ! tcp )
die_out_of_memory ( ) ;
2011-06-23 15:16:23 +04:00
for ( c = 0 ; c < tcbtabsize ; c + + )
tcbtab [ c ] = tcp + + ;
2002-12-18 07:16:10 +03:00
1999-02-19 03:21:36 +03:00
outf = stderr ;
2006-01-12 12:50:49 +03:00
set_sortby ( DEFAULT_SORTBY ) ;
set_personality ( DEFAULT_PERSONALITY ) ;
1999-02-19 03:21:36 +03:00
qualify ( " trace=all " ) ;
qualify ( " abbrev=all " ) ;
qualify ( " verbose=all " ) ;
qualify ( " signal=all " ) ;
while ( ( c = getopt ( argc , argv ,
2011-04-08 00:25:40 +04:00
" +cCdfFhiqrtTvVxyz "
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
" D "
2012-01-29 19:53:03 +04:00
" a:e:o:O:p:s:S:u:E:P:I: " ) ) ! = EOF ) {
1999-02-19 03:21:36 +03:00
switch ( c ) {
case ' c ' :
2010-03-28 23:24:54 +04:00
if ( cflag = = CFLAG_BOTH ) {
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " -c and -C are mutually exclusive options " ) ;
2010-03-28 23:24:54 +04:00
}
cflag = CFLAG_ONLY_STATS ;
break ;
case ' C ' :
if ( cflag = = CFLAG_ONLY_STATS ) {
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " -c and -C are mutually exclusive options " ) ;
2010-03-28 23:24:54 +04:00
}
cflag = CFLAG_BOTH ;
1999-02-19 03:21:36 +03:00
break ;
case ' d ' :
debug + + ;
break ;
Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
2008-12-30 23:51:30 +03:00
case ' D ' :
daemonized_tracer = 1 ;
break ;
2008-07-18 04:25:10 +04:00
case ' F ' :
2008-07-25 19:42:34 +04:00
optF = 1 ;
break ;
1999-02-19 03:21:36 +03:00
case ' f ' :
followfork + + ;
break ;
case ' h ' :
usage ( stdout , 0 ) ;
break ;
case ' i ' :
iflag + + ;
break ;
case ' q ' :
qflag + + ;
break ;
case ' r ' :
rflag + + ;
tflag + + ;
break ;
case ' t ' :
tflag + + ;
break ;
case ' T ' :
dtime + + ;
break ;
case ' x ' :
xflag + + ;
break ;
2011-04-08 00:25:40 +04:00
case ' y ' :
show_fd_path = 1 ;
break ;
1999-02-19 03:21:36 +03:00
case ' v ' :
qualify ( " abbrev=none " ) ;
break ;
case ' V ' :
2003-02-20 05:56:29 +03:00
printf ( " %s -- version %s \n " , PACKAGE_NAME , VERSION ) ;
1999-02-19 03:21:36 +03:00
exit ( 0 ) ;
break ;
2002-11-06 16:17:21 +03:00
case ' z ' :
not_failing_only = 1 ;
break ;
1999-02-19 03:21:36 +03:00
case ' a ' :
acolumn = atoi ( optarg ) ;
2011-08-25 03:27:59 +04:00
if ( acolumn < 0 )
error_msg_and_die ( " Bad column width '%s' " , optarg ) ;
1999-02-19 03:21:36 +03:00
break ;
case ' e ' :
qualify ( optarg ) ;
break ;
case ' o ' :
outfname = strdup ( optarg ) ;
break ;
case ' O ' :
set_overhead ( atoi ( optarg ) ) ;
break ;
case ' p ' :
2012-03-09 16:01:04 +04:00
process_opt_p_list ( optarg ) ;
1999-02-19 03:21:36 +03:00
break ;
2011-04-08 00:25:40 +04:00
case ' P ' :
tracing_paths = 1 ;
if ( pathtrace_select ( optarg ) ) {
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " Failed to select path '%s' " , optarg ) ;
2011-04-08 00:25:40 +04:00
}
break ;
1999-02-19 03:21:36 +03:00
case ' s ' :
max_strlen = atoi ( optarg ) ;
2005-05-09 11:45:47 +04:00
if ( max_strlen < 0 ) {
2012-01-29 19:53:03 +04:00
error_msg_and_die ( " Invalid -%c argument: '%s' " , c , optarg ) ;
2005-05-09 11:45:47 +04:00
}
1999-02-19 03:21:36 +03:00
break ;
case ' S ' :
set_sortby ( optarg ) ;
break ;
case ' u ' :
username = strdup ( optarg ) ;
break ;
2003-01-24 07:31:23 +03:00
case ' E ' :
2011-08-31 16:00:02 +04:00
if ( putenv ( optarg ) < 0 )
die_out_of_memory ( ) ;
2003-01-24 07:31:23 +03:00
break ;
2012-01-29 19:53:03 +04:00
case ' I ' :
opt_intr = atoi ( optarg ) ;
if ( opt_intr < = 0 | | opt_intr > = NUM_INTR_OPTS ) {
error_msg_and_die ( " Invalid -%c argument: '%s' " , c , optarg ) ;
}
break ;
1999-02-19 03:21:36 +03:00
default :
usage ( stderr , 1 ) ;
break ;
}
}
2012-01-24 14:37:03 +04:00
argv + = optind ;
/* argc -= optind; - no need, argc is not used below */
1999-02-19 03:21:36 +03:00
2011-08-25 03:27:59 +04:00
acolumn_spaces = malloc ( acolumn + 1 ) ;
if ( ! acolumn_spaces )
2011-08-31 16:00:02 +04:00
die_out_of_memory ( ) ;
2011-08-25 03:27:59 +04:00
memset ( acolumn_spaces , ' ' , acolumn ) ;
acolumn_spaces [ acolumn ] = ' \0 ' ;
2012-01-24 14:37:03 +04:00
/* Must have PROG [ARGS], or -p PID. Not both. */
2012-03-09 16:03:41 +04:00
if ( ! argv [ 0 ] = = ! nprocs )
2003-11-12 00:24:23 +03:00
usage ( stderr , 1 ) ;
2012-03-09 16:03:41 +04:00
if ( nprocs ! = 0 & & daemonized_tracer ) {
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " -D and -p are mutually exclusive options " ) ;
2010-08-05 10:30:11 +04:00
}
2008-07-25 19:42:34 +04:00
if ( ! followfork )
followfork = optF ;
2006-04-25 11:48:03 +04:00
if ( followfork > 1 & & cflag ) {
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " (-c or -C) and -ff are mutually exclusive options " ) ;
2006-04-25 11:48:03 +04:00
}
1999-02-19 03:21:36 +03:00
/* See if they want to run as another user. */
if ( username ! = NULL ) {
struct passwd * pent ;
if ( getuid ( ) ! = 0 | | geteuid ( ) ! = 0 ) {
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " You must be root to use the -u option " ) ;
1999-02-19 03:21:36 +03:00
}
2011-08-20 14:48:18 +04:00
pent = getpwnam ( username ) ;
if ( pent = = NULL ) {
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " Cannot find user '%s' " , username ) ;
1999-02-19 03:21:36 +03:00
}
run_uid = pent - > pw_uid ;
run_gid = pent - > pw_gid ;
}
else {
run_uid = getuid ( ) ;
run_gid = getgid ( ) ;
}
2011-08-16 22:57:29 +04:00
if ( followfork )
test_ptrace_setoptions_followfork ( ) ;
2011-05-23 23:29:03 +04:00
test_ptrace_setoptions_for_all ( ) ;
2012-01-29 05:01:44 +04:00
test_ptrace_seize ( ) ;
2010-12-07 15:50:49 +03:00
1999-02-19 03:21:36 +03:00
/* Check if they want to redirect the output. */
if ( outfname ) {
2003-11-07 05:26:54 +03:00
/* See if they want to pipe the output. */
if ( outfname [ 0 ] = = ' | ' | | outfname [ 0 ] = = ' ! ' ) {
/*
* We can ' t do the < outfname > . PID funny business
* when using popen , so prohibit it .
*/
2011-06-22 15:03:56 +04:00
if ( followfork > 1 )
error_msg_and_die ( " Piping the output and -ff are mutually exclusive " ) ;
outf = strace_popen ( outfname + 1 ) ;
2001-08-03 15:43:35 +04:00
}
2011-06-22 15:17:16 +04:00
else if ( followfork < = 1 )
outf = strace_fopen ( outfname ) ;
1999-02-19 03:21:36 +03:00
}
2011-06-23 15:05:29 +04:00
if ( ! outfname | | outfname [ 0 ] = = ' | ' | | outfname [ 0 ] = = ' ! ' ) {
2012-01-24 14:31:51 +04:00
char * buf = malloc ( BUFSIZ ) ;
if ( ! buf )
die_out_of_memory ( ) ;
1999-02-19 03:21:36 +03:00
setvbuf ( outf , buf , _IOLBF , BUFSIZ ) ;
2011-06-23 15:05:29 +04:00
}
2012-01-24 14:37:03 +04:00
if ( outfname & & argv [ 0 ] ) {
2012-01-29 19:53:03 +04:00
if ( ! opt_intr )
opt_intr = INTR_NEVER ;
1999-02-19 03:21:36 +03:00
qflag = 1 ;
2003-06-03 05:35:20 +04:00
}
2012-01-29 19:53:03 +04:00
if ( ! opt_intr )
opt_intr = INTR_WHILE_WAIT ;
/* argv[0] -pPID -oFILE Default interactive setting
* yes 0 0 INTR_WHILE_WAIT
* no 1 0 INTR_WHILE_WAIT
* yes 0 1 INTR_NEVER
* no 1 1 INTR_WHILE_WAIT
2007-11-04 02:34:11 +03:00
*/
/* STARTUP_CHILD must be called before the signal handlers get
installed below as they are inherited into the spawned process .
Also we do not need to be protected by them as during interruption
in the STARTUP_CHILD mode we kill the spawned process anyway . */
2012-01-24 14:37:03 +04:00
if ( argv [ 0 ] )
startup_child ( argv ) ;
1999-02-19 03:21:36 +03:00
sigemptyset ( & empty_set ) ;
sigemptyset ( & blocked_set ) ;
sa . sa_handler = SIG_IGN ;
sigemptyset ( & sa . sa_mask ) ;
sa . sa_flags = 0 ;
2012-01-29 19:53:03 +04:00
sigaction ( SIGTTOU , & sa , NULL ) ; /* SIG_IGN */
sigaction ( SIGTTIN , & sa , NULL ) ; /* SIG_IGN */
if ( opt_intr ! = INTR_ANYWHERE ) {
if ( opt_intr = = INTR_BLOCK_TSTP_TOO )
sigaction ( SIGTSTP , & sa , NULL ) ; /* SIG_IGN */
/*
* In interactive mode ( if no - o OUTFILE , or - p PID is used ) ,
* fatal signals are blocked while syscall stop is processed ,
* and acted on in between , when waiting for new syscall stops .
* In non - interactive mode , signals are ignored .
*/
if ( opt_intr = = INTR_WHILE_WAIT ) {
sigaddset ( & blocked_set , SIGHUP ) ;
sigaddset ( & blocked_set , SIGINT ) ;
sigaddset ( & blocked_set , SIGQUIT ) ;
sigaddset ( & blocked_set , SIGPIPE ) ;
sigaddset ( & blocked_set , SIGTERM ) ;
sa . sa_handler = interrupt ;
}
/* SIG_IGN, or set handler for these */
sigaction ( SIGHUP , & sa , NULL ) ;
sigaction ( SIGINT , & sa , NULL ) ;
sigaction ( SIGQUIT , & sa , NULL ) ;
sigaction ( SIGPIPE , & sa , NULL ) ;
sigaction ( SIGTERM , & sa , NULL ) ;
1999-02-19 03:21:36 +03:00
}
2002-12-16 23:40:39 +03:00
/* Make sure SIGCHLD has the default action so that waitpid
definitely works without losing track of children . The user
should not have given us a bogus state to inherit , but he might
have . Arguably we should detect SIG_IGN here and pass it on
to children , but probably noone really needs that . */
sa . sa_handler = SIG_DFL ;
sigaction ( SIGCHLD , & sa , NULL ) ;
1999-02-19 03:21:36 +03:00
2012-03-09 16:03:41 +04:00
if ( nprocs ! = 0 | | daemonized_tracer )
2007-06-12 02:06:31 +04:00
startup_attach ( ) ;
2012-03-09 16:03:41 +04:00
/* Do we want pids printed in our -o OUTFILE?
* - ff : no ( every pid has its own file ) ; or
* - f : yes ( there can be more pids in the future ) ; or
* - p PID1 , PID2 : yes ( there are already more than one pid )
*/
print_pid_pfx = ( outfname & & followfork < 2 & & ( followfork = = 1 | | nprocs > 1 ) ) ;
1999-02-19 03:21:36 +03:00
if ( trace ( ) < 0 )
exit ( 1 ) ;
2012-01-29 19:53:03 +04:00
1999-02-19 03:21:36 +03:00
cleanup ( ) ;
2008-11-10 20:14:58 +03:00
fflush ( NULL ) ;
if ( exit_code > 0xff ) {
2012-02-22 04:15:27 +04:00
/* Avoid potential core file clobbering. */
struct rlimit rlim = { 0 , 0 } ;
setrlimit ( RLIMIT_CORE , & rlim ) ;
2008-11-10 20:14:58 +03:00
/* Child was killed by a signal, mimic that. */
exit_code & = 0xff ;
signal ( exit_code , SIG_DFL ) ;
raise ( exit_code ) ;
/* Paranoia - what if this signal is not fatal?
Exit with 128 + signo then . */
exit_code + = 128 ;
}
exit ( exit_code ) ;
1999-02-19 03:21:36 +03:00
}
2011-06-22 14:45:25 +04:00
static void
Two cleanups: tcb table expansion failure is not really a survivable
event, we do not have any viable way to continue. No wonder most
places where that is detected have FIXMEs.
It's way simpler to treat as fatal failure, and handle it inside
tcb table expansion finctions.
Second cleanup: tidy up haphazard locations of a few externs.
* defs.h: Change return type of expand_tcbtab() to void.
Declare change_syscall().
* process.c: Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(fork_tcb): Change return type to void - it can't fail now.
* strace.c: Move extern declarations out of function bodies.
Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(expand_tcbtab): Change return type to void - it can't fail now.
On failure to expand, print a message, clean up, and exit.
(alloc_tcb): On failure to expand, print a message, clean up, and exit.
* util.c (setbpt): Remove extern declaration from function body.
2009-01-17 04:52:54 +03:00
expand_tcbtab ( void )
2004-06-04 05:50:45 +04:00
{
/* Allocate some more TCBs and expand the table.
We don ' t want to relocate the TCBs because our
callers have pointers and it would be a pain .
So tcbtab is a table of pointers . Since we never
free the TCBs , we allocate a single chunk of many . */
2011-06-22 14:41:57 +04:00
int i = tcbtabsize ;
struct tcb * newtcbs = calloc ( tcbtabsize , sizeof ( newtcbs [ 0 ] ) ) ;
struct tcb * * newtab = realloc ( tcbtab , tcbtabsize * 2 * sizeof ( tcbtab [ 0 ] ) ) ;
2011-08-31 16:00:02 +04:00
if ( ! newtab | | ! newtcbs )
die_out_of_memory ( ) ;
2004-06-04 05:50:45 +04:00
tcbtabsize * = 2 ;
tcbtab = newtab ;
2011-06-22 14:41:57 +04:00
while ( i < tcbtabsize )
tcbtab [ i + + ] = newtcbs + + ;
2004-06-04 05:50:45 +04:00
}
1999-02-19 03:21:36 +03:00
struct tcb *
2006-12-14 00:45:31 +03:00
alloc_tcb ( int pid , int command_options_parsed )
1999-02-19 03:21:36 +03:00
{
int i ;
struct tcb * tcp ;
Two cleanups: tcb table expansion failure is not really a survivable
event, we do not have any viable way to continue. No wonder most
places where that is detected have FIXMEs.
It's way simpler to treat as fatal failure, and handle it inside
tcb table expansion finctions.
Second cleanup: tidy up haphazard locations of a few externs.
* defs.h: Change return type of expand_tcbtab() to void.
Declare change_syscall().
* process.c: Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(fork_tcb): Change return type to void - it can't fail now.
* strace.c: Move extern declarations out of function bodies.
Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(expand_tcbtab): Change return type to void - it can't fail now.
On failure to expand, print a message, clean up, and exit.
(alloc_tcb): On failure to expand, print a message, clean up, and exit.
* util.c (setbpt): Remove extern declaration from function body.
2009-01-17 04:52:54 +03:00
if ( nprocs = = tcbtabsize )
expand_tcbtab ( ) ;
2002-12-18 07:16:10 +03:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
tcp = tcbtab [ i ] ;
1999-02-19 03:21:36 +03:00
if ( ( tcp - > flags & TCB_INUSE ) = = 0 ) {
2011-06-22 14:41:57 +04:00
memset ( tcp , 0 , sizeof ( * tcp ) ) ;
1999-02-19 03:21:36 +03:00
tcp - > pid = pid ;
2011-09-05 15:59:39 +04:00
tcp - > flags = TCB_INUSE ;
1999-02-19 03:21:36 +03:00
tcp - > outf = outf ; /* Initialise to current out file */
2011-12-23 04:50:49 +04:00
# if SUPPORTED_PERSONALITIES > 1
tcp - > currpers = current_personality ;
2011-08-20 15:44:56 +04:00
# endif
1999-02-19 03:21:36 +03:00
nprocs + + ;
2011-06-24 18:41:35 +04:00
if ( debug )
fprintf ( stderr , " new tcb for pid %d, active tcbs:%d \n " , tcp - > pid , nprocs ) ;
2006-12-14 00:45:31 +03:00
if ( command_options_parsed )
newoutf ( tcp ) ;
1999-02-19 03:21:36 +03:00
return tcp ;
}
}
2011-06-22 14:41:57 +04:00
error_msg_and_die ( " bug in alloc_tcb " ) ;
1999-02-19 03:21:36 +03:00
}
2012-01-27 18:24:48 +04:00
static struct tcb *
2010-09-15 05:59:20 +04:00
pid2tcb ( int pid )
1999-02-19 03:21:36 +03:00
{
int i ;
2010-09-15 05:59:20 +04:00
if ( pid < = 0 )
return NULL ;
1999-02-19 03:21:36 +03:00
2002-12-18 07:16:10 +03:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
2010-09-15 05:59:20 +04:00
struct tcb * tcp = tcbtab [ i ] ;
if ( tcp - > pid = = pid & & ( tcp - > flags & TCB_INUSE ) )
1999-02-19 03:21:36 +03:00
return tcp ;
}
2010-09-15 05:59:20 +04:00
1999-02-19 03:21:36 +03:00
return NULL ;
}
void
2011-05-30 16:00:14 +04:00
droptcb ( struct tcb * tcp )
1999-02-19 03:21:36 +03:00
{
if ( tcp - > pid = = 0 )
return ;
Do not detach when we think tracee is going to die.
Current code plays some ungodly tricks, trying to not detach
thread group leader until all threads exit.
Also, it detaches from a tracee when signal delivery is detected
which will cause tracee to exit.
This operation is racy (not to mention the determination
whether signal is set to SIG_DFL is a horrible hack):
after we determined that this signal is indeed fatal
but before we detach and let process die,
*other thread* may set a handler to this signal, and
we will leak the process, falsely displaying it as killed!
I need to look in the past to figure out why we even do it.
First guess is that it's a workaround for old kernel bugs:
kernel used to deliver exit notifications to the tracer,
not to real parent. These workarounds are ancient
(internal_exit is from 1995).
The patch deletes the hacks. We no longer need tcp->nclone_threads,
TCB_EXITING and TCB_GROUP_EXITING. We also lose a few rather
ugly functions.
I also added a new message: "+++ exited with EXITCODE +++"
which shows exact moment strace got exit notification.
It is analogous to existing "+++ killed by SIG +++" message.
* defs.h: Delete struct tcb::nclone_threads field,
TCB_EXITING and TCB_GROUP_EXITING constants,
declarations of sigishandled() and internal_exit().
* process.c (internal_exit): Delete this function.
(handle_new_child): Don't ++tcp->nclone_threads.
* signal.c (parse_sigset_t): Delete this function.
(sigishandled): Delete this function.
* strace.c (startup_attach): Don't tcbtab[tcbi]->nclone_threads++.
(droptcb): Don't delay dropping if tcp->nclone_threads > 0,
don't drop parent if its nclone_threads reached 0:
just drop (only) this tcb unconditionally.
(detach): don't drop parent.
(handle_group_exit): Delete this function.
(handle_ptrace_event): Instead of handle_group_exit, just drop tcb;
do not panic if we see WIFEXITED from an attached pid;
print "+++ exited with EXITCODE +++" for every WIFEXITED pid.
* syscall.c (internal_syscall): Do not treat sys_exit specially -
don't call internal_exit on it.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 12:45:32 +04:00
1999-02-19 03:21:36 +03:00
nprocs - - ;
2011-06-24 18:41:35 +04:00
if ( debug )
fprintf ( stderr , " dropped tcb for pid %d, %d remain \n " , tcp - > pid , nprocs ) ;
2002-04-01 21:48:02 +04:00
2002-04-03 14:55:14 +04:00
if ( outfname & & followfork > 1 & & tcp - > outf )
1999-02-19 03:21:36 +03:00
fclose ( tcp - > outf ) ;
2002-04-01 21:48:02 +04:00
Do not detach when we think tracee is going to die.
Current code plays some ungodly tricks, trying to not detach
thread group leader until all threads exit.
Also, it detaches from a tracee when signal delivery is detected
which will cause tracee to exit.
This operation is racy (not to mention the determination
whether signal is set to SIG_DFL is a horrible hack):
after we determined that this signal is indeed fatal
but before we detach and let process die,
*other thread* may set a handler to this signal, and
we will leak the process, falsely displaying it as killed!
I need to look in the past to figure out why we even do it.
First guess is that it's a workaround for old kernel bugs:
kernel used to deliver exit notifications to the tracer,
not to real parent. These workarounds are ancient
(internal_exit is from 1995).
The patch deletes the hacks. We no longer need tcp->nclone_threads,
TCB_EXITING and TCB_GROUP_EXITING. We also lose a few rather
ugly functions.
I also added a new message: "+++ exited with EXITCODE +++"
which shows exact moment strace got exit notification.
It is analogous to existing "+++ killed by SIG +++" message.
* defs.h: Delete struct tcb::nclone_threads field,
TCB_EXITING and TCB_GROUP_EXITING constants,
declarations of sigishandled() and internal_exit().
* process.c (internal_exit): Delete this function.
(handle_new_child): Don't ++tcp->nclone_threads.
* signal.c (parse_sigset_t): Delete this function.
(sigishandled): Delete this function.
* strace.c (startup_attach): Don't tcbtab[tcbi]->nclone_threads++.
(droptcb): Don't delay dropping if tcp->nclone_threads > 0,
don't drop parent if its nclone_threads reached 0:
just drop (only) this tcb unconditionally.
(detach): don't drop parent.
(handle_group_exit): Delete this function.
(handle_ptrace_event): Instead of handle_group_exit, just drop tcb;
do not panic if we see WIFEXITED from an attached pid;
print "+++ exited with EXITCODE +++" for every WIFEXITED pid.
* syscall.c (internal_syscall): Do not treat sys_exit specially -
don't call internal_exit on it.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 12:45:32 +04:00
memset ( tcp , 0 , sizeof ( * tcp ) ) ;
1999-02-19 03:21:36 +03:00
}
2007-07-05 22:43:16 +04:00
/* detach traced process; continue with sig
2012-03-09 18:15:24 +04:00
* Never call DETACH twice on the same process as both unattached and
* attached - unstopped processes give the same ESRCH . For unattached process we
* would SIGSTOP it and wait for its SIGSTOP notification forever .
*/
1999-02-19 03:21:36 +03:00
static int
2012-01-04 18:11:09 +04:00
detach ( struct tcb * tcp )
1999-02-19 03:21:36 +03:00
{
2012-03-09 18:15:24 +04:00
int error ;
int status , sigstop_expected ;
1999-02-19 03:21:36 +03:00
if ( tcp - > flags & TCB_BPTSET )
2010-01-12 13:16:32 +03:00
clearbpt ( tcp ) ;
1999-02-19 03:21:36 +03:00
/*
* Linux wrongly insists the child be stopped
2002-12-16 23:42:50 +03:00
* before detaching . Arghh . We go through hoops
* to make a clean break of things .
1999-02-19 03:21:36 +03:00
*/
2002-12-16 23:42:50 +03:00
# if defined(SPARC)
# undef PTRACE_DETACH
# define PTRACE_DETACH PTRACE_SUNDETACH
# endif
2012-03-09 18:15:24 +04:00
sigstop_expected = 0 ;
if ( tcp - > flags & TCB_ATTACHED ) {
/*
* We attached but possibly didn ' t see the expected SIGSTOP .
* We must catch exactly one as otherwise the detached process
* would be left stopped ( process state T ) .
*/
sigstop_expected = ( tcp - > flags & TCB_IGNORE_ONE_SIGSTOP ) ;
error = ptrace ( PTRACE_DETACH , tcp - > pid , ( char * ) 1 , 0 ) ;
if ( error = = 0 ) {
/* On a clear day, you can see forever. */
}
else if ( errno ! = ESRCH ) {
/* Shouldn't happen. */
perror ( " detach: ptrace(PTRACE_DETACH, ...) " ) ;
}
else if ( my_tkill ( tcp - > pid , 0 ) < 0 ) {
if ( errno ! = ESRCH )
perror ( " detach: checking sanity " ) ;
}
else if ( ! sigstop_expected & & my_tkill ( tcp - > pid , SIGSTOP ) < 0 ) {
if ( errno ! = ESRCH )
perror ( " detach: stopping child " ) ;
}
else
sigstop_expected = 1 ;
2002-12-16 23:42:50 +03:00
}
2012-03-09 18:15:24 +04:00
if ( sigstop_expected ) {
1999-02-19 03:21:36 +03:00
for ( ; ; ) {
2002-12-17 13:48:05 +03:00
# ifdef __WALL
if ( wait4 ( tcp - > pid , & status , __WALL , NULL ) < 0 ) {
if ( errno = = ECHILD ) /* Already gone. */
break ;
if ( errno ! = EINVAL ) {
1999-02-19 03:21:36 +03:00
perror ( " detach: waiting " ) ;
2002-12-17 13:48:05 +03:00
break ;
}
# endif /* __WALL */
/* No __WALL here. */
if ( waitpid ( tcp - > pid , & status , 0 ) < 0 ) {
if ( errno ! = ECHILD ) {
perror ( " detach: waiting " ) ;
break ;
}
# ifdef __WCLONE
/* If no processes, try clones. */
if ( wait4 ( tcp - > pid , & status , __WCLONE ,
NULL ) < 0 ) {
if ( errno ! = ECHILD )
perror ( " detach: waiting " ) ;
break ;
}
# endif /* __WCLONE */
}
# ifdef __WALL
1999-02-19 03:21:36 +03:00
}
2002-12-17 13:48:05 +03:00
# endif
1999-02-19 03:21:36 +03:00
if ( ! WIFSTOPPED ( status ) ) {
/* Au revoir, mon ami. */
break ;
}
if ( WSTOPSIG ( status ) = = SIGSTOP ) {
2012-01-04 18:11:09 +04:00
ptrace_restart ( PTRACE_DETACH , tcp , 0 ) ;
1999-02-19 03:21:36 +03:00
break ;
}
2008-12-17 22:21:59 +03:00
error = ptrace_restart ( PTRACE_CONT , tcp ,
2011-05-27 16:36:01 +04:00
WSTOPSIG ( status ) = = syscall_trap_sig ? 0
2008-12-17 22:21:59 +03:00
: WSTOPSIG ( status ) ) ;
if ( error < 0 )
1999-02-19 03:21:36 +03:00
break ;
}
2009-01-07 00:45:06 +03:00
}
1999-02-19 03:21:36 +03:00
2012-03-09 18:15:24 +04:00
if ( ! qflag & & ( tcp - > flags & TCB_ATTACHED ) )
1999-02-19 03:21:36 +03:00
fprintf ( stderr , " Process %u detached \n " , tcp - > pid ) ;
droptcb ( tcp ) ;
2005-08-03 15:23:46 +04:00
1999-02-19 03:21:36 +03:00
return error ;
}
static void
2011-05-30 16:00:14 +04:00
cleanup ( void )
1999-02-19 03:21:36 +03:00
{
int i ;
struct tcb * tcp ;
2012-01-30 00:17:56 +04:00
int fatal_sig ;
/* 'interrupted' is a volatile object, fetch it only once */
fatal_sig = interrupted ;
if ( ! fatal_sig )
fatal_sig = SIGTERM ;
1999-02-19 03:21:36 +03:00
2002-12-18 07:16:10 +03:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
tcp = tcbtab [ i ] ;
1999-02-19 03:21:36 +03:00
if ( ! ( tcp - > flags & TCB_INUSE ) )
continue ;
if ( debug )
fprintf ( stderr ,
" cleanup: looking at pid %u \n " , tcp - > pid ) ;
2012-01-28 04:25:03 +04:00
if ( printing_tcp & &
( ! outfname | | followfork < 2 | | printing_tcp = = tcp ) ) {
tprints ( " <unfinished ...> \n " ) ;
printing_tcp = NULL ;
1999-02-19 03:21:36 +03:00
}
2012-03-09 18:15:24 +04:00
if ( ! ( tcp - > flags & TCB_STRACE_CHILD ) )
2012-01-04 18:11:09 +04:00
detach ( tcp ) ;
1999-02-19 03:21:36 +03:00
else {
kill ( tcp - > pid , SIGCONT ) ;
2012-01-29 19:43:51 +04:00
kill ( tcp - > pid , fatal_sig ) ;
1999-02-19 03:21:36 +03:00
}
}
if ( cflag )
call_summary ( outf ) ;
}
static void
2011-05-30 16:00:14 +04:00
interrupt ( int sig )
1999-02-19 03:21:36 +03:00
{
2012-01-29 19:43:51 +04:00
interrupted = sig ;
1999-02-19 03:21:36 +03:00
}
# ifndef HAVE_STRERROR
2002-12-30 03:51:30 +03:00
# if !HAVE_DECL_SYS_ERRLIST
1999-02-19 03:21:36 +03:00
extern int sys_nerr ;
extern char * sys_errlist [ ] ;
2002-12-30 03:51:30 +03:00
# endif /* HAVE_DECL_SYS_ERRLIST */
1999-02-19 03:21:36 +03:00
const char *
2011-05-30 16:00:14 +04:00
strerror ( int err_no )
1999-02-19 03:21:36 +03:00
{
2012-02-27 17:37:48 +04:00
static char buf [ sizeof ( " Unknown error %d " ) + sizeof ( int ) * 3 ] ;
1999-02-19 03:21:36 +03:00
2011-05-25 17:33:26 +04:00
if ( err_no < 1 | | err_no > = sys_nerr ) {
sprintf ( buf , " Unknown error %d " , err_no ) ;
1999-02-19 03:21:36 +03:00
return buf ;
}
2011-05-25 17:33:26 +04:00
return sys_errlist [ err_no ] ;
1999-02-19 03:21:36 +03:00
}
# endif /* HAVE_STERRROR */
# ifndef HAVE_STRSIGNAL
2003-01-14 10:53:33 +03:00
# if defined HAVE_SYS_SIGLIST && !defined HAVE_DECL_SYS_SIGLIST
2002-12-30 03:51:30 +03:00
extern char * sys_siglist [ ] ;
1999-02-19 03:21:36 +03:00
# endif
2003-01-14 10:53:33 +03:00
# if defined HAVE_SYS__SIGLIST && !defined HAVE_DECL__SYS_SIGLIST
extern char * _sys_siglist [ ] ;
# endif
1999-02-19 03:21:36 +03:00
const char *
2011-05-30 16:00:14 +04:00
strsignal ( int sig )
1999-02-19 03:21:36 +03:00
{
2012-02-27 17:37:48 +04:00
static char buf [ sizeof ( " Unknown signal %d " ) + sizeof ( int ) * 3 ] ;
1999-02-19 03:21:36 +03:00
if ( sig < 1 | | sig > = NSIG ) {
sprintf ( buf , " Unknown signal %d " , sig ) ;
return buf ;
}
# ifdef HAVE__SYS_SIGLIST
return _sys_siglist [ sig ] ;
# else
return sys_siglist [ sig ] ;
# endif
}
# endif /* HAVE_STRSIGNAL */
static int
2011-05-30 16:00:14 +04:00
trace ( void )
1999-02-19 03:21:36 +03:00
{
struct rusage ru ;
2011-08-15 14:24:14 +04:00
struct rusage * rup = cflag ? & ru : NULL ;
# ifdef __WALL
2009-06-03 03:49:22 +04:00
static int wait4_options = __WALL ;
2011-08-15 14:24:14 +04:00
# endif
1999-02-19 03:21:36 +03:00
2009-06-03 03:49:22 +04:00
while ( nprocs ! = 0 ) {
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
int pid ;
int wait_errno ;
int status , sig ;
2012-01-29 05:01:44 +04:00
int stopped ;
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
struct tcb * tcp ;
unsigned event ;
2009-03-17 17:29:59 +03:00
if ( interrupted )
2009-06-03 03:49:22 +04:00
return 0 ;
if ( interactive )
sigprocmask ( SIG_SETMASK , & empty_set , NULL ) ;
2011-08-15 14:24:14 +04:00
# ifdef __WALL
pid = wait4 ( - 1 , & status , wait4_options , rup ) ;
2002-12-17 07:50:47 +03:00
if ( pid < 0 & & ( wait4_options & __WALL ) & & errno = = EINVAL ) {
2001-03-28 18:40:14 +04:00
/* this kernel does not support __WALL */
wait4_options & = ~ __WALL ;
2011-08-15 14:24:14 +04:00
pid = wait4 ( - 1 , & status , wait4_options , rup ) ;
2001-03-28 18:40:14 +04:00
}
2002-12-17 07:50:47 +03:00
if ( pid < 0 & & ! ( wait4_options & __WALL ) & & errno = = ECHILD ) {
2001-03-28 18:40:14 +04:00
/* most likely a "cloned" process */
2011-08-15 14:24:14 +04:00
pid = wait4 ( - 1 , & status , __WCLONE , rup ) ;
if ( pid < 0 ) {
2011-06-23 15:05:29 +04:00
perror_msg ( " wait4(__WCLONE) failed " ) ;
2001-03-28 18:40:14 +04:00
}
}
2011-08-15 14:24:14 +04:00
# else
pid = wait4 ( - 1 , & status , 0 , rup ) ;
# endif /* __WALL */
1999-02-19 03:21:36 +03:00
wait_errno = errno ;
2009-06-03 03:49:22 +04:00
if ( interactive )
sigprocmask ( SIG_BLOCK , & blocked_set , NULL ) ;
1999-02-19 03:21:36 +03:00
2011-08-15 14:24:14 +04:00
if ( pid < 0 ) {
2009-06-03 03:49:22 +04:00
switch ( wait_errno ) {
case EINTR :
1999-02-19 03:21:36 +03:00
continue ;
2009-06-03 03:49:22 +04:00
case ECHILD :
1999-02-19 03:21:36 +03:00
/*
* We would like to verify this case
* but sometimes a race in Solbourne ' s
* version of SunOS sometimes reports
* ECHILD before sending us SIGCHILD .
*/
2009-06-03 03:49:22 +04:00
return 0 ;
default :
errno = wait_errno ;
2012-03-08 14:54:10 +04:00
perror_msg ( " wait " ) ;
2009-06-03 03:49:22 +04:00
return - 1 ;
1999-02-19 03:21:36 +03:00
}
}
2006-12-14 00:45:31 +03:00
if ( pid = = popen_pid ) {
if ( WIFEXITED ( status ) | | WIFSIGNALED ( status ) )
2011-06-22 15:03:56 +04:00
popen_pid = 0 ;
2006-12-14 00:45:31 +03:00
continue ;
}
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
event = ( ( unsigned ) status > > 16 ) ;
2011-06-24 18:41:35 +04:00
if ( debug ) {
char buf [ sizeof ( " WIFEXITED,exitcode=%u " ) + sizeof ( int ) * 3 /*paranoia:*/ + 16 ] ;
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
if ( event ! = 0 ) {
2011-06-24 18:41:35 +04:00
static const char * const event_names [ ] = {
[ PTRACE_EVENT_CLONE ] = " CLONE " ,
[ PTRACE_EVENT_FORK ] = " FORK " ,
[ PTRACE_EVENT_VFORK ] = " VFORK " ,
[ PTRACE_EVENT_VFORK_DONE ] = " VFORK_DONE " ,
[ PTRACE_EVENT_EXEC ] = " EXEC " ,
[ PTRACE_EVENT_EXIT ] = " EXIT " ,
} ;
const char * e ;
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
if ( event < ARRAY_SIZE ( event_names ) )
e = event_names [ event ] ;
2011-06-24 18:41:35 +04:00
else {
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
sprintf ( buf , " ?? (%u) " , event ) ;
2011-06-24 18:41:35 +04:00
e = buf ;
}
fprintf ( stderr , " PTRACE_EVENT_%s " , e ) ;
}
strcpy ( buf , " ??? " ) ;
if ( WIFSIGNALED ( status ) )
# ifdef WCOREDUMP
sprintf ( buf , " WIFSIGNALED,%ssig=%s " ,
WCOREDUMP ( status ) ? " core, " : " " ,
signame ( WTERMSIG ( status ) ) ) ;
# else
sprintf ( buf , " WIFSIGNALED,sig=%s " ,
signame ( WTERMSIG ( status ) ) ) ;
# endif
if ( WIFEXITED ( status ) )
sprintf ( buf , " WIFEXITED,exitcode=%u " , WEXITSTATUS ( status ) ) ;
if ( WIFSTOPPED ( status ) )
sprintf ( buf , " WIFSTOPPED,sig=%s " , signame ( WSTOPSIG ( status ) ) ) ;
2011-08-15 13:36:09 +04:00
# ifdef WIFCONTINUED
2011-06-24 18:41:35 +04:00
if ( WIFCONTINUED ( status ) )
strcpy ( buf , " WIFCONTINUED " ) ;
2011-08-15 13:36:09 +04:00
# endif
2011-06-24 18:41:35 +04:00
fprintf ( stderr , " [wait(0x%04x) = %u] %s \n " , status , pid , buf ) ;
}
1999-02-19 03:21:36 +03:00
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
/* Look up 'pid' in our table. */
2011-08-20 14:48:18 +04:00
tcp = pid2tcb ( pid ) ;
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
/* Under Linux, execve changes pid to thread leader's pid,
* and we see this changed pid on EVENT_EXEC and later ,
* execve sysexit . Leader " disappears " without exit
* notification . Let user know that , drop leader ' s tcb ,
* and fix up pid in execve thread ' s tcb .
* Effectively , execve thread ' s tcb replaces leader ' s tcb .
*
* BTW , leader is ' stuck undead ' ( doesn ' t report WIFEXITED
* on exit syscall ) in multithreaded programs exactly
* in order to handle this case .
*
* PTRACE_GETEVENTMSG returns old pid starting from Linux 3.0 .
* On 2.6 and earlier , it can return garbage .
*/
if ( event = = PTRACE_EVENT_EXEC & & os_release [ 0 ] > = ' 3 ' ) {
long old_pid = 0 ;
if ( ptrace ( PTRACE_GETEVENTMSG , pid , NULL , ( long ) & old_pid ) > = 0
& & old_pid > 0
& & old_pid ! = pid
) {
struct tcb * execve_thread = pid2tcb ( old_pid ) ;
if ( tcp ) {
outf = tcp - > outf ;
curcol = tcp - > curcol ;
if ( ! cflag ) {
2012-01-28 04:25:03 +04:00
if ( printing_tcp )
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
tprints ( " <unfinished ...> \n " ) ;
printleader ( tcp ) ;
2012-01-28 04:25:03 +04:00
tprintf ( " +++ superseded by execve in pid %lu +++ \n " , old_pid ) ;
printing_tcp = NULL ;
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
fflush ( outf ) ;
}
if ( execve_thread ) {
/* swap output FILEs (needed for -ff) */
tcp - > outf = execve_thread - > outf ;
execve_thread - > outf = outf ;
}
droptcb ( tcp ) ;
}
tcp = execve_thread ;
if ( tcp ) {
tcp - > pid = pid ;
tcp - > flags | = TCB_REPRINT ;
}
}
}
2011-08-20 14:48:18 +04:00
if ( tcp = = NULL ) {
2008-07-18 04:25:10 +04:00
if ( followfork ) {
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
/* This is needed to go with the CLONE_PTRACE
changes in process . c / util . c : we might see
the child ' s initial trap before we see the
parent return from the clone syscall .
Leave the child suspended until the parent
returns from its system call . Only then
will we have the association of parent and
child so that we know how to do clearbpt
in the child . */
Two cleanups: tcb table expansion failure is not really a survivable
event, we do not have any viable way to continue. No wonder most
places where that is detected have FIXMEs.
It's way simpler to treat as fatal failure, and handle it inside
tcb table expansion finctions.
Second cleanup: tidy up haphazard locations of a few externs.
* defs.h: Change return type of expand_tcbtab() to void.
Declare change_syscall().
* process.c: Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(fork_tcb): Change return type to void - it can't fail now.
* strace.c: Move extern declarations out of function bodies.
Change all callsites of alloctcb(), alloc_tcb() and
fork_tcb(), removing now-redundant error checks.
(expand_tcbtab): Change return type to void - it can't fail now.
On failure to expand, print a message, clean up, and exit.
(alloc_tcb): On failure to expand, print a message, clean up, and exit.
* util.c (setbpt): Remove extern declaration from function body.
2009-01-17 04:52:54 +03:00
tcp = alloctcb ( pid ) ;
2012-01-29 05:01:44 +04:00
tcp - > flags | = TCB_ATTACHED | TCB_STARTUP | post_attach_sigstop ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
if ( ! qflag )
Remove TCB_SUSPENDED constant and related code.
Since we no longer suspend waitpid'ing tracees, we have only one case when
we suspend tracee: when we pick up a new tracee created by clone/fork/vfork.
Background: on some other OSes, attach to child is done this way:
get fork's result (pid), loop ptrace(PTRACE_ATTACH) until you hook up
new process/thread. This is ugly and not safe, but what matters for us
is that it doesn't require suspending. Suspending is required
on Linux only, because on Linux attach to child is done differently.
On Linux, we use two methods of catching new tracee:
adding CLONE_THREAD bit to syscall (if needed, we change
[v]fork into clone before that), or using ptrace options.
In both cases, it may be so that new tracee appears before one which
created it returns from syscall. In this case, current code
suspends new tracee until its creator returns. Only then
strace can determine who is its parent (it needs child's pid for this,
which is visible in parent's [v]fork/clone result).
This is inherently racy. For example, what if SIGKILL kills
creator after it succeeded creating child, but before it returns?
Looks like we will have child suspended forever.
But after previous commit, we DO NOT NEED parent<->child link for anything.
Therefore we do not need suspending too. Bingo!
This patch removes suspending code. Now new tracees will be continued
right away. Next patch will remove tcp->parent member.
* defs.h: Remove TCB_SUSPENDED constant
* process.c (handle_new_child): Delete this function.
(internal_fork): Do not call handle_new_child on syscall exit.
* strace.c (handle_ptrace_event): Delete this function.
(trace): Do not suspend new child; remove all handling
of now impossible TCB_SUSPENDED condition.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 13:30:56 +04:00
fprintf ( stderr , " Process %d attached \n " ,
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
pid ) ;
2000-02-04 00:58:30 +03:00
}
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
else
/* This can happen if a clone call used
CLONE_PTRACE itself . */
{
if ( WIFSTOPPED ( status ) )
2012-03-09 18:11:21 +04:00
ptrace ( PTRACE_CONT , pid , ( char * ) 0 , 0 ) ;
2011-06-23 15:05:29 +04:00
error_msg_and_die ( " Unknown pid: %u " , pid ) ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
}
1999-02-19 03:21:36 +03:00
}
2009-06-03 03:49:22 +04:00
/* set current output file */
outf = tcp - > outf ;
2009-10-27 18:27:13 +03:00
curcol = tcp - > curcol ;
2011-06-25 01:01:57 +04:00
if ( cflag ) {
1999-02-19 03:21:36 +03:00
tv_sub ( & tcp - > dtime , & ru . ru_stime , & tcp - > stime ) ;
tcp - > stime = ru . ru_stime ;
}
2009-06-03 03:49:22 +04:00
1999-02-19 03:21:36 +03:00
if ( WIFSIGNALED ( status ) ) {
2008-11-10 20:14:58 +03:00
if ( pid = = strace_child )
exit_code = 0x100 | WTERMSIG ( status ) ;
2010-03-28 23:24:54 +04:00
if ( cflag ! = CFLAG_ONLY_STATS
1999-02-19 03:21:36 +03:00
& & ( qual_flags [ WTERMSIG ( status ) ] & QUAL_SIGNAL ) ) {
printleader ( tcp ) ;
2011-06-25 01:01:57 +04:00
# ifdef WCOREDUMP
2012-01-28 04:25:03 +04:00
tprintf ( " +++ killed by %s %s+++ \n " ,
2004-01-13 12:59:45 +03:00
signame ( WTERMSIG ( status ) ) ,
2011-06-25 01:01:57 +04:00
WCOREDUMP ( status ) ? " (core dumped) " : " " ) ;
# else
2012-01-28 04:25:03 +04:00
tprintf ( " +++ killed by %s +++ \n " ,
2011-06-25 01:01:57 +04:00
signame ( WTERMSIG ( status ) ) ) ;
2004-01-13 12:59:45 +03:00
# endif
2012-01-28 04:25:03 +04:00
printing_tcp = NULL ;
1999-02-19 03:21:36 +03:00
}
2012-01-18 19:20:56 +04:00
fflush ( tcp - > outf ) ;
1999-02-19 03:21:36 +03:00
droptcb ( tcp ) ;
continue ;
}
if ( WIFEXITED ( status ) ) {
2008-11-10 20:14:58 +03:00
if ( pid = = strace_child )
exit_code = WEXITSTATUS ( status ) ;
2012-01-28 04:25:03 +04:00
if ( tcp = = printing_tcp ) {
tprints ( " <unfinished ...> \n " ) ;
printing_tcp = NULL ;
2003-06-10 07:05:53 +04:00
}
Do not detach when we think tracee is going to die.
Current code plays some ungodly tricks, trying to not detach
thread group leader until all threads exit.
Also, it detaches from a tracee when signal delivery is detected
which will cause tracee to exit.
This operation is racy (not to mention the determination
whether signal is set to SIG_DFL is a horrible hack):
after we determined that this signal is indeed fatal
but before we detach and let process die,
*other thread* may set a handler to this signal, and
we will leak the process, falsely displaying it as killed!
I need to look in the past to figure out why we even do it.
First guess is that it's a workaround for old kernel bugs:
kernel used to deliver exit notifications to the tracer,
not to real parent. These workarounds are ancient
(internal_exit is from 1995).
The patch deletes the hacks. We no longer need tcp->nclone_threads,
TCB_EXITING and TCB_GROUP_EXITING. We also lose a few rather
ugly functions.
I also added a new message: "+++ exited with EXITCODE +++"
which shows exact moment strace got exit notification.
It is analogous to existing "+++ killed by SIG +++" message.
* defs.h: Delete struct tcb::nclone_threads field,
TCB_EXITING and TCB_GROUP_EXITING constants,
declarations of sigishandled() and internal_exit().
* process.c (internal_exit): Delete this function.
(handle_new_child): Don't ++tcp->nclone_threads.
* signal.c (parse_sigset_t): Delete this function.
(sigishandled): Delete this function.
* strace.c (startup_attach): Don't tcbtab[tcbi]->nclone_threads++.
(droptcb): Don't delay dropping if tcp->nclone_threads > 0,
don't drop parent if its nclone_threads reached 0:
just drop (only) this tcb unconditionally.
(detach): don't drop parent.
(handle_group_exit): Delete this function.
(handle_ptrace_event): Instead of handle_group_exit, just drop tcb;
do not panic if we see WIFEXITED from an attached pid;
print "+++ exited with EXITCODE +++" for every WIFEXITED pid.
* syscall.c (internal_syscall): Do not treat sys_exit specially -
don't call internal_exit on it.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 12:45:32 +04:00
if ( ! cflag /* && (qual_flags[WTERMSIG(status)] & QUAL_SIGNAL) */ ) {
printleader ( tcp ) ;
2012-01-28 04:25:03 +04:00
tprintf ( " +++ exited with %d +++ \n " , WEXITSTATUS ( status ) ) ;
printing_tcp = NULL ;
Do not detach when we think tracee is going to die.
Current code plays some ungodly tricks, trying to not detach
thread group leader until all threads exit.
Also, it detaches from a tracee when signal delivery is detected
which will cause tracee to exit.
This operation is racy (not to mention the determination
whether signal is set to SIG_DFL is a horrible hack):
after we determined that this signal is indeed fatal
but before we detach and let process die,
*other thread* may set a handler to this signal, and
we will leak the process, falsely displaying it as killed!
I need to look in the past to figure out why we even do it.
First guess is that it's a workaround for old kernel bugs:
kernel used to deliver exit notifications to the tracer,
not to real parent. These workarounds are ancient
(internal_exit is from 1995).
The patch deletes the hacks. We no longer need tcp->nclone_threads,
TCB_EXITING and TCB_GROUP_EXITING. We also lose a few rather
ugly functions.
I also added a new message: "+++ exited with EXITCODE +++"
which shows exact moment strace got exit notification.
It is analogous to existing "+++ killed by SIG +++" message.
* defs.h: Delete struct tcb::nclone_threads field,
TCB_EXITING and TCB_GROUP_EXITING constants,
declarations of sigishandled() and internal_exit().
* process.c (internal_exit): Delete this function.
(handle_new_child): Don't ++tcp->nclone_threads.
* signal.c (parse_sigset_t): Delete this function.
(sigishandled): Delete this function.
* strace.c (startup_attach): Don't tcbtab[tcbi]->nclone_threads++.
(droptcb): Don't delay dropping if tcp->nclone_threads > 0,
don't drop parent if its nclone_threads reached 0:
just drop (only) this tcb unconditionally.
(detach): don't drop parent.
(handle_group_exit): Delete this function.
(handle_ptrace_event): Instead of handle_group_exit, just drop tcb;
do not panic if we see WIFEXITED from an attached pid;
print "+++ exited with EXITCODE +++" for every WIFEXITED pid.
* syscall.c (internal_syscall): Do not treat sys_exit specially -
don't call internal_exit on it.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 12:45:32 +04:00
}
2012-01-18 19:20:56 +04:00
fflush ( tcp - > outf ) ;
1999-02-19 03:21:36 +03:00
droptcb ( tcp ) ;
continue ;
}
if ( ! WIFSTOPPED ( status ) ) {
fprintf ( stderr , " PANIC: pid %u not stopped \n " , pid ) ;
droptcb ( tcp ) ;
continue ;
}
2011-09-05 16:05:46 +04:00
/* Is this the very first time we see this tracee stopped? */
if ( tcp - > flags & TCB_STARTUP ) {
if ( debug )
fprintf ( stderr , " pid %d has TCB_STARTUP, initializing it \n " , tcp - > pid ) ;
1999-02-19 03:21:36 +03:00
tcp - > flags & = ~ TCB_STARTUP ;
2007-06-12 02:06:31 +04:00
if ( tcp - > flags & TCB_BPTSET ) {
1999-02-19 03:21:36 +03:00
/*
2007-06-12 02:06:31 +04:00
* One example is a breakpoint inherited from
2011-08-21 19:35:39 +04:00
* parent through fork ( ) .
1999-02-19 03:21:36 +03:00
*/
2011-09-02 18:23:53 +04:00
if ( clearbpt ( tcp ) < 0 ) {
/* Pretty fatal */
1999-02-19 03:21:36 +03:00
droptcb ( tcp ) ;
cleanup ( ) ;
return - 1 ;
}
}
Remove tcp->parent and TCB_CLONE_THREAD.
tcp->parent is used for only two things:
(1) to send signal on detach via tgkill (need to know tgid).
Solution: use tkill, it needs only tid.
(2) to optimize out ptrace options setting for new tracees.
Not a big deal if we drop this optimization: "set options" op is fast,
doing it just one extra time once per each tracee is hardly measurable.
TCB_CLONE_THREAD is a misnomer. It used only to flag sibling we attached to
in startup_attach. This is used to prevent infinite recursive rescanning
of /proc/PID/task.
Despite the name, there is no guarantee it is set only on non-leader:
if one would run "strace -f -p THREAD_ID" and THREAD_ID is *not*
a thread leader, strace will happily attach to it and all siblings
and will think that THREAD_ID is the leader! Which is a bug, but
since we no longer detach when we think tracee is going to die,
this bug no longer matters, because we do not use the knowledge
about thread group leaders for anything. (We used it to delay
leader's exit).
IOW: after this patch strace has no need to know about threads, parents
and children, and so on. Therefore it does not track that information.
It treats all tracees as independent entities. Overall,
this simplifies code a lot.
* defs.h: Add TCB_ATTACH_DONE flag, remove TCB_CLONE_THREAD flag
and struct tcb::parent field.
* process.c (internal_fork): Don't set tcpchild->parent.
* strace.c (startup_attach): Use TCB_ATTACH_DONE flag instead of
TCB_CLONE_THREAD to avoid attach attempts on already-attached threads.
Unlike TCB_CLONE_THREAD, TCB_ATTACH_DONE bit is used only temporarily,
and only in this function. We clear it on every tcb before we return.
(detach): Use tkill instead of tgkill.
(trace): Set ptrace options on new tracees unconditionally,
not only when tcp->parent == NULL.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-08-17 17:18:21 +04:00
if ( ptrace_setoptions ) {
if ( debug )
fprintf ( stderr , " setting opts %x on pid %d \n " , ptrace_setoptions , tcp - > pid ) ;
if ( ptrace ( PTRACE_SETOPTIONS , tcp - > pid , NULL , ptrace_setoptions ) < 0 ) {
if ( errno ! = ESRCH ) {
/* Should never happen, really */
perror_msg_and_die ( " PTRACE_SETOPTIONS " ) ;
2011-05-23 23:29:03 +04:00
}
}
}
2011-09-05 16:05:46 +04:00
}
2012-01-29 05:01:44 +04:00
sig = WSTOPSIG ( status ) ;
Fix handling of test/threaded_execve.c testcase
Since 3.0, Linux has a way to identify which thread execve'ed.
This patch makes use of it in order to properly dispose
of disappeared ("superseded") thread leader,
and replace it with execve'ed thread.
Before this patch, strace was "leaking" thread which exec'ed.
It was thinking that it still runs. It would look like this:
18460 pause( <unfinished ...> <=== thread leader
18466 execve("/proc/self/exe", ["exe", "exe"], [/* 47 vars */] <unfinished ...>
18465 +++ exited with 0 +++ <=== exits from other threads
18460 <... pause resumed> ) = 0
The last line is wrong: it's not pause resumed, it's execve resumed.
If thread leader would do exit instead of pause, it is much worse:
strace panics because it thinks it sees return from exit syscall!
And strace isn't aware 18466 (exec'ed thread) is gone.
It still thinks it's executes execve syscall.
* strace.c: New variable "static char *os_release".
(get_os_release): New static function.
(main): Call get_os_release to retrieve Linux version.
(trace): If we see PTRACE_EVENT_EXEC, retrieve old pid, and if it
differs from new one, free one of tcbs and print correct messages.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-01-28 04:16:02 +04:00
if ( event ! = 0 ) {
2012-01-29 05:01:44 +04:00
/* Ptrace event */
# ifdef USE_SEIZE
if ( event = = PTRACE_EVENT_STOP | | event = = PTRACE_EVENT_STOP1 ) {
2012-01-29 19:46:46 +04:00
/*
* PTRACE_INTERRUPT - stop or group - stop .
* PTRACE_INTERRUPT - stop has sig = = SIGTRAP here .
*/
2012-01-29 05:01:44 +04:00
if ( sig = = SIGSTOP
| | sig = = SIGTSTP
| | sig = = SIGTTIN
| | sig = = SIGTTOU
) {
stopped = 1 ;
goto show_stopsig ;
}
}
# endif
2011-09-05 16:05:46 +04:00
goto restart_tracee_with_sig_0 ;
}
/* Is this post-attach SIGSTOP?
* Interestingly , the process may stop
* with STOPSIG equal to some other signal
* than SIGSTOP if we happend to attach
* just before the process takes a signal .
*/
if ( sig = = SIGSTOP & & ( tcp - > flags & TCB_IGNORE_ONE_SIGSTOP ) ) {
if ( debug )
fprintf ( stderr , " ignored SIGSTOP on pid %d \n " , tcp - > pid ) ;
tcp - > flags & = ~ TCB_IGNORE_ONE_SIGSTOP ;
2011-09-02 18:23:53 +04:00
goto restart_tracee_with_sig_0 ;
1999-02-19 03:21:36 +03:00
}
2011-09-02 18:23:53 +04:00
if ( sig ! = syscall_trap_sig ) {
2012-01-29 05:01:44 +04:00
siginfo_t si ;
/* Nonzero (true) if tracee is stopped by signal
* ( as opposed to " tracee received signal " ) .
*/
stopped = ( ptrace ( PTRACE_GETSIGINFO , pid , 0 , ( long ) & si ) < 0 ) ;
2012-01-29 19:46:46 +04:00
# ifdef USE_SEIZE
2012-01-29 05:01:44 +04:00
show_stopsig :
2012-01-29 19:46:46 +04:00
# endif
2010-03-28 23:24:54 +04:00
if ( cflag ! = CFLAG_ONLY_STATS
2011-09-02 18:23:53 +04:00
& & ( qual_flags [ sig ] & QUAL_SIGNAL ) ) {
2011-03-10 17:44:45 +03:00
# if defined(PT_CR_IPSR) && defined(PT_CR_IIP)
2011-03-11 00:20:35 +03:00
long pc = 0 ;
long psr = 0 ;
2001-10-10 03:47:38 +04:00
2008-12-16 21:18:40 +03:00
upeek ( tcp , PT_CR_IPSR , & psr ) ;
upeek ( tcp , PT_CR_IIP , & pc ) ;
2001-10-10 03:47:38 +04:00
2011-03-11 00:20:35 +03:00
# define PSR_RI 41
2001-10-10 03:47:38 +04:00
pc + = ( psr > > PSR_RI ) & 0x3 ;
2011-03-11 00:20:35 +03:00
# define PC_FORMAT_STR " @ %lx"
2011-08-21 19:35:39 +04:00
# define PC_FORMAT_ARG , pc
2011-03-11 00:20:35 +03:00
# else
2011-08-21 19:35:39 +04:00
# define PC_FORMAT_STR ""
# define PC_FORMAT_ARG /* nothing */
2001-10-10 03:47:38 +04:00
# endif
1999-02-19 03:21:36 +03:00
printleader ( tcp ) ;
2012-01-29 05:01:44 +04:00
if ( ! stopped ) {
2011-09-01 12:00:28 +04:00
tprints ( " --- " ) ;
2011-03-11 00:20:35 +03:00
printsiginfo ( & si , verbose ( tcp ) ) ;
2012-01-28 04:25:03 +04:00
tprintf ( " (%s) " PC_FORMAT_STR " --- \n " ,
2011-09-02 18:23:53 +04:00
strsignal ( sig )
2011-03-11 00:20:35 +03:00
PC_FORMAT_ARG ) ;
} else
2012-01-28 04:25:03 +04:00
tprintf ( " --- %s by %s " PC_FORMAT_STR " --- \n " ,
2011-09-02 18:23:53 +04:00
strsignal ( sig ) ,
signame ( sig )
2011-03-11 00:20:35 +03:00
PC_FORMAT_ARG ) ;
2012-01-28 04:25:03 +04:00
printing_tcp = NULL ;
2012-01-18 19:20:56 +04:00
fflush ( tcp - > outf ) ;
1999-02-19 03:21:36 +03:00
}
2012-01-29 05:01:44 +04:00
if ( ! stopped )
/* It's signal-delivery-stop. Inject the signal */
goto restart_tracee ;
/* It's group-stop */
# ifdef USE_SEIZE
if ( use_seize ) {
/*
* This ends ptrace - stop , but does * not * end group - stop .
* This makes stopping signals work properly on straced process
* ( that is , process really stops . It used to continue to run ) .
*/
if ( ptrace_restart ( PTRACE_LISTEN , tcp , 0 ) < 0 ) {
cleanup ( ) ;
return - 1 ;
}
continue ;
}
/* We don't have PTRACE_LISTEN support... */
# endif
2011-09-02 18:23:53 +04:00
goto restart_tracee ;
1999-02-19 03:21:36 +03:00
}
2011-08-21 19:35:39 +04:00
/* We handled quick cases, we are permitted to interrupt now. */
2007-06-12 02:06:31 +04:00
if ( interrupted )
return 0 ;
2011-08-21 19:35:39 +04:00
/* This should be syscall entry or exit.
* ( Or it still can be that pesky post - execve SIGTRAP ! )
* Handle it .
*/
2009-06-03 03:49:22 +04:00
if ( trace_syscall ( tcp ) < 0 & & ! tcp - > ptrace_errno ) {
/* ptrace() failed in trace_syscall() with ESRCH.
* Likely a result of process disappearing mid - flight .
* Observed case : exit_group ( ) terminating
2012-01-04 18:15:26 +04:00
* all processes in thread group .
2009-01-07 00:45:06 +03:00
*/
2012-03-09 18:15:24 +04:00
if ( ! ( tcp - > flags & TCB_STRACE_CHILD ) ) {
//TODO: why do we handle our child differently??
2012-01-28 04:25:03 +04:00
if ( printing_tcp ) {
2009-06-03 03:49:22 +04:00
/* Do we have dangling line "syscall(param, param"?
2011-06-25 00:54:25 +04:00
* Finish the line then .
2009-06-03 03:49:22 +04:00
*/
2012-01-28 04:25:03 +04:00
printing_tcp - > flags | = TCB_REPRINT ;
tprints ( " <unfinished ...> \n " ) ;
printing_tcp = NULL ;
2012-01-18 19:20:56 +04:00
fflush ( tcp - > outf ) ;
2009-06-03 03:49:22 +04:00
}
2012-01-04 18:15:26 +04:00
/* We assume that ptrace error was caused by process death.
* We used to detach ( tcp ) here , but since we no longer
* implement " detach before death " policy / hack ,
* we can let this process to report its death to us
* normally , via WIFEXITED or WIFSIGNALED wait status .
*/
2009-01-07 00:45:06 +03:00
} else {
2012-01-04 18:15:26 +04:00
/* It's our real child (and we also trace it) */
/* my_tkill(pid, SIGKILL); - why? */
/* droptcb(tcp); - why? */
1999-02-19 03:21:36 +03:00
}
continue ;
}
2011-09-02 18:23:53 +04:00
restart_tracee_with_sig_0 :
sig = 0 ;
restart_tracee :
2009-10-27 18:27:13 +03:00
/* Remember current print column before continuing. */
tcp - > curcol = curcol ;
2011-09-02 18:23:53 +04:00
if ( ptrace_restart ( PTRACE_SYSCALL , tcp , sig ) < 0 ) {
1999-02-19 03:21:36 +03:00
cleanup ( ) ;
return - 1 ;
}
}
return 0 ;
}
void
tprintf ( const char * fmt , . . . )
{
va_list args ;
2009-10-27 18:56:43 +03:00
va_start ( args , fmt ) ;
2003-11-07 02:41:22 +03:00
if ( outf ) {
int n = vfprintf ( outf , fmt , args ) ;
2009-10-27 18:27:13 +03:00
if ( n < 0 ) {
if ( outf ! = stderr )
perror ( outfname = = NULL
? " <writing to pipe> " : outfname ) ;
} else
2003-11-07 02:41:22 +03:00
curcol + = n ;
}
1999-02-19 03:21:36 +03:00
va_end ( args ) ;
2011-09-01 11:55:05 +04:00
}
void
tprints ( const char * str )
{
if ( outf ) {
int n = fputs ( str , outf ) ;
if ( n > = 0 ) {
curcol + = strlen ( str ) ;
return ;
}
if ( outf ! = stderr )
perror ( outfname = = NULL
? " <writing to pipe> " : outfname ) ;
}
1999-02-19 03:21:36 +03:00
}
void
2011-05-30 16:00:14 +04:00
printleader ( struct tcb * tcp )
1999-02-19 03:21:36 +03:00
{
2012-01-28 04:25:03 +04:00
if ( printing_tcp ) {
if ( printing_tcp - > ptrace_errno ) {
if ( printing_tcp - > flags & TCB_INSYSCALL ) {
2011-09-01 12:00:28 +04:00
tprints ( " <unavailable>) " ) ;
2011-08-25 03:27:59 +04:00
tabto ( ) ;
2008-12-17 22:21:59 +03:00
}
2011-09-01 12:00:28 +04:00
tprints ( " = ? <unavailable> \n " ) ;
2012-01-28 04:25:03 +04:00
printing_tcp - > ptrace_errno = 0 ;
} else if ( ! outfname | | followfork < 2 | | printing_tcp = = tcp ) {
printing_tcp - > flags | = TCB_REPRINT ;
2011-09-01 12:00:28 +04:00
tprints ( " <unfinished ...> \n " ) ;
2008-12-17 22:21:59 +03:00
}
1999-02-19 03:21:36 +03:00
}
2012-01-28 04:25:03 +04:00
printing_tcp = tcp ;
1999-02-19 03:21:36 +03:00
curcol = 0 ;
2012-03-09 16:03:41 +04:00
if ( print_pid_pfx )
1999-02-19 03:21:36 +03:00
tprintf ( " %-5d " , tcp - > pid ) ;
else if ( nprocs > 1 & & ! outfname )
tprintf ( " [pid %5u] " , tcp - > pid ) ;
2012-03-09 16:03:41 +04:00
1999-02-19 03:21:36 +03:00
if ( tflag ) {
char str [ sizeof ( " HH:MM:SS " ) ] ;
struct timeval tv , dtv ;
static struct timeval otv ;
gettimeofday ( & tv , NULL ) ;
if ( rflag ) {
if ( otv . tv_sec = = 0 )
otv = tv ;
tv_sub ( & dtv , & tv , & otv ) ;
tprintf ( " %6ld.%06ld " ,
( long ) dtv . tv_sec , ( long ) dtv . tv_usec ) ;
otv = tv ;
}
else if ( tflag > 2 ) {
tprintf ( " %ld.%06ld " ,
( long ) tv . tv_sec , ( long ) tv . tv_usec ) ;
}
else {
time_t local = tv . tv_sec ;
strftime ( str , sizeof ( str ) , " %T " , localtime ( & local ) ) ;
if ( tflag > 1 )
tprintf ( " %s.%06ld " , str , ( long ) tv . tv_usec ) ;
else
tprintf ( " %s " , str ) ;
}
}
if ( iflag )
printcall ( tcp ) ;
}
void
2011-08-25 03:27:59 +04:00
tabto ( void )
1999-02-19 03:21:36 +03:00
{
2011-08-25 03:27:59 +04:00
if ( curcol < acolumn )
2011-09-01 11:55:05 +04:00
tprints ( acolumn_spaces + curcol ) ;
1999-02-19 03:21:36 +03:00
}