1999-02-19 00:21:36 +00: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 14:20:14 +00:00
* Copyright ( c ) 1996 - 1999 Wichert Akkerman < wichert @ cistron . nl >
2018-02-13 22:00:00 +00:00
* Copyright ( c ) 1999 - 2018 The strace developers .
1999-02-19 00:21:36 +00: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 .
*/
# include "defs.h"
2011-05-23 21:29:03 +02:00
# include <stdarg.h>
2018-02-11 00:26:09 +00:00
# include <limits.h>
1999-02-19 00:21:36 +00:00
# include <fcntl.h>
2018-02-11 00:26:09 +00:00
# include "ptrace.h"
2015-07-17 23:56:54 +00:00
# include <signal.h>
1999-02-19 00:21:36 +00:00
# include <sys/resource.h>
# include <sys/wait.h>
# include <sys/stat.h>
2017-12-15 12:38:59 +01:00
# ifdef HAVE_PATHS_H
# include <paths.h>
# endif
1999-02-19 00:21:36 +00:00
# include <pwd.h>
# include <grp.h>
2004-04-09 00:25:21 +00:00
# include <dirent.h>
2017-04-01 13:14:15 +00:00
# include <locale.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 01:16:02 +01:00
# include <sys/utsname.h>
2013-05-13 18:43:28 +00:00
# ifdef HAVE_PRCTL
# include <sys / prctl.h>
# endif
2016-12-31 22:12:08 +00:00
# include <asm/unistd.h>
2015-02-13 00:26:38 +00:00
2018-01-30 10:09:54 +01:00
# include "largefile_wrappers.h"
2018-02-26 23:22:24 +00:00
# include "mmap_cache.h"
Move number_set interface to separate files
* number_set.c: New file.
* number_set.h: Likewise.
* Makefile.am (strace_SOURCES): Add them.
* basic_filters.c: Include "number_set.h".
(number_slot_t, struct number_set): Move to number_set.h.
(BITS_PER_SLOT, number_setbit, number_isset, reallocate_number_set,
add_number_to_set, is_number_in_set): Move to number_set.c.
* defs.h (struct number_set): Remove forward declaration.
(read_set, write_set, signal_set, is_number_in_set): Move to number_set.h.
* filter.h (add_number_to_set): Move to number_set.h.
* filter_qualify.c: Include "number_set.h".
(number_slot_t, struct number_set): Remove.
* strace.c: Include "number_set.h".
* syscall.c: Likewise.
2017-08-22 21:23:49 +00:00
# include "number_set.h"
2017-01-02 22:37:57 +00:00
# include "scno.h"
2015-07-17 23:56:54 +00:00
# include "printsiginfo.h"
2017-12-25 23:39:40 +01:00
# include "trace_event.h"
2018-01-05 01:18:05 +00:00
# include "xstring.h"
2018-03-28 04:29:22 +00:00
# include "delay.h"
2015-02-13 00:26:38 +00:00
Tidy up order of includes; make bool variables explicit.
Bool variables are more compact in data and (on x86) on code too:
text data bss dec hex filename
237950 676 19044 257670 3ee86 strace.before
237838 676 19012 257526 3edf6 strace
* defs.h: Group library includes at the top of the file.
Rename dtime to Tflag, debug to debug_flag.
Change debug_flag,Tflag,qflag,not_failing_only,show_fd_path,tracing_paths
variable declarations from int to bool.
* strace.c: Change corresponding definitions. Do the same for static
variables iflag,rflag,print_pid_pfx.
Rename dtime to Tflag, debug to debug_flag.
* syscall.c: Rename dtime to Tflag, debug to debug_flag.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-15 12:49:52 +01:00
/* In some libc, these aren't declared. Do it ourself: */
2008-12-29 19:13:27 +00: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 01:52:54 +00:00
extern int optind ;
extern char * optarg ;
2008-12-29 19:13:27 +00:00
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
2013-07-23 00:11:35 -07:00
/* if this is true do the stack trace for every system call */
2017-06-17 19:20:11 +00:00
bool stack_trace_enabled ;
2013-07-23 00:11:35 -07:00
# endif
Tidy up order of includes; make bool variables explicit.
Bool variables are more compact in data and (on x86) on code too:
text data bss dec hex filename
237950 676 19044 257670 3ee86 strace.before
237838 676 19012 257526 3edf6 strace
* defs.h: Group library includes at the top of the file.
Rename dtime to Tflag, debug to debug_flag.
Change debug_flag,Tflag,qflag,not_failing_only,show_fd_path,tracing_paths
variable declarations from int to bool.
* strace.c: Change corresponding definitions. Do the same for static
variables iflag,rflag,print_pid_pfx.
Rename dtime to Tflag, debug to debug_flag.
* syscall.c: Rename dtime to Tflag, debug to debug_flag.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-15 12:49:52 +01:00
2017-01-03 13:46:26 +00:00
# define my_tkill(tid, sig) syscall(__NR_tkill, (tid), (sig))
Tidy up order of includes; make bool variables explicit.
Bool variables are more compact in data and (on x86) on code too:
text data bss dec hex filename
237950 676 19044 257670 3ee86 strace.before
237838 676 19012 257526 3edf6 strace
* defs.h: Group library includes at the top of the file.
Rename dtime to Tflag, debug to debug_flag.
Change debug_flag,Tflag,qflag,not_failing_only,show_fd_path,tracing_paths
variable declarations from int to bool.
* strace.c: Change corresponding definitions. Do the same for static
variables iflag,rflag,print_pid_pfx.
Rename dtime to Tflag, debug to debug_flag.
* syscall.c: Rename dtime to Tflag, debug to debug_flag.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-15 12:49:52 +01:00
2013-02-19 15:30:12 +01:00
/* Glue for systems without a MMU that cannot provide fork() */
# if !defined(HAVE_FORK)
# undef NOMMU_SYSTEM
# define NOMMU_SYSTEM 1
# endif
# if NOMMU_SYSTEM
# define fork() vfork()
# endif
Remove support for systems without PTRACE_SETOPTIONS
Assume that the kernel is v2.5.46 or newer, i.e. PTRACE_SETOPTIONS
and PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEEXEC|PTRACE_O_TRACECLONE
are universally available.
This change removes all code that implemented post-execve SIGTRAP
handling and fork/vfork/clone->CLONE_PTRACE substitution.
* defs.h (TCB_BPTSET, TCB_WAITEXECVE): Remove macros.
(need_fork_exec_workarounds, setbpt, clearbpt): Remove declarations.
* strace.c (need_fork_exec_workarounds,
test_ptrace_setoptions_followfork, test_ptrace_setoptions_for_all):
Remove.
(syscall_trap_sig): Set to (SIGTRAP | 0x80).
(ptrace_setoptions): Set to (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC).
(detach): Do not test for TCB_BPTSET.
(init): Do not call test_ptrace_setoptions_followfork and
test_ptrace_setoptions_for_all. Do not test for TCB_BPTSET.
* syscall.c (syscall_fixup_on_sysenter, internal_fork, internal_exec,
syscall_fixup_for_fork_exec, syscall_fixup_on_sysexit): Remove.
(trace_syscall_entering): Do not test for TCB_WAITEXECVE. Do not call
syscall_fixup_on_sysenter and syscall_fixup_for_fork_exec.
(trace_syscall_exiting): Do not call syscall_fixup_on_sysexit and
syscall_fixup_for_fork_exec.
[IA64] (ia64_ia32mode): Make static.
* linux/ia64/arch_regs.h (ia64_ia32mode): Remove declaration.
* util.c: Do not include "syscall.h".
(arg_setup, get_arg0, get_arg1, set_arg0, set_arg1, restore_arg0,
restore_arg1, arg_finish_change, change_syscall, setbpt, clearbpt):
Remove.
* tests/ptrace_setoptions.test: Remove.
* tests/Makefile.am (TESTS): Remove it.
2015-02-08 13:05:53 +00:00
const unsigned int syscall_trap_sig = SIGTRAP | 0x80 ;
Tidy up order of includes; make bool variables explicit.
Bool variables are more compact in data and (on x86) on code too:
text data bss dec hex filename
237950 676 19044 257670 3ee86 strace.before
237838 676 19012 257526 3edf6 strace
* defs.h: Group library includes at the top of the file.
Rename dtime to Tflag, debug to debug_flag.
Change debug_flag,Tflag,qflag,not_failing_only,show_fd_path,tracing_paths
variable declarations from int to bool.
* strace.c: Change corresponding definitions. Do the same for static
variables iflag,rflag,print_pid_pfx.
Rename dtime to Tflag, debug to debug_flag.
* syscall.c: Rename dtime to Tflag, debug to debug_flag.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-15 12:49:52 +01:00
cflag_t cflag = CFLAG_NONE ;
2017-06-17 19:20:11 +00:00
unsigned int followfork ;
2016-11-26 23:45:05 +00:00
unsigned int ptrace_setoptions = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC
| PTRACE_O_TRACEEXIT ;
2017-06-17 19:20:11 +00:00
unsigned int xflag ;
bool debug_flag ;
bool Tflag ;
bool iflag ;
bool count_wallclock ;
unsigned int qflag ;
static unsigned int tflag ;
static bool rflag ;
static bool print_pid_pfx ;
2012-01-29 16:53:03 +01:00
/* -I n */
enum {
2017-06-17 22:23:09 +00:00
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
2012-01-29 16:53:03 +01:00
} ;
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 20:51:30 +00: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 .
*/
2017-06-17 19:20:11 +00:00
static bool daemonized_tracer ;
1999-02-19 00:21:36 +00:00
2012-01-29 02:01:44 +01:00
static int post_attach_sigstop = TCB_IGNORE_ONE_SIGSTOP ;
2018-03-13 20:54:25 +00:00
# define use_seize (post_attach_sigstop == 0)
2012-01-29 02:01:44 +01:00
2002-11-06 13:17:21 +00:00
/* Sometimes we want to print only succeeding syscalls. */
2017-06-17 19:20:11 +00:00
bool not_failing_only ;
2002-11-06 13:17:21 +00:00
2011-04-07 20:25:40 +00:00
/* Show path associated with fd arguments */
2017-06-17 19:20:11 +00:00
unsigned int show_fd_path ;
2011-04-07 20:25:40 +00:00
2017-06-17 19:20:11 +00:00
static bool detach_on_execve ;
2012-03-15 13:44:17 +01:00
2016-07-29 17:51:54 +00:00
static int exit_code ;
2017-06-17 19:20:11 +00:00
static int strace_child ;
static int strace_tracer_pid ;
2009-06-02 16:49:22 -07:00
2017-07-20 22:55:57 +00:00
static const char * username ;
2011-06-24 22:49:58 +02:00
static uid_t run_uid ;
static gid_t run_gid ;
1999-02-19 00:21:36 +00:00
Tidy up order of includes; make bool variables explicit.
Bool variables are more compact in data and (on x86) on code too:
text data bss dec hex filename
237950 676 19044 257670 3ee86 strace.before
237838 676 19012 257526 3edf6 strace
* defs.h: Group library includes at the top of the file.
Rename dtime to Tflag, debug to debug_flag.
Change debug_flag,Tflag,qflag,not_failing_only,show_fd_path,tracing_paths
variable declarations from int to bool.
* strace.c: Change corresponding definitions. Do the same for static
variables iflag,rflag,print_pid_pfx.
Rename dtime to Tflag, debug to debug_flag.
* syscall.c: Rename dtime to Tflag, debug to debug_flag.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-15 12:49:52 +01:00
unsigned int max_strlen = DEFAULT_STRLEN ;
2012-03-25 21:49:48 +00:00
static int acolumn = DEFAULT_ACOLUMN ;
2011-08-25 01:27:59 +02:00
static char * acolumn_spaces ;
2012-03-22 09:56:20 +01:00
Introduce XLAT_STYLE_DEFAULT
This will be needed later, with the introduction of user-configurable
xlat style setting (stored in xlat_verbosity variable).
* defs.h (XLAT_STYLE_VERBOSITY_MASK): New macro constant.
(enum xlat_style) <XLAT_STYLE_DEFAULT>: New enumeration entity.
(xlat_verbosity): New external declaration.
(printxvals, printxval_searchn, printxval_search_ex, sprintxval,
sprintflags, printflags64): Use XLAT_STYLE_DEFAULT instead of
XLAT_STYLE_ABBREV.
* strace.c (xlat_verbosity): New variable.
* xlat.c (get_xlat_style): New function.
(printxvals_ex, sprintxval_ex, printxval_searchn_ex, sprintflags_ex,
printflags_ex): Use it.
2018-03-10 05:12:02 +01:00
/* Default output style for xlat entities */
enum xlat_style xlat_verbosity = XLAT_STYLE_ABBREV ;
2017-07-20 22:55:57 +00:00
static const char * outfname ;
2012-03-22 09:56:20 +01:00
/* If -ff, points to stderr. Else, it's our common output log */
static FILE * shared_log ;
2018-03-28 16:49:57 +02:00
static bool open_append ;
2012-03-22 09:56:20 +01:00
2017-06-17 19:20:11 +00:00
struct tcb * printing_tcp ;
2012-03-22 09:56:20 +01:00
static struct tcb * current_tcp ;
2018-08-14 13:43:34 +00:00
struct tcb_wait_data {
enum trace_event te ; /**< Event passed to dispatch_event() */
int status ; /**< status, returned by wait4() */
siginfo_t si ; /**< siginfo, returned by PTRACE_GETSIGINFO */
} ;
2011-06-24 22:49:58 +02:00
static struct tcb * * tcbtab ;
2017-08-08 16:37:39 +02:00
static unsigned int nprocs ;
static size_t tcbtabsize ;
2017-07-02 00:31:50 +00:00
# ifndef HAVE_PROGRAM_INVOCATION_NAME
char * program_invocation_name ;
# endif
1999-02-19 00:21:36 +00:00
2013-02-14 03:29:48 +01:00
unsigned os_release ; /* generated from uname()'s u.release */
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 01:16:02 +01:00
2013-06-20 11:20:23 +02:00
static void detach ( struct tcb * tcp ) ;
2009-10-27 16:56:43 +01:00
static void cleanup ( void ) ;
static void interrupt ( int sig ) ;
1999-02-19 00:21:36 +00:00
# ifdef HAVE_SIG_ATOMIC_T
2018-03-23 00:20:33 +00:00
static volatile sig_atomic_t interrupted , restart_failed ;
2012-01-29 16:43:51 +01:00
# else
2018-03-23 00:20:33 +00:00
static volatile int interrupted , restart_failed ;
2012-01-29 16:43:51 +01:00
# endif
1999-02-19 00:21:36 +00:00
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
static sigset_t timer_set ;
static void timer_sighandler ( int ) ;
2012-03-15 17:24:49 +01:00
# ifndef HAVE_STRERROR
# if !HAVE_DECL_SYS_ERRLIST
extern int sys_nerr ;
extern char * sys_errlist [ ] ;
2012-03-16 12:02:22 +01:00
# endif
2012-03-15 17:24:49 +01:00
const char *
strerror ( int err_no )
{
static char buf [ sizeof ( " Unknown error %d " ) + sizeof ( int ) * 3 ] ;
if ( err_no < 1 | | err_no > = sys_nerr ) {
2018-01-05 01:18:05 +00:00
xsprintf ( buf , " Unknown error %d " , err_no ) ;
2012-03-15 17:24:49 +01:00
return buf ;
}
return sys_errlist [ err_no ] ;
}
# endif /* HAVE_STERRROR */
2016-12-28 17:09:05 +00:00
static void
print_version ( void )
{
2017-07-31 18:52:01 +02:00
static const char features [ ] =
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
" stack-trace= " USE_UNWINDER
# endif
2018-01-09 20:00:27 +01:00
# ifdef USE_DEMANGLE
" stack-demangle "
2018-04-07 23:35:45 +00:00
# endif
2018-01-09 20:09:05 +01:00
# if SUPPORTED_PERSONALITIES > 1
# if defined HAVE_M32_MPERS
" m32-mpers "
# else
" no-m32-mpers "
# endif
# endif /* SUPPORTED_PERSONALITIES > 1 */
# if SUPPORTED_PERSONALITIES > 2
# if defined HAVE_MX32_MPERS
" mx32-mpers "
# else
" no-mx32-mpers "
# endif
# endif /* SUPPORTED_PERSONALITIES > 2 */
2017-07-31 18:52:01 +02:00
" " ;
2016-12-28 17:09:05 +00:00
printf ( " %s -- version %s \n "
2017-05-24 22:01:05 +02:00
" Copyright (c) 1991-%s The strace developers <%s>. \n "
2016-12-28 17:09:05 +00:00
" This is free software; see the source for copying conditions. There is NO \n "
" warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. \n " ,
2017-04-04 14:30:39 +02:00
PACKAGE_NAME , PACKAGE_VERSION , COPYRIGHT_YEAR , PACKAGE_URL ) ;
2017-07-31 18:52:01 +02:00
printf ( " \n Optional features enabled:%s \n " ,
features [ 0 ] ? features : " (none) " ) ;
2016-12-28 17:09:05 +00:00
}
1999-02-19 00:21:36 +00:00
static void
2016-06-13 01:41:33 +03:00
usage ( void )
1999-02-19 00:21:36 +00:00
{
2015-11-26 17:18:00 +03:00
printf ( " \
2015-11-26 06:25:12 +03:00
usage : strace [ - CdffhiqrtttTvVwxxy ] [ - I n ] [ - e expr ] . . . \ n \
2012-03-26 13:10:50 +02:00
[ - a column ] [ - o file ] [ - s strsize ] [ - P path ] . . . \ n \
- p pid . . . / [ - D ] [ - E var = val ] . . . [ - u username ] PROG [ ARGS ] \ n \
2015-11-26 06:25:12 +03:00
or : strace - c [ dfw ] [ - I n ] [ - e expr ] . . . [ - O overhead ] [ - S sortby ] \ n \
2012-03-26 13:10:50 +02:00
- p pid . . . / [ - D ] [ - E var = val ] . . . [ - u username ] PROG [ ARGS ] \ n \
2015-11-26 06:25:12 +03:00
\ n \
Output format : \ n \
- a column alignment COLUMN for printing syscall results ( default % d ) \ n \
- i print instruction pointer at time of syscall \ n \
2017-01-06 15:16:27 +08:00
"
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
2017-01-06 15:16:27 +08:00
" \
2018-04-11 15:41:40 +00:00
- k obtain stack trace between each syscall \ n \
2017-01-06 15:16:27 +08:00
"
# endif
" \
2015-11-26 06:25:12 +03:00
- o file send trace output to FILE instead of stderr \ n \
- q suppress messages about attaching , detaching , etc . \ n \
- r print relative timestamp \ n \
- s strsize limit length of print strings to STRSIZE chars ( default % d ) \ n \
- t print absolute timestamp \ n \
- tt print absolute timestamp with usecs \ n \
- T print time spent in each syscall \ n \
- x print non - ascii strings in hex \ n \
- xx print all strings in hex \ n \
- y print paths associated with file descriptor arguments \ n \
2016-05-26 15:29:41 +00:00
- yy print protocol specific information associated with socket file descriptors \ n \
2015-11-26 06:25:12 +03:00
\ n \
Statistics : \ n \
- c count time , calls , and errors for each syscall and report summary \ n \
- C like - c but also print regular output \ n \
- O overhead set overhead for tracing syscalls to OVERHEAD usecs \ n \
- S sortby sort syscall counts by : time , calls , name , nothing ( default % s ) \ n \
- w summarise syscall latency ( default is system time ) \ n \
\ n \
Filtering : \ n \
- e expr a qualifying expression : option = [ ! ] all or option = [ ! ] val1 [ , val2 ] . . . \ n \
2018-07-11 00:00:57 +00:00
options : trace , abbrev , verbose , raw , signal , read , write , fault , inject , kvm "
kvm: attach the exit reason of vcpu as auxstr to KVM_RUN output
In KVM, a virtual machine implementation like Qemu can access a vcpu
via ioctl. KVM_RUN is an ioctl command to enter vcpu. The command
returns control for various reasons: needs of device emulation or
consuming time slices are the typical ones. The vmi takes a different
action for the reason.
We, strace users, want to know the reason to understand kvm. This
change prints the reason as auxstr if "-e kvm=vcpu" option is given,
and if strace runs on Linux 4.16.0 or higher, which includes commit
e46b469278a59781f9b25ff608af84892963821b, "kvm: embed vcpu id to dentry
of vcpu anon inode."
The way to get the reason is a bit complicated because the ioctl does
not return it to the userspace directly. Instead, the vmi and kvm
communicate via an area of the process virtual memory where the fd of
vcpu is mmap'ed. strace must peek the area to know the reason.
The change does three things: (1) recording the area for the given vcpu
when the target calls VCPU_CREATE to vcpu_info_list per tcb data field,
(2) verifying the data recorded in vcpu_info_list before doing (3), and
(3) decoding the exit reason field of the area.
The change is complicated because there is a case that strace
does not have a chance to do (1) if -p option is used.
In this case, vcpu_info data created in the step (2).
The area has more fields than "exit reason",
dumping them may be implemented in the future.
* defs.h (struct tcb) [HAVE_LINUX_KVM_H]: Add vcpu_info_list field.
[HAVE_LINUX_KVM_H]: (kvm_run_structure_decoder_init,
kvm_vcpu_info_free): New declarations.
* strace.c (usage): Add "kvm" as a new expression for -e option.
(droptcb): Call kvm_vcpu_info_free.
* filter_qualify.c (qualify_kvm): New function calling
kvm_run_structure_decoder_init to enable for attaching the exit
reason to auxstr.
(qual_options): Add "kvm" as an entry.
* xlat/kvm_exit_reason.in: New file.
* kvm.c: Include xmalloc.h and mmap_cache.h.
(dump_kvm_run_structure): New static variable.
(kvm_run_structure_decoder_init): New function.
(vcpu_info): New struct definition representing the 3-tuple: vcpu file
descriptor, id of the vcpu, and mmap'ed entry.
(vcpu_find, vcpu_alloc, vcpu_register, vcpu_getinfo,
kvm_vcpu_info_free): New functions to access tcb's vcpu_info_list
field and vcpu_info data type.
(is_map_for_file, map_len): New helper functions.
(kvm_ioclt_run_attach_auxstr, kvm_ioctl_decode_run): New functions
decoding vcpu exit reason and attaching the decoded data to auxstr
field of tcb.
(kvm_ioctl_create_vcpu): Call vcpu_register to make an entry mapping
a file descriptor and the vcpu id associated with the fd.
(kvm_ioctl): Call kvm_ioctl_decode_run.
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-07-07 16:49:11 +09:00
" \n \
2015-11-26 06:25:12 +03:00
- P path trace accesses to path \ n \
\ n \
Tracing : \ n \
- b execve detach on execve syscall \ n \
- D run tracer process as a detached grandchild , not as parent \ n \
- f follow forks \ n \
- ff follow forks with output into separate files \ n \
- 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 \
\ n \
Startup : \ n \
- E var remove var from the environment for command \ n \
- E var = val put var = val in the environment for command \ n \
- p pid trace process with process id PID , may be repeated \ n \
- u username run command as username handling setuid and / or setgid \ n \
\ n \
Miscellaneous : \ n \
- d enable debug output to stderr \ n \
- v verbose mode : print unabbreviated argv , stat , termios , etc . args \ n \
- h print help message \ n \
- V print version \ n \
2012-03-15 13:44:17 +01:00
"
2013-02-26 11:33:54 +01:00
/* ancient, no one should use it
- F - - attempt to follow vforks ( deprecated , use - f ) \ n \
*/
2012-03-15 13:44:17 +01:00
/* this is broken, so don't document it
2002-11-06 13:17:21 +00:00
- z - - print only succeeding syscalls \ n \
2012-03-15 13:44:17 +01:00
*/
2003-01-24 04:31:23 +00:00
, DEFAULT_ACOLUMN , DEFAULT_STRLEN , DEFAULT_SORTBY ) ;
2015-11-26 17:18:00 +03:00
exit ( 0 ) ;
1999-02-19 00:21:36 +00:00
}
Move err/mem subroutines to separate files
In order to allow usage of utility functions by other binaries
included in the strace package (like the upcoming asinfo utility),
these functions should be moved to separate files.
* error_prints.h: New file.
* xmalloc.h: Likewise.
* defs.h: Include "xmalloc.h" and "error_prints.h".
(error_msg, error_msg_and_die, error_msg_and_help, perror_msg,
perror_msg_and_die): Move to error_prints.h.
(xcalloc, xmalloc, xreallocarray, xstrdup, xstrndup): Move to xmalloc.h.
* strace.c (die): Remove static quialifier to make visible
by error_prints.c.
(error_msg, error_msg_and_die, error_msg_and_help, perror_msg,
perror_msg_and_die, verror_msg): Move ...
* error_prints.c: ... to the new file.
* xmalloc.c: Include "config.h", <stdlib.h>, <string.h>,
"error_prints.h", and "xmalloc.h" instead of "defs.h".
Use int instead of bool. Fix codestyle.
* Makefile.am (strace_SOURCES): Add error_prints.c, error_prints.h,
and xmalloc.h.
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
Signed-off-by: Dmitry V. Levin <ldv@altlinux.org>
2017-08-05 04:57:34 +03:00
void ATTRIBUTE_NORETURN
Use macros for gcc attributes
* defs.h (error_msg, perror_msg, error_msg_and_die, perror_msg_and_die,
die_out_of_memory, printllval, printnum_int, printnum_long, tprintf):
Use ATTRIBUTE_* macros for gcc attributes.
* file.c (struct stat64): Likewise.
* statfs.c (struct compat_statfs64): Likewise.
* strace.c (die, exec_or_die, init): Likewise.
* linux/sparc/arch_sigreturn.c: Likewise.
* linux/ubi-user.h: Likewise.
2015-03-29 22:45:03 +00:00
die ( void )
2011-05-27 14:36:01 +02:00
{
if ( strace_tracer_pid = = getpid ( ) ) {
cflag = 0 ;
cleanup ( ) ;
2017-08-04 09:38:50 +02:00
exit ( 1 ) ;
2011-05-27 14:36:01 +02:00
}
2017-08-04 09:38:50 +02:00
_exit ( 1 ) ;
2011-05-27 14:36:01 +02:00
}
2012-03-25 21:49:48 +00:00
static void
error_opt_arg ( int opt , const char * arg )
{
2015-11-26 17:18:00 +03:00
error_msg_and_help ( " invalid -%c argument: '%s' " , opt , arg ) ;
2012-03-25 21:49:48 +00:00
}
2016-07-25 17:12:42 +00:00
static const char * ptrace_attach_cmd ;
2012-01-29 02:01:44 +01:00
static int
ptrace_attach_or_seize ( int pid )
{
int r ;
if ( ! use_seize )
2016-07-25 17:12:42 +00:00
return ptrace_attach_cmd = " PTRACE_ATTACH " ,
ptrace ( PTRACE_ATTACH , pid , 0L , 0L ) ;
Remove support for systems without PTRACE_SETOPTIONS
Assume that the kernel is v2.5.46 or newer, i.e. PTRACE_SETOPTIONS
and PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEEXEC|PTRACE_O_TRACECLONE
are universally available.
This change removes all code that implemented post-execve SIGTRAP
handling and fork/vfork/clone->CLONE_PTRACE substitution.
* defs.h (TCB_BPTSET, TCB_WAITEXECVE): Remove macros.
(need_fork_exec_workarounds, setbpt, clearbpt): Remove declarations.
* strace.c (need_fork_exec_workarounds,
test_ptrace_setoptions_followfork, test_ptrace_setoptions_for_all):
Remove.
(syscall_trap_sig): Set to (SIGTRAP | 0x80).
(ptrace_setoptions): Set to (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC).
(detach): Do not test for TCB_BPTSET.
(init): Do not call test_ptrace_setoptions_followfork and
test_ptrace_setoptions_for_all. Do not test for TCB_BPTSET.
* syscall.c (syscall_fixup_on_sysenter, internal_fork, internal_exec,
syscall_fixup_for_fork_exec, syscall_fixup_on_sysexit): Remove.
(trace_syscall_entering): Do not test for TCB_WAITEXECVE. Do not call
syscall_fixup_on_sysenter and syscall_fixup_for_fork_exec.
(trace_syscall_exiting): Do not call syscall_fixup_on_sysexit and
syscall_fixup_for_fork_exec.
[IA64] (ia64_ia32mode): Make static.
* linux/ia64/arch_regs.h (ia64_ia32mode): Remove declaration.
* util.c: Do not include "syscall.h".
(arg_setup, get_arg0, get_arg1, set_arg0, set_arg1, restore_arg0,
restore_arg1, arg_finish_change, change_syscall, setbpt, clearbpt):
Remove.
* tests/ptrace_setoptions.test: Remove.
* tests/Makefile.am (TESTS): Remove it.
2015-02-08 13:05:53 +00:00
r = ptrace ( PTRACE_SEIZE , pid , 0L , ( unsigned long ) ptrace_setoptions ) ;
2012-01-29 02:01:44 +01:00
if ( r )
2016-07-25 17:12:42 +00:00
return ptrace_attach_cmd = " PTRACE_SEIZE " , r ;
2013-07-10 14:33:05 +02:00
r = ptrace ( PTRACE_INTERRUPT , pid , 0L , 0L ) ;
2016-07-25 17:12:42 +00:00
return ptrace_attach_cmd = " PTRACE_INTERRUPT " , r ;
}
2012-01-29 02:01:44 +01:00
2012-03-20 16:26:25 +01:00
/*
* Used when we want to unblock stopped traced process .
* Should be only used with PTRACE_CONT , PTRACE_DETACH and PTRACE_SYSCALL .
* Returns 0 on success or if error was ESRCH
* ( presumably process was killed while we talk to it ) .
* Otherwise prints error message and returns - 1.
*/
static int
2016-12-23 23:30:33 +00:00
ptrace_restart ( const unsigned int op , struct tcb * const tcp , unsigned int sig )
2012-03-20 16:26:25 +01:00
{
int err ;
const char * msg ;
errno = 0 ;
2016-12-23 23:30:33 +00:00
ptrace ( op , tcp - > pid , 0L , ( unsigned long ) sig ) ;
2012-03-20 16:26:25 +01:00
err = errno ;
Report some ptrace failures; nuke tcp->ptrace_errno
Report some (not all) ptrace errors, namely,
errors on ptrace restart operations.
Before: 10533 sendto(-1, 0x804895e, 17, 0, NULL, 0 <unfinished ...>
After: 10533 sendto(-1, 0x804895e, 17, 0, NULL, 0 <ptrace(SYSCALL):No such process>
This tells user that strace failed to let sendto syscall
to be entered - process was dead at that point of time.
It is (marginally) better than to always say "<unfinished ...>"
While at it, patch removes tcp->ptrace_errno.
I added it many months ago, and it looks that after all
it is not needed for ptrace error detection: I failed to execute
a single existing code path which is accessible
through that variable only.
* defs.h: Remove struct tcp::ptrace_errno field.
* strace.c (ptrace_restart): Emit message to log on error.
(printleader): Remove "if (printing_tcp->ptrace_errno)..." code.
(trace): Remove !tcp->ptrace_errno check, it's always true.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-21 10:32:49 +01:00
if ( ! err )
2012-03-20 16:26:25 +01:00
return 0 ;
2016-12-23 23:39:35 +00:00
switch ( op ) {
case PTRACE_CONT :
msg = " CONT " ;
break ;
case PTRACE_DETACH :
msg = " DETACH " ;
break ;
case PTRACE_LISTEN :
msg = " LISTEN " ;
break ;
default :
msg = " SYSCALL " ;
}
Report some ptrace failures; nuke tcp->ptrace_errno
Report some (not all) ptrace errors, namely,
errors on ptrace restart operations.
Before: 10533 sendto(-1, 0x804895e, 17, 0, NULL, 0 <unfinished ...>
After: 10533 sendto(-1, 0x804895e, 17, 0, NULL, 0 <ptrace(SYSCALL):No such process>
This tells user that strace failed to let sendto syscall
to be entered - process was dead at that point of time.
It is (marginally) better than to always say "<unfinished ...>"
While at it, patch removes tcp->ptrace_errno.
I added it many months ago, and it looks that after all
it is not needed for ptrace error detection: I failed to execute
a single existing code path which is accessible
through that variable only.
* defs.h: Remove struct tcp::ptrace_errno field.
* strace.c (ptrace_restart): Emit message to log on error.
(printleader): Remove "if (printing_tcp->ptrace_errno)..." code.
(trace): Remove !tcp->ptrace_errno check, it's always true.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-21 10:32:49 +01:00
/*
* Why curcol ! = 0 ? Otherwise sometimes we get this :
*
* 10252 kill ( 10253 , SIGKILL ) = 0
* < ptrace ( SYSCALL , 10252 ) : No such process > 10253 . . . next decode . . .
*
* 10252 died after we retrieved syscall exit data ,
* but before we tried to restart it . Log looks ugly .
*/
2012-03-22 09:56:20 +01:00
if ( current_tcp & & current_tcp - > curcol ! = 0 ) {
Report some ptrace failures; nuke tcp->ptrace_errno
Report some (not all) ptrace errors, namely,
errors on ptrace restart operations.
Before: 10533 sendto(-1, 0x804895e, 17, 0, NULL, 0 <unfinished ...>
After: 10533 sendto(-1, 0x804895e, 17, 0, NULL, 0 <ptrace(SYSCALL):No such process>
This tells user that strace failed to let sendto syscall
to be entered - process was dead at that point of time.
It is (marginally) better than to always say "<unfinished ...>"
While at it, patch removes tcp->ptrace_errno.
I added it many months ago, and it looks that after all
it is not needed for ptrace error detection: I failed to execute
a single existing code path which is accessible
through that variable only.
* defs.h: Remove struct tcp::ptrace_errno field.
* strace.c (ptrace_restart): Emit message to log on error.
(printleader): Remove "if (printing_tcp->ptrace_errno)..." code.
(trace): Remove !tcp->ptrace_errno check, it's always true.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-21 10:32:49 +01:00
tprintf ( " <ptrace(%s):%s> \n " , msg , strerror ( err ) ) ;
line_ended ( ) ;
}
if ( err = = ESRCH )
return 0 ;
errno = err ;
2016-12-23 23:30:33 +00:00
perror_msg ( " ptrace(PTRACE_%s,pid:%d,sig:%u) " , msg , tcp - > pid , sig ) ;
2012-03-20 16:26:25 +01:00
return - 1 ;
}
2011-06-22 13:11:23 +02:00
static void
2006-12-13 21:45:31 +00:00
set_cloexec_flag ( int fd )
{
2011-06-22 13:11:23 +02: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-13 21:45:31 +00:00
}
newflags = flags | FD_CLOEXEC ;
if ( flags = = newflags )
2011-06-22 13:11:23 +02:00
return ;
2006-12-13 21:45:31 +00:00
2018-05-02 20:59:32 +02:00
if ( fcntl ( fd , F_SETFD , newflags ) ) /* never fails */
perror_msg_and_die ( " fcntl(%d, F_SETFD, %#x) " , fd , newflags ) ;
2006-12-13 21:45:31 +00:00
}
2016-06-13 01:43:15 +03:00
static void
kill_save_errno ( pid_t pid , int sig )
2012-03-09 15:15:24 +01:00
{
int saved_errno = errno ;
( void ) kill ( pid , sig ) ;
errno = saved_errno ;
}
2012-03-16 15:11:34 +01: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 ( ) ;
if ( euid ! = uid & & setreuid ( euid , uid ) < 0 ) {
perror_msg_and_die ( " setreuid " ) ;
}
}
static FILE *
strace_fopen ( const char * path )
{
FILE * fp ;
swap_uid ( ) ;
2018-03-28 16:49:57 +02:00
fp = fopen_stream ( path , open_append ? " a " : " w " ) ;
2012-03-16 15:11:34 +01:00
if ( ! fp )
perror_msg_and_die ( " Can't fopen '%s' " , path ) ;
swap_uid ( ) ;
set_cloexec_flag ( fileno ( fp ) ) ;
return fp ;
}
2017-06-17 19:20:11 +00:00
static int popen_pid ;
2012-03-16 15:11:34 +01: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 )
{
FILE * fp ;
2013-07-02 11:31:24 +02:00
int pid ;
2012-03-16 15:11:34 +01:00
int fds [ 2 ] ;
swap_uid ( ) ;
if ( pipe ( fds ) < 0 )
perror_msg_and_die ( " pipe " ) ;
set_cloexec_flag ( fds [ 1 ] ) ; /* never fails */
2013-07-02 11:31:24 +02:00
pid = vfork ( ) ;
if ( pid < 0 )
2012-03-16 15:11:34 +01:00
perror_msg_and_die ( " vfork " ) ;
2013-07-02 11:31:24 +02:00
if ( pid = = 0 ) {
2012-03-16 15:11:34 +01:00
/* child */
close ( fds [ 1 ] ) ;
if ( fds [ 0 ] ! = 0 ) {
if ( dup2 ( fds [ 0 ] , 0 ) )
perror_msg_and_die ( " dup2 " ) ;
close ( fds [ 0 ] ) ;
}
execl ( _PATH_BSHELL , " sh " , " -c " , command , NULL ) ;
perror_msg_and_die ( " Can't execute '%s' " , _PATH_BSHELL ) ;
}
/* parent */
2013-07-02 11:31:24 +02:00
popen_pid = pid ;
2012-03-16 15:11:34 +01:00
close ( fds [ 0 ] ) ;
swap_uid ( ) ;
fp = fdopen ( fds [ 1 ] , " w " ) ;
if ( ! fp )
2017-06-26 22:41:28 +00:00
perror_msg_and_die ( " fdopen " ) ;
2012-03-16 15:11:34 +01:00
return fp ;
}
2018-01-16 14:36:06 +01:00
static void
outf_perror ( const struct tcb * const tcp )
{
if ( tcp - > outf = = stderr )
return ;
/* This is ugly, but we don't store separate file names */
if ( followfork > = 2 )
perror_msg ( " %s.%u " , outfname , tcp - > pid ) ;
else
perror_msg ( " %s " , outfname ) ;
}
Introduce tprintf_comment and tprints_comment functions
* defs.h (tprintf_comment, tprints_comment): New prototypes.
* strace.c (tvprintf): New function.
(tprintf): Use it.
(tprintf_comment, tprints_comment): New functions.
* aio.c (tprint_lio_opcode): Use tprints_comment.
* dm.c (dm_decode_dm_target_spec, dm_decode_dm_target_deps,
dm_decode_dm_name_list, dm_decode_dm_target_versions,
dm_decode_dm_target_msg, dm_decode_string, dm_known_ioctl): Likewise.
* futex.c (SYS_FUNC(futex)): Likewise.
* perf.c (print_perf_event_attr): Likewise.
* seccomp.c (decode_bpf_code): Likewise.
* util.c (printxvals, printxval_searchn, printflags64): Likewise.
* btrfs.c (print_u64, btrfs_print_key_type, btrfs_print_objectid,
print_key_value_internal): Likewise.
(btrfs_ioctl): Use tprints_comment and tprintf_comment.
* dirent.c (SYS_FUNC(getdents)): Likewise.
* dirent64.c (SYS_FUNC(getdents64)): Likewise.
* execve.c (printargc): Use tprintf_comment.
* tests/btrfs.c (btrfs_test_get_dev_stats_ioctl,
btrfs_test_features_ioctls): Update expected output.
2017-04-24 19:31:54 +00:00
ATTRIBUTE_FORMAT ( ( printf , 1 , 0 ) )
static void
tvprintf ( const char * const fmt , va_list args )
2012-03-12 23:02:26 +01:00
{
2012-03-22 09:56:20 +01:00
if ( current_tcp ) {
2017-01-04 13:22:19 +00:00
int n = vfprintf ( current_tcp - > outf , fmt , args ) ;
2012-03-12 23:02:26 +01:00
if ( n < 0 ) {
2017-08-06 11:26:52 +00:00
/* very unlikely due to vfprintf buffering */
2018-01-16 14:36:06 +01:00
outf_perror ( current_tcp ) ;
2012-03-12 23:02:26 +01:00
} else
2012-03-22 09:56:20 +01:00
current_tcp - > curcol + = n ;
2012-03-12 23:02:26 +01:00
}
Introduce tprintf_comment and tprints_comment functions
* defs.h (tprintf_comment, tprints_comment): New prototypes.
* strace.c (tvprintf): New function.
(tprintf): Use it.
(tprintf_comment, tprints_comment): New functions.
* aio.c (tprint_lio_opcode): Use tprints_comment.
* dm.c (dm_decode_dm_target_spec, dm_decode_dm_target_deps,
dm_decode_dm_name_list, dm_decode_dm_target_versions,
dm_decode_dm_target_msg, dm_decode_string, dm_known_ioctl): Likewise.
* futex.c (SYS_FUNC(futex)): Likewise.
* perf.c (print_perf_event_attr): Likewise.
* seccomp.c (decode_bpf_code): Likewise.
* util.c (printxvals, printxval_searchn, printflags64): Likewise.
* btrfs.c (print_u64, btrfs_print_key_type, btrfs_print_objectid,
print_key_value_internal): Likewise.
(btrfs_ioctl): Use tprints_comment and tprintf_comment.
* dirent.c (SYS_FUNC(getdents)): Likewise.
* dirent64.c (SYS_FUNC(getdents64)): Likewise.
* execve.c (printargc): Use tprintf_comment.
* tests/btrfs.c (btrfs_test_get_dev_stats_ioctl,
btrfs_test_features_ioctls): Update expected output.
2017-04-24 19:31:54 +00:00
}
void
tprintf ( const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
tvprintf ( fmt , args ) ;
2012-03-12 23:02:26 +01:00
va_end ( args ) ;
}
2014-02-26 00:01:00 +00:00
# ifndef HAVE_FPUTS_UNLOCKED
# define fputs_unlocked fputs
# endif
2012-03-12 23:02:26 +01:00
void
tprints ( const char * str )
{
2012-03-22 09:56:20 +01:00
if ( current_tcp ) {
2012-04-16 18:10:15 +02:00
int n = fputs_unlocked ( str , current_tcp - > outf ) ;
2012-03-12 23:02:26 +01:00
if ( n > = 0 ) {
2012-03-22 09:56:20 +01:00
current_tcp - > curcol + = strlen ( str ) ;
2012-03-12 23:02:26 +01:00
return ;
}
2017-08-06 11:26:52 +00:00
/* very unlikely due to fputs_unlocked buffering */
2018-01-16 14:36:06 +01:00
outf_perror ( current_tcp ) ;
2012-03-12 23:02:26 +01:00
}
}
Introduce tprintf_comment and tprints_comment functions
* defs.h (tprintf_comment, tprints_comment): New prototypes.
* strace.c (tvprintf): New function.
(tprintf): Use it.
(tprintf_comment, tprints_comment): New functions.
* aio.c (tprint_lio_opcode): Use tprints_comment.
* dm.c (dm_decode_dm_target_spec, dm_decode_dm_target_deps,
dm_decode_dm_name_list, dm_decode_dm_target_versions,
dm_decode_dm_target_msg, dm_decode_string, dm_known_ioctl): Likewise.
* futex.c (SYS_FUNC(futex)): Likewise.
* perf.c (print_perf_event_attr): Likewise.
* seccomp.c (decode_bpf_code): Likewise.
* util.c (printxvals, printxval_searchn, printflags64): Likewise.
* btrfs.c (print_u64, btrfs_print_key_type, btrfs_print_objectid,
print_key_value_internal): Likewise.
(btrfs_ioctl): Use tprints_comment and tprintf_comment.
* dirent.c (SYS_FUNC(getdents)): Likewise.
* dirent64.c (SYS_FUNC(getdents64)): Likewise.
* execve.c (printargc): Use tprintf_comment.
* tests/btrfs.c (btrfs_test_get_dev_stats_ioctl,
btrfs_test_features_ioctls): Update expected output.
2017-04-24 19:31:54 +00:00
void
tprints_comment ( const char * const str )
{
if ( str & & * str )
tprintf ( " /* %s */ " , str ) ;
}
void
tprintf_comment ( const char * fmt , . . . )
{
if ( ! fmt | | ! * fmt )
return ;
va_list args ;
va_start ( args , fmt ) ;
tprints ( " /* " ) ;
tvprintf ( fmt , args ) ;
tprints ( " */ " ) ;
va_end ( args ) ;
}
2017-08-06 11:26:52 +00:00
static void
flush_tcp_output ( const struct tcb * const tcp )
{
2018-01-16 14:36:06 +01:00
if ( fflush ( tcp - > outf ) )
outf_perror ( tcp ) ;
2017-08-06 11:26:52 +00:00
}
2012-03-13 11:44:31 +01:00
void
line_ended ( void )
{
2012-03-22 09:56:20 +01:00
if ( current_tcp ) {
current_tcp - > curcol = 0 ;
2017-08-06 11:26:52 +00:00
flush_tcp_output ( current_tcp ) ;
2012-03-22 09:56:20 +01:00
}
if ( printing_tcp ) {
printing_tcp - > curcol = 0 ;
printing_tcp = NULL ;
}
2012-03-13 11:44:31 +01:00
}
2018-01-16 17:26:22 +01:00
void
set_current_tcp ( const struct tcb * tcp )
{
current_tcp = ( struct tcb * ) tcp ;
/* Sync current_personality and stuff */
if ( current_tcp )
set_personality ( current_tcp - > currpers ) ;
}
2012-03-12 23:02:26 +01:00
void
printleader ( struct tcb * tcp )
{
2012-03-13 11:44:31 +01:00
/* If -ff, "previous tcb we printed" is always the same as current,
* because we have per - tcb output files .
*/
if ( followfork > = 2 )
printing_tcp = tcp ;
2012-03-12 23:02:26 +01:00
if ( printing_tcp ) {
2018-01-16 17:26:22 +01:00
set_current_tcp ( printing_tcp ) ;
2012-03-13 11:44:31 +01:00
if ( printing_tcp - > curcol ! = 0 & & ( followfork < 2 | | printing_tcp = = tcp ) ) {
/*
* case 1 : we have a shared log ( i . e . not - ff ) , and last line
* wasn ' t finished ( same or different tcb , doesn ' t matter ) .
* case 2 : split log , we are the same tcb , but our last line
* didn ' t finish ( " SIGKILL nuked us after syscall entry " etc ) .
*/
2012-03-12 23:02:26 +01:00
tprints ( " <unfinished ...> \n " ) ;
2012-03-13 11:44:31 +01:00
printing_tcp - > curcol = 0 ;
2012-03-12 23:02:26 +01:00
}
}
printing_tcp = tcp ;
2018-01-16 17:26:22 +01:00
set_current_tcp ( tcp ) ;
2012-03-22 09:56:20 +01:00
current_tcp - > curcol = 0 ;
2012-03-12 23:02:26 +01:00
if ( print_pid_pfx )
tprintf ( " %-5d " , tcp - > pid ) ;
else if ( nprocs > 1 & & ! outfname )
tprintf ( " [pid %5u] " , tcp - > pid ) ;
if ( tflag ) {
Replace struct timeval with struct timespec in time measurements
This is required to implement more precise time measurements.
* Makefile.am (strace_LDADD): Add $(clock_LIBS).
* defs.h (struct tcb): Change the type of stime, dtime, and etime fields
from struct timeval to struct timespec, all users updated.
(syscall_exiting_decode, syscall_exiting_trace, count_syscall): Change
the type of "struct timeval *" argument to "struct timespec *", all
users updated.
(tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul): Rename to
ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul. Change
the type of all "struct timeval *" arguments to "struct timespec *",
all users updated.
* util.c (tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul):
Rename to ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul.
Change the type of all "struct timeval *" arguments to "struct timespec *".
* count.c (struct call_counts): Change the type of "time" field
from struct timeval to struct timespec, all users updated.
(overhead): Change type from struct timeval to struct timespec, all
users updated.
(count_syscall): Change the type of "struct timeval *" argument to
"struct timespec *".
* strace.c (printleader): Change the type of struct timeval variables
to struct timespec, call clock_gettime instead of gettimeofday.
(next_event, trace_syscall): Change the type of struct timeval variables
to struct timespec.
* syscall.c (syscall_entering_finish, syscall_exiting_decode): Call
clock_gettime instead of gettimeofday.
2018-03-16 00:55:58 +00:00
struct timespec ts ;
2018-03-28 16:22:04 +02:00
clock_gettime ( CLOCK_REALTIME , & ts ) ;
2018-03-16 00:55:58 +00:00
2018-03-28 16:22:04 +02:00
if ( tflag > 2 ) {
Replace struct timeval with struct timespec in time measurements
This is required to implement more precise time measurements.
* Makefile.am (strace_LDADD): Add $(clock_LIBS).
* defs.h (struct tcb): Change the type of stime, dtime, and etime fields
from struct timeval to struct timespec, all users updated.
(syscall_exiting_decode, syscall_exiting_trace, count_syscall): Change
the type of "struct timeval *" argument to "struct timespec *", all
users updated.
(tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul): Rename to
ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul. Change
the type of all "struct timeval *" arguments to "struct timespec *",
all users updated.
* util.c (tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul):
Rename to ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul.
Change the type of all "struct timeval *" arguments to "struct timespec *".
* count.c (struct call_counts): Change the type of "time" field
from struct timeval to struct timespec, all users updated.
(overhead): Change type from struct timeval to struct timespec, all
users updated.
(count_syscall): Change the type of "struct timeval *" argument to
"struct timespec *".
* strace.c (printleader): Change the type of struct timeval variables
to struct timespec, call clock_gettime instead of gettimeofday.
(next_event, trace_syscall): Change the type of struct timeval variables
to struct timespec.
* syscall.c (syscall_entering_finish, syscall_exiting_decode): Call
clock_gettime instead of gettimeofday.
2018-03-16 00:55:58 +00:00
tprintf ( " %lld.%06ld " ,
( long long ) ts . tv_sec , ( long ) ts . tv_nsec / 1000 ) ;
2017-06-17 22:23:09 +00:00
} else {
Replace struct timeval with struct timespec in time measurements
This is required to implement more precise time measurements.
* Makefile.am (strace_LDADD): Add $(clock_LIBS).
* defs.h (struct tcb): Change the type of stime, dtime, and etime fields
from struct timeval to struct timespec, all users updated.
(syscall_exiting_decode, syscall_exiting_trace, count_syscall): Change
the type of "struct timeval *" argument to "struct timespec *", all
users updated.
(tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul): Rename to
ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul. Change
the type of all "struct timeval *" arguments to "struct timespec *",
all users updated.
* util.c (tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul):
Rename to ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul.
Change the type of all "struct timeval *" arguments to "struct timespec *".
* count.c (struct call_counts): Change the type of "time" field
from struct timeval to struct timespec, all users updated.
(overhead): Change type from struct timeval to struct timespec, all
users updated.
(count_syscall): Change the type of "struct timeval *" argument to
"struct timespec *".
* strace.c (printleader): Change the type of struct timeval variables
to struct timespec, call clock_gettime instead of gettimeofday.
(next_event, trace_syscall): Change the type of struct timeval variables
to struct timespec.
* syscall.c (syscall_entering_finish, syscall_exiting_decode): Call
clock_gettime instead of gettimeofday.
2018-03-16 00:55:58 +00:00
time_t local = ts . tv_sec ;
2018-03-26 08:15:26 +02:00
char str [ MAX ( sizeof ( " HH:MM:SS " ) , sizeof ( ts . tv_sec ) * 3 ) ] ;
struct tm * tm = localtime ( & local ) ;
2018-03-16 00:55:58 +00:00
2018-03-26 08:15:26 +02:00
if ( tm )
strftime ( str , sizeof ( str ) , " %T " , tm ) ;
else
xsprintf ( str , " %lld " , ( long long ) local ) ;
2012-03-12 23:02:26 +01:00
if ( tflag > 1 )
Replace struct timeval with struct timespec in time measurements
This is required to implement more precise time measurements.
* Makefile.am (strace_LDADD): Add $(clock_LIBS).
* defs.h (struct tcb): Change the type of stime, dtime, and etime fields
from struct timeval to struct timespec, all users updated.
(syscall_exiting_decode, syscall_exiting_trace, count_syscall): Change
the type of "struct timeval *" argument to "struct timespec *", all
users updated.
(tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul): Rename to
ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul. Change
the type of all "struct timeval *" arguments to "struct timespec *",
all users updated.
* util.c (tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul):
Rename to ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul.
Change the type of all "struct timeval *" arguments to "struct timespec *".
* count.c (struct call_counts): Change the type of "time" field
from struct timeval to struct timespec, all users updated.
(overhead): Change type from struct timeval to struct timespec, all
users updated.
(count_syscall): Change the type of "struct timeval *" argument to
"struct timespec *".
* strace.c (printleader): Change the type of struct timeval variables
to struct timespec, call clock_gettime instead of gettimeofday.
(next_event, trace_syscall): Change the type of struct timeval variables
to struct timespec.
* syscall.c (syscall_entering_finish, syscall_exiting_decode): Call
clock_gettime instead of gettimeofday.
2018-03-16 00:55:58 +00:00
tprintf ( " %s.%06ld " ,
str , ( long ) ts . tv_nsec / 1000 ) ;
2012-03-12 23:02:26 +01:00
else
tprintf ( " %s " , str ) ;
}
}
2018-03-28 16:22:04 +02:00
if ( rflag ) {
struct timespec ts ;
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
static struct timespec ots ;
if ( ots . tv_sec = = 0 )
ots = ts ;
struct timespec dts ;
ts_sub ( & dts , & ts , & ots ) ;
ots = ts ;
tprintf ( " %s%6ld.%06ld%s " ,
tflag ? " (+ " : " " ,
( long ) dts . tv_sec , ( long ) dts . tv_nsec / 1000 ,
tflag ? " ) " : " " ) ;
}
2012-03-12 23:02:26 +01:00
if ( iflag )
2013-07-01 12:49:14 +02:00
print_pc ( tcp ) ;
2012-03-12 23:02:26 +01:00
}
void
tabto ( void )
{
2012-03-22 09:56:20 +01:00
if ( current_tcp - > curcol < acolumn )
tprints ( acolumn_spaces + current_tcp - > curcol ) ;
2012-03-12 23:02:26 +01:00
}
2012-03-16 15:15:14 +01:00
/* Should be only called directly *after successful attach* to a tracee.
* Otherwise , " strace -oFILE -ff -p<nonexistant_pid> "
* may create bogus empty FILE . < nonexistant_pid > , and then die .
*/
2006-12-13 21:45:31 +00:00
static void
2018-04-19 18:03:58 +00:00
after_successful_attach ( struct tcb * tcp , const unsigned int flags )
2006-12-13 21:45:31 +00:00
{
2018-04-19 18:03:58 +00:00
tcp - > flags | = TCB_ATTACHED | TCB_STARTUP | flags ;
2012-03-22 09:56:20 +01:00
tcp - > outf = shared_log ; /* if not -ff mode, the same file is for all */
2012-03-16 15:15:14 +01:00
if ( followfork > = 2 ) {
2018-01-04 10:52:07 +00:00
char name [ PATH_MAX ] ;
2018-01-05 01:18:05 +00:00
xsprintf ( name , " %s.%u " , outfname , tcp - > pid ) ;
2012-03-16 15:11:34 +01:00
tcp - > outf = strace_fopen ( name ) ;
2006-12-13 21:45:31 +00:00
}
2018-04-19 18:03:58 +00:00
# ifdef ENABLE_STACKTRACE
if ( stack_trace_enabled )
unwind_tcb_init ( tcp ) ;
# endif
2006-12-13 21:45:31 +00:00
}
2012-03-16 15:11:34 +01:00
static void
expand_tcbtab ( void )
{
2016-03-05 13:35:57 +01:00
/* Allocate some (more) TCBs (and expand the table).
2012-03-16 15:11:34 +01:00
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 . */
2017-08-08 16:37:39 +02:00
size_t old_tcbtabsize ;
2016-03-05 13:35:57 +01:00
struct tcb * newtcbs ;
2017-08-08 16:37:39 +02:00
struct tcb * * tcb_ptr ;
2016-03-05 13:35:57 +01:00
2017-08-08 16:37:39 +02:00
old_tcbtabsize = tcbtabsize ;
tcbtab = xgrowarray ( tcbtab , & tcbtabsize , sizeof ( tcbtab [ 0 ] ) ) ;
newtcbs = xcalloc ( tcbtabsize - old_tcbtabsize , sizeof ( newtcbs [ 0 ] ) ) ;
2016-03-05 13:35:57 +01:00
2017-08-08 16:37:39 +02:00
for ( tcb_ptr = tcbtab + old_tcbtabsize ;
tcb_ptr < tcbtab + tcbtabsize ; tcb_ptr + + , newtcbs + + )
* tcb_ptr = newtcbs ;
2012-03-16 15:11:34 +01:00
}
2007-07-05 20:03:16 +00:00
2012-03-16 15:11:34 +01:00
static struct tcb *
2012-03-16 15:15:14 +01:00
alloctcb ( int pid )
2006-12-13 21:45:31 +00:00
{
2014-09-10 13:46:04 +00:00
unsigned int i ;
2012-03-16 15:11:34 +01:00
struct tcb * tcp ;
2006-12-13 21:45:31 +00:00
2012-03-16 15:11:34 +01:00
if ( nprocs = = tcbtabsize )
expand_tcbtab ( ) ;
for ( i = 0 ; i < tcbtabsize ; i + + ) {
tcp = tcbtab [ i ] ;
2013-06-26 14:14:29 +02:00
if ( ! tcp - > pid ) {
2012-03-16 15:11:34 +01:00
memset ( tcp , 0 , sizeof ( * tcp ) ) ;
tcp - > pid = pid ;
# if SUPPORTED_PERSONALITIES > 1
tcp - > currpers = current_personality ;
# endif
nprocs + + ;
2017-08-04 08:11:02 +02:00
debug_msg ( " new tcb for pid %d, active tcbs:%d " ,
tcp - > pid , nprocs ) ;
2012-03-16 15:11:34 +01:00
return tcp ;
}
}
2012-03-16 15:15:14 +01:00
error_msg_and_die ( " bug in alloctcb " ) ;
2006-12-13 21:45:31 +00:00
}
2015-08-24 14:42:47 +02:00
void *
get_tcb_priv_data ( const struct tcb * tcp )
{
return tcp - > _priv_data ;
}
int
set_tcb_priv_data ( struct tcb * tcp , void * const priv_data ,
void ( * const free_priv_data ) ( void * ) )
{
if ( tcp - > _priv_data )
return - 1 ;
tcp - > _free_priv_data = free_priv_data ;
tcp - > _priv_data = priv_data ;
return 0 ;
}
void
free_tcb_priv_data ( struct tcb * tcp )
{
if ( tcp - > _priv_data ) {
if ( tcp - > _free_priv_data ) {
tcp - > _free_priv_data ( tcp - > _priv_data ) ;
tcp - > _free_priv_data = NULL ;
}
tcp - > _priv_data = NULL ;
}
}
2012-03-16 15:11:34 +01:00
static void
droptcb ( struct tcb * tcp )
{
if ( tcp - > pid = = 0 )
return ;
2006-12-13 21:45:31 +00:00
Implement syscall fault injection
Introduce new -e fault=EXPR syntax that can be used to specify a subset
of syscalls that are subject of syscall fault injection, an error code
that has to be injected, and a frequency of injection.
The expression specifying syscall fault injection has the following
format: SET[:error=ERRNO][:when=FIRST[+[STEP]]]
where only SET is a required part and all the rest is optional.
The method used to implement syscall fault injection is the following:
on entering syscall the syscall number is substituted by an invalid
syscall number -1, and on exiting syscall the error code returned by
the kernel is substituted with the error code specified in the fault
expression.
This implementaion is based on the prototype developed
by Nahim El Atmani as a part of his GSoC 2016 strace project.
* defs.h (struct fault_opts): New forward declaration.
(struct tcb): Add fault_vec field.
(TCB_FAULT_INJ, QUAL_FAULT): New macros.
* strace.1: Document -e fault expression syntax.
* strace.c (usage): Mention -e fault expression.
(droptcb): Deallocate fault_vec member.
* syscall.c (qual_fault, arch_set_scno, arch_set_error): New prototypes.
(qual_options): Add "fault" option.
(struct fault_opts): New structure.
(num_faults): New variable.
(fault_vec): New array.
(syscall_fault_injected, tcb_fault_opts, reallocate_fault,
find_errno_by_name, qual_syscall_ex, strip_prefix, parse_fault_token,
parse_fault_expression, qual_fault, inject_syscall_fault_entering,
update_syscall_fault_exiting): New functions.
(qual_syscall): Use qual_syscall_ex.
(qualify_one): Add argument: a pointer to struct fault_opts, all callers
changed. Copy struct fault_opts from the pointer to fault_vec.
Use reallocate_fault.
(qualify_scno, qualify_syscall_class, qualify_syscall_name): Add
argument: a pointer to struct fault_opts.
(qualify): Use reallocate_fault. Do not check "all" class for
QUAL_FAULT qualifier.
(lookup_class): Check for "all" class.
(trace_syscall_entering): Use inject_syscall_fault_entering.
(trace_syscall_exiting): Use update_syscall_fault_exiting. Clear
TCB_FAULT_INJ flag along with TCB_INSYSCALL. Print " (INJECTED)" suffix
when the syscall has been injected successfully.
[ARCH_REGS_FOR_GETREGSET && !HAVE_GETREGS_OLD]
(ptrace_setregset): New function.
(ptrace_setregset_or_setregs): Define to ptrace_setregset.
[ARCH_REGS_FOR_GETREGS && !HAVE_GETREGS_OLD]
(ptrace_setregs): New function.
(ptrace_setregset_or_setregs): Define to ptrace_setregs.
[ptrace_setregset_or_setregs] (set_regs): New function.
Include "set_scno.c" and "set_error.c"
* NEWS: Mention this enhancement.
2016-11-16 17:26:58 +00:00
int p ;
for ( p = 0 ; p < SUPPORTED_PERSONALITIES ; + + p )
2017-02-08 09:28:07 +00:00
free ( tcp - > inject_vec [ p ] ) ;
Implement syscall fault injection
Introduce new -e fault=EXPR syntax that can be used to specify a subset
of syscalls that are subject of syscall fault injection, an error code
that has to be injected, and a frequency of injection.
The expression specifying syscall fault injection has the following
format: SET[:error=ERRNO][:when=FIRST[+[STEP]]]
where only SET is a required part and all the rest is optional.
The method used to implement syscall fault injection is the following:
on entering syscall the syscall number is substituted by an invalid
syscall number -1, and on exiting syscall the error code returned by
the kernel is substituted with the error code specified in the fault
expression.
This implementaion is based on the prototype developed
by Nahim El Atmani as a part of his GSoC 2016 strace project.
* defs.h (struct fault_opts): New forward declaration.
(struct tcb): Add fault_vec field.
(TCB_FAULT_INJ, QUAL_FAULT): New macros.
* strace.1: Document -e fault expression syntax.
* strace.c (usage): Mention -e fault expression.
(droptcb): Deallocate fault_vec member.
* syscall.c (qual_fault, arch_set_scno, arch_set_error): New prototypes.
(qual_options): Add "fault" option.
(struct fault_opts): New structure.
(num_faults): New variable.
(fault_vec): New array.
(syscall_fault_injected, tcb_fault_opts, reallocate_fault,
find_errno_by_name, qual_syscall_ex, strip_prefix, parse_fault_token,
parse_fault_expression, qual_fault, inject_syscall_fault_entering,
update_syscall_fault_exiting): New functions.
(qual_syscall): Use qual_syscall_ex.
(qualify_one): Add argument: a pointer to struct fault_opts, all callers
changed. Copy struct fault_opts from the pointer to fault_vec.
Use reallocate_fault.
(qualify_scno, qualify_syscall_class, qualify_syscall_name): Add
argument: a pointer to struct fault_opts.
(qualify): Use reallocate_fault. Do not check "all" class for
QUAL_FAULT qualifier.
(lookup_class): Check for "all" class.
(trace_syscall_entering): Use inject_syscall_fault_entering.
(trace_syscall_exiting): Use update_syscall_fault_exiting. Clear
TCB_FAULT_INJ flag along with TCB_INSYSCALL. Print " (INJECTED)" suffix
when the syscall has been injected successfully.
[ARCH_REGS_FOR_GETREGSET && !HAVE_GETREGS_OLD]
(ptrace_setregset): New function.
(ptrace_setregset_or_setregs): Define to ptrace_setregset.
[ARCH_REGS_FOR_GETREGS && !HAVE_GETREGS_OLD]
(ptrace_setregs): New function.
(ptrace_setregset_or_setregs): Define to ptrace_setregs.
[ptrace_setregset_or_setregs] (set_regs): New function.
Include "set_scno.c" and "set_error.c"
* NEWS: Mention this enhancement.
2016-11-16 17:26:58 +00:00
2015-08-24 14:42:47 +02:00
free_tcb_priv_data ( tcp ) ;
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
2018-04-19 18:03:58 +00:00
if ( stack_trace_enabled )
2014-04-16 15:33:08 +09:00
unwind_tcb_fin ( tcp ) ;
# endif
kvm: attach the exit reason of vcpu as auxstr to KVM_RUN output
In KVM, a virtual machine implementation like Qemu can access a vcpu
via ioctl. KVM_RUN is an ioctl command to enter vcpu. The command
returns control for various reasons: needs of device emulation or
consuming time slices are the typical ones. The vmi takes a different
action for the reason.
We, strace users, want to know the reason to understand kvm. This
change prints the reason as auxstr if "-e kvm=vcpu" option is given,
and if strace runs on Linux 4.16.0 or higher, which includes commit
e46b469278a59781f9b25ff608af84892963821b, "kvm: embed vcpu id to dentry
of vcpu anon inode."
The way to get the reason is a bit complicated because the ioctl does
not return it to the userspace directly. Instead, the vmi and kvm
communicate via an area of the process virtual memory where the fd of
vcpu is mmap'ed. strace must peek the area to know the reason.
The change does three things: (1) recording the area for the given vcpu
when the target calls VCPU_CREATE to vcpu_info_list per tcb data field,
(2) verifying the data recorded in vcpu_info_list before doing (3), and
(3) decoding the exit reason field of the area.
The change is complicated because there is a case that strace
does not have a chance to do (1) if -p option is used.
In this case, vcpu_info data created in the step (2).
The area has more fields than "exit reason",
dumping them may be implemented in the future.
* defs.h (struct tcb) [HAVE_LINUX_KVM_H]: Add vcpu_info_list field.
[HAVE_LINUX_KVM_H]: (kvm_run_structure_decoder_init,
kvm_vcpu_info_free): New declarations.
* strace.c (usage): Add "kvm" as a new expression for -e option.
(droptcb): Call kvm_vcpu_info_free.
* filter_qualify.c (qualify_kvm): New function calling
kvm_run_structure_decoder_init to enable for attaching the exit
reason to auxstr.
(qual_options): Add "kvm" as an entry.
* xlat/kvm_exit_reason.in: New file.
* kvm.c: Include xmalloc.h and mmap_cache.h.
(dump_kvm_run_structure): New static variable.
(kvm_run_structure_decoder_init): New function.
(vcpu_info): New struct definition representing the 3-tuple: vcpu file
descriptor, id of the vcpu, and mmap'ed entry.
(vcpu_find, vcpu_alloc, vcpu_register, vcpu_getinfo,
kvm_vcpu_info_free): New functions to access tcb's vcpu_info_list
field and vcpu_info data type.
(is_map_for_file, map_len): New helper functions.
(kvm_ioclt_run_attach_auxstr, kvm_ioctl_decode_run): New functions
decoding vcpu exit reason and attaching the decoded data to auxstr
field of tcb.
(kvm_ioctl_create_vcpu): Call vcpu_register to make an entry mapping
a file descriptor and the vcpu id associated with the fd.
(kvm_ioctl): Call kvm_ioctl_decode_run.
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-07-07 16:49:11 +09:00
# ifdef HAVE_LINUX_KVM_H
kvm_vcpu_info_free ( tcp ) ;
# endif
2018-05-04 14:45:44 +00:00
if ( tcp - > mmap_cache )
tcp - > mmap_cache - > free_fn ( tcp , __func__ ) ;
2018-02-17 04:37:12 +09:00
2012-03-16 15:11:34 +01:00
nprocs - - ;
2017-08-04 08:11:02 +02:00
debug_msg ( " dropped tcb for pid %d, %d remain " , tcp - > pid , nprocs ) ;
2006-12-13 21:45:31 +00:00
2012-03-16 15:11:34 +01:00
if ( tcp - > outf ) {
2012-03-22 09:35:51 +01:00
if ( followfork > = 2 ) {
2012-03-16 15:11:34 +01:00
if ( tcp - > curcol ! = 0 )
fprintf ( tcp - > outf , " <detached ...> \n " ) ;
fclose ( tcp - > outf ) ;
} else {
if ( printing_tcp = = tcp & & tcp - > curcol ! = 0 )
fprintf ( tcp - > outf , " <detached ...> \n " ) ;
2017-08-06 11:26:52 +00:00
flush_tcp_output ( tcp ) ;
2012-03-16 15:11:34 +01:00
}
}
2012-03-22 09:56:20 +01:00
if ( current_tcp = = tcp )
2018-01-16 17:26:22 +01:00
set_current_tcp ( NULL ) ;
2012-03-16 15:11:34 +01:00
if ( printing_tcp = = tcp )
printing_tcp = NULL ;
memset ( tcp , 0 , sizeof ( * tcp ) ) ;
}
2013-06-18 18:09:39 +02:00
/* Detach traced process.
2012-03-16 15:11:34 +01: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 .
2006-12-13 21:45:31 +00:00
*/
2013-06-20 11:20:23 +02:00
static void
2012-03-16 15:11:34 +01:00
detach ( struct tcb * tcp )
2006-12-13 21:45:31 +00:00
{
2012-03-16 15:11:34 +01:00
int error ;
2013-06-21 16:11:10 +02:00
int status ;
2006-12-13 21:45:31 +00:00
2012-03-16 15:11:34 +01:00
/*
* Linux wrongly insists the child be stopped
* before detaching . Arghh . We go through hoops
* to make a clean break of things .
*/
2006-12-13 21:45:31 +00:00
2013-06-21 16:11:10 +02:00
if ( ! ( tcp - > flags & TCB_ATTACHED ) )
goto drop ;
/* 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 ) .
*/
if ( tcp - > flags & TCB_IGNORE_ONE_SIGSTOP )
goto wait_loop ;
error = ptrace ( PTRACE_DETACH , tcp - > pid , 0 , 0 ) ;
if ( ! error ) {
/* On a clear day, you can see forever. */
goto drop ;
}
if ( errno ! = ESRCH ) {
/* Shouldn't happen. */
2017-08-04 08:55:35 +02:00
perror_func_msg ( " ptrace(PTRACE_DETACH,%u) " , tcp - > pid ) ;
2013-06-21 16:11:10 +02:00
goto drop ;
}
/* ESRCH: process is either not stopped or doesn't exist. */
if ( my_tkill ( tcp - > pid , 0 ) < 0 ) {
if ( errno ! = ESRCH )
/* Shouldn't happen. */
2017-08-04 08:55:35 +02:00
perror_func_msg ( " tkill(%u,0) " , tcp - > pid ) ;
2013-06-21 16:11:10 +02:00
/* else: process doesn't exist. */
goto drop ;
}
/* Process is not stopped, need to stop it. */
if ( use_seize ) {
2012-03-16 15:11:34 +01:00
/*
2013-06-21 16:11:10 +02:00
* With SEIZE , tracee can be in group - stop already .
* In this state sending it another SIGSTOP does nothing .
* Need to use INTERRUPT .
* Testcase : trying to ^ C a " strace -p <stopped_process> " .
2012-03-16 15:11:34 +01:00
*/
2013-06-21 16:11:10 +02:00
error = ptrace ( PTRACE_INTERRUPT , tcp - > pid , 0 , 0 ) ;
if ( ! error )
2013-06-21 15:50:41 +02:00
goto wait_loop ;
2013-06-21 16:11:10 +02:00
if ( errno ! = ESRCH )
2017-08-04 08:55:35 +02:00
perror_func_msg ( " ptrace(PTRACE_INTERRUPT,%u) " , tcp - > pid ) ;
2017-06-17 22:23:09 +00:00
} else {
2013-06-21 16:11:10 +02:00
error = my_tkill ( tcp - > pid , SIGSTOP ) ;
if ( ! error )
goto wait_loop ;
if ( errno ! = ESRCH )
2017-08-04 08:55:35 +02:00
perror_func_msg ( " tkill(%u,SIGSTOP) " , tcp - > pid ) ;
2013-06-21 16:11:10 +02:00
}
/* Either process doesn't exist, or some weird error. */
goto drop ;
2006-12-13 21:45:31 +00:00
2013-06-21 15:50:41 +02:00
wait_loop :
2013-06-21 16:11:10 +02:00
/* We end up here in three cases:
* 1. We sent PTRACE_INTERRUPT ( use_seize case )
* 2. We sent SIGSTOP ( ! use_seize )
* 3. Attach SIGSTOP was already pending ( TCB_IGNORE_ONE_SIGSTOP set )
*/
for ( ; ; ) {
2014-09-10 13:46:04 +00:00
unsigned int sig ;
2013-06-21 16:11:10 +02:00
if ( waitpid ( tcp - > pid , & status , __WALL ) < 0 ) {
if ( errno = = EINTR )
continue ;
/*
* if ( errno = = ECHILD ) break ;
* ^ ^ ^ WRONG ! We expect this PID to exist ,
* and want to emit a message otherwise :
*/
2017-08-04 08:55:35 +02:00
perror_func_msg ( " waitpid(%u) " , tcp - > pid ) ;
2013-06-21 16:11:10 +02:00
break ;
}
if ( ! WIFSTOPPED ( status ) ) {
/*
* Tracee exited or was killed by signal .
* We shouldn ' t normally reach this place :
* we don ' t want to consume exit status .
* Consider " strace -p PID " being ^ C - ed :
* we want merely to detach from PID .
*
* However , we _can_ end up here if tracee
* was SIGKILLed .
*/
break ;
}
sig = WSTOPSIG ( status ) ;
2017-08-04 08:11:02 +02:00
debug_msg ( " detach wait: event:%d sig:%d " ,
( unsigned ) status > > 16 , sig ) ;
2013-06-21 16:11:10 +02:00
if ( use_seize ) {
unsigned event = ( unsigned ) status > > 16 ;
if ( event = = PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/ ) {
2013-06-19 15:31:39 +02:00
/*
2013-06-21 16:11:10 +02:00
* sig = = SIGTRAP : PTRACE_INTERRUPT stop .
* sig = = other : process was already stopped
* with this stopping sig ( see tests / detach - stopped ) .
* Looks like re - injecting this sig is not necessary
* in DETACH for the tracee to remain stopped .
2013-06-19 15:31:39 +02:00
*/
2013-06-21 16:11:10 +02:00
sig = 0 ;
2013-06-19 15:31:39 +02:00
}
2013-06-21 16:11:10 +02:00
/*
* PTRACE_INTERRUPT is not guaranteed to produce
* the above event if other ptrace - stop is pending .
* See tests / detach - sleeping testcase :
* strace got SIGINT while tracee is sleeping .
* We sent PTRACE_INTERRUPT .
* We see syscall exit , not PTRACE_INTERRUPT stop .
* We won ' t get PTRACE_INTERRUPT stop
* if we would CONT now . Need to DETACH .
*/
2013-06-19 15:31:39 +02:00
if ( sig = = syscall_trap_sig )
sig = 0 ;
2013-06-21 16:11:10 +02:00
/* else: not sure in which case we can be here.
* Signal stop ? Inject it while detaching .
*/
ptrace_restart ( PTRACE_DETACH , tcp , sig ) ;
break ;
}
/* Note: this check has to be after use_seize check */
/* (else, in use_seize case SIGSTOP will be mistreated) */
if ( sig = = SIGSTOP ) {
/* Detach, suppressing SIGSTOP */
ptrace_restart ( PTRACE_DETACH , tcp , 0 ) ;
break ;
}
if ( sig = = syscall_trap_sig )
sig = 0 ;
/* Can't detach just yet, may need to wait for SIGSTOP */
error = ptrace_restart ( PTRACE_CONT , tcp , sig ) ;
if ( error < 0 ) {
/* Should not happen.
* Note : ptrace_restart returns 0 on ESRCH , so it ' s not it .
* ptrace_restart already emitted error message .
*/
break ;
2006-12-13 21:45:31 +00:00
}
}
2011-06-22 13:03:56 +02:00
2013-06-21 16:11:10 +02:00
drop :
2012-03-16 15:11:34 +01:00
if ( ! qflag & & ( tcp - > flags & TCB_ATTACHED ) )
Consistently use error_msg instead of fprintf(stderr)
* linux/alpha/get_scno.c: Use error_msg.
* linux/arm/get_scno.c: Likewise.
* linux/mips/get_scno.c: Likewise.
* linux/sh/get_scno.c: Likewise.
* linux/x86_64/get_scno.c: Likewise.
* exit.c (sys_exit): Likewise.
* pathtrace.c (pathtrace_select, pathtrace_match): Likewise.
* strace.c (alloctcb, droptcb, detach, startup_attach,
test_ptrace_seize, init, cleanup, print_debug_info,
maybe_allocate_tcb, startup_tcb, trace): Likewise.
* syscall.c (update_personality, trace_syscall_exiting,
get_scno): Likewise.
* unwind.c (DPRINTF): Likewise.
* tests/bexecve.test: Update patterns.
* tests/detach-stopped.test: Likewise.
2015-05-25 22:51:19 +00:00
error_msg ( " Process %u detached " , tcp - > pid ) ;
2006-12-13 21:45:31 +00:00
2012-03-16 15:11:34 +01:00
droptcb ( tcp ) ;
2006-12-13 21:45:31 +00:00
}
2012-03-12 23:32:16 +01:00
static void
process_opt_p_list ( char * opt )
2012-03-09 13:01:04 +01:00
{
while ( * opt ) {
/*
* We accept - p PID , PID ; - p " `pidof PROG` " ; - p " `pgrep PROG` " .
* pidof uses space as delim , pgrep uses newline . : (
*/
int pid ;
2017-06-17 22:23:09 +00:00
char * delim = opt + strcspn ( opt , " \n \t , " ) ;
2012-03-09 13:01:04 +01:00
char c = * delim ;
* delim = ' \0 ' ;
2012-03-25 21:49:48 +00:00
pid = string_to_uint ( opt ) ;
2012-03-09 13:01:04 +01:00
if ( pid < = 0 ) {
2012-03-25 21:49:48 +00:00
error_msg_and_die ( " Invalid process id: '%s' " , opt ) ;
2012-03-09 13:01:04 +01:00
}
if ( pid = = strace_tracer_pid ) {
2012-03-25 21:49:48 +00:00
error_msg_and_die ( " I'm sorry, I can't let you do that, Dave. " ) ;
2012-03-09 13:01:04 +01:00
}
* delim = c ;
2012-03-16 15:15:14 +01:00
alloctcb ( pid ) ;
2012-03-09 13:01:04 +01:00
if ( c = = ' \0 ' )
break ;
opt = delim + 1 ;
}
}
2016-07-25 16:25:12 +00:00
static void
attach_tcb ( struct tcb * const tcp )
{
if ( ptrace_attach_or_seize ( tcp - > pid ) < 0 ) {
2016-07-25 17:12:42 +00:00
perror_msg ( " attach: ptrace(%s, %d) " ,
ptrace_attach_cmd , tcp - > pid ) ;
2016-07-25 16:25:12 +00:00
droptcb ( tcp ) ;
return ;
}
2018-04-19 18:03:58 +00:00
after_successful_attach ( tcp , TCB_GRABBED | post_attach_sigstop ) ;
2017-08-04 08:11:02 +02:00
debug_msg ( " attach to pid %d (main) succeeded " , tcp - > pid ) ;
2016-07-25 16:25:12 +00:00
2017-08-04 08:52:27 +02:00
static const char task_path [ ] = " /proc/%d/task " ;
char procdir [ sizeof ( task_path ) + sizeof ( int ) * 3 ] ;
2016-07-25 18:48:50 +00:00
DIR * dir ;
unsigned int ntid = 0 , nerr = 0 ;
if ( followfork & & tcp - > pid ! = strace_child & &
2017-08-04 08:52:27 +02:00
xsprintf ( procdir , task_path , tcp - > pid ) > 0 & &
2016-07-25 18:48:50 +00:00
( dir = opendir ( procdir ) ) ! = NULL ) {
struct_dirent * de ;
while ( ( de = read_dir ( dir ) ) ! = NULL ) {
if ( de - > d_fileno = = 0 )
continue ;
int tid = string_to_uint ( de - > d_name ) ;
if ( tid < = 0 | | tid = = tcp - > pid )
continue ;
+ + ntid ;
if ( ptrace_attach_or_seize ( tid ) < 0 ) {
+ + nerr ;
2017-08-04 08:11:02 +02:00
debug_perror_msg ( " attach: ptrace(%s, %d) " ,
ptrace_attach_cmd , tid ) ;
2016-07-25 18:48:50 +00:00
continue ;
}
2018-04-19 18:03:58 +00:00
after_successful_attach ( alloctcb ( tid ) ,
TCB_GRABBED | post_attach_sigstop ) ;
debug_msg ( " attach to pid %d succeeded " , tid ) ;
2016-07-25 18:48:50 +00:00
}
closedir ( dir ) ;
}
if ( ! qflag ) {
if ( ntid > nerr )
error_msg ( " Process %u attached "
" with %u threads " ,
tcp - > pid , ntid - nerr + 1 ) ;
else
error_msg ( " Process %u attached " ,
tcp - > pid ) ;
}
2016-07-25 16:25:12 +00:00
}
2007-06-11 22:06:31 +00:00
static void
startup_attach ( void )
{
2016-01-22 14:37:14 +00:00
pid_t parent_pid = strace_tracer_pid ;
2014-09-10 13:46:04 +00:00
unsigned int tcbi ;
2007-06-11 22:06:31 +00:00
struct tcb * tcp ;
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 20:51:30 +00:00
if ( daemonized_tracer ) {
pid_t pid = fork ( ) ;
2017-08-04 08:55:35 +02:00
if ( pid < 0 )
perror_func_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 20:51:30 +00:00
if ( pid ) { /* parent */
/*
2011-05-27 14:36:01 +02: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 20:51:30 +00:00
* it proceeds to exec the straced program .
*/
pause ( ) ;
_exit ( 0 ) ; /* paranoia */
}
2011-05-27 14:36:01 +02: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 20:51:30 +00:00
}
2007-06-11 22:06:31 +00: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 15:18:21 +02:00
2013-06-26 14:14:29 +02:00
if ( ! tcp - > pid )
2012-03-09 15:15:24 +01:00
continue ;
2011-09-05 14:01:33 +02:00
/* Is this a process we should attach to, but not yet attached? */
2012-03-09 15:15:24 +01:00
if ( tcp - > flags & TCB_ATTACHED )
continue ; /* no, we already attached it */
2011-09-05 14:01:33 +02:00
2016-01-22 14:37:14 +00:00
if ( tcp - > pid = = parent_pid | | tcp - > pid = = strace_tracer_pid ) {
errno = EPERM ;
2016-07-25 16:25:12 +00:00
perror_msg ( " attach: pid %d " , tcp - > pid ) ;
2007-06-11 22:06:31 +00:00
droptcb ( tcp ) ;
continue ;
}
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 20:51:30 +00:00
2016-07-25 16:25:12 +00:00
attach_tcb ( tcp ) ;
2018-03-22 06:23:25 +00:00
if ( interrupted )
return ;
2011-06-24 16:51:16 +02:00
} /* for each tcbtab[] */
2007-06-11 22:06:31 +00:00
2016-01-22 14:37:14 +00:00
if ( daemonized_tracer ) {
/*
* Make parent go away .
* Also makes grandparent ' s wait ( ) unblock .
*/
kill ( parent_pid , SIGKILL ) ;
strace_child = 0 ;
}
2007-06-11 22:06:31 +00:00
}
2013-02-19 16:30:31 +01:00
/* Stack-o-phobic exec helper, in the hope to work around
* NOMMU + " daemonized tracer " difficulty .
*/
struct exec_params {
int fd_to_close ;
uid_t run_euid ;
gid_t run_egid ;
char * * argv ;
char * pathname ;
2017-05-27 15:58:54 +00:00
struct sigaction child_sa ;
2013-02-19 16:30:31 +01:00
} ;
static struct exec_params params_for_tracee ;
Use macros for gcc attributes
* defs.h (error_msg, perror_msg, error_msg_and_die, perror_msg_and_die,
die_out_of_memory, printllval, printnum_int, printnum_long, tprintf):
Use ATTRIBUTE_* macros for gcc attributes.
* file.c (struct stat64): Likewise.
* statfs.c (struct compat_statfs64): Likewise.
* strace.c (die, exec_or_die, init): Likewise.
* linux/sparc/arch_sigreturn.c: Likewise.
* linux/ubi-user.h: Likewise.
2015-03-29 22:45:03 +00:00
static void ATTRIBUTE_NOINLINE ATTRIBUTE_NORETURN
2013-02-19 16:30:31 +01:00
exec_or_die ( void )
{
struct exec_params * params = & params_for_tracee ;
if ( params - > fd_to_close > = 0 )
close ( params - > fd_to_close ) ;
if ( ! daemonized_tracer & & ! use_seize ) {
if ( ptrace ( PTRACE_TRACEME , 0L , 0L , 0L ) < 0 ) {
perror_msg_and_die ( " ptrace(PTRACE_TRACEME, ...) " ) ;
}
}
if ( username ! = NULL ) {
/*
* It is important to set groups before we
* lose privileges on setuid .
*/
if ( initgroups ( username , run_gid ) < 0 ) {
perror_msg_and_die ( " initgroups " ) ;
}
if ( setregid ( run_gid , params - > run_egid ) < 0 ) {
perror_msg_and_die ( " setregid " ) ;
}
if ( setreuid ( run_uid , params - > run_euid ) < 0 ) {
perror_msg_and_die ( " setreuid " ) ;
}
2017-06-17 22:23:09 +00:00
} else if ( geteuid ( ) ! = 0 )
2013-02-19 16:30:31 +01:00
if ( setreuid ( run_uid , run_uid ) < 0 ) {
perror_msg_and_die ( " setreuid " ) ;
}
if ( ! daemonized_tracer ) {
/*
* Induce a ptrace stop . Tracer ( our parent )
* will resume us with PTRACE_SYSCALL and display
* the immediately following execve syscall .
* Can ' t do this on NOMMU systems , we are after
* vfork : parent is blocked , stopping would deadlock .
*/
if ( ! NOMMU_SYSTEM )
kill ( getpid ( ) , SIGSTOP ) ;
} else {
alarm ( 3 ) ;
/* we depend on SIGCHLD set to SIG_DFL by init code */
/* if it happens to be SIG_IGN'ed, wait won't block */
wait ( NULL ) ;
alarm ( 0 ) ;
}
2017-05-27 15:58:54 +00:00
if ( params_for_tracee . child_sa . sa_handler ! = SIG_DFL )
sigaction ( SIGCHLD , & params_for_tracee . child_sa , NULL ) ;
2013-02-19 16:30:31 +01:00
execv ( params - > pathname , params - > argv ) ;
perror_msg_and_die ( " exec " ) ;
}
2016-08-13 22:04:59 +00:00
/*
* Open a dummy descriptor for use as a placeholder .
* The descriptor is O_RDONLY with FD_CLOEXEC flag set .
* A read attempt from such descriptor ends with EOF ,
* a write attempt is rejected with EBADF .
*/
2016-02-08 17:46:58 +00:00
static int
open_dummy_desc ( void )
2016-02-08 18:08:46 +01:00
{
2016-02-08 17:46:58 +00:00
int fds [ 2 ] ;
if ( pipe ( fds ) )
2017-08-04 08:55:35 +02:00
perror_func_msg_and_die ( " pipe " ) ;
2016-02-08 17:46:58 +00:00
close ( fds [ 1 ] ) ;
2016-08-13 22:04:59 +00:00
set_cloexec_flag ( fds [ 0 ] ) ;
2016-02-08 17:46:58 +00:00
return fds [ 0 ] ;
2016-02-08 18:08:46 +01:00
}
2016-08-13 22:04:59 +00:00
/* placeholder fds status for stdin and stdout */
static bool fd_is_placeholder [ 2 ] ;
/*
* Ensure that all standard file descriptors are open by opening placeholder
* file descriptors for those standard file descriptors that are not open .
*
* The information which descriptors have been made open is saved
* in fd_is_placeholder for later use .
*/
static void
ensure_standard_fds_opened ( void )
{
int fd ;
while ( ( fd = open_dummy_desc ( ) ) < = 2 ) {
if ( fd = = 2 )
break ;
fd_is_placeholder [ fd ] = true ;
}
if ( fd > 2 )
close ( fd ) ;
}
/*
* Redirect stdin and stdout unless they have been opened earlier
* by ensure_standard_fds_opened as placeholders .
*/
static void
redirect_standard_fds ( void )
{
int i ;
/*
* It might be a good idea to redirect stderr as well ,
* but we sometimes need to print error messages .
*/
for ( i = 0 ; i < = 1 ; + + i ) {
if ( ! fd_is_placeholder [ i ] ) {
close ( i ) ;
open_dummy_desc ( ) ;
}
}
}
2007-06-11 22:06:31 +00:00
static void
2011-05-30 14:00:14 +02:00
startup_child ( char * * argv )
2007-06-11 22:06:31 +00:00
{
2013-03-20 21:40:15 +00:00
struct_stat statbuf ;
2007-06-11 22:06:31 +00:00
const char * filename ;
2015-02-28 14:50:09 +00:00
size_t filename_len ;
2014-11-21 22:28:34 +00:00
char pathname [ PATH_MAX ] ;
2013-02-19 16:30:31 +01:00
int pid ;
2007-06-11 22:06:31 +00:00
struct tcb * tcp ;
filename = argv [ 0 ] ;
2015-02-28 14:50:09 +00:00
filename_len = strlen ( filename ) ;
if ( filename_len > sizeof ( pathname ) - 1 ) {
errno = ENAMETOOLONG ;
perror_msg_and_die ( " exec " ) ;
}
2007-06-11 22:06:31 +00:00
if ( strchr ( filename , ' / ' ) ) {
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 .
*/
2013-03-20 21:40:15 +00:00
else if ( stat_file ( filename , & statbuf ) = = 0 )
2007-06-11 22:06:31 +00:00
strcpy ( pathname , filename ) ;
# endif /* USE_DEBUGGING_EXEC */
else {
2010-09-06 22:08:24 +00:00
const char * path ;
2014-09-10 13:46:04 +00:00
size_t m , n , len ;
2007-06-11 22:06:31 +00:00
for ( path = getenv ( " PATH " ) ; path & & * path ; path + = m ) {
2012-01-29 22:38:35 +01:00
const char * colon = strchr ( path , ' : ' ) ;
if ( colon ) {
n = colon - path ;
2007-06-11 22:06:31 +00:00
m = n + 1 ;
2017-06-17 22:23:09 +00:00
} else
2007-06-11 22:06:31 +00:00
m = n = strlen ( path ) ;
if ( n = = 0 ) {
2014-11-21 22:28:34 +00:00
if ( ! getcwd ( pathname , PATH_MAX ) )
2007-06-11 22:06:31 +00:00
continue ;
len = strlen ( pathname ) ;
2017-06-17 22:23:09 +00:00
} else if ( n > sizeof ( pathname ) - 1 )
2007-06-11 22:06:31 +00:00
continue ;
else {
strncpy ( pathname , path , n ) ;
len = n ;
}
if ( len & & pathname [ len - 1 ] ! = ' / ' )
pathname [ len + + ] = ' / ' ;
2015-02-28 14:50:09 +00:00
if ( filename_len + len > sizeof ( pathname ) - 1 )
continue ;
2007-06-11 22:06:31 +00:00
strcpy ( pathname + len , filename ) ;
2013-03-20 21:40:15 +00:00
if ( stat_file ( pathname , & statbuf ) = = 0 & &
2007-06-11 22:06:31 +00:00
/* 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 ;
}
2015-02-28 14:50:09 +00:00
if ( ! path | | ! * path )
pathname [ 0 ] = ' \0 ' ;
2007-06-11 22:06:31 +00:00
}
2013-03-20 21:40:15 +00:00
if ( stat_file ( pathname , & statbuf ) < 0 ) {
2011-06-23 13:05:29 +02:00
perror_msg_and_die ( " Can't stat '%s' " , filename ) ;
2007-06-11 22:06:31 +00:00
}
2013-02-19 16:30:31 +01:00
params_for_tracee . fd_to_close = ( shared_log ! = stderr ) ? fileno ( shared_log ) : - 1 ;
params_for_tracee . run_euid = ( statbuf . st_mode & S_ISUID ) ? statbuf . st_uid : run_uid ;
params_for_tracee . run_egid = ( statbuf . st_mode & S_ISGID ) ? statbuf . st_gid : run_gid ;
params_for_tracee . argv = argv ;
/*
* On NOMMU , can be safely freed only after execve in tracee .
* It ' s hard to know when that happens , so we just leak it .
*/
Introduce memory allocation wrappers
Introduce wrappers to the following functions that do memory allocation:
malloc, calloc, realloc, strdup.
This commit is a follow-up to the related discussions in strace-devel ML:
http://sourceforge.net/p/strace/mailman/message/33618180/
http://sourceforge.net/p/strace/mailman/message/33733470/
* defs.h (xmalloc, xcalloc, xreallocarray, xstrdup): New prototypes.
* xmalloc.c: New file.
* Makefile.am (strace_SOURCES): Add it.
* count.c (count_syscall, call_summary_pers): Use xcalloc.
* desc.c (decode_select): Use xmalloc.
* dirent.c (sys_getdents, sys_getdents64): Likewise.
* net.c (sys_recvmmsg): Use xstrdup.
* pathtrace.c (storepath): Use xreallocarray.
(pathtrace_match): Use xmalloc.
* strace.c (die_out_of_memory): Move to xmalloc.c.
(expand_tcbtab): Use xcalloc and xreallocarray.
(startup_child): Use xstrdup.
(init): Use xmalloc, xcalloc, and xstrdup.
* syscall.c (reallocate_qual): Use xreallocarray.
(qualify): Use xstrdup.
* unwind.c (unwind_tcb_init): Use xmalloc.
(build_mmap_cache): Use xcalloc, xreallocarray, and xstrdup.
(get_symbol_name): Use xreallocarray.
(stacktrace_walk, queue_put): Use xmalloc.
* util.c (printstr): Use xmalloc.
* vsprintf.c (strace_vfprintf): Likewise.
2015-05-25 20:41:02 +00:00
params_for_tracee . pathname = NOMMU_SYSTEM ? xstrdup ( pathname ) : pathname ;
2013-02-19 16:30:31 +01:00
2013-05-13 18:43:28 +00:00
# if defined HAVE_PRCTL && defined PR_SET_PTRACER && defined PR_SET_PTRACER_ANY
if ( daemonized_tracer )
prctl ( PR_SET_PTRACER , PR_SET_PTRACER_ANY ) ;
# endif
2013-06-26 14:27:11 +02:00
pid = fork ( ) ;
2017-08-04 08:55:35 +02:00
if ( pid < 0 )
perror_func_msg_and_die ( " fork " ) ;
2013-02-19 16:30:31 +01:00
if ( ( pid ! = 0 & & daemonized_tracer )
| | ( pid = = 0 & & ! daemonized_tracer )
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 20:51:30 +00:00
) {
2013-02-19 16:30:31 +01:00
/* We are to become the tracee. Two cases:
* - D : we are parent
* not - D : we are child
*/
exec_or_die ( ) ;
2007-06-11 22:06:31 +00: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 20:51:30 +00:00
2011-09-01 10:23:09 +02:00
/* We are the tracer */
2011-05-27 14:36:01 +02:00
2011-09-01 10:23:09 +02:00
if ( ! daemonized_tracer ) {
2013-06-26 14:27:11 +02:00
strace_child = pid ;
2012-01-29 02:01:44 +01:00
if ( ! use_seize ) {
/* child did PTRACE_TRACEME, nothing to do in parent */
} else {
2013-02-19 15:30:12 +01:00
if ( ! NOMMU_SYSTEM ) {
2012-01-29 02:01:44 +01:00
/* 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 15:15:24 +01:00
kill_save_errno ( pid , SIGKILL ) ;
2017-01-07 16:44:44 +00:00
perror_msg_and_die ( " Unexpected wait status %#x " ,
status ) ;
2012-01-29 02:01:44 +01:00
}
}
2013-02-19 15:30:12 +01:00
/* Else: NOMMU case, we have no way to sync.
2012-01-29 02:01:44 +01:00
* 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 15:15:24 +01:00
kill_save_errno ( pid , SIGKILL ) ;
2016-07-25 17:12:42 +00:00
perror_msg_and_die ( " attach: ptrace(%s, %d) " ,
ptrace_attach_cmd , pid ) ;
2012-01-29 02:01:44 +01:00
}
2013-02-19 15:30:12 +01:00
if ( ! NOMMU_SYSTEM )
2012-01-29 02:01:44 +01:00
kill ( pid , SIGCONT ) ;
}
2011-09-01 10:23:09 +02:00
tcp = alloctcb ( pid ) ;
2018-04-19 18:03:58 +00:00
after_successful_attach ( tcp , TCB_SKIP_DETACH_ON_FIRST_EXEC
| ( NOMMU_SYSTEM ? 0
: ( TCB_HIDE_LOG
| post_attach_sigstop ) ) ) ;
2017-06-17 22:23:09 +00:00
} else {
2016-01-22 14:37:14 +00:00
/* With -D, we are *child* here, the tracee is our parent. */
strace_child = strace_tracer_pid ;
2011-09-01 10:23:09 +02:00
strace_tracer_pid = getpid ( ) ;
2016-11-26 18:08:01 +00:00
tcp = alloctcb ( strace_child ) ;
tcp - > flags | = TCB_SKIP_DETACH_ON_FIRST_EXEC | TCB_HIDE_LOG ;
2018-04-19 18:03:58 +00:00
/*
* Attaching will be done later , by startup_attach .
* Note : we don ' t do after_successful_attach ( ) here either !
*/
2013-02-19 15:30:12 +01:00
2013-02-19 16:30:31 +01:00
/* NOMMU BUG! -D mode is active, we (child) return,
* and we will scribble over parent ' s stack !
* When parent later unpauses , it segfaults .
*
* We work around it
* ( 1 ) by declaring exec_or_die ( ) NORETURN ,
* hopefully compiler will just jump to it
* instead of call ( won ' t push anything to stack ) ,
* ( 2 ) by trying very hard in exec_or_die ( )
* to not use any stack ,
2014-11-21 22:28:34 +00:00
* ( 3 ) having a really big ( PATH_MAX ) stack object
2013-02-19 16:30:31 +01:00
* in this function , which creates a " buffer " between
* child ' s and parent ' s stack pointers .
* This may save us if ( 1 ) and ( 2 ) failed
* and compiler decided to use stack in exec_or_die ( ) anyway
* ( happens on i386 because of stack parameter passing ) .
2013-02-26 12:00:34 +01:00
*
* A cleaner solution is to use makecontext + setcontext
* to create a genuine separate stack and execute on it .
2013-02-19 16:30:31 +01: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 20:51:30 +00:00
}
2016-02-08 14:52:32 +01:00
/*
* A case where straced process is part of a pipe :
* { sleep 1 ; yes | head - n99999 ; } | strace - o / dev / null sh - c ' exec < & - ; sleep 9 '
* If strace won ' t close its fd # 0 , closing it in tracee is not enough :
* the pipe is still open , it has a reader . Thus , " head " will not get its
* SIGPIPE at once , on the first write .
*
2016-08-13 22:04:59 +00:00
* Preventing it by redirecting strace ' s stdin / out .
2016-02-08 14:52:32 +01:00
* ( Don ' t leave fds 0 and 1 closed , this is bad practice : future opens
* will reuse them , unexpectedly making a newly opened object " stdin " ) .
*/
2016-08-13 22:04:59 +00:00
redirect_standard_fds ( ) ;
2007-06-11 22:06:31 +00:00
}
2012-01-29 02:01:44 +01:00
static void
test_ptrace_seize ( void )
{
int pid ;
2013-02-26 12:00:34 +01:00
/* Need fork for test. NOMMU has no forks */
2013-02-19 15:30:12 +01:00
if ( NOMMU_SYSTEM ) {
post_attach_sigstop = 0 ; /* this sets use_seize to 1 */
return ;
}
2012-01-29 02:01:44 +01:00
pid = fork ( ) ;
if ( pid < 0 )
2017-08-04 08:55:35 +02:00
perror_func_msg_and_die ( " fork " ) ;
2012-01-29 02:01:44 +01:00
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 .
*/
2012-07-10 16:36:32 +02:00
if ( ptrace ( PTRACE_SEIZE , pid , 0 , 0 ) = = 0 ) {
2012-01-29 02:01:44 +01:00
post_attach_sigstop = 0 ; /* this sets use_seize to 1 */
2017-08-04 08:11:02 +02:00
} else {
debug_msg ( " PTRACE_SEIZE doesn't work " ) ;
2012-01-29 02:01:44 +01:00
}
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 ;
2017-08-04 08:55:35 +02:00
perror_func_msg_and_die ( " unexpected wait result %d " ,
tracee_pid ) ;
2012-01-29 02:01:44 +01:00
}
2017-08-04 09:01:47 +02:00
if ( WIFSIGNALED ( status ) )
2012-01-29 02:01:44 +01:00
return ;
2017-08-04 08:55:35 +02:00
error_func_msg_and_die ( " unexpected wait status %#x " , status ) ;
2012-01-29 02:01:44 +01:00
}
}
2012-03-15 14:36:28 +01:00
static unsigned
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 01:16:02 +01:00
get_os_release ( void )
{
2012-03-15 14:36:28 +01:00
unsigned rel ;
const char * p ;
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 01:16:02 +01:00
struct utsname u ;
if ( uname ( & u ) < 0 )
perror_msg_and_die ( " uname " ) ;
2012-03-15 14:36:28 +01:00
/* u.release has this form: "3.2.9[-some-garbage]" */
rel = 0 ;
p = u . release ;
for ( ; ; ) {
if ( ! ( * p > = ' 0 ' & & * p < = ' 9 ' ) )
error_msg_and_die ( " Bad OS release string: '%s' " , u . release ) ;
/* Note: this open-codes KERNEL_VERSION(): */
rel = ( rel < < 8 ) | atoi ( p ) ;
2017-06-17 22:23:09 +00:00
if ( rel > = KERNEL_VERSION ( 1 , 0 , 0 ) )
2012-03-15 14:36:28 +01:00
break ;
while ( * p > = ' 0 ' & & * p < = ' 9 ' )
p + + ;
2012-05-14 23:42:10 +00:00
if ( * p ! = ' . ' ) {
2017-06-17 22:23:09 +00:00
if ( rel > = KERNEL_VERSION ( 0 , 1 , 0 ) ) {
2012-05-14 23:42:10 +00:00
/* "X.Y-something" means "X.Y.0" */
rel < < = 8 ;
break ;
}
2012-03-15 14:36:28 +01:00
error_msg_and_die ( " Bad OS release string: '%s' " , u . release ) ;
2012-05-14 23:42:10 +00:00
}
2012-03-15 14:36:28 +01:00
p + + ;
}
return rel ;
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 01:16:02 +01:00
}
2017-05-27 12:26:05 +00:00
static void
2018-01-07 05:38:45 +03:00
set_sighandler ( int signo , void ( * sighandler ) ( int ) , struct sigaction * oldact )
2017-05-27 12:26:05 +00:00
{
const struct sigaction sa = { . sa_handler = sighandler } ;
sigaction ( signo , & sa , oldact ) ;
}
2012-03-12 23:05:25 +01:00
/*
* Initialization part of main ( ) was eating much stack ( ~ 0.5 k ) ,
* which was unused after init .
* We can reuse it if we move init code into a separate function .
*
* Don ' t want main ( ) to inline us and defeat the reason
* we have a separate function .
*/
Use macros for gcc attributes
* defs.h (error_msg, perror_msg, error_msg_and_die, perror_msg_and_die,
die_out_of_memory, printllval, printnum_int, printnum_long, tprintf):
Use ATTRIBUTE_* macros for gcc attributes.
* file.c (struct stat64): Likewise.
* statfs.c (struct compat_statfs64): Likewise.
* strace.c (die, exec_or_die, init): Likewise.
* linux/sparc/arch_sigreturn.c: Likewise.
* linux/ubi-user.h: Likewise.
2015-03-29 22:45:03 +00:00
static void ATTRIBUTE_NOINLINE
2012-03-12 23:05:25 +01:00
init ( int argc , char * argv [ ] )
1999-02-19 00:21:36 +00:00
{
2012-03-25 21:49:48 +00:00
int c , i ;
2008-07-25 15:42:34 +00:00
int optF = 0 ;
1999-02-19 00:21:36 +00:00
2017-07-02 00:31:50 +00:00
if ( ! program_invocation_name | | ! * program_invocation_name ) {
static char name [ ] = " strace " ;
program_invocation_name =
2017-12-31 17:57:33 +01:00
( argc > 0 & & argv [ 0 ] & & * argv [ 0 ] ) ? argv [ 0 ] : name ;
2017-07-02 00:31:50 +00:00
}
2007-10-08 21:04:41 +00:00
2011-05-27 14:36:01 +02:00
strace_tracer_pid = getpid ( ) ;
2012-03-15 14:36:28 +01:00
os_release = get_os_release ( ) ;
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 01:16:02 +01:00
2012-03-22 09:56:20 +01:00
shared_log = stderr ;
2006-01-12 09:50:49 +00:00
set_sortby ( DEFAULT_SORTBY ) ;
set_personality ( DEFAULT_PERSONALITY ) ;
1999-02-19 00:21:36 +00:00
qualify ( " trace=all " ) ;
qualify ( " abbrev=all " ) ;
qualify ( " verbose=all " ) ;
2013-02-21 16:13:47 +01:00
# if DEFAULT_QUAL_FLAGS != (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
# error Bug in DEFAULT_QUAL_FLAGS
# endif
1999-02-19 00:21:36 +00:00
qualify ( " signal=all " ) ;
2017-12-20 15:45:55 +01:00
while ( ( c = getopt ( argc , argv , " + "
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
2017-12-20 15:45:55 +01:00
" k "
2013-07-23 00:11:35 -07:00
# endif
2018-03-10 05:12:45 +01:00
" a:Ab:cCdDe:E:fFhiI:o:O:p:P:qrs:S:tTu:vVwxX:yz " ) ) ! = EOF ) {
1999-02-19 00:21:36 +00:00
switch ( c ) {
2018-01-01 17:33:13 +01:00
case ' a ' :
acolumn = string_to_uint ( optarg ) ;
if ( acolumn < 0 )
error_opt_arg ( c , optarg ) ;
break ;
2018-03-28 16:49:57 +02:00
case ' A ' :
open_append = true ;
break ;
2012-03-15 13:44:17 +01:00
case ' b ' :
2013-02-27 12:15:19 +01:00
if ( strcmp ( optarg , " execve " ) ! = 0 )
error_msg_and_die ( " Syscall '%s' for -b isn't supported " ,
optarg ) ;
2012-03-15 13:44:17 +01:00
detach_on_execve = 1 ;
break ;
1999-02-19 00:21:36 +00:00
case ' c ' :
2010-03-28 19:24:54 +00:00
if ( cflag = = CFLAG_BOTH ) {
2015-11-26 17:18:00 +03:00
error_msg_and_help ( " -c and -C are mutually exclusive " ) ;
2010-03-28 19:24:54 +00:00
}
cflag = CFLAG_ONLY_STATS ;
break ;
case ' C ' :
if ( cflag = = CFLAG_ONLY_STATS ) {
2015-11-26 17:18:00 +03:00
error_msg_and_help ( " -c and -C are mutually exclusive " ) ;
2010-03-28 19:24:54 +00:00
}
cflag = CFLAG_BOTH ;
1999-02-19 00:21:36 +00:00
break ;
case ' d ' :
Tidy up order of includes; make bool variables explicit.
Bool variables are more compact in data and (on x86) on code too:
text data bss dec hex filename
237950 676 19044 257670 3ee86 strace.before
237838 676 19012 257526 3edf6 strace
* defs.h: Group library includes at the top of the file.
Rename dtime to Tflag, debug to debug_flag.
Change debug_flag,Tflag,qflag,not_failing_only,show_fd_path,tracing_paths
variable declarations from int to bool.
* strace.c: Change corresponding definitions. Do the same for static
variables iflag,rflag,print_pid_pfx.
Rename dtime to Tflag, debug to debug_flag.
* syscall.c: Rename dtime to Tflag, debug to debug_flag.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-15 12:49:52 +01:00
debug_flag = 1 ;
1999-02-19 00:21:36 +00:00
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 20:51:30 +00:00
case ' D ' :
daemonized_tracer = 1 ;
break ;
2018-01-01 17:33:13 +01:00
case ' e ' :
qualify ( optarg ) ;
break ;
case ' E ' :
if ( putenv ( optarg ) < 0 )
perror_msg_and_die ( " putenv " ) ;
2008-07-25 15:42:34 +00:00
break ;
1999-02-19 00:21:36 +00:00
case ' f ' :
followfork + + ;
break ;
2018-01-01 17:33:13 +01:00
case ' F ' :
optF = 1 ;
break ;
1999-02-19 00:21:36 +00:00
case ' h ' :
2015-11-26 17:18:00 +03:00
usage ( ) ;
1999-02-19 00:21:36 +00:00
break ;
case ' i ' :
Tidy up order of includes; make bool variables explicit.
Bool variables are more compact in data and (on x86) on code too:
text data bss dec hex filename
237950 676 19044 257670 3ee86 strace.before
237838 676 19012 257526 3edf6 strace
* defs.h: Group library includes at the top of the file.
Rename dtime to Tflag, debug to debug_flag.
Change debug_flag,Tflag,qflag,not_failing_only,show_fd_path,tracing_paths
variable declarations from int to bool.
* strace.c: Change corresponding definitions. Do the same for static
variables iflag,rflag,print_pid_pfx.
Rename dtime to Tflag, debug to debug_flag.
* syscall.c: Rename dtime to Tflag, debug to debug_flag.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2012-03-15 12:49:52 +01:00
iflag = 1 ;
1999-02-19 00:21:36 +00:00
break ;
2018-01-01 17:33:13 +01:00
case ' I ' :
opt_intr = string_to_uint_upto ( optarg , NUM_INTR_OPTS - 1 ) ;
if ( opt_intr < = 0 )
2012-03-25 21:49:48 +00:00
error_opt_arg ( c , optarg ) ;
1999-02-19 00:21:36 +00:00
break ;
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
2018-01-01 17:33:13 +01:00
case ' k ' :
stack_trace_enabled = true ;
1999-02-19 00:21:36 +00:00
break ;
2018-01-01 17:33:13 +01:00
# endif
1999-02-19 00:21:36 +00:00
case ' o ' :
2017-07-20 22:55:57 +00:00
outfname = optarg ;
1999-02-19 00:21:36 +00:00
break ;
case ' O ' :
2012-03-25 21:49:48 +00:00
i = string_to_uint ( optarg ) ;
if ( i < 0 )
error_opt_arg ( c , optarg ) ;
set_overhead ( i ) ;
1999-02-19 00:21:36 +00:00
break ;
case ' p ' :
2012-03-09 13:01:04 +01:00
process_opt_p_list ( optarg ) ;
1999-02-19 00:21:36 +00:00
break ;
2011-04-07 20:25:40 +00:00
case ' P ' :
2013-03-05 15:46:34 +01:00
pathtrace_select ( optarg ) ;
2011-04-07 20:25:40 +00:00
break ;
2018-01-01 17:33:13 +01:00
case ' q ' :
qflag + + ;
break ;
case ' r ' :
rflag = 1 ;
break ;
1999-02-19 00:21:36 +00:00
case ' s ' :
2012-03-25 21:49:48 +00:00
i = string_to_uint ( optarg ) ;
2017-06-26 22:30:19 +00:00
if ( i < 0 | | ( unsigned int ) i > - 1U / 4 )
2012-03-25 21:49:48 +00:00
error_opt_arg ( c , optarg ) ;
max_strlen = i ;
1999-02-19 00:21:36 +00:00
break ;
case ' S ' :
set_sortby ( optarg ) ;
break ;
2018-01-01 17:33:13 +01:00
case ' t ' :
tflag + + ;
break ;
case ' T ' :
Tflag = 1 ;
break ;
1999-02-19 00:21:36 +00:00
case ' u ' :
2017-07-20 22:55:57 +00:00
username = optarg ;
1999-02-19 00:21:36 +00:00
break ;
2018-01-01 17:33:13 +01:00
case ' v ' :
qualify ( " abbrev=none " ) ;
2013-07-23 00:11:35 -07:00
break ;
2018-01-01 17:33:13 +01:00
case ' V ' :
print_version ( ) ;
exit ( 0 ) ;
2003-01-24 04:31:23 +00:00
break ;
2018-01-01 17:33:13 +01:00
case ' w ' :
count_wallclock = 1 ;
break ;
case ' x ' :
xflag + + ;
break ;
2018-03-10 05:12:45 +01:00
case ' X ' :
if ( ! strcmp ( optarg , " raw " ) )
xlat_verbosity = XLAT_STYLE_RAW ;
else if ( ! strcmp ( optarg , " abbrev " ) )
xlat_verbosity = XLAT_STYLE_ABBREV ;
else if ( ! strcmp ( optarg , " verbose " ) )
xlat_verbosity = XLAT_STYLE_VERBOSE ;
else
error_opt_arg ( c , optarg ) ;
break ;
2018-01-01 17:33:13 +01:00
case ' y ' :
show_fd_path + + ;
break ;
case ' z ' :
not_failing_only = 1 ;
2012-01-29 16:53:03 +01:00
break ;
1999-02-19 00:21:36 +00:00
default :
2015-11-26 17:18:00 +03:00
error_msg_and_help ( NULL ) ;
1999-02-19 00:21:36 +00:00
break ;
}
}
2017-07-02 10:01:22 +00:00
argv + = optind ;
argc - = optind ;
2011-08-25 01:27:59 +02:00
2017-12-31 17:57:33 +01:00
if ( argc < 0 | | ( ! nprocs & & ! argc ) ) {
2015-11-26 17:18:00 +03:00
error_msg_and_help ( " must have PROG [ARGS] or -p PID " ) ;
}
2003-11-11 21:24:23 +00:00
2017-12-31 17:57:33 +01:00
if ( ! argc & & daemonized_tracer ) {
2016-01-22 14:37:14 +00:00
error_msg_and_help ( " PROG [ARGS] must be specified with -D " ) ;
2010-08-05 14:30:11 +08:00
}
2017-12-20 16:25:20 +01:00
if ( optF ) {
if ( followfork ) {
error_msg ( " deprecated option -F ignored " ) ;
} else {
error_msg ( " option -F is deprecated, "
" please use -f instead " ) ;
followfork = optF ;
}
}
2008-07-25 15:42:34 +00:00
2012-03-16 15:15:14 +01:00
if ( followfork > = 2 & & cflag ) {
2015-11-26 17:18:00 +03:00
error_msg_and_help ( " (-c or -C) and -ff are mutually exclusive " ) ;
2006-04-25 07:48:03 +00:00
}
2014-05-28 17:52:40 +01:00
if ( count_wallclock & & ! cflag ) {
2015-11-26 17:18:00 +03:00
error_msg_and_help ( " -w must be given with (-c or -C) " ) ;
2014-05-28 17:52:40 +01:00
}
2014-06-03 13:20:05 +00:00
if ( cflag = = CFLAG_ONLY_STATS ) {
if ( iflag )
error_msg ( " -%c has no effect with -c " , ' i ' ) ;
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
2014-06-03 13:20:05 +00:00
if ( stack_trace_enabled )
error_msg ( " -%c has no effect with -c " , ' k ' ) ;
# endif
if ( rflag )
error_msg ( " -%c has no effect with -c " , ' r ' ) ;
if ( tflag )
error_msg ( " -%c has no effect with -c " , ' t ' ) ;
if ( Tflag )
error_msg ( " -%c has no effect with -c " , ' T ' ) ;
if ( show_fd_path )
error_msg ( " -%c has no effect with -c " , ' y ' ) ;
}
2017-07-02 10:01:22 +00:00
acolumn_spaces = xmalloc ( acolumn + 1 ) ;
memset ( acolumn_spaces , ' ' , acolumn ) ;
acolumn_spaces [ acolumn ] = ' \0 ' ;
2018-01-07 05:38:45 +03:00
set_sighandler ( SIGCHLD , SIG_DFL , & params_for_tracee . child_sa ) ;
2017-05-27 15:58:54 +00:00
2018-04-07 23:35:45 +00:00
# ifdef ENABLE_STACKTRACE
2018-04-19 18:03:58 +00:00
if ( stack_trace_enabled )
2014-06-03 13:20:05 +00:00
unwind_init ( ) ;
# endif
1999-02-19 00:21:36 +00:00
/* See if they want to run as another user. */
if ( username ! = NULL ) {
struct passwd * pent ;
if ( getuid ( ) ! = 0 | | geteuid ( ) ! = 0 ) {
2011-06-23 13:05:29 +02:00
error_msg_and_die ( " You must be root to use the -u option " ) ;
1999-02-19 00:21:36 +00:00
}
2011-08-20 12:48:18 +02:00
pent = getpwnam ( username ) ;
if ( pent = = NULL ) {
2011-06-23 13:05:29 +02:00
error_msg_and_die ( " Cannot find user '%s' " , username ) ;
1999-02-19 00:21:36 +00:00
}
run_uid = pent - > pw_uid ;
run_gid = pent - > pw_gid ;
2017-06-17 22:23:09 +00:00
} else {
1999-02-19 00:21:36 +00:00
run_uid = getuid ( ) ;
run_gid = getgid ( ) ;
}
2011-08-16 18:57:29 +00:00
if ( followfork )
Remove support for systems without PTRACE_SETOPTIONS
Assume that the kernel is v2.5.46 or newer, i.e. PTRACE_SETOPTIONS
and PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEEXEC|PTRACE_O_TRACECLONE
are universally available.
This change removes all code that implemented post-execve SIGTRAP
handling and fork/vfork/clone->CLONE_PTRACE substitution.
* defs.h (TCB_BPTSET, TCB_WAITEXECVE): Remove macros.
(need_fork_exec_workarounds, setbpt, clearbpt): Remove declarations.
* strace.c (need_fork_exec_workarounds,
test_ptrace_setoptions_followfork, test_ptrace_setoptions_for_all):
Remove.
(syscall_trap_sig): Set to (SIGTRAP | 0x80).
(ptrace_setoptions): Set to (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC).
(detach): Do not test for TCB_BPTSET.
(init): Do not call test_ptrace_setoptions_followfork and
test_ptrace_setoptions_for_all. Do not test for TCB_BPTSET.
* syscall.c (syscall_fixup_on_sysenter, internal_fork, internal_exec,
syscall_fixup_for_fork_exec, syscall_fixup_on_sysexit): Remove.
(trace_syscall_entering): Do not test for TCB_WAITEXECVE. Do not call
syscall_fixup_on_sysenter and syscall_fixup_for_fork_exec.
(trace_syscall_exiting): Do not call syscall_fixup_on_sysexit and
syscall_fixup_for_fork_exec.
[IA64] (ia64_ia32mode): Make static.
* linux/ia64/arch_regs.h (ia64_ia32mode): Remove declaration.
* util.c: Do not include "syscall.h".
(arg_setup, get_arg0, get_arg1, set_arg0, set_arg1, restore_arg0,
restore_arg1, arg_finish_change, change_syscall, setbpt, clearbpt):
Remove.
* tests/ptrace_setoptions.test: Remove.
* tests/Makefile.am (TESTS): Remove it.
2015-02-08 13:05:53 +00:00
ptrace_setoptions | = PTRACE_O_TRACECLONE |
PTRACE_O_TRACEFORK |
PTRACE_O_TRACEVFORK ;
2017-08-04 08:11:02 +02:00
debug_msg ( " ptrace_setoptions = %#x " , ptrace_setoptions ) ;
2012-01-29 02:01:44 +01:00
test_ptrace_seize ( ) ;
2010-12-07 12:50:49 +00:00
2016-08-13 22:04:59 +00:00
/*
* Is something weird with our stdin and / or stdout -
* for example , may they be not open ? In this case ,
* ensure that none of the future opens uses them .
*
* This was seen in the wild when / proc / sys / kernel / core_pattern
* was set to " |/bin/strace -o/tmp/LOG PROG " :
* kernel runs coredump helper with fd # 0 open but fd # 1 closed ( ! ) ,
* therefore LOG gets opened to fd # 1 , and fd # 1 is closed by
* " don't hold up stdin/out open " code soon after .
*/
ensure_standard_fds_opened ( ) ;
2016-02-08 18:08:46 +01:00
1999-02-19 00:21:36 +00:00
/* Check if they want to redirect the output. */
if ( outfname ) {
2003-11-07 02:26:54 +00: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 .
*/
2012-03-16 15:15:14 +01:00
if ( followfork > = 2 )
2017-08-04 09:01:47 +02:00
error_msg_and_help ( " piping the output and -ff "
" are mutually exclusive " ) ;
2012-03-22 09:56:20 +01:00
shared_log = strace_popen ( outfname + 1 ) ;
2017-08-04 09:01:47 +02:00
} else if ( followfork < 2 ) {
2012-03-22 09:56:20 +01:00
shared_log = strace_fopen ( outfname ) ;
2018-01-04 10:52:07 +00:00
} else if ( strlen ( outfname ) > = PATH_MAX - sizeof ( int ) * 3 ) {
errno = ENAMETOOLONG ;
perror_msg_and_die ( " %s " , outfname ) ;
2017-08-04 09:01:47 +02:00
}
2012-03-12 23:34:13 +01:00
} else {
/* -ff without -o FILE is the same as single -f */
2012-03-16 15:15:14 +01:00
if ( followfork > = 2 )
2012-03-12 23:34:13 +01:00
followfork = 1 ;
1999-02-19 00:21:36 +00:00
}
2011-06-23 13:05:29 +02:00
if ( ! outfname | | outfname [ 0 ] = = ' | ' | | outfname [ 0 ] = = ' ! ' ) {
2016-12-04 18:36:31 +00:00
setvbuf ( shared_log , NULL , _IOLBF , 0 ) ;
2011-06-23 13:05:29 +02:00
}
2017-05-27 10:00:43 +00:00
/*
* argv [ 0 ] - pPID - oFILE Default interactive setting
* yes * 0 INTR_WHILE_WAIT
* no 1 0 INTR_WHILE_WAIT
* yes * 1 INTR_NEVER
* no 1 1 INTR_WHILE_WAIT
*/
2017-12-31 17:57:33 +01:00
if ( outfname & & argc ) {
2012-01-29 16:53:03 +01:00
if ( ! opt_intr )
opt_intr = INTR_NEVER ;
2015-06-29 14:37:26 +00:00
if ( ! qflag )
qflag = 1 ;
2003-06-03 01:35:20 +00:00
}
2012-01-29 16:53:03 +01:00
if ( ! opt_intr )
opt_intr = INTR_WHILE_WAIT ;
2017-05-27 10:00:43 +00:00
/*
* startup_child ( ) must be called before the signal handlers get
2013-02-19 16:30:31 +01:00
* 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 .
*/
2017-12-31 17:57:33 +01:00
if ( argc ) {
2012-01-24 11:37:03 +01:00
startup_child ( argv ) ;
2012-03-15 13:44:17 +01:00
}
1999-02-19 00:21:36 +00:00
2018-01-07 05:38:45 +03:00
set_sighandler ( SIGTTOU , SIG_IGN , NULL ) ;
set_sighandler ( SIGTTIN , SIG_IGN , NULL ) ;
2012-01-29 16:53:03 +01:00
if ( opt_intr ! = INTR_ANYWHERE ) {
if ( opt_intr = = INTR_BLOCK_TSTP_TOO )
2018-01-07 05:38:45 +03:00
set_sighandler ( SIGTSTP , SIG_IGN , NULL ) ;
2012-01-29 16:53:03 +01:00
/*
* In interactive mode ( if no - o OUTFILE , or - p PID is used ) ,
2018-03-22 06:23:25 +00:00
* fatal signals are handled asynchronously and acted
* when waiting for process state changes .
* In non - interactive mode these signals are ignored .
2012-01-29 16:53:03 +01:00
*/
2018-01-07 05:38:45 +03:00
set_sighandler ( SIGHUP , interactive ? interrupt : SIG_IGN , NULL ) ;
set_sighandler ( SIGINT , interactive ? interrupt : SIG_IGN , NULL ) ;
set_sighandler ( SIGQUIT , interactive ? interrupt : SIG_IGN , NULL ) ;
set_sighandler ( SIGPIPE , interactive ? interrupt : SIG_IGN , NULL ) ;
set_sighandler ( SIGTERM , interactive ? interrupt : SIG_IGN , NULL ) ;
1999-02-19 00:21:36 +00:00
}
2017-05-27 12:26:05 +00:00
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
sigemptyset ( & timer_set ) ;
sigaddset ( & timer_set , SIGALRM ) ;
sigprocmask ( SIG_BLOCK , & timer_set , NULL ) ;
set_sighandler ( SIGALRM , timer_sighandler , NULL ) ;
2012-03-09 13:03:41 +01:00
if ( nprocs ! = 0 | | daemonized_tracer )
2007-06-11 22:06:31 +00:00
startup_attach ( ) ;
2012-03-09 13:03:41 +01: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 00:21:36 +00:00
}
2012-01-27 15:24:48 +01:00
static struct tcb *
2016-03-01 14:42:58 +00:00
pid2tcb ( const int pid )
1999-02-19 00:21:36 +00:00
{
2010-09-14 18:59:20 -07:00
if ( pid < = 0 )
return NULL ;
1999-02-19 00:21:36 +00:00
2016-03-01 14:42:58 +00:00
# define PID2TCB_CACHE_SIZE 1024U
# define PID2TCB_CACHE_MASK (PID2TCB_CACHE_SIZE - 1)
static struct tcb * pid2tcb_cache [ PID2TCB_CACHE_SIZE ] ;
struct tcb * * const ptcp = & pid2tcb_cache [ pid & PID2TCB_CACHE_MASK ] ;
struct tcb * tcp = * ptcp ;
if ( tcp & & tcp - > pid = = pid )
return tcp ;
for ( unsigned int i = 0 ; i < tcbtabsize ; + + i ) {
tcp = tcbtab [ i ] ;
2013-06-26 14:14:29 +02:00
if ( tcp - > pid = = pid )
2016-03-01 14:42:58 +00:00
return * ptcp = tcp ;
1999-02-19 00:21:36 +00:00
}
2010-09-14 18:59:20 -07:00
1999-02-19 00:21:36 +00:00
return NULL ;
}
static void
2011-05-30 14:00:14 +02:00
cleanup ( void )
1999-02-19 00:21:36 +00:00
{
2014-09-10 13:46:04 +00:00
unsigned int i ;
1999-02-19 00:21:36 +00:00
struct tcb * tcp ;
2012-01-29 21:17:56 +01: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 00:21:36 +00:00
2002-12-18 04:16:10 +00:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
tcp = tcbtab [ i ] ;
2013-06-26 14:14:29 +02:00
if ( ! tcp - > pid )
1999-02-19 00:21:36 +00:00
continue ;
2017-08-04 08:11:02 +02:00
debug_func_msg ( " looking at pid %u " , tcp - > pid ) ;
2013-06-26 14:14:29 +02:00
if ( tcp - > pid = = strace_child ) {
1999-02-19 00:21:36 +00:00
kill ( tcp - > pid , SIGCONT ) ;
2012-01-29 16:43:51 +01:00
kill ( tcp - > pid , fatal_sig ) ;
1999-02-19 00:21:36 +00:00
}
2012-03-13 11:44:31 +01:00
detach ( tcp ) ;
1999-02-19 00:21:36 +00:00
}
if ( cflag )
2012-03-22 09:56:20 +01:00
call_summary ( shared_log ) ;
1999-02-19 00:21:36 +00:00
}
static void
2011-05-30 14:00:14 +02:00
interrupt ( int sig )
1999-02-19 00:21:36 +00:00
{
2012-01-29 16:43:51 +01:00
interrupted = sig ;
1999-02-19 00:21:36 +00:00
}
2013-07-01 13:02:33 +02:00
static void
2015-02-22 02:13:04 +00:00
print_debug_info ( const int pid , int status )
1999-02-19 00:21:36 +00:00
{
2015-02-07 17:31:54 +00:00
const unsigned int event = ( unsigned int ) status > > 16 ;
char buf [ sizeof ( " WIFEXITED,exitcode=%u " ) + sizeof ( int ) * 3 /*paranoia:*/ + 16 ] ;
char evbuf [ sizeof ( " ,EVENT_VFORK_DONE (%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 01:16:02 +01:00
2015-02-07 17:31:54 +00:00
strcpy ( buf , " ??? " ) ;
if ( WIFSIGNALED ( status ) )
# ifdef WCOREDUMP
2018-01-05 01:18:05 +00:00
xsprintf ( buf , " WIFSIGNALED,%ssig=%s " ,
2015-02-07 17:31:54 +00:00
WCOREDUMP ( status ) ? " core, " : " " ,
signame ( WTERMSIG ( status ) ) ) ;
# else
2018-01-05 01:18:05 +00:00
xsprintf ( buf , " WIFSIGNALED,sig=%s " ,
2015-02-07 17:31:54 +00:00
signame ( WTERMSIG ( status ) ) ) ;
# endif
if ( WIFEXITED ( status ) )
2018-01-05 01:18:05 +00:00
xsprintf ( buf , " WIFEXITED,exitcode=%u " , WEXITSTATUS ( status ) ) ;
2015-02-07 17:31:54 +00:00
if ( WIFSTOPPED ( status ) )
2018-01-05 01:18:05 +00:00
xsprintf ( buf , " WIFSTOPPED,sig=%s " , signame ( WSTOPSIG ( status ) ) ) ;
2015-02-07 17:31:54 +00:00
evbuf [ 0 ] = ' \0 ' ;
if ( event ! = 0 ) {
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 " ,
/* [PTRACE_EVENT_STOP (=128)] would make biggish array */
} ;
const char * e = " ?? " ;
if ( event < ARRAY_SIZE ( event_names ) )
e = event_names [ event ] ;
else if ( event = = PTRACE_EVENT_STOP )
e = " STOP " ;
2018-01-05 01:18:05 +00:00
xsprintf ( evbuf , " ,EVENT_%s (%u) " , e , event ) ;
2015-02-07 17:31:54 +00:00
}
Consistently use error_msg instead of fprintf(stderr)
* linux/alpha/get_scno.c: Use error_msg.
* linux/arm/get_scno.c: Likewise.
* linux/mips/get_scno.c: Likewise.
* linux/sh/get_scno.c: Likewise.
* linux/x86_64/get_scno.c: Likewise.
* exit.c (sys_exit): Likewise.
* pathtrace.c (pathtrace_select, pathtrace_match): Likewise.
* strace.c (alloctcb, droptcb, detach, startup_attach,
test_ptrace_seize, init, cleanup, print_debug_info,
maybe_allocate_tcb, startup_tcb, trace): Likewise.
* syscall.c (update_personality, trace_syscall_exiting,
get_scno): Likewise.
* unwind.c (DPRINTF): Likewise.
* tests/bexecve.test: Update patterns.
* tests/detach-stopped.test: Likewise.
2015-05-25 22:51:19 +00:00
error_msg ( " [wait(0x%06x) = %u] %s%s " , status , pid , buf , evbuf ) ;
2015-02-07 17:31:54 +00:00
}
2013-06-20 11:06:58 +02:00
2015-02-07 17:31:54 +00:00
static struct tcb *
2015-02-22 02:13:04 +00:00
maybe_allocate_tcb ( const int pid , int status )
2015-02-07 17:31:54 +00:00
{
if ( ! WIFSTOPPED ( status ) ) {
2015-02-07 18:48:55 +00:00
if ( detach_on_execve & & pid = = strace_child ) {
/* example: strace -bexecve sh -c 'exec true' */
strace_child = 0 ;
return NULL ;
}
2015-02-07 17:31:54 +00:00
/*
* This can happen if we inherited an unknown child .
2015-02-07 18:48:55 +00:00
* Example : ( sleep 1 & exec strace true )
2015-02-07 17:31:54 +00:00
*/
error_msg ( " Exit of unknown pid %u ignored " , pid ) ;
return NULL ;
}
if ( followfork ) {
/* We assume it's a fork/vfork/clone child */
struct tcb * tcp = alloctcb ( pid ) ;
2018-04-19 18:03:58 +00:00
after_successful_attach ( tcp , post_attach_sigstop ) ;
2015-02-07 17:31:54 +00:00
if ( ! qflag )
Consistently use error_msg instead of fprintf(stderr)
* linux/alpha/get_scno.c: Use error_msg.
* linux/arm/get_scno.c: Likewise.
* linux/mips/get_scno.c: Likewise.
* linux/sh/get_scno.c: Likewise.
* linux/x86_64/get_scno.c: Likewise.
* exit.c (sys_exit): Likewise.
* pathtrace.c (pathtrace_select, pathtrace_match): Likewise.
* strace.c (alloctcb, droptcb, detach, startup_attach,
test_ptrace_seize, init, cleanup, print_debug_info,
maybe_allocate_tcb, startup_tcb, trace): Likewise.
* syscall.c (update_personality, trace_syscall_exiting,
get_scno): Likewise.
* unwind.c (DPRINTF): Likewise.
* tests/bexecve.test: Update patterns.
* tests/detach-stopped.test: Likewise.
2015-05-25 22:51:19 +00:00
error_msg ( " Process %d attached " , pid ) ;
2015-02-07 17:31:54 +00:00
return tcp ;
} else {
2017-08-04 11:33:04 +02:00
/*
* This can happen if a clone call misused CLONE_PTRACE itself .
2017-08-06 15:10:56 +00:00
*
* There used to be a dance around possible re - injection of
* WSTOPSIG ( status ) , but it was later removed as the only
* observable stop here is the initial ptrace - stop .
2015-02-07 17:31:54 +00:00
*/
2017-08-06 15:10:56 +00:00
ptrace ( PTRACE_DETACH , pid , NULL , 0L ) ;
error_msg ( " Detached unknown pid %d " , pid ) ;
2015-02-07 17:31:54 +00:00
return NULL ;
}
}
2013-07-08 13:55:04 +02:00
2015-02-07 17:31:54 +00:00
static struct tcb *
maybe_switch_tcbs ( struct tcb * tcp , const int pid )
{
FILE * fp ;
struct tcb * execve_thread ;
long old_pid = 0 ;
2016-12-23 23:33:40 +00:00
if ( ptrace ( PTRACE_GETEVENTMSG , pid , NULL , & old_pid ) < 0 )
2015-02-07 17:31:54 +00:00
return tcp ;
/* Avoid truncation in pid2tcb() param passing */
if ( old_pid < = 0 | | old_pid = = pid )
return tcp ;
if ( ( unsigned long ) old_pid > UINT_MAX )
return tcp ;
execve_thread = pid2tcb ( old_pid ) ;
/* It should be !NULL, but I feel paranoid */
if ( ! execve_thread )
return tcp ;
if ( execve_thread - > curcol ! = 0 ) {
/*
* One case we are here is - ff :
* try " strace -oLOG -ff test/threaded_execve "
*/
fprintf ( execve_thread - > outf , " <pid changed to %d ...> \n " , pid ) ;
/*execve_thread->curcol = 0; - no need, see code below */
}
/* Swap output FILEs (needed for -ff) */
fp = execve_thread - > outf ;
execve_thread - > outf = tcp - > outf ;
tcp - > outf = fp ;
/* And their column positions */
execve_thread - > curcol = tcp - > curcol ;
tcp - > curcol = 0 ;
/* Drop leader, but close execve'd thread outfile (if -ff) */
droptcb ( tcp ) ;
/* Switch to the thread, reusing leader's outfile and pid */
tcp = execve_thread ;
tcp - > pid = pid ;
if ( cflag ! = CFLAG_ONLY_STATS ) {
printleader ( tcp ) ;
tprintf ( " +++ superseded by execve in pid %lu +++ \n " , old_pid ) ;
line_ended ( ) ;
tcp - > flags | = TCB_REPRINT ;
}
1999-02-19 00:21:36 +00:00
2015-02-07 17:31:54 +00:00
return tcp ;
}
2013-06-20 11:06:58 +02:00
2015-02-07 17:31:54 +00:00
static void
2015-02-22 02:13:04 +00:00
print_signalled ( struct tcb * tcp , const int pid , int status )
2015-02-07 17:31:54 +00:00
{
2015-02-07 19:41:48 +00:00
if ( pid = = strace_child ) {
2015-02-07 17:31:54 +00:00
exit_code = 0x100 | WTERMSIG ( status ) ;
2015-02-07 19:41:48 +00:00
strace_child = 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 01:16:02 +01:00
2015-02-07 17:31:54 +00:00
if ( cflag ! = CFLAG_ONLY_STATS
Hide struct number_set implementation details from users
* number_set.h (number_slot_t, struct number_set): Move to number_set.c.
(struct number_set): Add forward declaration.
(read_set, write_set, signal_set): Change prototypes from objects
to pointers.
* filter_qualify.c (read_set, write_set, signal_set): Change definitions
from objects to pointers.
(abbrev_set, inject_set, raw_set, trace_set, verbose_set): Change
definitions from arrays to pointers.
(qualify_read): Initialize read_set before first use.
(qualify_write): Initialize write_set before first use.
(qualify_signals): Initialize signal_set before first use.
(qualify_trace): Initialize trace_set before first use.
(qualify_abbrev): Initialize abbrev_set before first use.
(qualify_verbose): Initialize verbose_set before first use.
(qualify_raw): Initialize raw_set before first use.
(qualify_inject_common): Initialize inject_set before first use.
* strace.c (print_signalled, print_stopped): Update signal_set usage.
* syscall.c (dumpio): Update usage of read_set and write_set.
2017-08-22 21:23:49 +00:00
& & is_number_in_set ( WTERMSIG ( status ) , signal_set ) ) {
2015-02-07 17:31:54 +00:00
printleader ( tcp ) ;
2011-06-24 16:41:35 +02:00
# ifdef WCOREDUMP
2015-02-07 17:31:54 +00:00
tprintf ( " +++ killed by %s %s+++ \n " ,
signame ( WTERMSIG ( status ) ) ,
WCOREDUMP ( status ) ? " (core dumped) " : " " ) ;
2011-06-24 16:41:35 +02:00
# else
2015-02-07 17:31:54 +00:00
tprintf ( " +++ killed by %s +++ \n " ,
signame ( WTERMSIG ( status ) ) ) ;
2011-08-15 11:36:09 +02:00
# endif
2015-02-07 17:31:54 +00:00
line_ended ( ) ;
}
}
1999-02-19 00:21:36 +00:00
2015-02-07 17:31:54 +00:00
static void
2015-02-22 02:13:04 +00:00
print_exited ( struct tcb * tcp , const int pid , int status )
2015-02-07 17:31:54 +00:00
{
2015-02-07 19:41:48 +00:00
if ( pid = = strace_child ) {
2015-02-07 17:31:54 +00:00
exit_code = WEXITSTATUS ( status ) ;
2015-02-07 19:41:48 +00:00
strace_child = 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 01:16:02 +01:00
2015-02-07 17:31:54 +00:00
if ( cflag ! = CFLAG_ONLY_STATS & &
qflag < 2 ) {
printleader ( tcp ) ;
tprintf ( " +++ exited with %d +++ \n " , WEXITSTATUS ( status ) ) ;
line_ended ( ) ;
}
}
static void
print_stopped ( struct tcb * tcp , const siginfo_t * si , const unsigned int sig )
{
if ( cflag ! = CFLAG_ONLY_STATS
2016-11-26 18:08:01 +00:00
& & ! hide_log ( tcp )
Hide struct number_set implementation details from users
* number_set.h (number_slot_t, struct number_set): Move to number_set.c.
(struct number_set): Add forward declaration.
(read_set, write_set, signal_set): Change prototypes from objects
to pointers.
* filter_qualify.c (read_set, write_set, signal_set): Change definitions
from objects to pointers.
(abbrev_set, inject_set, raw_set, trace_set, verbose_set): Change
definitions from arrays to pointers.
(qualify_read): Initialize read_set before first use.
(qualify_write): Initialize write_set before first use.
(qualify_signals): Initialize signal_set before first use.
(qualify_trace): Initialize trace_set before first use.
(qualify_abbrev): Initialize abbrev_set before first use.
(qualify_verbose): Initialize verbose_set before first use.
(qualify_raw): Initialize raw_set before first use.
(qualify_inject_common): Initialize inject_set before first use.
* strace.c (print_signalled, print_stopped): Update signal_set usage.
* syscall.c (dumpio): Update usage of read_set and write_set.
2017-08-22 21:23:49 +00:00
& & is_number_in_set ( sig , signal_set ) ) {
2015-02-07 17:31:54 +00:00
printleader ( tcp ) ;
if ( si ) {
tprintf ( " --- %s " , signame ( sig ) ) ;
2016-05-13 14:16:12 +00:00
printsiginfo ( si ) ;
2015-02-07 17:31:54 +00:00
tprints ( " --- \n " ) ;
} else
tprintf ( " --- stopped by %s --- \n " , signame ( sig ) ) ;
line_ended ( ) ;
}
}
2015-03-21 18:13:45 +01:00
static void
2015-02-07 17:31:54 +00:00
startup_tcb ( struct tcb * tcp )
{
2017-08-04 08:11:02 +02:00
debug_msg ( " pid %d has TCB_STARTUP, initializing it " , tcp - > pid ) ;
2015-02-07 17:31:54 +00:00
tcp - > flags & = ~ TCB_STARTUP ;
Remove support for systems without PTRACE_SETOPTIONS
Assume that the kernel is v2.5.46 or newer, i.e. PTRACE_SETOPTIONS
and PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEEXEC|PTRACE_O_TRACECLONE
are universally available.
This change removes all code that implemented post-execve SIGTRAP
handling and fork/vfork/clone->CLONE_PTRACE substitution.
* defs.h (TCB_BPTSET, TCB_WAITEXECVE): Remove macros.
(need_fork_exec_workarounds, setbpt, clearbpt): Remove declarations.
* strace.c (need_fork_exec_workarounds,
test_ptrace_setoptions_followfork, test_ptrace_setoptions_for_all):
Remove.
(syscall_trap_sig): Set to (SIGTRAP | 0x80).
(ptrace_setoptions): Set to (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC).
(detach): Do not test for TCB_BPTSET.
(init): Do not call test_ptrace_setoptions_followfork and
test_ptrace_setoptions_for_all. Do not test for TCB_BPTSET.
* syscall.c (syscall_fixup_on_sysenter, internal_fork, internal_exec,
syscall_fixup_for_fork_exec, syscall_fixup_on_sysexit): Remove.
(trace_syscall_entering): Do not test for TCB_WAITEXECVE. Do not call
syscall_fixup_on_sysenter and syscall_fixup_for_fork_exec.
(trace_syscall_exiting): Do not call syscall_fixup_on_sysexit and
syscall_fixup_for_fork_exec.
[IA64] (ia64_ia32mode): Make static.
* linux/ia64/arch_regs.h (ia64_ia32mode): Remove declaration.
* util.c: Do not include "syscall.h".
(arg_setup, get_arg0, get_arg1, set_arg0, set_arg1, restore_arg0,
restore_arg1, arg_finish_change, change_syscall, setbpt, clearbpt):
Remove.
* tests/ptrace_setoptions.test: Remove.
* tests/Makefile.am (TESTS): Remove it.
2015-02-08 13:05:53 +00:00
if ( ! use_seize ) {
2017-08-04 08:11:02 +02:00
debug_msg ( " setting opts 0x%x on pid %d " ,
ptrace_setoptions , tcp - > pid ) ;
2015-02-07 17:31:54 +00:00
if ( ptrace ( PTRACE_SETOPTIONS , tcp - > pid , NULL , ptrace_setoptions ) < 0 ) {
if ( errno ! = ESRCH ) {
/* Should never happen, really */
perror_msg_and_die ( " PTRACE_SETOPTIONS " ) ;
2012-03-22 09:35:51 +01:00
}
}
2015-02-07 17:31:54 +00:00
}
2017-04-11 04:04:37 +00:00
2018-01-03 17:36:25 +01:00
if ( ( tcp - > flags & TCB_GRABBED ) & & ( get_scno ( tcp ) = = 1 ) )
2017-04-11 04:04:37 +00:00
tcp - > s_prev_ent = tcp - > s_ent ;
2015-02-07 17:31:54 +00:00
}
2016-11-26 23:45:05 +00:00
static void
print_event_exit ( struct tcb * tcp )
{
if ( entering ( tcp ) | | filtered ( tcp ) | | hide_log ( tcp )
| | cflag = = CFLAG_ONLY_STATS ) {
return ;
}
if ( followfork < 2 & & printing_tcp & & printing_tcp ! = tcp
& & printing_tcp - > curcol ! = 0 ) {
2018-01-16 17:26:22 +01:00
set_current_tcp ( printing_tcp ) ;
2016-11-26 23:45:05 +00:00
tprints ( " <unfinished ...> \n " ) ;
2017-08-06 11:26:52 +00:00
flush_tcp_output ( printing_tcp ) ;
2016-11-26 23:45:05 +00:00
printing_tcp - > curcol = 0 ;
2018-01-16 17:26:22 +01:00
set_current_tcp ( tcp ) ;
2016-11-26 23:45:05 +00:00
}
if ( ( followfork < 2 & & printing_tcp ! = tcp )
| | ( tcp - > flags & TCB_REPRINT ) ) {
tcp - > flags & = ~ TCB_REPRINT ;
printleader ( tcp ) ;
tprintf ( " <... %s resumed> " , tcp - > s_ent - > sys_name ) ;
}
if ( ! ( tcp - > sys_func_rval & RVAL_DECODED ) ) {
/*
* The decoder has probably decided to print something
* on exiting syscall which is not going to happen .
*/
tprints ( " <unfinished ...> " ) ;
}
2018-01-22 01:55:05 +01:00
printing_tcp = tcp ;
2016-11-26 23:45:05 +00:00
tprints ( " ) " ) ;
tabto ( ) ;
tprints ( " = ? \n " ) ;
line_ended ( ) ;
}
2018-08-14 13:43:34 +00:00
static const struct tcb_wait_data *
next_event ( void )
2015-02-07 17:31:54 +00:00
{
2018-08-14 13:43:34 +00:00
static struct tcb_wait_data wait_data ;
2015-02-07 17:31:54 +00:00
int pid ;
int status ;
struct tcb * tcp ;
2018-08-14 13:43:34 +00:00
struct tcb_wait_data * wd = & wait_data ;
2015-02-07 17:31:54 +00:00
struct rusage ru ;
if ( interrupted )
2018-08-14 13:43:34 +00:00
return NULL ;
2015-02-07 17:31:54 +00:00
2015-03-21 18:40:53 +01:00
/*
* Used to exit simply when nprocs hits zero , but in this testcase :
2017-06-17 19:08:36 +00:00
* int main ( void ) { _exit ( ! ! fork ( ) ) ; }
2015-03-21 18:40:53 +01:00
* under strace - f , parent sometimes ( rarely ) manages
* to exit before we see the first stop of the child ,
* and we are losing track of it :
* 19923 clone ( . . . ) = 19924
* 19923 exit_group ( 1 ) = ?
* 19923 + + + exited with 1 + + +
* Exiting only when wait ( ) returns ECHILD works better .
*/
if ( popen_pid ! = 0 ) {
/* However, if -o|logger is in use, we can't do that.
* Can work around that by double - forking the logger ,
* but that loses the ability to wait for its completion
* on exit . Oh well . . .
*/
if ( nprocs = = 0 )
2018-08-14 13:43:34 +00:00
return NULL ;
2015-03-21 18:40:53 +01:00
}
2015-02-07 17:31:54 +00:00
2018-03-23 00:20:33 +00:00
const bool unblock_delay_timer = is_delay_timer_armed ( ) ;
2018-03-23 00:20:33 +00:00
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
/*
* The window of opportunity to handle expirations
* of the delay timer opens here .
*
* Unblock the signal handler for the delay timer
* iff the delay timer is already created .
*/
2018-03-23 00:20:33 +00:00
if ( unblock_delay_timer )
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
sigprocmask ( SIG_UNBLOCK , & timer_set , NULL ) ;
/*
* If the delay timer has expired , then its expiration
* has been handled already by the signal handler .
*
* If the delay timer expires during wait4 ( ) ,
* then the system call will be interrupted and
* the expiration will be handled by the signal handler .
*/
2018-08-14 13:43:34 +00:00
pid = wait4 ( - 1 , & status , __WALL , ( cflag ? & ru : NULL ) ) ;
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
const int wait_errno = errno ;
/*
* The window of opportunity to handle expirations
* of the delay timer closes here .
*
* Block the signal handler for the delay timer
2018-03-23 00:20:33 +00:00
* iff it was unblocked earlier .
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
*/
2018-03-23 00:20:33 +00:00
if ( unblock_delay_timer ) {
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
sigprocmask ( SIG_BLOCK , & timer_set , NULL ) ;
2018-03-23 00:20:33 +00:00
if ( restart_failed )
2018-08-14 13:43:34 +00:00
return NULL ;
2018-03-23 00:20:33 +00:00
}
2015-02-07 17:31:54 +00:00
if ( pid < 0 ) {
2018-08-14 13:43:34 +00:00
if ( wait_errno = = EINTR ) {
wd - > te = TE_NEXT ;
return wd ;
}
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
if ( nprocs = = 0 & & wait_errno = = ECHILD )
2018-08-14 13:43:34 +00:00
return NULL ;
2015-02-07 17:31:54 +00:00
/*
* If nprocs > 0 , ECHILD is not expected ,
* treat it as any other error here :
*/
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
errno = wait_errno ;
2015-02-07 17:31:54 +00:00
perror_msg_and_die ( " wait4(__WALL) " ) ;
}
2018-08-14 13:43:34 +00:00
wd - > status = status ;
2017-06-02 17:05:56 +03:00
2015-02-07 17:31:54 +00:00
if ( pid = = popen_pid ) {
if ( ! WIFSTOPPED ( status ) )
popen_pid = 0 ;
2018-08-14 13:43:34 +00:00
wd - > te = TE_NEXT ;
return wd ;
2015-02-07 17:31:54 +00:00
}
2012-03-22 09:35:51 +01:00
2015-02-07 17:31:54 +00:00
if ( debug_flag )
print_debug_info ( pid , status ) ;
/* Look up 'pid' in our table. */
tcp = pid2tcb ( pid ) ;
if ( ! tcp ) {
tcp = maybe_allocate_tcb ( pid , status ) ;
2018-08-14 13:43:34 +00:00
if ( ! tcp ) {
wd - > te = TE_NEXT ;
return wd ;
}
2015-02-07 17:31:54 +00:00
}
Optimize out PTRACE_PEEKUSER with -i
strace -i was fetching PC with a separate PEEKUSER
despite having GETREGS data:
ptrace(PTRACE_GETREGS, 22331, 0, 0x8087f00) = 0
ptrace(PTRACE_PEEKUSER, 22331, 4*EIP, [0x80dd7b7]) = 0
write(3, "[080dd7b7] ioctl(0, SNDCTL_TMR_T"..., 82) = 82
ptrace(PTRACE_SYSCALL, 22331, 0, SIG_0) = 0
Now it does this:
ptrace(PTRACE_GETREGS, 22549, 0, 0x8087ea0) = 0
write(3, "[080dd7b7] ioctl(0, SNDCTL_TMR_T"..., 82) = 82
ptrace(PTRACE_SYSCALL, 22549, 0, SIG_0) = 0
Analogous improvement in sys_sigreturn() is also implemented.
* defs.h: Declare extern struct pt_regs regs for SPARC[64] and ARM.
Declare clear_regs(), get_regs() and get_regs_error flag variable.
* strace.c (trace): Call get_regs(pid) as soon as we know the tcb
and that it is stopped.
* syscall.c (get_regs): New function. Used to fetch registers early,
just after tracee has stopped.
(printcall): Move it here from util.c. Use global regs.REG data,
if available on the arch, instead of re-fetching it.
(get_scno): Use global regs.REG data.
(get_syscall_result): Likewise.
* signal.c (sys_sigreturn): Likewise.
* util.c (printcall): Moved to syscall.c.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2013-02-05 16:36:13 +01:00
2017-12-26 00:09:41 +01:00
clear_regs ( tcp ) ;
2015-02-07 17:31:54 +00:00
/* Set current output file */
2018-01-16 17:26:22 +01:00
set_current_tcp ( tcp ) ;
2012-03-13 11:44:31 +01:00
2015-02-07 17:31:54 +00:00
if ( cflag ) {
Replace struct timeval with struct timespec in time measurements
This is required to implement more precise time measurements.
* Makefile.am (strace_LDADD): Add $(clock_LIBS).
* defs.h (struct tcb): Change the type of stime, dtime, and etime fields
from struct timeval to struct timespec, all users updated.
(syscall_exiting_decode, syscall_exiting_trace, count_syscall): Change
the type of "struct timeval *" argument to "struct timespec *", all
users updated.
(tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul): Rename to
ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul. Change
the type of all "struct timeval *" arguments to "struct timespec *",
all users updated.
* util.c (tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul):
Rename to ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul.
Change the type of all "struct timeval *" arguments to "struct timespec *".
* count.c (struct call_counts): Change the type of "time" field
from struct timeval to struct timespec, all users updated.
(overhead): Change type from struct timeval to struct timespec, all
users updated.
(count_syscall): Change the type of "struct timeval *" argument to
"struct timespec *".
* strace.c (printleader): Change the type of struct timeval variables
to struct timespec, call clock_gettime instead of gettimeofday.
(next_event, trace_syscall): Change the type of struct timeval variables
to struct timespec.
* syscall.c (syscall_entering_finish, syscall_exiting_decode): Call
clock_gettime instead of gettimeofday.
2018-03-16 00:55:58 +00:00
struct timespec stime = {
. tv_sec = ru . ru_stime . tv_sec ,
. tv_nsec = ru . ru_stime . tv_usec * 1000
} ;
ts_sub ( & tcp - > dtime , & stime , & tcp - > stime ) ;
tcp - > stime = stime ;
2015-02-07 17:31:54 +00:00
}
2009-06-02 16:49:22 -07:00
2018-08-14 13:43:34 +00:00
if ( WIFSIGNALED ( status ) ) {
wd - > te = TE_SIGNALLED ;
return wd ;
}
1999-02-19 00:21:36 +00:00
2018-08-14 13:43:34 +00:00
if ( WIFEXITED ( status ) ) {
wd - > te = TE_EXITED ;
return wd ;
}
2011-09-05 14:05:46 +02:00
2017-06-02 20:01:31 +00:00
/*
* As WCONTINUED flag has not been specified to wait4 ,
* it cannot be WIFCONTINUED ( status ) , so the only case
* that remains is WIFSTOPPED ( status ) .
*/
2015-02-07 17:31:54 +00:00
/* Is this the very first time we see this tracee stopped? */
2017-06-02 17:05:56 +03:00
if ( tcp - > flags & TCB_STARTUP )
2015-03-21 18:13:45 +01:00
startup_tcb ( tcp ) ;
2015-02-07 17:31:54 +00:00
2017-06-02 21:49:02 +00:00
const unsigned int sig = WSTOPSIG ( status ) ;
const unsigned int event = ( unsigned int ) status > > 16 ;
2012-01-29 02:01:44 +01:00
2016-11-26 23:45:05 +00:00
switch ( event ) {
2017-06-02 17:05:56 +03:00
case 0 :
/*
* Is this post - attach SIGSTOP ?
* Interestingly , the process may stop
* with STOPSIG equal to some other signal
2017-06-16 22:54:04 +00:00
* than SIGSTOP if we happened to attach
2017-06-02 17:05:56 +03:00
* just before the process takes a signal .
*/
if ( sig = = SIGSTOP & & ( tcp - > flags & TCB_IGNORE_ONE_SIGSTOP ) ) {
2017-08-04 08:11:02 +02:00
debug_func_msg ( " ignored SIGSTOP on pid %d " , tcp - > pid ) ;
2017-06-02 17:05:56 +03:00
tcp - > flags & = ~ TCB_IGNORE_ONE_SIGSTOP ;
2018-08-14 13:43:34 +00:00
wd - > te = TE_RESTART ;
2017-06-02 17:05:56 +03:00
} else if ( sig = = syscall_trap_sig ) {
2018-08-14 13:43:34 +00:00
wd - > te = TE_SYSCALL_STOP ;
2017-06-02 17:05:56 +03:00
} else {
2018-08-14 13:43:34 +00:00
memset ( & wd - > si , 0 , sizeof ( wd - > si ) ) ;
2015-02-07 17:31:54 +00:00
/*
2017-06-02 17:05:56 +03:00
* True if tracee is stopped by signal
* ( as opposed to " tracee received signal " ) .
* TODO : shouldn ' t we check for errno = = EINVAL too ?
* We can get ESRCH instead , you know . . .
2015-02-07 17:31:54 +00:00
*/
2018-08-14 13:43:34 +00:00
bool stopped = ptrace ( PTRACE_GETSIGINFO , pid , 0 , & wd - > si ) < 0 ;
wd - > te = stopped ? TE_GROUP_STOP : TE_SIGNAL_DELIVERY_STOP ;
2017-06-02 17:05:56 +03:00
}
break ;
case PTRACE_EVENT_STOP :
/*
* PTRACE_INTERRUPT - stop or group - stop .
* PTRACE_INTERRUPT - stop has sig = = SIGTRAP here .
*/
switch ( sig ) {
case SIGSTOP :
case SIGTSTP :
case SIGTTIN :
case SIGTTOU :
2018-08-14 13:43:34 +00:00
wd - > te = TE_GROUP_STOP ;
break ;
default :
wd - > te = TE_RESTART ;
2017-06-02 17:05:56 +03:00
}
2018-08-14 13:43:34 +00:00
break ;
2017-06-02 21:49:02 +00:00
case PTRACE_EVENT_EXEC :
2018-08-14 13:43:34 +00:00
wd - > te = TE_STOP_BEFORE_EXECVE ;
break ;
2017-06-02 17:05:56 +03:00
case PTRACE_EVENT_EXIT :
2018-08-14 13:43:34 +00:00
wd - > te = TE_STOP_BEFORE_EXIT ;
break ;
2017-06-02 17:05:56 +03:00
default :
2018-08-14 13:43:34 +00:00
wd - > te = TE_RESTART ;
2015-02-07 17:31:54 +00:00
}
2018-08-14 13:43:34 +00:00
return wd ;
2017-06-02 17:05:56 +03:00
}
2011-09-05 14:05:46 +02:00
syscall.c: split trace_syscall() into 6 functions
This change removes the trace_syscall function. Now, the code that uses
syscall.c trace functions is expected to check whether it is a syscall
entry or exit (with entering(tcp)/exiting(tcp)) itself, and then make
an appropriate sequence of function calls.
* defs.h: Update comment on TCB_INSYSCALL.
(trace_syscall): Remove prototype.
(syscall_entering_decode, syscall_entering_trace,
syscall_entering_finish, syscall_exiting_decode, syscall_exiting_trace,
syscall_exiting_finish): New prototypes.
* strace.c (trace_syscall): New static replacement for old trace_syscall.
* syscall.c (trace_syscall): Remove.
(trace_syscall_entering): Split into ...
(syscall_entering_decode, syscall_entering_trace,
syscall_entering_finish): ... new functions.
(trace_syscall_exiting): Split into ...
(syscall_exiting_decode, syscall_exiting_trace,
syscall_exiting_finish): ... new functions.
2017-06-05 22:19:07 +03:00
static int
trace_syscall ( struct tcb * tcp , unsigned int * sig )
{
if ( entering ( tcp ) ) {
int res = syscall_entering_decode ( tcp ) ;
switch ( res ) {
case 0 :
return 0 ;
case 1 :
res = syscall_entering_trace ( tcp , sig ) ;
}
syscall_entering_finish ( tcp , res ) ;
return res ;
} else {
Replace struct timeval with struct timespec in time measurements
This is required to implement more precise time measurements.
* Makefile.am (strace_LDADD): Add $(clock_LIBS).
* defs.h (struct tcb): Change the type of stime, dtime, and etime fields
from struct timeval to struct timespec, all users updated.
(syscall_exiting_decode, syscall_exiting_trace, count_syscall): Change
the type of "struct timeval *" argument to "struct timespec *", all
users updated.
(tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul): Rename to
ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul. Change
the type of all "struct timeval *" arguments to "struct timespec *",
all users updated.
* util.c (tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul):
Rename to ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul.
Change the type of all "struct timeval *" arguments to "struct timespec *".
* count.c (struct call_counts): Change the type of "time" field
from struct timeval to struct timespec, all users updated.
(overhead): Change type from struct timeval to struct timespec, all
users updated.
(count_syscall): Change the type of "struct timeval *" argument to
"struct timespec *".
* strace.c (printleader): Change the type of struct timeval variables
to struct timespec, call clock_gettime instead of gettimeofday.
(next_event, trace_syscall): Change the type of struct timeval variables
to struct timespec.
* syscall.c (syscall_entering_finish, syscall_exiting_decode): Call
clock_gettime instead of gettimeofday.
2018-03-16 00:55:58 +00:00
struct timespec ts = { } ;
int res = syscall_exiting_decode ( tcp , & ts ) ;
syscall.c: split trace_syscall() into 6 functions
This change removes the trace_syscall function. Now, the code that uses
syscall.c trace functions is expected to check whether it is a syscall
entry or exit (with entering(tcp)/exiting(tcp)) itself, and then make
an appropriate sequence of function calls.
* defs.h: Update comment on TCB_INSYSCALL.
(trace_syscall): Remove prototype.
(syscall_entering_decode, syscall_entering_trace,
syscall_entering_finish, syscall_exiting_decode, syscall_exiting_trace,
syscall_exiting_finish): New prototypes.
* strace.c (trace_syscall): New static replacement for old trace_syscall.
* syscall.c (trace_syscall): Remove.
(trace_syscall_entering): Split into ...
(syscall_entering_decode, syscall_entering_trace,
syscall_entering_finish): ... new functions.
(trace_syscall_exiting): Split into ...
(syscall_exiting_decode, syscall_exiting_trace,
syscall_exiting_finish): ... new functions.
2017-06-05 22:19:07 +03:00
if ( res ! = 0 ) {
Replace struct timeval with struct timespec in time measurements
This is required to implement more precise time measurements.
* Makefile.am (strace_LDADD): Add $(clock_LIBS).
* defs.h (struct tcb): Change the type of stime, dtime, and etime fields
from struct timeval to struct timespec, all users updated.
(syscall_exiting_decode, syscall_exiting_trace, count_syscall): Change
the type of "struct timeval *" argument to "struct timespec *", all
users updated.
(tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul): Rename to
ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul. Change
the type of all "struct timeval *" arguments to "struct timespec *",
all users updated.
* util.c (tv_nz, tv_cmp, tv_float, tv_add, tv_sub, tv_div, tv_mul):
Rename to ts_nz, ts_cmp, ts_float, ts_add, ts_sub, ts_div, and ts_mul.
Change the type of all "struct timeval *" arguments to "struct timespec *".
* count.c (struct call_counts): Change the type of "time" field
from struct timeval to struct timespec, all users updated.
(overhead): Change type from struct timeval to struct timespec, all
users updated.
(count_syscall): Change the type of "struct timeval *" argument to
"struct timespec *".
* strace.c (printleader): Change the type of struct timeval variables
to struct timespec, call clock_gettime instead of gettimeofday.
(next_event, trace_syscall): Change the type of struct timeval variables
to struct timespec.
* syscall.c (syscall_entering_finish, syscall_exiting_decode): Call
clock_gettime instead of gettimeofday.
2018-03-16 00:55:58 +00:00
res = syscall_exiting_trace ( tcp , & ts , res ) ;
syscall.c: split trace_syscall() into 6 functions
This change removes the trace_syscall function. Now, the code that uses
syscall.c trace functions is expected to check whether it is a syscall
entry or exit (with entering(tcp)/exiting(tcp)) itself, and then make
an appropriate sequence of function calls.
* defs.h: Update comment on TCB_INSYSCALL.
(trace_syscall): Remove prototype.
(syscall_entering_decode, syscall_entering_trace,
syscall_entering_finish, syscall_exiting_decode, syscall_exiting_trace,
syscall_exiting_finish): New prototypes.
* strace.c (trace_syscall): New static replacement for old trace_syscall.
* syscall.c (trace_syscall): Remove.
(trace_syscall_entering): Split into ...
(syscall_entering_decode, syscall_entering_trace,
syscall_entering_finish): ... new functions.
(trace_syscall_exiting): Split into ...
(syscall_exiting_decode, syscall_exiting_trace,
syscall_exiting_finish): ... new functions.
2017-06-05 22:19:07 +03:00
}
syscall_exiting_finish ( tcp ) ;
return res ;
}
}
2017-06-02 17:05:56 +03:00
/* Returns true iff the main trace loop has to continue. */
static bool
2018-08-14 13:43:34 +00:00
dispatch_event ( const struct tcb_wait_data * wd )
2017-06-02 17:05:56 +03:00
{
unsigned int restart_op = PTRACE_SYSCALL ;
unsigned int restart_sig = 0 ;
2018-08-14 13:43:34 +00:00
enum trace_event te = wd ? wd - > te : TE_BREAK ;
/*
* Copy wd - > status to a non - const variable to workaround glibc bugs
* around union wait fixed by glibc commit glibc - 2.24 ~ 391
*/
int status = wd ? wd - > status : 0 ;
1999-02-19 00:21:36 +00:00
2018-08-14 13:43:34 +00:00
switch ( te ) {
2017-06-02 17:05:56 +03:00
case TE_BREAK :
return false ;
2012-01-29 02:01:44 +01:00
2017-06-02 17:05:56 +03:00
case TE_NEXT :
return true ;
2012-01-29 02:01:44 +01:00
2017-06-02 17:05:56 +03:00
case TE_RESTART :
break ;
2012-01-29 02:01:44 +01:00
2017-06-02 17:05:56 +03:00
case TE_SYSCALL_STOP :
if ( trace_syscall ( current_tcp , & restart_sig ) < 0 ) {
/*
* ptrace ( ) failed in trace_syscall ( ) .
* Likely a result of process disappearing mid - flight .
* Observed case : exit_group ( ) or SIGKILL terminating
* all processes in thread group .
* We assume that ptrace error was caused by process death .
* We used to detach ( current_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 .
*/
return true ;
}
break ;
case TE_SIGNAL_DELIVERY_STOP :
2018-08-14 13:43:34 +00:00
restart_sig = WSTOPSIG ( status ) ;
print_stopped ( current_tcp , & wd - > si , restart_sig ) ;
2017-06-02 17:05:56 +03:00
break ;
case TE_SIGNALLED :
2018-08-14 13:43:34 +00:00
print_signalled ( current_tcp , current_tcp - > pid , status ) ;
2017-06-02 17:05:56 +03:00
droptcb ( current_tcp ) ;
return true ;
case TE_GROUP_STOP :
2018-08-14 13:43:34 +00:00
restart_sig = WSTOPSIG ( status ) ;
2017-06-02 17:05:56 +03:00
print_stopped ( current_tcp , NULL , restart_sig ) ;
2015-02-07 17:31:54 +00:00
if ( use_seize ) {
/*
* This ends ptrace - stop , but does * not * end group - stop .
2017-06-02 17:05:56 +03:00
* This makes stopping signals work properly on straced
* process ( that is , process really stops . It used to
* continue to run ) .
2015-02-07 17:31:54 +00:00
*/
2017-06-02 17:05:56 +03:00
restart_op = PTRACE_LISTEN ;
restart_sig = 0 ;
1999-02-19 00:21:36 +00:00
}
2017-06-02 17:05:56 +03:00
break ;
2011-08-21 17:35:39 +02:00
2017-06-02 17:05:56 +03:00
case TE_EXITED :
2018-08-14 13:43:34 +00:00
print_exited ( current_tcp , current_tcp - > pid , status ) ;
2017-06-02 17:05:56 +03:00
droptcb ( current_tcp ) ;
return true ;
2011-08-21 17:35:39 +02:00
2017-06-02 17:05:56 +03:00
case TE_STOP_BEFORE_EXECVE :
2018-01-22 03:53:43 +01:00
/*
* Check that we are inside syscall now ( next event after
* PTRACE_EVENT_EXEC should be for syscall exiting ) . If it is
* not the case , we might have a situation when we attach to a
* process and the first thing we see is a PTRACE_EVENT_EXEC
* and all the following syscall state tracking is screwed up
* otherwise .
*/
if ( entering ( current_tcp ) ) {
int ret ;
error_msg ( " Stray PTRACE_EVENT_EXEC from pid %d "
" , trying to recover... " ,
current_tcp - > pid ) ;
current_tcp - > flags | = TCB_RECOVERING ;
ret = trace_syscall ( current_tcp , & restart_sig ) ;
current_tcp - > flags & = ~ TCB_RECOVERING ;
if ( ret < 0 ) {
/* The reason is described in TE_SYSCALL_STOP */
return true ;
}
}
2015-02-07 17:31:54 +00:00
/*
2017-06-02 17:05:56 +03: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 .
2011-08-21 17:35:39 +02:00
*/
2017-06-17 22:23:09 +00:00
if ( os_release > = KERNEL_VERSION ( 3 , 0 , 0 ) )
2018-01-16 17:26:22 +01:00
set_current_tcp ( maybe_switch_tcbs ( current_tcp ,
current_tcp - > pid ) ) ;
2017-06-02 17:05:56 +03:00
if ( detach_on_execve ) {
if ( current_tcp - > flags & TCB_SKIP_DETACH_ON_FIRST_EXEC ) {
current_tcp - > flags & = ~ TCB_SKIP_DETACH_ON_FIRST_EXEC ;
} else {
detach ( current_tcp ) ; /* do "-b execve" thingy */
return true ;
}
}
break ;
case TE_STOP_BEFORE_EXIT :
print_event_exit ( current_tcp ) ;
break ;
2015-02-07 17:31:54 +00:00
}
2017-06-02 17:05:56 +03:00
/* We handled quick cases, we are permitted to interrupt now. */
if ( interrupted )
return false ;
2015-02-07 17:31:54 +00:00
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
/* If the process is being delayed, do not ptrace_restart just yet */
if ( syscall_delayed ( current_tcp ) )
return true ;
2017-06-02 17:05:56 +03:00
if ( ptrace_restart ( restart_op , current_tcp , restart_sig ) < 0 ) {
2015-02-07 17:31:54 +00:00
/* Note: ptrace_restart emitted error message */
exit_code = 1 ;
return false ;
}
return true ;
1999-02-19 00:21:36 +00:00
}
2012-03-12 23:05:25 +01:00
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
static bool
restart_delayed_tcb ( struct tcb * const tcp )
{
2018-08-14 13:43:34 +00:00
const struct tcb_wait_data wd = { . te = TE_RESTART } ;
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
debug_func_msg ( " pid %d " , tcp - > pid ) ;
tcp - > flags & = ~ TCB_DELAYED ;
struct tcb * const prev_tcp = current_tcp ;
current_tcp = tcp ;
2018-08-14 13:43:34 +00:00
bool ret = dispatch_event ( & wd ) ;
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
current_tcp = prev_tcp ;
return ret ;
}
static bool
restart_delayed_tcbs ( void )
{
struct tcb * tcp_next = NULL ;
struct timespec ts_now ;
clock_gettime ( CLOCK_MONOTONIC , & ts_now ) ;
for ( size_t i = 0 ; i < tcbtabsize ; i + + ) {
struct tcb * tcp = tcbtab [ i ] ;
if ( tcp - > pid & & syscall_delayed ( tcp ) ) {
if ( ts_cmp ( & ts_now , & tcp - > delay_expiration_time ) > 0 ) {
if ( ! restart_delayed_tcb ( tcp ) )
return false ;
} else {
/* Check whether this tcb is the next. */
if ( ! tcp_next | |
ts_cmp ( & tcp_next - > delay_expiration_time ,
& tcp - > delay_expiration_time ) > 0 ) {
tcp_next = tcp ;
}
}
}
}
if ( tcp_next )
arm_delay_timer ( tcp_next ) ;
return true ;
}
/*
* As this signal handler does a lot of work that is not suitable
* for signal handlers , extra care must be taken to ensure that
* it is enabled only in those places where it ' s safe .
*/
static void
timer_sighandler ( int sig )
{
2018-03-23 00:20:33 +00:00
delay_timer_expired ( ) ;
2018-03-23 00:20:33 +00:00
if ( restart_failed )
return ;
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
int saved_errno = errno ;
if ( ! restart_delayed_tcbs ( ) )
2018-03-23 00:20:33 +00:00
restart_failed = 1 ;
Implement delay injection
Add -e inject=SET:delay_enter= and -e inject=SET:delay_exit= options.
* configure.ac (AC_SEARCH_LIBS): Check for timer_create -lrt.
* delay.c: New file.
* Makefile.am (strace_SOURCES): Add it.
(strace_LDADD): Add $(timer_LIBS).
* defs.h (INJECT_F_DELAY_ENTER, INJECT_F_DELAY_EXIT,
TCB_INJECT_DELAY_EXIT, TCB_DELAYED, inject_delay_exit, syscall_delayed):
New macros.
(alloc_delay_data, fill_delay_data, is_delay_timer_created,
arm_delay_timer, delay_tcb): New prototypes.
(struct inject_data): Replace reserved field with delay_idx.
(struct tcb): Add delay_expiration_time field.
* filter_qualify.c (parse_delay_token): New function.
(parse_inject_token): Use it.
(qualify_inject_common): Initialize struct inject_opts.data.delay_idx.
* strace.c: Include <setjmp.h>
(timer_jmp_buf, timer_set): New static variables.
(timer_sighandler, restart_delayed_tcb, restart_delayed_tcbs): New
functions.
(init): Block SIGALRM, set SIGALRM handler.
(dispatch_event): Do not restart delayed syscalls.
(next_event): Unblock SIGALRM during wait4 invocation.
* syscall.c (tamper_with_syscall_entering): Arm delay timer if
INJECT_F_DELAY_ENTER injection flag is set, set TCB_INJECT_DELAY_EXIT
flag if INJECT_F_DELAY_EXIT injection flag is set.
tamper_with_syscall_exiting): Arm delay timer if inject_delay_exit.
(syscall_exiting_trace): Call tamper_with_syscall_exiting in case of
inject_delay_exit.
(syscall_exiting_finish): Clear TCB_INJECT_DELAY_EXIT flag.
* strace.1.in: Document delay injection.
* NEWS: Mention this improvement.
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
2018-02-18 23:21:48 +03:00
errno = saved_errno ;
}
2017-05-27 18:58:31 +00:00
# ifdef ENABLE_COVERAGE_GCOV
2017-06-17 19:08:36 +00:00
extern void __gcov_flush ( void ) ;
2017-05-27 18:58:31 +00:00
# endif
2017-06-05 14:46:34 +03:00
static void ATTRIBUTE_NORETURN
terminate ( void )
2012-03-12 23:05:25 +01:00
{
cleanup ( ) ;
fflush ( NULL ) ;
2012-07-12 20:54:46 +00:00
if ( shared_log ! = stderr )
fclose ( shared_log ) ;
if ( popen_pid ) {
while ( waitpid ( popen_pid , NULL , 0 ) < 0 & & errno = = EINTR )
;
}
2012-03-12 23:05:25 +01:00
if ( exit_code > 0xff ) {
/* Avoid potential core file clobbering. */
2013-03-20 21:40:15 +00:00
struct_rlimit rlim = { 0 , 0 } ;
set_rlimit ( RLIMIT_CORE , & rlim ) ;
2012-03-12 23:05:25 +01:00
/* Child was killed by a signal, mimic that. */
exit_code & = 0xff ;
signal ( exit_code , SIG_DFL ) ;
2017-05-27 18:58:31 +00:00
# ifdef ENABLE_COVERAGE_GCOV
__gcov_flush ( ) ;
# endif
2012-03-12 23:05:25 +01:00
raise ( exit_code ) ;
2017-05-27 17:59:44 +00:00
/* Unblock the signal. */
sigset_t mask ;
sigemptyset ( & mask ) ;
sigaddset ( & mask , exit_code ) ;
2017-05-27 18:58:31 +00:00
# ifdef ENABLE_COVERAGE_GCOV
__gcov_flush ( ) ;
# endif
2017-05-27 17:59:44 +00:00
sigprocmask ( SIG_UNBLOCK , & mask , NULL ) ;
2012-03-12 23:05:25 +01:00
/* Paranoia - what if this signal is not fatal?
Exit with 128 + signo then . */
exit_code + = 128 ;
}
2017-06-05 14:46:34 +03:00
exit ( exit_code ) ;
}
int
main ( int argc , char * argv [ ] )
{
2017-04-01 13:14:15 +00:00
setlocale ( LC_ALL , " " ) ;
2017-06-05 14:46:34 +03:00
init ( argc , argv ) ;
2012-03-12 23:05:25 +01:00
2017-06-05 14:46:34 +03:00
exit_code = ! nprocs ;
2018-08-14 13:43:34 +00:00
while ( dispatch_event ( next_event ( ) ) )
2017-06-05 14:46:34 +03:00
;
terminate ( ) ;
2012-03-12 23:05:25 +01:00
}