Add a test program for recently added ptrace(2) interfaces.
MFC after: 1 week
This commit is contained in:
parent
7bc72c360a
commit
e1976efd69
7
tools/test/ptrace/Makefile
Normal file
7
tools/test/ptrace/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= scescx
|
||||
NO_MAN=
|
||||
WARNS?= 6
|
||||
|
||||
.include <bsd.prog.mk>
|
406
tools/test/ptrace/scescx.c
Normal file
406
tools/test/ptrace/scescx.c
Normal file
@ -0,0 +1,406 @@
|
||||
/*-
|
||||
* Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define TRACE ">>>> "
|
||||
|
||||
static const char *
|
||||
decode_wait_status(int status)
|
||||
{
|
||||
static char c[128];
|
||||
char b[32];
|
||||
int first;
|
||||
|
||||
c[0] = '\0';
|
||||
first = 1;
|
||||
if (WIFCONTINUED(status)) {
|
||||
first = 0;
|
||||
strlcat(c, "CONT", sizeof(c));
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
strlcat(c, ",", sizeof(c));
|
||||
snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
|
||||
strlcat(c, b, sizeof(c));
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
strlcat(c, ",", sizeof(c));
|
||||
snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status)));
|
||||
strlcat(c, b, sizeof(c));
|
||||
if (WCOREDUMP(status))
|
||||
strlcat(c, ",CORE", sizeof(c));
|
||||
}
|
||||
if (WIFSTOPPED(status)) {
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
strlcat(c, ",", sizeof(c));
|
||||
snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
|
||||
strlcat(c, b, sizeof(c));
|
||||
}
|
||||
return (c);
|
||||
}
|
||||
|
||||
static const char *
|
||||
decode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
|
||||
{
|
||||
static char c[128];
|
||||
static struct decode_tag {
|
||||
int flag;
|
||||
const char *desc;
|
||||
} decode[] = {
|
||||
{ PL_FLAG_SA, "SA" },
|
||||
{ PL_FLAG_BOUND, "BOUND" },
|
||||
{ PL_FLAG_SCE, "SCE" },
|
||||
{ PL_FLAG_SCX, "SCX" },
|
||||
{ PL_FLAG_EXEC, "EXEC" },
|
||||
{ PL_FLAG_SI, "SI" },
|
||||
{ PL_FLAG_FORKED, "FORKED" },
|
||||
};
|
||||
char de[32];
|
||||
unsigned first, flags, i;
|
||||
|
||||
c[0] = '\0';
|
||||
first = 1;
|
||||
flags = lwpinfo->pl_flags;
|
||||
for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
|
||||
if ((flags & decode[i].flag) != 0) {
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
strlcat(c, ",", sizeof(c));
|
||||
strlcat(c, decode[i].desc, sizeof(c));
|
||||
flags &= ~decode[i].flag;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < sizeof(flags) * NBBY; i++) {
|
||||
if ((flags & (1 << i)) != 0) {
|
||||
if (first)
|
||||
first = 0;
|
||||
else
|
||||
strlcat(c, ",", sizeof(c));
|
||||
snprintf(de, sizeof(de), "<%d>", i);
|
||||
strlcat(c, de, sizeof(c));
|
||||
}
|
||||
}
|
||||
return (c);
|
||||
}
|
||||
|
||||
static const char *
|
||||
decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
|
||||
{
|
||||
|
||||
switch (lwpinfo->pl_event) {
|
||||
case PL_EVENT_NONE:
|
||||
return ("NONE");
|
||||
|
||||
case PL_EVENT_SIGNAL:
|
||||
return ("SIG");
|
||||
|
||||
default:
|
||||
return ("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_pathname(pid_t pid)
|
||||
{
|
||||
char pathname[PATH_MAX];
|
||||
int error, name[4];
|
||||
size_t len;
|
||||
|
||||
name[0] = CTL_KERN;
|
||||
name[1] = KERN_PROC;
|
||||
name[2] = KERN_PROC_PATHNAME;
|
||||
name[3] = pid;
|
||||
|
||||
len = sizeof(pathname);
|
||||
error = sysctl(name, 4, pathname, &len, NULL, 0);
|
||||
if (error < 0) {
|
||||
if (errno != ESRCH) {
|
||||
fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
|
||||
pid, strerror(errno));
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "pid %d exited\n", pid);
|
||||
return;
|
||||
}
|
||||
if (len == 0 || strlen(pathname) == 0) {
|
||||
fprintf(stderr, "No cached pathname for process %d\n", pid);
|
||||
return;
|
||||
}
|
||||
printf(TRACE "pid %d path %s\n", pid, pathname);
|
||||
}
|
||||
|
||||
static void
|
||||
wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
|
||||
{
|
||||
|
||||
printf(TRACE "pid %d wait %s", pid,
|
||||
decode_wait_status(status));
|
||||
if (lwpinfo != NULL) {
|
||||
printf(" event %s flags %s",
|
||||
decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int
|
||||
trace_sc(int pid)
|
||||
{
|
||||
struct ptrace_lwpinfo lwpinfo;
|
||||
int status;
|
||||
|
||||
if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
|
||||
perror("PT_TO_SCE");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
perror("waitpid");
|
||||
return (-1);
|
||||
}
|
||||
if (WIFEXITED(status) || WIFSIGNALED(status)) {
|
||||
wait_info(pid, status, NULL);
|
||||
return (-1);
|
||||
}
|
||||
assert(WIFSTOPPED(status));
|
||||
assert(WSTOPSIG(status) == SIGTRAP);
|
||||
|
||||
if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
|
||||
perror("PT_LWPINFO");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
wait_info(pid, status, &lwpinfo);
|
||||
assert(lwpinfo.pl_flags & PL_FLAG_SCE);
|
||||
|
||||
if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
|
||||
perror("PT_TO_SCX");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
perror("waitpid");
|
||||
return (-1);
|
||||
}
|
||||
if (WIFEXITED(status) || WIFSIGNALED(status)) {
|
||||
wait_info(pid, status, NULL);
|
||||
return (-1);
|
||||
}
|
||||
assert(WIFSTOPPED(status));
|
||||
assert(WSTOPSIG(status) == SIGTRAP);
|
||||
|
||||
if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
|
||||
perror("PT_LWPINFO");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
wait_info(pid, status, &lwpinfo);
|
||||
assert(lwpinfo.pl_flags & PL_FLAG_SCX);
|
||||
|
||||
if (lwpinfo.pl_flags & PL_FLAG_EXEC)
|
||||
get_pathname(pid);
|
||||
|
||||
if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
|
||||
printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
|
||||
return (lwpinfo.pl_child_pid);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
trace_cont(int pid)
|
||||
{
|
||||
struct ptrace_lwpinfo lwpinfo;
|
||||
int status;
|
||||
|
||||
if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
|
||||
perror("PT_CONTINUE");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
perror("waitpid");
|
||||
return (-1);
|
||||
}
|
||||
if (WIFEXITED(status) || WIFSIGNALED(status)) {
|
||||
wait_info(pid, status, NULL);
|
||||
return (-1);
|
||||
}
|
||||
assert(WIFSTOPPED(status));
|
||||
assert(WSTOPSIG(status) == SIGTRAP);
|
||||
|
||||
if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
|
||||
perror("PT_LWPINFO");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
wait_info(pid, status, &lwpinfo);
|
||||
|
||||
if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
|
||||
(PL_FLAG_EXEC | PL_FLAG_SCX))
|
||||
get_pathname(pid);
|
||||
|
||||
if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
|
||||
(PL_FLAG_FORKED | PL_FLAG_SCX)) {
|
||||
printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
|
||||
return (lwpinfo.pl_child_pid);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int trace_syscalls = 1;
|
||||
|
||||
static int
|
||||
trace(pid_t pid)
|
||||
{
|
||||
|
||||
return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct ptrace_lwpinfo lwpinfo;
|
||||
int c, status, use_vfork;
|
||||
pid_t pid, pid1;
|
||||
|
||||
trace_syscalls = 1;
|
||||
use_vfork = 0;
|
||||
while ((c = getopt(argc, argv, "csv")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
trace_syscalls = 0;
|
||||
break;
|
||||
case 's':
|
||||
trace_syscalls = 1;
|
||||
break;
|
||||
case 'v':
|
||||
use_vfork = 1;
|
||||
break;
|
||||
default:
|
||||
case '?':
|
||||
fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
|
||||
return (2);
|
||||
}
|
||||
}
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
perror("fork");
|
||||
return 1;
|
||||
}
|
||||
else if (pid == 0) {
|
||||
if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
|
||||
perror("PT_TRACE_ME");
|
||||
_exit(1);
|
||||
}
|
||||
kill(getpid(), SIGSTOP);
|
||||
getpid();
|
||||
if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
|
||||
perror("fork1");
|
||||
return (1);
|
||||
} else if (pid1 == 0) {
|
||||
printf("Hi from child %d\n", getpid());
|
||||
execl("/bin/ls", "ls", "/", (char *)NULL);
|
||||
}
|
||||
}
|
||||
else { /* parent */
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
perror("waitpid");
|
||||
return (-1);
|
||||
}
|
||||
assert(WIFSTOPPED(status));
|
||||
assert(WSTOPSIG(status) == SIGSTOP);
|
||||
|
||||
if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
|
||||
sizeof(lwpinfo)) < 0) {
|
||||
perror("PT_LWPINFO");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
wait_info(pid, status, &lwpinfo);
|
||||
|
||||
if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
|
||||
perror("PT_FOLLOW_FORK");
|
||||
ptrace(PT_KILL, pid, NULL, 0);
|
||||
return (2);
|
||||
}
|
||||
|
||||
while ((pid1 = trace(pid)) >= 0) {
|
||||
if (pid1 != 0) {
|
||||
printf(TRACE "attached to pid %d\n", pid1);
|
||||
#if 0
|
||||
kill(pid1, SIGCONT);
|
||||
#endif
|
||||
if (waitpid(pid1, &status, 0) == -1) {
|
||||
perror("waitpid");
|
||||
return (-1);
|
||||
}
|
||||
printf(TRACE "nested loop, pid %d status %s\n",
|
||||
pid1, decode_wait_status(status));
|
||||
assert(WIFSTOPPED(status));
|
||||
assert(WSTOPSIG(status) == SIGSTOP);
|
||||
if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
|
||||
sizeof(lwpinfo)) < 0) {
|
||||
perror("PT_LWPINFO");
|
||||
ptrace(PT_KILL, pid1, NULL, 0);
|
||||
return (-1);
|
||||
}
|
||||
wait_info(pid1, status, &lwpinfo);
|
||||
|
||||
while (trace(pid1) >= 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
|
||||
}
|
||||
return (0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user