IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an
email to Administrator. User accounts are meant only to access repo
and report issues and/or generate pull requests.
This is a purpose-specific Git hosting for
BaseALT
projects. Thank you for your understanding!
Только зарегистрированные пользователи имеют доступ к сервису!
Для получения аккаунта, обратитесь к администратору.
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>
text data bss dec hex filename
238258 668 28676 267602 41552 strace.before
238274 668 20484 259426 3f562 strace
* strace.c (main): Allocate -o OUTFILE buffer only if needed:
unused buffer in bss is not entirely free.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This change partially reverts commit 44d0532.
In code before commit 44d0532, single fprintf was used on purpose:
we want to send entire message as one write() call. Since stderr
is unbuffered, separate fprintf's to it always result in separate
writes, they are not coalesced. If we aren't the only program
which writes to this particular stderr, this may result
in interleaved messages.
Since this function is not performance critical, I guess
it's ok to make it less efficient.
* strace.c (verror_msg): Attempt to print the message in single
write operation. Use separate fprintfs as a fallback if malloc fails.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
On attempts to block or set SIGTRAP handler,
for example, using sigaction syscall, we generate
an additional SIGSTOP.
This change gets rid of this SIGSTOP sending/ignoring.
It appears to work just fine.
It also works if I force strace to not use PTRACE_O_TRACESYSGOOD,
which means strace stops will be marked with SIGTRAP,
not (SIGTRAP | 0x80) - I wondered maybe that's when
this hack is needed.
So, why we even have TCB_SIGTRAPPED? No one knows. It predates
version control: this code was present in the initial commit,
in 1999. No adequate comments, either.
Moreover, TCB_SIGTRAPPED is not set in sys_rt_sigaction
and sys_sigprocmask syscalls - the ones which are most usually
used to implement signal blocking, it is only set in obsolete
sys_signal, sys_sigaction, sys_sigsetmask, and in some dead
non-Linux code.
I think whatever bug it was fixing is gone long ago -
at least as long as sys_rt_sigaction is used by glibc.
Again, since glibc (and uclibc) uses sys_rt_sigaction
and sys_sigprocmask, modified code paths are not used
by most programs anyway.
* defs.h: Remove definition of TCB_SIGTRAPPED.
* signal.c (sys_sigvec): Don't set TCB_SIGTRAPPED and don't send SIGSTOP.
(sys_sigsetmask): Likewise.
(sys_sigaction): Likewise.
(sys_signal): Likewise.
* strace.c (trace): Remove code which executes if TCB_SIGTRAPPED is set.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
I observed a case when signal delivery message was buffered
by stdio until it was flushed along with the next syscall
entry message.
* strace.c (trace): Flush output buffers in a few more cases.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Before this patch, if a thread got nuked by exit in another thread
and we happened to poke it at the same time, we print "????(" thingy
and detach the thread. Since we removed "detach before death" logic,
this no longer matches the behavior of other threads.
Before patch:
[pid 1780] exit_group(1) = ?
[pid 1778] ????( <unfinished ...>
Process 1778 detached
[pid 5860] +++ exited with 1 +++
After:
[pid 17765] exit_group(1) = ?
[pid 21680] ????( <unfinished ...>
[pid 17791] +++ exited with 1 +++
[pid 21680] +++ exited with 1 +++
* strace (trace): Do not detach from tracee which experienced ptrace error.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* strace.c (detach): Drop sig parameter - it is zero in all calls.
(cleanup): Don't pass sig = 0 to detach() call.
(detach): Ditto.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
On syscall entry, save current personality in the tcb structure
along with scno.
On syscall exit, restore current personality from the tcb structure.
* defs.h (struct tcb) [SUPPORTED_PERSONALITIES > 1]: Add currpers
field.
* strace.c (alloc_tcb) [SUPPORTED_PERSONALITIES > 1]: Initialize
tcp->currpers.
* syscall.c (update_personality) [SUPPORTED_PERSONALITIES > 1]: New
function.
(get_scno, trace_syscall_exiting): Use it.
Reported-by: Michael A Fetterman <mafetter@nvidia.com>
We set ptrace options when we see post-attach SIGSTOP.
This is wrong: it's better to set them right away on the very first
stop (whichever it will be). It also will make adding SEIZE support easier,
since SEIZE has no post-attach SIGSTOP.
We do it by adding a new bit, TCB_IGNORE_ONE_SIGSTOP, and treating
TCB_STARTUP and TCB_IGNORE_ONE_SIGSTOP as two slightly different things.
* defs.h: Add a new flag bit, TCB_IGNORE_ONE_SIGSTOP.
* process.c (internal_fork): Set TCB_IGNORE_ONE_SIGSTOP on a newly added child.
* strace.c (startup_attach): Set TCB_IGNORE_ONE_SIGSTOP after attach.
Fix a case when "strace -p PID" found PID dead but sone other of its threads
still alive.
(startup_child): Set TCB_IGNORE_ONE_SIGSTOP after attach, _if needed_.
This fixes a bogus case where we can ignore a _real_ SIGSTOP on NOMMU.
(detach): Perform anti-SIGSTOP dance only if TCB_IGNORE_ONE_SIGSTOP is set,
not if TCB_STARTUP is set.
(trace): Set TCB_IGNORE_ONE_SIGSTOP after attach.
Clear TCB_STARTUP and initialize tracee on the very first tracee stop.
Clear TCB_IGNORE_ONE_SIGSTOP when SIGSTOP is seen.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This fixes logic in detach() which thinks that TCB_STARTUP
means that we are already attached, but did not see SIGSTOP yet.
This also allows to get rid of TCB_ATTACH_DONE flag.
* process.c (internal_fork): Set TCB_STARTUP after attach.
* strace.c (startup_attach): Likewise.
(startup_child): Likewise.
(alloc_tcb): Do not set TCB_STARTUP on tcb allocation - we are
not attached yet.
(trace): Set TCB_STARTUP when we detech an auto-attached child.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
New code does the same as old one, but is more readable (I hope).
* strace.c (startup_child): Reformat setuid-ing code.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
tabto is used in many lines of strace output.
On glibc, tprintf("%*s", col - curcol, "") is noticeably slow
compared to tprintf(" "). Use the latter.
Observed ~15% reduction of time spent in userspace.
* defs.h: Drop extern declaration of acolumn. Make tabto()
take no parameters.
* process.c (sys_exit): Call tabto() with no parameters.
* syscall.c (trace_syscall_exiting): Call tabto() with no parameters.
* strace.c: Make acolumn static, add static char *acolumn_spaces.
(main): Allocate acolumn_spaces as a string of spaces.
(printleader): Call tabto() with no parameters.
(tabto): Use simpler method to print lots of spaces.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Currently, get_scno does *much* more than "get syscall no".
It checks for post-execve SIGTRAP. It checks for changes
in personality. It retrieves params on entry and registers on exit.
Worse still, it is different in different architectures: for example,
for AVR32 regs are fetched in get_scno(), while for e.g. I386
it is done in syscall_enter().
Another problem is that get_scno() is called on both syscall entry and
syscall exit, which is stupid: we don't need to know scno on syscall
exit, it is already known from last syscall entry and stored in
tcp->scno! In essence, get_scno() does two completely different things
on syscall entry and on exit, they are just mixed into one bottle, like
shampoo and conditioner.
The following patches will try to improve this situation.
This change duplicates get_scno into identical get_scno_on_sysenter,
get_scno_on_sysexit functions. Call them in syscall enter and syscall
exit, correspondingly.
* defs.h: Rename get_scno to get_scno_on_sysenter; declare it only
if USE_PROCFS.
* strace.c (proc_open): Call get_scno_on_sysenter instead of get_scno.
* syscall.c (get_scno): Split into two (so far identical) functions
get_scno_on_sysenter and get_scno_on_sysexit.
(trace_syscall_entering): Call get_scno_on_sysenter instead of get_scno.
(trace_syscall_exiting): Call get_scno_on_sysexit instead of get_scno.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* defs.h: Explain TCB_INSYSCALL and TCB_WAITEXECVE bits in detail.
* strace.c (choose_pfd): Use entering/exiting macros instead of direct check
for TCB_INSYSCALL.
* syscall.c (get_scno): Use entering/exiting macros instead of direct check
for TCB_INSYSCALL. Fix comments about post-execve SIGTRAP.
(syscall_fixup): Use entering/exiting instead of direct check
for TCB_INSYSCALL. Add a comment what "not a syscall entry" message
usually means. Change wrong "stray syscall exit" messages into
"not a syscall entry" ones.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* strace.c (trace): Optimize out dummy PC printing on signal delivery.
While at it, tweak comments.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* defs.h: Make struct tcb::pfd fields conditional on USE_PROCFS.
* strace.c (alloc_tcb): Use tcp->pfd only if USE_PROCFS.
(droptcb): Likewise.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* strace.c [LINUX] (kill_save_errno): New function.
(test_ptrace_setoptions_followfork): Change return type to void.
Fix and harden error handling. Use kill_save_errno() to avoid errno
clobbering. Treat EIO from ptrace() the same way as EINVAL.
(test_ptrace_setoptions_for_all): Use kill_save_errno() to avoid errno
clobbering. Treat EIO from ptrace() the same way as EINVAL.
(main): Update use of test_ptrace_setoptions_followfork().
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>
Since we no longer suspend waitpid'ing tracees, we have only one case when
we suspend tracee: when we pick up a new tracee created by clone/fork/vfork.
Background: on some other OSes, attach to child is done this way:
get fork's result (pid), loop ptrace(PTRACE_ATTACH) until you hook up
new process/thread. This is ugly and not safe, but what matters for us
is that it doesn't require suspending. Suspending is required
on Linux only, because on Linux attach to child is done differently.
On Linux, we use two methods of catching new tracee:
adding CLONE_THREAD bit to syscall (if needed, we change
[v]fork into clone before that), or using ptrace options.
In both cases, it may be so that new tracee appears before one which
created it returns from syscall. In this case, current code
suspends new tracee until its creator returns. Only then
strace can determine who is its parent (it needs child's pid for this,
which is visible in parent's [v]fork/clone result).
This is inherently racy. For example, what if SIGKILL kills
creator after it succeeded creating child, but before it returns?
Looks like we will have child suspended forever.
But after previous commit, we DO NOT NEED parent<->child link for anything.
Therefore we do not need suspending too. Bingo!
This patch removes suspending code. Now new tracees will be continued
right away. Next patch will remove tcp->parent member.
* defs.h: Remove TCB_SUSPENDED constant
* process.c (handle_new_child): Delete this function.
(internal_fork): Do not call handle_new_child on syscall exit.
* strace.c (handle_ptrace_event): Delete this function.
(trace): Do not suspend new child; remove all handling
of now impossible TCB_SUSPENDED condition.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Current code plays some ungodly tricks, trying to not detach
thread group leader until all threads exit.
Also, it detaches from a tracee when signal delivery is detected
which will cause tracee to exit.
This operation is racy (not to mention the determination
whether signal is set to SIG_DFL is a horrible hack):
after we determined that this signal is indeed fatal
but before we detach and let process die,
*other thread* may set a handler to this signal, and
we will leak the process, falsely displaying it as killed!
I need to look in the past to figure out why we even do it.
First guess is that it's a workaround for old kernel bugs:
kernel used to deliver exit notifications to the tracer,
not to real parent. These workarounds are ancient
(internal_exit is from 1995).
The patch deletes the hacks. We no longer need tcp->nclone_threads,
TCB_EXITING and TCB_GROUP_EXITING. We also lose a few rather
ugly functions.
I also added a new message: "+++ exited with EXITCODE +++"
which shows exact moment strace got exit notification.
It is analogous to existing "+++ killed by SIG +++" message.
* defs.h: Delete struct tcb::nclone_threads field,
TCB_EXITING and TCB_GROUP_EXITING constants,
declarations of sigishandled() and internal_exit().
* process.c (internal_exit): Delete this function.
(handle_new_child): Don't ++tcp->nclone_threads.
* signal.c (parse_sigset_t): Delete this function.
(sigishandled): Delete this function.
* strace.c (startup_attach): Don't tcbtab[tcbi]->nclone_threads++.
(droptcb): Don't delay dropping if tcp->nclone_threads > 0,
don't drop parent if its nclone_threads reached 0:
just drop (only) this tcb unconditionally.
(detach): don't drop parent.
(handle_group_exit): Delete this function.
(handle_ptrace_event): Instead of handle_group_exit, just drop tcb;
do not panic if we see WIFEXITED from an attached pid;
print "+++ exited with EXITCODE +++" for every WIFEXITED pid.
* syscall.c (internal_syscall): Do not treat sys_exit specially -
don't call internal_exit on it.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* strace.c (trace): Do not recalculate "cflag ? &ru : NULL"
again and again. Do not clear errno unnecessarily.
Consistently check wait errors as pid < 0, not pid == -1.
Indent ifdefs for better readability.
Remove comments after endif if ifdef/endif block is really tiny.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* strace.c (trace): Change ifdef LINUX to make a bit more sense,
remove wrong comment at its endif. Slightly optimize
"+++ killed by SIG +++" message for systems without WCOREDUMP macro.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
* strace.c (alloc_tcb): Print number of allocated tcb's if -d.
(droptcb): Likewise.
(handle_ptrace_event): Remove PTRACE_EVENT_EXEC debug message.
(trace): Improve logging of waitpid: show WIFxxx, exitcode/signal,
ptrace event name, WCOREDUMP - all on one line.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
TCB_FOLLOWFORK flag seems to be unnecessary, because we either follow
all [v]forks/clones or don't follow any, therefore global variable
followfork is an already existing indicator of what we want to do.
This patch drops all setting/clearing of TCB_FOLLOWFORK bit,
and replaces checks for this bit by checks of followfork value.
In internal_fork, check is moved to in front of if(), since
the check is needed on both "entering" and "exiting" branch.
* defs.h: Remove TCB_FOLLOWFORK define.
* process.c (internal_fork): Do not set/clear TCB_FOLLOWFORK,
test followfork instead of tcp->flags & TCB_FOLLOWFORK.
(handle_new_child): Likewise.
* strace.c (startup_attach): Likewise.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>