#define _GNU_SOURCE /* See feature_test_macros(7) */ #include /* For SYS_xxx definitions */ #include #include #include #include #include #include #include #include #include #include #include /* Copied from the gdb header gdb/nat/x86-dregs.h */ /* Debug registers' indices. */ #define DR_FIRSTADDR 0 #define DR_LASTADDR 3 #define DR_NADDR 4 /* The number of debug address registers. */ #define DR_STATUS 6 /* Index of debug status register (DR6). */ #define DR_CONTROL 7 /* Index of debug control register (DR7). */ #define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */ #define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */ #define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */ /* Locally enable the break/watchpoint in the I'th debug register. */ #define X86_DR_LOCAL_ENABLE(i) (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i))) # define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define pr_perror(fmt, ...) \ fprintf(stderr, "%04d: " fmt ": %s\n", __LINE__, ##__VA_ARGS__, strerror(errno)) #define pr_err(fmt, ...) \ fprintf(stderr, "%04d: " fmt "\n", __LINE__, ##__VA_ARGS__) int ptrace_set_breakpoint(pid_t pid, void *addr) { int ret; /* Set a breakpoint */ if (ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[DR_FIRSTADDR]), addr)) { pr_perror("Unable to setup a breakpoint into %d", pid); return -1; } /* Enable the breakpoint */ if (ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[DR_CONTROL]), X86_DR_LOCAL_ENABLE(DR_FIRSTADDR))) { pr_perror("Unable to enable the breakpoint for %d", pid); return -1; } ret = ptrace(PTRACE_CONT, pid, NULL, NULL); if (ret) { pr_perror("Unable to restart the stopped tracee process %d", pid); return -1; } return 1; } static long xxx = -1; int child() { printf("child %d\n", xxx); syscall(__NR_getppid); if (xxx == 5) { pr_err("exit - %d", xxx); exit(1); } if (xxx > 0) xxx = 5; return 0; } int child2() { printf("child2 %d\n", xxx); syscall(__NR_getppid); if (xxx == 5) { pr_err("exit - %d", xxx); exit(1); } if (xxx > 0) xxx = 5; return 0; } int main(int argc, char **argv) { pid_t pid; int status, i = 0; int (*addr)(); if (argc > 1) addr = child2; else addr = child; pid = fork(); if (pid == 0) { while (1) addr(); } if (ptrace(PTRACE_ATTACH, pid, NULL, NULL)) { pr_perror("ptrace"); return 1; } if (waitpid(pid, &status, 0) == -1) { pr_perror("waitpid"); return 1; } if (ptrace_set_breakpoint(pid, addr) != 1) return 2; while (1) { if (waitpid(pid, &status, 0) != pid) { pr_perror("wait"); return 3; } printf("stop %d\n", i++); if (WIFEXITED(status)) { pr_err("exited, status=%d", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { pr_err("killed by signal %d", WTERMSIG(status)); } ptrace(PTRACE_POKEDATA, pid, &xxx, 1); printf("cont\n"); ptrace(PTRACE_CONT, pid, NULL, NULL); } return 0; }