Annotation of src/usr.bin/pmdb/pmdb.c, Revision 1.8
1.8 ! todd 1: /* $OpenBSD: pmdb.c,v 1.7 2002/06/05 18:02:27 fgsch Exp $ */
1.1 art 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>
1.7 fgsch 30: #include <sys/endian.h>
1.1 art 31:
32: #include <stdlib.h>
33: #include <stdio.h>
34: #include <unistd.h>
35: #include <signal.h>
36: #include <err.h>
37: #include <errno.h>
38: #include <string.h>
39:
40: #include "pmdb.h"
41: #include "symbol.h"
42: #include "clit.h"
43: #include "break.h"
1.7 fgsch 44: #include "core.h"
1.1 art 45:
46: static int cmd_show_registers(int, char **, void *);
47: static int cmd_show_backtrace(int, char **, void *);
48: static int cmd_quit(int, char **, void *);
49:
50: struct clit cmds[] = {
51: /* debugging info commands. */
52: { "regs", "show registers", 0, 0, cmd_show_registers, (void *)-1 },
53: { "trace", "show backtrace", 0, 0, cmd_show_backtrace, (void *)-1 },
54:
55: /* Process handling commands. */
56: { "run", "run process", 0, 0, cmd_process_run, (void *)-1 },
57: { "continue", "continue process", 0, 0, cmd_process_cont, (void *)-1 },
58: { "kill", "kill process", 0, 0, cmd_process_kill, (void *)-1 },
59:
60: /* signal handling commands. */
61: { "signal", "ignore signal", 2, 2, cmd_signal_ignore, (void *)-1 },
62: { "sigstate", "show signal state", 0, 0, cmd_signal_show, (void *)-1 },
63:
64: /* breakpoints */
65: { "break", "set breakpoint", 1, 1, cmd_bkpt_add, (void *)-1 },
66: { "step", "single step one insn", 0, 0, cmd_sstep, (void *)-1 },
1.3 art 67:
68: /* symbols */
69: { "sym_load", "load symbol table", 2, 2, cmd_sym_load, (void *)-1 },
1.1 art 70:
71: /* misc commands. */
72: { "help", "print help", 0, 1, cmd_help, NULL },
73: { "quit", "quit", 0, 0, cmd_quit, (void *)-1 },
74: { "exit", "quit", 0, 0, cmd_quit, (void *)-1 },
75: };
76:
1.6 fgsch 77: #define NCMDS sizeof(cmds)/sizeof(cmds[0])
78:
1.7 fgsch 79: void
80: usage()
81: {
82: extern char *__progname;
83:
1.8 ! todd 84: fprintf(stderr, "Usage: %s [-c core] [-p pid] <program> args\n", __progname);
1.7 fgsch 85: exit(1);
86: }
87:
1.1 art 88: int
89: main(int argc, char **argv)
90: {
91: struct pstate ps;
1.7 fgsch 92: int i, c;
1.1 art 93: int status;
94: void *cm;
1.8 ! todd 95: char *pmenv, *core, *perr;
1.1 art 96: int level;
1.8 ! todd 97: pid_t pid;
1.1 art 98:
99: if (argc < 2) {
1.7 fgsch 100: usage();
1.1 art 101: }
102:
1.7 fgsch 103: core = NULL;
1.8 ! todd 104: pid = 0;
1.7 fgsch 105:
1.8 ! todd 106: while((c = getopt(argc, argv, "c:p:")) != -1) {
1.7 fgsch 107: switch(c) {
108: case 'c':
109: core = optarg;
110: break;
1.8 ! todd 111: case 'p':
! 112: pid = (pid_t) strtol(optarg, &perr, 10);
! 113: if(*perr != '\0')
! 114: errx(1, "invalid PID");
! 115: break;
1.7 fgsch 116: case '?':
117: default:
118: usage();
119: /* NOTREACHED */
120: }
121: }
122: argc -= optind;
123: argv += optind;
124:
1.1 art 125: if ((pmenv = getenv("IN_PMDB")) != NULL) {
126: level = atoi(pmenv);
127: level++;
128: } else
129: level = 0;
130:
131: if (level > 0)
132: asprintf(&prompt_add, "(%d)", level);
133: asprintf(&pmenv, "%d", level);
134: setenv("IN_PMDB", pmenv, 1);
135:
1.8 ! todd 136: ps.ps_pid = pid;
1.1 art 137: ps.ps_state = NONE;
1.7 fgsch 138: ps.ps_argc = argc;
139: ps.ps_argv = argv;
1.1 art 140: ps.ps_flags = 0;
141: ps.ps_signum = 0;
142: ps.ps_npc = 1;
143: TAILQ_INIT(&ps.ps_bkpts);
144: TAILQ_INIT(&ps.ps_sstep_cbs);
145:
146: signal(SIGINT, SIG_IGN);
147:
1.6 fgsch 148: for (i = 0; i < NCMDS; i++)
1.1 art 149: if (cmds[i].arg == (void *)-1)
150: cmds[i].arg = &ps;
151:
152: md_def_init();
153: init_sigstate(&ps);
154:
155: process_load(&ps);
156:
1.7 fgsch 157: if (core != NULL)
158: read_core(core, &ps);
159:
1.6 fgsch 160: cm = cmdinit(cmds, NCMDS);
1.1 art 161: while (ps.ps_state != TERMINATED) {
162: int signum;
163: int stopped;
164: int cont;
165:
166: if (ps.ps_state == STOPPED) {
167: sym_update(&ps);
168: }
169:
170: if (ps.ps_state != RUNNING && cmdloop(cm) == 0) {
171: cmd_quit(0, NULL, &ps);
172: }
173:
174: if (ps.ps_state == TERMINATED)
175: break;
176:
177: if (wait(&status) == 0)
178: err(1, "wait");
179: if (WIFEXITED(status)) {
180: if ((ps.ps_flags & PSF_KILL) == 0) {
181: ps.ps_state = NONE;
182: } else {
183: ps.ps_state = TERMINATED;
184: }
185: fprintf(stderr, "process exited with status %d\n",
186: WEXITSTATUS(status));
187: continue;
188: }
189: if (WIFSIGNALED(status)) {
190: signum = WTERMSIG(status);
191: stopped = 0;
192: } else {
193: signum = WSTOPSIG(status);
194: stopped = 1;
195: }
196: cont = 0;
197: if (stopped)
198: cont = bkpt_check(&ps);
199: process_signal(&ps, signum, stopped, cont);
200: }
201:
202: cmdend(cm);
203:
204: sym_destroy(&ps);
205:
206: return (0);
207: }
208:
209: /* XXX - move to some other file. */
210: int
211: read_from_pid(pid_t pid, off_t from, void *to, size_t size)
212: {
213: struct ptrace_io_desc piod;
214:
215: piod.piod_op = PIOD_READ_D;
216: piod.piod_offs = (void *)(long)from;
217: piod.piod_addr = to;
218: piod.piod_len = size;
219:
1.4 art 220: return (ptrace(PT_IO, pid, (caddr_t)&piod, 0));
1.1 art 221: }
222:
223:
224: int
225: write_to_pid(pid_t pid, off_t to, void *from, size_t size)
226: {
227: struct ptrace_io_desc piod;
228:
229: piod.piod_op = PIOD_WRITE_D;
230: piod.piod_offs = (void *)(long)to;
231: piod.piod_addr = from;
232: piod.piod_len = size;
233:
1.4 art 234: return (ptrace(PT_IO, pid, (caddr_t)&piod, 0));
1.1 art 235: }
236:
237: static int
238: cmd_show_registers(int argc, char **argv, void *arg)
239: {
240: struct pstate *ps = arg;
241: char buf[256];
242: int i;
243: reg *rg;
244:
245: if (ps->ps_state != STOPPED) {
1.7 fgsch 246: if (ps->ps_flags & PSF_CORE) {
247: /* dump registers from core */
248: core_printregs(ps->ps_core);
249: return (0);
250: }
1.1 art 251: fprintf(stderr, "process not stopped\n");
1.7 fgsch 252: return (0);
1.1 art 253: }
254:
255: rg = alloca(sizeof(*rg) * md_def.nregs);
256:
257: if (md_getregs(ps, rg))
258: err(1, "can't get registers");
259: for (i = 0; i < md_def.nregs; i++)
260: printf("%s:\t0x%.*lx\t%s\n", md_def.md_reg_names[i],
261: (int)(sizeof(reg) * 2), (long)rg[i],
262: sym_print(ps, rg[i], buf, sizeof(buf)));
1.7 fgsch 263: return (0);
1.1 art 264: }
265:
266: static int
267: cmd_show_backtrace(int argc, char **argv, void *arg)
268: {
269: struct pstate *ps = arg;
270: int i;
271:
272: if (ps->ps_state != STOPPED) {
273: fprintf(stderr, "process not stopped\n");
1.7 fgsch 274: return (0);
1.1 art 275: }
276:
277: /* no more than 100 frames */
278: for (i = 0; i < 100; i++) {
279: struct md_frame mfr;
280: char namebuf[1024], *name;
281: reg offs;
282: int j;
283:
284: mfr.nargs = -1;
285:
286: if (md_getframe(ps, i, &mfr))
287: break;
288:
289: name = sym_name_and_offset(ps, mfr.pc, namebuf,
290: sizeof(namebuf), &offs);
291: if (name == NULL) {
292: snprintf(namebuf, sizeof(namebuf), "0x%lx", mfr.pc);
293: name = namebuf;
1.5 drahn 294: offs = 0;
1.1 art 295: }
296:
297: printf("%s(", name);
298: for (j = 0; j < mfr.nargs; j++) {
299: printf("0x%lx", mfr.args[j]);
300: if (j < mfr.nargs - 1)
301: printf(", ");
302: }
1.5 drahn 303: if (offs == 0) {
1.6 fgsch 304: printf(")\n");
1.5 drahn 305: } else {
306: printf(")+0x%lx\n", offs);
307: }
1.1 art 308: }
1.7 fgsch 309:
310: return (0);
1.1 art 311: }
312:
313: static int
314: cmd_quit(int argc, char **argv, void *arg)
315: {
316: struct pstate *ps = arg;
317:
1.8 ! todd 318: if ((ps->ps_flags & PSF_ATCH)) {
! 319: if ((ps->ps_flags & PSF_ATCH) &&
! 320: ptrace(PT_DETACH, ps->ps_pid, NULL, 0) < 0)
! 321: err(1, "ptrace(PT_DETACH)");
! 322: } else {
! 323: ps->ps_flags |= PSF_KILL;
1.1 art 324:
1.8 ! todd 325: if (process_kill(ps))
! 326: return 1;
! 327: }
1.1 art 328:
329: ps->ps_state = TERMINATED;
330: return 1;
331: }
332:
333: /*
334: * Perform command completion.
335: * Pretty simple. if there are spaces in "buf", the last string is a symbol
336: * otherwise it's a command.
337: */
338: int
339: cmd_complt(char *buf, size_t buflen)
340: {
341: struct clit *match;
342: char *start;
343: int command;
344: int i, j, len;
345: int onlymatch;
346:
347: command = (strchr(buf, ' ') == NULL);
348:
349: if (!command) {
350: /* XXX - can't handle symbols yet. */
351: return -1;
352: }
353:
354: start = buf;
355: len = strlen(buf);
356:
357: match = NULL;
358: for (i = 0; i < sizeof(cmds) / sizeof(cmds[i]); i++) {
359: if (strncmp(start, cmds[i].cmd, len) == 0) {
360: struct clit *cmdp;
361:
362: cmdp = &cmds[i];
363: if (match == NULL) {
364: onlymatch = 1;
365: match = cmdp;
366: strlcpy(buf, match->cmd, buflen);
367: continue;
368: }
369: onlymatch = 0;
370: for (j = len; j < buflen; j++) {
371: if (buf[j] != cmdp->cmd[j]) {
372: buf[j] = '\0';
373: break;
374: }
375: if (cmdp->cmd[j] == '\0')
376: break;
377: }
378: }
379: }
380:
381: /*
382: * Be nice. If there could be arguments for this command and it's
383: * the only match append a space.
384: */
385: if (match && onlymatch /*&& match->maxargc > 0*/)
386: strlcat(buf, " ", buflen);
387:
388: return (match && onlymatch) ? 0 : -1;
389: }
390:
391: /*
1.7 fgsch 392: * The "standard" wrapper
1.1 art 393: */
394: void *
395: emalloc(size_t sz)
396: {
397: void *ret;
398: if ((ret = malloc(sz)) == NULL)
399: err(1, "malloc");
400: return (ret);
401: }