Annotation of src/usr.bin/pmdb/pmdb.c, Revision 1.22
1.22 ! miod 1: /* $OpenBSD: pmdb.c,v 1.21 2007/08/06 19:16:05 sobrado 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>
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>
1.19 jfb 38: #include <paths.h>
1.1 art 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 *);
1.14 mickey 48: static int cmd_examine(int, char **, void *);
1.1 art 49: static int cmd_quit(int, char **, void *);
50:
51: struct clit cmds[] = {
52: /* debugging info commands. */
53: { "regs", "show registers", 0, 0, cmd_show_registers, (void *)-1 },
54: { "trace", "show backtrace", 0, 0, cmd_show_backtrace, (void *)-1 },
1.14 mickey 55: { "x", "examine memory", 1, 16, cmd_examine, (void *)-1 },
1.1 art 56:
57: /* Process handling commands. */
58: { "run", "run process", 0, 0, cmd_process_run, (void *)-1 },
59: { "continue", "continue process", 0, 0, cmd_process_cont, (void *)-1 },
60: { "kill", "kill process", 0, 0, cmd_process_kill, (void *)-1 },
1.12 art 61: { "setenv", "set env variables", 2, 2, cmd_process_setenv, (void *)-1 },
1.1 art 62:
63: /* signal handling commands. */
64: { "signal", "ignore signal", 2, 2, cmd_signal_ignore, (void *)-1 },
65: { "sigstate", "show signal state", 0, 0, cmd_signal_show, (void *)-1 },
66:
67: /* breakpoints */
68: { "break", "set breakpoint", 1, 1, cmd_bkpt_add, (void *)-1 },
69: { "step", "single step one insn", 0, 0, cmd_sstep, (void *)-1 },
1.3 art 70:
71: /* symbols */
72: { "sym_load", "load symbol table", 2, 2, cmd_sym_load, (void *)-1 },
1.1 art 73:
74: /* misc commands. */
75: { "help", "print help", 0, 1, cmd_help, NULL },
76: { "quit", "quit", 0, 0, cmd_quit, (void *)-1 },
77: { "exit", "quit", 0, 0, cmd_quit, (void *)-1 },
78: };
79:
1.6 fgsch 80: #define NCMDS sizeof(cmds)/sizeof(cmds[0])
81:
1.7 fgsch 82: void
1.17 deraadt 83: usage(void)
1.7 fgsch 84: {
85: extern char *__progname;
86:
1.21 sobrado 87: fprintf(stderr, "usage: %s [-c core] [-p pid] program ...\n",
1.9 fgsch 88: __progname);
1.7 fgsch 89: exit(1);
90: }
91:
1.1 art 92: int
93: main(int argc, char **argv)
94: {
95: struct pstate ps;
1.7 fgsch 96: int i, c;
1.1 art 97: int status;
98: void *cm;
1.19 jfb 99: char *pmenv, *core, *perr, execpath[MAXPATHLEN];
1.1 art 100: int level;
1.8 todd 101: pid_t pid;
1.1 art 102:
1.7 fgsch 103: core = NULL;
1.8 todd 104: pid = 0;
1.7 fgsch 105:
1.9 fgsch 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);
1.9 fgsch 113: if (*perr != '\0')
1.8 todd 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;
1.11 fgsch 124:
125: if (argc == 0)
126: usage();
1.7 fgsch 127:
1.1 art 128: if ((pmenv = getenv("IN_PMDB")) != NULL) {
129: level = atoi(pmenv);
130: level++;
131: } else
132: level = 0;
133:
134: if (level > 0)
135: asprintf(&prompt_add, "(%d)", level);
136: asprintf(&pmenv, "%d", level);
137: setenv("IN_PMDB", pmenv, 1);
1.15 pvalchev 138: if (pmenv)
139: free(pmenv);
1.1 art 140:
1.19 jfb 141: if (getexecpath(argv[0], execpath, sizeof(execpath)) == -1) {
142: err(1, "cannot find `%s'", argv[0]);
143: }
144: argv[0] = execpath;
145:
146: memset(&ps, 0, sizeof(ps));
147: process_setargv(&ps, argc, argv);
148:
1.8 todd 149: ps.ps_pid = pid;
1.1 art 150: ps.ps_state = NONE;
151: ps.ps_flags = 0;
152: ps.ps_signum = 0;
153: ps.ps_npc = 1;
154: TAILQ_INIT(&ps.ps_bkpts);
155: TAILQ_INIT(&ps.ps_sstep_cbs);
156:
157: signal(SIGINT, SIG_IGN);
158:
1.6 fgsch 159: for (i = 0; i < NCMDS; i++)
1.1 art 160: if (cmds[i].arg == (void *)-1)
161: cmds[i].arg = &ps;
162:
163: md_def_init();
164: init_sigstate(&ps);
165:
1.10 art 166: if ((core != NULL) && (read_core(core, &ps) < 0))
167: warnx("failed to load core file");
1.1 art 168:
1.10 art 169: if (process_load(&ps) < 0)
170: errx(1, "failed to load process");
1.7 fgsch 171:
1.6 fgsch 172: cm = cmdinit(cmds, NCMDS);
1.1 art 173: while (ps.ps_state != TERMINATED) {
174: int signum;
175: int stopped;
176: int cont;
177:
178: if (ps.ps_state == STOPPED) {
179: sym_update(&ps);
180: }
181:
182: if (ps.ps_state != RUNNING && cmdloop(cm) == 0) {
183: cmd_quit(0, NULL, &ps);
184: }
185:
186: if (ps.ps_state == TERMINATED)
187: break;
1.22 ! miod 188:
! 189: if (ps.ps_state == NONE || ps.ps_state == LOADED)
! 190: continue;
1.1 art 191:
192: if (wait(&status) == 0)
193: err(1, "wait");
194: if (WIFEXITED(status)) {
195: if ((ps.ps_flags & PSF_KILL) == 0) {
196: ps.ps_state = NONE;
197: } else {
198: ps.ps_state = TERMINATED;
199: }
200: fprintf(stderr, "process exited with status %d\n",
201: WEXITSTATUS(status));
202: continue;
203: }
204: if (WIFSIGNALED(status)) {
205: signum = WTERMSIG(status);
206: stopped = 0;
207: } else {
208: signum = WSTOPSIG(status);
209: stopped = 1;
210: }
211: cont = 0;
212: if (stopped)
213: cont = bkpt_check(&ps);
214: process_signal(&ps, signum, stopped, cont);
215: }
216:
217: cmdend(cm);
218:
219: sym_destroy(&ps);
220:
221: return (0);
222: }
223:
224:
225: static int
226: cmd_show_registers(int argc, char **argv, void *arg)
227: {
228: struct pstate *ps = arg;
229: char buf[256];
230: int i;
231: reg *rg;
232:
233: if (ps->ps_state != STOPPED) {
1.7 fgsch 234: if (ps->ps_flags & PSF_CORE) {
235: /* dump registers from core */
1.13 mickey 236: core_printregs(ps);
1.7 fgsch 237: return (0);
238: }
1.1 art 239: fprintf(stderr, "process not stopped\n");
1.7 fgsch 240: return (0);
1.1 art 241: }
242:
243: rg = alloca(sizeof(*rg) * md_def.nregs);
244:
245: if (md_getregs(ps, rg))
246: err(1, "can't get registers");
247: for (i = 0; i < md_def.nregs; i++)
248: printf("%s:\t0x%.*lx\t%s\n", md_def.md_reg_names[i],
249: (int)(sizeof(reg) * 2), (long)rg[i],
250: sym_print(ps, rg[i], buf, sizeof(buf)));
1.7 fgsch 251: return (0);
1.1 art 252: }
253:
254: static int
255: cmd_show_backtrace(int argc, char **argv, void *arg)
256: {
257: struct pstate *ps = arg;
258: int i;
259:
1.10 art 260: if (ps->ps_state != STOPPED && !(ps->ps_flags & PSF_CORE)) {
1.1 art 261: fprintf(stderr, "process not stopped\n");
1.7 fgsch 262: return (0);
1.1 art 263: }
264:
265: /* no more than 100 frames */
266: for (i = 0; i < 100; i++) {
267: struct md_frame mfr;
268: char namebuf[1024], *name;
269: reg offs;
270: int j;
271:
272: mfr.nargs = -1;
273:
274: if (md_getframe(ps, i, &mfr))
275: break;
276:
277: name = sym_name_and_offset(ps, mfr.pc, namebuf,
278: sizeof(namebuf), &offs);
279: if (name == NULL) {
280: snprintf(namebuf, sizeof(namebuf), "0x%lx", mfr.pc);
281: name = namebuf;
1.5 drahn 282: offs = 0;
1.1 art 283: }
284:
285: printf("%s(", name);
286: for (j = 0; j < mfr.nargs; j++) {
287: printf("0x%lx", mfr.args[j]);
288: if (j < mfr.nargs - 1)
289: printf(", ");
290: }
1.5 drahn 291: if (offs == 0) {
1.6 fgsch 292: printf(")\n");
1.5 drahn 293: } else {
294: printf(")+0x%lx\n", offs);
295: }
1.1 art 296: }
1.7 fgsch 297:
298: return (0);
1.1 art 299: }
300:
301: static int
302: cmd_quit(int argc, char **argv, void *arg)
303: {
304: struct pstate *ps = arg;
305:
1.8 todd 306: if ((ps->ps_flags & PSF_ATCH)) {
307: if ((ps->ps_flags & PSF_ATCH) &&
308: ptrace(PT_DETACH, ps->ps_pid, NULL, 0) < 0)
309: err(1, "ptrace(PT_DETACH)");
310: } else {
311: ps->ps_flags |= PSF_KILL;
1.1 art 312:
1.8 todd 313: if (process_kill(ps))
1.9 fgsch 314: return (1);
1.8 todd 315: }
1.1 art 316:
317: ps->ps_state = TERMINATED;
1.9 fgsch 318: return (1);
1.14 mickey 319: }
320:
321: static int
322: cmd_examine(int argc, char **argv, void *arg)
323: {
324: struct pstate *ps = arg;
325: char buf[256];
326: reg addr, val;
327: int i;
328:
329: for (i = 1; argv[i]; i++) {
330:
331: addr = strtoul(argv[i], NULL, 0);
332: if (!addr) { /* assume it's a symbol */
333: if (sym_lookup(ps, argv[i], &addr)) {
334: warn( "Can't find: %s", argv[i]);
335: return (0);
336: }
337: }
338:
1.18 mickey 339: if (process_read(ps, addr, &val, sizeof(val)) < 0) {
1.14 mickey 340: warn("Can't read process contents at 0x%lx", addr);
341: return (0);
342: }
343:
344: printf("%s:\t%s\n", argv[i],
345: sym_print(ps, val, buf, sizeof(buf)));
346: }
347:
348: return (0);
1.1 art 349: }
350:
351: /*
352: * Perform command completion.
353: * Pretty simple. if there are spaces in "buf", the last string is a symbol
354: * otherwise it's a command.
355: */
356: int
357: cmd_complt(char *buf, size_t buflen)
358: {
359: struct clit *match;
360: char *start;
361: int command;
362: int i, j, len;
363: int onlymatch;
364:
365: command = (strchr(buf, ' ') == NULL);
366:
367: if (!command) {
368: /* XXX - can't handle symbols yet. */
1.9 fgsch 369: return (-1);
1.1 art 370: }
371:
372: start = buf;
373: len = strlen(buf);
374:
375: match = NULL;
376: for (i = 0; i < sizeof(cmds) / sizeof(cmds[i]); i++) {
377: if (strncmp(start, cmds[i].cmd, len) == 0) {
378: struct clit *cmdp;
379:
380: cmdp = &cmds[i];
381: if (match == NULL) {
382: onlymatch = 1;
383: match = cmdp;
384: strlcpy(buf, match->cmd, buflen);
385: continue;
386: }
387: onlymatch = 0;
388: for (j = len; j < buflen; j++) {
389: if (buf[j] != cmdp->cmd[j]) {
390: buf[j] = '\0';
391: break;
392: }
393: if (cmdp->cmd[j] == '\0')
394: break;
395: }
396: }
397: }
398:
399: /*
400: * Be nice. If there could be arguments for this command and it's
401: * the only match append a space.
402: */
403: if (match && onlymatch /*&& match->maxargc > 0*/)
404: strlcat(buf, " ", buflen);
405:
406: return (match && onlymatch) ? 0 : -1;
407: }
408:
409: /*
1.7 fgsch 410: * The "standard" wrapper
1.1 art 411: */
412: void *
413: emalloc(size_t sz)
414: {
415: void *ret;
416: if ((ret = malloc(sz)) == NULL)
417: err(1, "malloc");
418: return (ret);
1.19 jfb 419: }
420:
421:
422: /*
423: * Find the first valid path to the executable whose name is <ename>.
424: * The resulting path is stored in <dst>, up to <dlen> - 1 bytes, and is
425: * NUL-terminated. If <dst> is too small, the result will be truncated to
426: * fit, but getexecpath() will return -1.
427: * Returns 0 on success, -1 on failure.
428: */
429:
430: int
431: getexecpath(const char *ename, char *dst, size_t dlen)
432: {
433: char *envp, *pathenv, *pp, *pfp;
434: struct stat pstat;
435:
436: if (stat(ename, &pstat) == 0) {
437: if (strlcpy(dst, ename, dlen) >= dlen)
438: return (-1);
439: return (0);
440: }
441:
442: if (strchr(ename, '/') != NULL) {
443: /* don't bother looking in PATH */
444: return (-1);
445: }
446:
447: envp = getenv("PATH");
448: if (envp == NULL)
449: envp = _PATH_DEFPATH;
450:
451: pathenv = strdup(envp);
452: if (pathenv == NULL) {
453: warn("failed to allocate PATH buffer");
454: return (-1);
455: }
456:
457: for (pp = pathenv; (pfp = strsep(&pp, ":")) != NULL; ) {
458: /* skip cwd, was already tested */
459: if (*pfp == '\0')
460: continue;
461:
462: if (snprintf(dst, dlen, "%s/%s", pfp, ename) >= (int)dlen)
463: continue;
464:
465: if (stat(dst, &pstat) != -1) {
466: free(pathenv);
467: return (0);
468: }
469: }
470:
471: free(pathenv);
472: return (-1);
1.1 art 473: }