📄 pcontrol.c
字号:
cwp->pr_vaddr = wp->pr_vaddr; cwp->pr_size = wp->pr_size; cwp->pr_wflags = wp->pr_wflags; if (write(P->ctlfd, ctl, sizeof (ctl)) != sizeof (ctl)) return (-1); return (0);}/* * Remove the watchpoint described by wp. */intPdelwapt(struct ps_prochandle *P, const prwatch_t *wp){ long ctl[1 + sizeof (prwatch_t) / sizeof (long)]; prwatch_t *cwp = (prwatch_t *)&ctl[1]; if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) { errno = ENOENT; return (-1); } ctl[0] = PCWATCH; cwp->pr_vaddr = wp->pr_vaddr; cwp->pr_size = wp->pr_size; cwp->pr_wflags = 0; if (write(P->ctlfd, ctl, sizeof (ctl)) != sizeof (ctl)) return (-1); return (0);}/* * Common code for Pxecwapt() and Lxecwapt(). Develop the array of requests * that will do the job, then write them to the specified control file * descriptor. Return the non-zero errno if the write fails. */static intexecute_wapt( int ctlfd, /* process or LWP control file descriptor */ const fltset_t *faultset, /* current set of traced faults */ const sigset_t *sigmask, /* current signal mask */ const prwatch_t *wp) /* watchpoint descriptor */{ long ctl[ 1 + sizeof (sigset_t) / sizeof (long) + /* PCSHOLD */ 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ 1 + sizeof (prwatch_t) / sizeof (long) + /* PCWATCH */ 2 + /* PCRUN */ 1 + /* PCWSTOP */ 1 + /* PCCFAULT */ 1 + sizeof (prwatch_t) / sizeof (long) + /* PCWATCH */ 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ 1 + sizeof (sigset_t) / sizeof (long)]; /* PCSHOLD */ long *ctlp = ctl; int error = 0; sigset_t unblock; sigset_t *holdp; fltset_t *faultp; prwatch_t *prw; ssize_t ssize; size_t size; (void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock); /* * Hold all posted signals in the victim process prior to stepping. */ *ctlp++ = PCSHOLD; holdp = (sigset_t *)ctlp; prfillset(holdp); prdelset(holdp, SIGKILL); prdelset(holdp, SIGSTOP); ctlp += sizeof (sigset_t) / sizeof (long); /* * Force tracing of FLTTRACE since we need to single step. */ if (!(prismember(faultset, FLTTRACE))) { *ctlp++ = PCSFAULT; faultp = (fltset_t *)ctlp; *faultp = *faultset; praddset(faultp, FLTTRACE); ctlp += sizeof (fltset_t) / sizeof (long); } /* * Clear only the current watchpoint by setting pr_wflags to zero. */ *ctlp++ = PCWATCH; prw = (prwatch_t *)ctlp; prw->pr_vaddr = wp->pr_vaddr; prw->pr_size = wp->pr_size; prw->pr_wflags = 0; ctlp += sizeof (prwatch_t) / sizeof (long); /* * Clear the current signal and fault; set running with single-step. * Then wait for the victim to stop and cancel the FLTTRACE. */ *ctlp++ = PCRUN; *ctlp++ = PRCSIG | PRCFAULT | PRSTEP; *ctlp++ = PCWSTOP; *ctlp++ = PCCFAULT; /* * Restore the current watchpoint. */ *ctlp++ = PCWATCH; (void) memcpy(ctlp, wp, sizeof (prwatch_t)); ctlp += sizeof (prwatch_t) / sizeof (long); /* * Restore fault tracing set if we modified it. */ if (!(prismember(faultset, FLTTRACE))) { *ctlp++ = PCSFAULT; *(fltset_t *)ctlp = *faultset; ctlp += sizeof (fltset_t) / sizeof (long); } /* * Restore the hold mask to the current hold mask (i.e. the one * before we executed any of the previous operations). */ *ctlp++ = PCSHOLD; *(sigset_t *)ctlp = *sigmask; ctlp += sizeof (sigset_t) / sizeof (long); size = (char *)ctlp - (char *)ctl; if ((ssize = write(ctlfd, ctl, size)) != size) error = (ssize == -1)? errno : EINTR; (void) sigprocmask(SIG_SETMASK, &unblock, NULL); return (error);}/* * Step over a watchpoint, i.e., execute the instruction that was stopped by * the watchpoint, and then leave the LWP stopped at the next instruction. */intPxecwapt(struct ps_prochandle *P, const prwatch_t *wp){ int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; int rv, error; if (P->state != PS_STOP) { errno = EBUSY; return (-1); } Psync(P); error = execute_wapt(ctlfd, &P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold, wp); rv = Pstopstatus(P, PCNULL, 0); if (error != 0) { if (P->status.pr_lwp.pr_why == PR_JOBCONTROL && error == EBUSY) { /* jobcontrol stop -- back off */ P->state = PS_RUN; return (0); } if (error == ENOENT) return (0); errno = error; return (-1); } return (rv);}intPsetflags(struct ps_prochandle *P, long flags){ int rc; long ctl[2]; ctl[0] = PCSET; ctl[1] = flags; if (write(P->ctlfd, ctl, 2*sizeof (long)) != 2*sizeof (long)) { rc = -1; } else { P->status.pr_flags |= flags; P->status.pr_lwp.pr_flags |= flags; rc = 0; } return (rc);}intPunsetflags(struct ps_prochandle *P, long flags){ int rc; long ctl[2]; ctl[0] = PCUNSET; ctl[1] = flags; if (write(P->ctlfd, ctl, 2*sizeof (long)) != 2*sizeof (long)) { rc = -1; } else { P->status.pr_flags &= ~flags; P->status.pr_lwp.pr_flags &= ~flags; rc = 0; } return (rc);}/* * Common function to allow clients to manipulate the action to be taken * on receipt of a signal, receipt of machine fault, entry to a system call, * or exit from a system call. We make use of our private prset_* functions * in order to make this code be common. The 'which' parameter identifies * the code for the event of interest (0 means change the entire set), and * the 'stop' parameter is a boolean indicating whether the process should * stop when the event of interest occurs. The previous value is returned * to the caller; -1 is returned if an error occurred. */static intPsetaction(struct ps_prochandle *P, void *sp, size_t size, uint_t flag, int max, int which, int stop){ int oldval; if (which < 0 || which > max) { errno = EINVAL; return (-1); } if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) { errno = ENOENT; return (-1); } oldval = prset_ismember(sp, size, which) ? TRUE : FALSE; if (stop) { if (which == 0) { prset_fill(sp, size); P->flags |= flag; } else if (!oldval) { prset_add(sp, size, which); P->flags |= flag; } } else { if (which == 0) { prset_empty(sp, size); P->flags |= flag; } else if (oldval) { prset_del(sp, size, which); P->flags |= flag; } } if (P->state == PS_RUN) Psync(P); return (oldval);}/* * Set action on specified signal. */intPsignal(struct ps_prochandle *P, int which, int stop){ int oldval; if (which == SIGKILL && stop != 0) { errno = EINVAL; return (-1); } oldval = Psetaction(P, &P->status.pr_sigtrace, sizeof (sigset_t), SETSIG, PRMAXSIG, which, stop); if (oldval != -1 && which == 0 && stop != 0) prdelset(&P->status.pr_sigtrace, SIGKILL); return (oldval);}/* * Set all signal tracing flags. */voidPsetsignal(struct ps_prochandle *P, const sigset_t *set){ if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) return; P->status.pr_sigtrace = *set; P->flags |= SETSIG; if (P->state == PS_RUN) Psync(P);}/* * Set action on specified fault. */intPfault(struct ps_prochandle *P, int which, int stop){ return (Psetaction(P, &P->status.pr_flttrace, sizeof (fltset_t), SETFAULT, PRMAXFAULT, which, stop));}/* * Set all machine fault tracing flags. */voidPsetfault(struct ps_prochandle *P, const fltset_t *set){ if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) return; P->status.pr_flttrace = *set; P->flags |= SETFAULT; if (P->state == PS_RUN) Psync(P);}/* * Set action on specified system call entry. */intPsysentry(struct ps_prochandle *P, int which, int stop){ return (Psetaction(P, &P->status.pr_sysentry, sizeof (sysset_t), SETENTRY, PRMAXSYS, which, stop));}/* * Set all system call entry tracing flags. */voidPsetsysentry(struct ps_prochandle *P, const sysset_t *set){ if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) return; P->status.pr_sysentry = *set; P->flags |= SETENTRY; if (P->state == PS_RUN) Psync(P);}/* * Set action on specified system call exit. */intPsysexit(struct ps_prochandle *P, int which, int stop){ return (Psetaction(P, &P->status.pr_sysexit, sizeof (sysset_t), SETEXIT, PRMAXSYS, which, stop));}/* * Set all system call exit tracing flags. */voidPsetsysexit(struct ps_prochandle *P, const sysset_t *set){ if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) return; P->status.pr_sysexit = *set; P->flags |= SETEXIT; if (P->state == PS_RUN) Psync(P);}/* * Utility function to read the contents of a file that contains a * prheader_t at the start (/proc/pid/lstatus or /proc/pid/lpsinfo). * Returns a malloc()d buffer or NULL on failure. */static prheader_t *read_lfile(struct ps_prochandle *P, const char *lname){ prheader_t *Lhp; char lpath[64]; struct stat64 statb; int fd; size_t size; ssize_t rval; (void) snprintf(lpath, sizeof (lpath), "/proc/%d/%s", (int)P->status.pr_pid, lname); if ((fd = open(lpath, O_RDONLY)) < 0 || fstat64(fd, &statb) != 0) { if (fd >= 0) (void) close(fd); return (NULL); } /* * 'size' is just the initial guess at the buffer size. * It will have to grow if the number of lwps increases * while we are looking at the process. * 'size' must be larger than the actual file size. */ size = statb.st_size + 32; for (;;) { if ((Lhp = malloc(size)) == NULL) break; if ((rval = pread(fd, Lhp, size, 0)) < 0 || rval <= sizeof (prheader_t)) { free(Lhp); Lhp = NULL; break; } if (rval < size) break; /* need a bigger buffer */ free(Lhp); size *= 2; } (void) close(fd); return (Lhp);}/* * LWP iteration interface. */intPlwp_iter(struct ps_prochandle *P, proc_lwp_f *func, void *cd){ prheader_t *Lhp; lwpstatus_t *Lsp; long nlwp; int rv; switch (P->state) { case PS_RUN: (void) Pstopstatus(P, PCNULL, 0); break; case PS_STOP: Psync(P); break; case PS_IDLE: errno = ENODATA; return (-1); } /* * For either live processes or cores, the single LWP case is easy: * the pstatus_t contains the lwpstatus_t for the only LWP. */ if (P->status.pr_nlwp <= 1) return (func(cd, &P->status.pr_lwp)); /* * For the core file multi-LWP case, we just iterate through the * list of LWP structs we read in from the core file. */ if (P->state == PS_DEAD) { lwp_info_t *lwp = list_prev(&P->core->core_lwp_head); uint_t i; for (i = 0; i < P->core->core_nlwp; i++, lwp = list_prev(lwp)) { if (lwp->lwp_psinfo.pr_sname != 'Z' && (rv = func(cd, &lwp->lwp_status)) != 0) break; } return (rv); } /* * For the live process multi-LWP case, we have to work a little * harder: the /proc/pid/lstatus file has the array of LWP structs. */ if ((Lhp = read_lfile(P, "lstatus")) == NULL) return (-1); for (nlwp = Lhp->pr_nent, Lsp = (lwpstatus_t *)(uintptr_t)(Lhp + 1); nlwp > 0; nlwp--, Lsp = (lwpstatus_t *)((uintptr_t)Lsp + Lhp->pr_entsize)) { if ((rv = func(cd, Lsp)) != 0) break; } free(Lhp); return (rv);}/* * Extended LWP iteration interface. * Iterate over all LWPs, active and zombie. */intPlwp_iter_all(struct ps_prochandle *P, proc_lwp_all_f *func, void *cd){ prheader_t *Lhp = NULL; lwpstatus_t *Lsp; lwpstatus_t *sp; prheader_t *Lphp = NULL; lwpsinfo_t *Lpsp; long nstat; long ninfo; int rv;retry: if (Lhp != NULL) free(Lhp); if (Lphp != NULL) free(Lphp); if (P->state == PS_RUN) (void) Pstopstatus(P, PCNULL, 0); (void) Ppsinfo(P); if (P->state == PS_STOP) Psync(P); /* * For either live processes or cores, the single LWP case is easy: * the pstatus_t contains the lwpstatus_t for the only LWP and * the psinfo_t contains the lwpsinfo_t for the only LWP. */ if (P->status.pr_nlwp + P->status.pr_nzomb <= 1) return (func(cd, &P->status.pr_lwp, &P->psinfo.pr_lwp)); /* * For the core file multi-LWP case, we just iterate through the * list of LWP structs we read in from the core file. */ if (P->state == PS_DEAD) { lwp_info_t *lwp = list_prev(&P->core->core_lwp_head); uint_t i; for (i = 0; i < P->core->core_nlwp; i++, lwp = list_prev(lwp)) { sp = (lwp->lwp_psinfo.pr_sname == 'Z')? NULL : &lwp->lwp_status; if ((rv = func(cd, sp, &lwp->lwp_psinfo)) != 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -