Sorry for delay, I was offline 3 days. On 09/25, Oleg Nesterov wrote: > > IOW, every time we need to check whether we should generate/report > the artificial SYSCALL_EXIT stop event, we must also check whether > we need send_sigtrap(). > > With this patch we pass the new step-from-clone.c test. > > Further changes: > > - I guess, we should also send SIGTRAP when SINGLESTEP resumes > the tracee from SYSCALL_EXIT stop. This is trivial, but I need > to write the test-case first.
Yes, with this change the kernel passes all tests plus the test-case below. I wrote it because I lost the hope to understand the required behaviour ;) I'll send the patch tomorrow (hopefully I'll also send ->siginfo changes). The last commit in your utrace-ptrace branch is implement UTRACE_SIGNAL_HANDLER stepping 4492770dc8d2312da9518e8b85fb0e49dc3da510 OK, I'll drop the changes I did since then, and redo/resend. Also. When I did the testing, I noticed the test-case below runs much, much slower compared to vanilla kernel. It took me a lot of time to understand that the reason is simple, do_ptrace_notify_stop() needs the trivial backport of 53da1d9456fe7f87a920a78fdbdcf1225d197cb7. I still wonder though, why this makes such a noticeable difference. The slowdown mostly comes from do { resume(pid, PTRACE_SINGLESTEP, 0x57F); } while (regs.rip != rip - 2); loop which does only 70 iterations. But I am not going to dig further. Note also the couple of "assert(regs.rax == -ENOSYS)" which were commented out, here we differ from vanilla kernel and this can't be "fixed". But, as you pointed out previously, we do not need to be bug-compatible here. Oleg. #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <errno.h> #include <sys/wait.h> #include <sys/ptrace.h> #include <assert.h> #include <sys/user.h> #include <sys/debugreg.h> #include <sys/syscall.h> #define WEVENT(s) ((s & 0xFF0000) >> 16) static int verbose; #define d_printf(fmt, ...) do { if (verbose) printf(fmt, ##__VA_ARGS__); } while (0) static struct user_regs_struct regs; static void resume(int pid, int req, int ck_stat) { int stat; assert(0 == ptrace(req, pid, 0, 0)); assert(waitpid(pid, &stat, __WALL) == pid); //d_printf("===> %06X\n %06X\n", ck_stat, stat); assert(stat == ck_stat); assert(0 == ptrace(PTRACE_GETREGS, pid, NULL, ®s)); } int main(int argc, const char *argv[]) { int pid, child, stat; long rip, nxt_rip; if (getpid() == __NR_getppid) { printf("sorry, restart\n"); return 0; } verbose = argc > 1; pid = fork(); if (!pid) { assert(0 == ptrace(PTRACE_TRACEME, 0,0,0)); kill(getpid(), SIGSTOP); // 1: SYSCALL + SYSCALL + STEP getppid(); // 2: SYSCALL + STEP getppid(); // 3: STEP getppid(); // 4: SYSCALL + STEP if (!fork()) exit(73); // 5: STEPs only if (!fork()) exit(73); assert(0); } assert(wait(&stat) == pid); assert(WIFSTOPPED(stat) && WSTOPSIG(stat) == SIGSTOP); assert(0 == ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK)); //----------------------------------------------------------------------------- d_printf("1: syscall enter\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == -ENOSYS); rip = regs.rip; d_printf("1: syscall leave\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("1: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("1: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.rip != rip); //-------------------------------------------------------------------------------- d_printf("2: stop before syscall insn\n"); do { resume(pid, PTRACE_SINGLESTEP, 0x57F); } while (regs.rip != rip - 2); assert(regs.rax == __NR_getppid); d_printf("2: syscall enter\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == -ENOSYS); assert(regs.rip == rip); d_printf("2: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("2: singlestep\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.rip != rip); //-------------------------------------------------------------------------------- d_printf("3: stop before syscall insn\n"); do { resume(pid, PTRACE_SINGLESTEP, 0x57F); } while (regs.rip != rip - 2); assert(regs.rax == __NR_getppid); d_printf("3: step over syscall\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.orig_rax == __NR_getppid); assert(regs.rax == getpid()); assert(regs.rip == rip); d_printf("3: step over syscall\n"); resume(pid, PTRACE_SINGLESTEP, 0x57F); assert(regs.rip != rip); //---------------------------------------------------------------------------------- d_printf("4: syscall enter\n"); resume(pid, PTRACE_SYSCALL, 0x857F); assert(regs.orig_rax == __NR_clone); assert(regs.rax == -ENOSYS); rip = regs.rip; child = waitpid (-1, &stat, WNOHANG); assert(child == 0); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F | (PTRACE_EVENT_FORK << 16)); assert(regs.orig_rax == __NR_clone); //assert(regs.rax == -ENOSYS); assert(regs.rip == rip); child = waitpid (-1, &stat, __WALL); assert(child > 0 && child != pid); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.orig_rax == __NR_clone); assert(regs.rax == child); assert(regs.rip == rip); d_printf("4: provoke SIGCHLD\n"); kill(child, SIGKILL); assert(child == waitpid (-1, &stat, __WALL)); assert(stat = SIGKILL); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x7F | (SIGCHLD << 8)); assert(regs.rip == rip); d_printf("4: SIGTERM + STEP\n"); kill(pid, SIGTERM); resume(pid, PTRACE_SINGLESTEP, 0x7F | (SIGTERM << 8)); assert(regs.rip == rip); d_printf("4: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.rip != rip); nxt_rip = regs.rip; //--------------------------------------------------------------------------- d_printf("5: stop before syscall insn\n"); do { resume(pid, PTRACE_SINGLESTEP, 0x57F); } while (regs.rip != rip - 2); assert(regs.rax == __NR_clone); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F | (PTRACE_EVENT_FORK << 16)); assert(regs.orig_rax == __NR_clone); //assert(regs.rax == -ENOSYS); assert(regs.rip == rip); child = waitpid (-1, &stat, __WALL); assert(child > 0 && child != pid); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.orig_rax == __NR_clone); assert(regs.rax == child); assert(regs.rip == rip); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.rip == nxt_rip); kill(child, SIGKILL); assert(child == waitpid (-1, &stat, __WALL)); assert(stat = SIGKILL); d_printf("5: step\n"); resume(pid, PTRACE_CONT, 0x7F | (SIGCHLD << 8)); assert(regs.rip == nxt_rip); d_printf("5: step\n"); resume(pid, PTRACE_SINGLESTEP, 0x057F); assert(regs.rip != nxt_rip); //---------------------------------------------------------------------------- kill(pid, SIGKILL); return 0; }