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