Annotation of src/usr.bin/pmdb/break.c, Revision 1.1
1.1 ! art 1: /* $PMDB: break.c,v 1.7 2002/03/12 11:28:28 art Exp $ */
! 2: /*
! 3: * Copyright (c) 2002 Artur Grabowski <art@openbsd.org>
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: *
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. The name of the author may not be used to endorse or promote products
! 13: * derived from this software without specific prior written permission.
! 14: *
! 15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 25: */
! 26:
! 27: #include <sys/types.h>
! 28: #include <sys/ptrace.h>
! 29: #include <sys/wait.h>
! 30:
! 31: #include <stdlib.h>
! 32: #include <stdio.h>
! 33: #include <unistd.h>
! 34: #include <signal.h>
! 35: #include <err.h>
! 36: #include <errno.h>
! 37: #include <string.h>
! 38:
! 39: #include "pmdb.h"
! 40: #include "symbol.h"
! 41: #include "pmdb_machdep.h"
! 42: #include "break.h"
! 43:
! 44: struct callback {
! 45: TAILQ_ENTRY(callback) cb_list;
! 46: int (*cb_fun)(struct pstate *, void *);
! 47: void *cb_arg;
! 48: };
! 49:
! 50: struct breakpoint {
! 51: TAILQ_ENTRY(breakpoint) bkpt_list;
! 52: TAILQ_HEAD(,callback) bkpt_cbs; /* list of all callbacks */
! 53: char bkpt_old[BREAKPOINT_LEN]; /* old contents at bkpt */
! 54: reg bkpt_pc;
! 55: };
! 56:
! 57: static char bkpt_insn[BREAKPOINT_LEN] = BREAKPOINT;
! 58:
! 59: /*
! 60: * Find a breakpoint at this address.
! 61: */
! 62: struct breakpoint *
! 63: bkpt_find_at_pc(struct pstate *ps, reg pc)
! 64: {
! 65: struct breakpoint *bkpt;
! 66:
! 67: TAILQ_FOREACH(bkpt, &ps->ps_bkpts, bkpt_list)
! 68: if (bkpt->bkpt_pc == pc)
! 69: break;
! 70:
! 71: return (bkpt);
! 72: }
! 73:
! 74: /*
! 75: * Enable this breakpoint.
! 76: */
! 77: static int
! 78: bkpt_enable(struct pstate *ps, struct breakpoint *bkpt)
! 79: {
! 80: reg pc = bkpt->bkpt_pc;
! 81:
! 82: if (read_from_pid(ps->ps_pid, pc, &bkpt->bkpt_old, BREAKPOINT_LEN)) {
! 83: warn("Can't read process contents at 0x%lx", pc);
! 84: return (-1);
! 85: }
! 86: if (write_to_pid(ps->ps_pid, pc, &bkpt_insn, BREAKPOINT_LEN)) {
! 87: warn("Can't write breakpoint at 0x%lx, attempting backout.", pc);
! 88: if (write_to_pid(ps->ps_pid, pc, &bkpt->bkpt_old,
! 89: BREAKPOINT_LEN))
! 90: warn("Backout failed, process unstable");
! 91: return (-1);
! 92: }
! 93: return (0);
! 94: }
! 95:
! 96: /*
! 97: * Create a new breakpoint and enable it.
! 98: */
! 99: int
! 100: bkpt_add_cb(struct pstate *ps, reg pc, int (*fun)(struct pstate *, void *),
! 101: void *arg)
! 102: {
! 103: struct breakpoint *bkpt;
! 104: struct callback *cb;
! 105:
! 106: if ((bkpt = bkpt_find_at_pc(ps, pc)) == NULL) {
! 107: bkpt = emalloc(sizeof(*bkpt));
! 108: TAILQ_INIT(&bkpt->bkpt_cbs);
! 109: TAILQ_INSERT_TAIL(&ps->ps_bkpts, bkpt, bkpt_list);
! 110: bkpt->bkpt_pc = pc;
! 111: if (bkpt_enable(ps, bkpt)) {
! 112: free(bkpt);
! 113: return (-1);
! 114: }
! 115: }
! 116:
! 117: cb = emalloc(sizeof(*cb));
! 118: cb->cb_fun = fun;
! 119: cb->cb_arg = arg;
! 120: TAILQ_INSERT_TAIL(&bkpt->bkpt_cbs, cb, cb_list);
! 121:
! 122: return (0);
! 123: }
! 124:
! 125: /*
! 126: * Disable and delete a breakpoint.
! 127: */
! 128: void
! 129: bkpt_delete(struct pstate *ps, struct breakpoint *bkpt)
! 130: {
! 131: TAILQ_REMOVE(&ps->ps_bkpts, bkpt, bkpt_list);
! 132:
! 133: if (write_to_pid(ps->ps_pid, bkpt->bkpt_pc, &bkpt->bkpt_old,
! 134: BREAKPOINT_LEN))
! 135: warn("Breakpoint removal failed, process unstable");
! 136:
! 137: free(bkpt);
! 138: }
! 139:
! 140: /*
! 141: * Normal standard breakpoint. Keep it.
! 142: */
! 143: static int
! 144: bkpt_normal(struct pstate *ps, void *arg)
! 145: {
! 146: return (BKPT_KEEP_STOP);
! 147: }
! 148:
! 149: /*
! 150: * Single-step callback for "stepping over" a breakpoint (we restore the
! 151: * breakpoint instruction to what it was, single-step over it and then
! 152: * call this function).
! 153: */
! 154: static int
! 155: sstep_bkpt_readd(struct pstate *ps, void *arg)
! 156: {
! 157: reg pc = (reg)arg;
! 158:
! 159: bkpt_add_cb(ps, pc, bkpt_normal, NULL);
! 160:
! 161: return (0); /* let the process continue */
! 162: }
! 163:
! 164: /*
! 165: * Return 0 for stop, 1 for silent continue.
! 166: */
! 167: int
! 168: bkpt_check(struct pstate *ps)
! 169: {
! 170: struct breakpoint *bkpt;
! 171: struct callback *cb;
! 172: TAILQ_HEAD(,callback) sstep_cbs;
! 173: reg *rg, pc;
! 174: int ret;
! 175: int didsome = 0;
! 176: int stop = 0;
! 177:
! 178: /* Requeue all single-step callbacks because bkpts can add ssteps. */
! 179: TAILQ_INIT(&sstep_cbs);
! 180: while ((cb = TAILQ_FIRST(&ps->ps_sstep_cbs)) != NULL) {
! 181: TAILQ_REMOVE(&ps->ps_sstep_cbs, cb, cb_list);
! 182: TAILQ_INSERT_TAIL(&sstep_cbs, cb, cb_list);
! 183: }
! 184:
! 185: /*
! 186: * The default is to stop. Unless we do some processing and none
! 187: * of the callbacks require a stop.
! 188: */
! 189: rg = alloca(sizeof(*rg) * md_def.nregs);
! 190: if (md_getregs(ps, rg))
! 191: err(1, "bkpt_check: Can't get registers.");
! 192:
! 193: pc = rg[md_def.pcoff];
! 194: pc -= BREAKPOINT_DECR_PC;
! 195:
! 196: bkpt = bkpt_find_at_pc(ps, pc);
! 197: if (bkpt == NULL)
! 198: goto sstep;
! 199:
! 200: ps->ps_npc = pc;
! 201:
! 202: while ((cb = TAILQ_FIRST(&bkpt->bkpt_cbs)) != NULL) {
! 203: didsome = 1;
! 204: TAILQ_REMOVE(&bkpt->bkpt_cbs, cb, cb_list);
! 205: ret = (*cb->cb_fun)(ps, cb->cb_arg);
! 206: free(cb);
! 207: switch (ret) {
! 208: case BKPT_DEL_STOP:
! 209: stop = 1;
! 210: case BKPT_DEL_CONT:
! 211: break;
! 212: case BKPT_KEEP_STOP:
! 213: stop = 1;
! 214: case BKPT_KEEP_CONT:
! 215: sstep_set(ps, sstep_bkpt_readd, (void *)bkpt->bkpt_pc);
! 216: break;
! 217: default:
! 218: errx(1, "unkonwn bkpt_fun return, internal error");
! 219: }
! 220: }
! 221:
! 222: bkpt_delete(ps, bkpt);
! 223:
! 224: sstep:
! 225:
! 226: while ((cb = TAILQ_FIRST(&sstep_cbs)) != NULL) {
! 227: didsome = 1;
! 228: TAILQ_REMOVE(&sstep_cbs, cb, cb_list);
! 229: stop |= (*cb->cb_fun)(ps, cb->cb_arg);
! 230: free(cb);
! 231: }
! 232: ps->ps_flags &= ~PSF_STEP;
! 233:
! 234: return (didsome && !stop);
! 235: }
! 236:
! 237: int
! 238: cmd_bkpt_add(int argc, char **argv, void *arg)
! 239: {
! 240: struct pstate *ps = arg;
! 241: char *ep, *bkpt_name;
! 242: reg pc;
! 243:
! 244: if (ps->ps_state != STOPPED && ps->ps_state != LOADED) {
! 245: fprintf(stderr, "Process not loaded and stopped %d\n",
! 246: ps->ps_state);
! 247: return (0);
! 248: }
! 249:
! 250: bkpt_name = argv[1];
! 251: pc = strtol(bkpt_name, &ep, 0);
! 252: if (bkpt_name[0] == '\0' || *ep != '\0' || pc < 1) {
! 253: if (sym_lookup(ps, bkpt_name, &pc)) {
! 254: warnx("%s is not a valid pc", bkpt_name);
! 255: return (0);
! 256: }
! 257: }
! 258:
! 259: if (bkpt_add_cb(ps, pc, bkpt_normal, 0))
! 260: warn("Can't set break point");
! 261:
! 262: return (0);
! 263: }
! 264:
! 265: static int
! 266: sstep_normal(struct pstate *ps, void *arg)
! 267: {
! 268: return (1); /* stop the command line. */
! 269: }
! 270:
! 271: int
! 272: cmd_sstep(int argc, char **argv, void *arg)
! 273: {
! 274: struct pstate *ps = arg;
! 275:
! 276: if (ps->ps_state != STOPPED) {
! 277: fprintf(stderr, "Process not loaded and stopped %d\n",
! 278: ps->ps_state);
! 279: return 0;
! 280: }
! 281:
! 282: if (sstep_set(ps, sstep_normal, NULL))
! 283: warn("Can't set single step");
! 284:
! 285: return (cmd_process_cont(argc, argv, arg));
! 286: }
! 287:
! 288: int
! 289: sstep_set(struct pstate *ps, int (*fun)(struct pstate *, void *), void *arg)
! 290: {
! 291: struct callback *cb;
! 292:
! 293: cb = emalloc(sizeof(*cb));
! 294: cb->cb_fun = fun;
! 295: cb->cb_arg = arg;
! 296: TAILQ_INSERT_TAIL(&ps->ps_sstep_cbs, cb, cb_list);
! 297:
! 298: ps->ps_flags |= PSF_STEP;
! 299:
! 300: return (0);
! 301: }