File: [local] / src / usr.bin / pmdb / Attic / pmdb.c (download)
Revision 1.21, Mon Aug 6 19:16:05 2007 UTC (16 years, 9 months ago) by sobrado
Branch: MAIN
CVS Tags: OPENBSD_4_3_BASE, OPENBSD_4_3, OPENBSD_4_2_BASE, OPENBSD_4_2 Changes since 1.20: +2 -2 lines
the ellipsis is not an optional argument; while here, sync the usage
and synopsis of commands
lots of good ideas by jmc@
ok jmc@
|
/* $OpenBSD: pmdb.c,v 1.21 2007/08/06 19:16:05 sobrado Exp $ */
/*
* Copyright (c) 2002 Artur Grabowski <art@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <err.h>
#include <errno.h>
#include <string.h>
#include <paths.h>
#include "pmdb.h"
#include "symbol.h"
#include "clit.h"
#include "break.h"
#include "core.h"
static int cmd_show_registers(int, char **, void *);
static int cmd_show_backtrace(int, char **, void *);
static int cmd_examine(int, char **, void *);
static int cmd_quit(int, char **, void *);
struct clit cmds[] = {
/* debugging info commands. */
{ "regs", "show registers", 0, 0, cmd_show_registers, (void *)-1 },
{ "trace", "show backtrace", 0, 0, cmd_show_backtrace, (void *)-1 },
{ "x", "examine memory", 1, 16, cmd_examine, (void *)-1 },
/* Process handling commands. */
{ "run", "run process", 0, 0, cmd_process_run, (void *)-1 },
{ "continue", "continue process", 0, 0, cmd_process_cont, (void *)-1 },
{ "kill", "kill process", 0, 0, cmd_process_kill, (void *)-1 },
{ "setenv", "set env variables", 2, 2, cmd_process_setenv, (void *)-1 },
/* signal handling commands. */
{ "signal", "ignore signal", 2, 2, cmd_signal_ignore, (void *)-1 },
{ "sigstate", "show signal state", 0, 0, cmd_signal_show, (void *)-1 },
/* breakpoints */
{ "break", "set breakpoint", 1, 1, cmd_bkpt_add, (void *)-1 },
{ "step", "single step one insn", 0, 0, cmd_sstep, (void *)-1 },
/* symbols */
{ "sym_load", "load symbol table", 2, 2, cmd_sym_load, (void *)-1 },
/* misc commands. */
{ "help", "print help", 0, 1, cmd_help, NULL },
{ "quit", "quit", 0, 0, cmd_quit, (void *)-1 },
{ "exit", "quit", 0, 0, cmd_quit, (void *)-1 },
};
#define NCMDS sizeof(cmds)/sizeof(cmds[0])
void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-c core] [-p pid] program ...\n",
__progname);
exit(1);
}
int
main(int argc, char **argv)
{
struct pstate ps;
int i, c;
int status;
void *cm;
char *pmenv, *core, *perr, execpath[MAXPATHLEN];
int level;
pid_t pid;
core = NULL;
pid = 0;
while ((c = getopt(argc, argv, "c:p:")) != -1) {
switch(c) {
case 'c':
core = optarg;
break;
case 'p':
pid = (pid_t) strtol(optarg, &perr, 10);
if (*perr != '\0')
errx(1, "invalid PID");
break;
case '?':
default:
usage();
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;
if (argc == 0)
usage();
if ((pmenv = getenv("IN_PMDB")) != NULL) {
level = atoi(pmenv);
level++;
} else
level = 0;
if (level > 0)
asprintf(&prompt_add, "(%d)", level);
asprintf(&pmenv, "%d", level);
setenv("IN_PMDB", pmenv, 1);
if (pmenv)
free(pmenv);
if (getexecpath(argv[0], execpath, sizeof(execpath)) == -1) {
err(1, "cannot find `%s'", argv[0]);
}
argv[0] = execpath;
memset(&ps, 0, sizeof(ps));
process_setargv(&ps, argc, argv);
ps.ps_pid = pid;
ps.ps_state = NONE;
ps.ps_flags = 0;
ps.ps_signum = 0;
ps.ps_npc = 1;
TAILQ_INIT(&ps.ps_bkpts);
TAILQ_INIT(&ps.ps_sstep_cbs);
signal(SIGINT, SIG_IGN);
for (i = 0; i < NCMDS; i++)
if (cmds[i].arg == (void *)-1)
cmds[i].arg = &ps;
md_def_init();
init_sigstate(&ps);
if ((core != NULL) && (read_core(core, &ps) < 0))
warnx("failed to load core file");
if (process_load(&ps) < 0)
errx(1, "failed to load process");
cm = cmdinit(cmds, NCMDS);
while (ps.ps_state != TERMINATED) {
int signum;
int stopped;
int cont;
if (ps.ps_state == STOPPED) {
sym_update(&ps);
}
if (ps.ps_state != RUNNING && cmdloop(cm) == 0) {
cmd_quit(0, NULL, &ps);
}
if (ps.ps_state == TERMINATED)
break;
if (wait(&status) == 0)
err(1, "wait");
if (WIFEXITED(status)) {
if ((ps.ps_flags & PSF_KILL) == 0) {
ps.ps_state = NONE;
} else {
ps.ps_state = TERMINATED;
}
fprintf(stderr, "process exited with status %d\n",
WEXITSTATUS(status));
continue;
}
if (WIFSIGNALED(status)) {
signum = WTERMSIG(status);
stopped = 0;
} else {
signum = WSTOPSIG(status);
stopped = 1;
}
cont = 0;
if (stopped)
cont = bkpt_check(&ps);
process_signal(&ps, signum, stopped, cont);
}
cmdend(cm);
sym_destroy(&ps);
return (0);
}
static int
cmd_show_registers(int argc, char **argv, void *arg)
{
struct pstate *ps = arg;
char buf[256];
int i;
reg *rg;
if (ps->ps_state != STOPPED) {
if (ps->ps_flags & PSF_CORE) {
/* dump registers from core */
core_printregs(ps);
return (0);
}
fprintf(stderr, "process not stopped\n");
return (0);
}
rg = alloca(sizeof(*rg) * md_def.nregs);
if (md_getregs(ps, rg))
err(1, "can't get registers");
for (i = 0; i < md_def.nregs; i++)
printf("%s:\t0x%.*lx\t%s\n", md_def.md_reg_names[i],
(int)(sizeof(reg) * 2), (long)rg[i],
sym_print(ps, rg[i], buf, sizeof(buf)));
return (0);
}
static int
cmd_show_backtrace(int argc, char **argv, void *arg)
{
struct pstate *ps = arg;
int i;
if (ps->ps_state != STOPPED && !(ps->ps_flags & PSF_CORE)) {
fprintf(stderr, "process not stopped\n");
return (0);
}
/* no more than 100 frames */
for (i = 0; i < 100; i++) {
struct md_frame mfr;
char namebuf[1024], *name;
reg offs;
int j;
mfr.nargs = -1;
if (md_getframe(ps, i, &mfr))
break;
name = sym_name_and_offset(ps, mfr.pc, namebuf,
sizeof(namebuf), &offs);
if (name == NULL) {
snprintf(namebuf, sizeof(namebuf), "0x%lx", mfr.pc);
name = namebuf;
offs = 0;
}
printf("%s(", name);
for (j = 0; j < mfr.nargs; j++) {
printf("0x%lx", mfr.args[j]);
if (j < mfr.nargs - 1)
printf(", ");
}
if (offs == 0) {
printf(")\n");
} else {
printf(")+0x%lx\n", offs);
}
}
return (0);
}
static int
cmd_quit(int argc, char **argv, void *arg)
{
struct pstate *ps = arg;
if ((ps->ps_flags & PSF_ATCH)) {
if ((ps->ps_flags & PSF_ATCH) &&
ptrace(PT_DETACH, ps->ps_pid, NULL, 0) < 0)
err(1, "ptrace(PT_DETACH)");
} else {
ps->ps_flags |= PSF_KILL;
if (process_kill(ps))
return (1);
}
ps->ps_state = TERMINATED;
return (1);
}
static int
cmd_examine(int argc, char **argv, void *arg)
{
struct pstate *ps = arg;
char buf[256];
reg addr, val;
int i;
for (i = 1; argv[i]; i++) {
addr = strtoul(argv[i], NULL, 0);
if (!addr) { /* assume it's a symbol */
if (sym_lookup(ps, argv[i], &addr)) {
warn( "Can't find: %s", argv[i]);
return (0);
}
}
if (process_read(ps, addr, &val, sizeof(val)) < 0) {
warn("Can't read process contents at 0x%lx", addr);
return (0);
}
printf("%s:\t%s\n", argv[i],
sym_print(ps, val, buf, sizeof(buf)));
}
return (0);
}
/*
* Perform command completion.
* Pretty simple. if there are spaces in "buf", the last string is a symbol
* otherwise it's a command.
*/
int
cmd_complt(char *buf, size_t buflen)
{
struct clit *match;
char *start;
int command;
int i, j, len;
int onlymatch;
command = (strchr(buf, ' ') == NULL);
if (!command) {
/* XXX - can't handle symbols yet. */
return (-1);
}
start = buf;
len = strlen(buf);
match = NULL;
for (i = 0; i < sizeof(cmds) / sizeof(cmds[i]); i++) {
if (strncmp(start, cmds[i].cmd, len) == 0) {
struct clit *cmdp;
cmdp = &cmds[i];
if (match == NULL) {
onlymatch = 1;
match = cmdp;
strlcpy(buf, match->cmd, buflen);
continue;
}
onlymatch = 0;
for (j = len; j < buflen; j++) {
if (buf[j] != cmdp->cmd[j]) {
buf[j] = '\0';
break;
}
if (cmdp->cmd[j] == '\0')
break;
}
}
}
/*
* Be nice. If there could be arguments for this command and it's
* the only match append a space.
*/
if (match && onlymatch /*&& match->maxargc > 0*/)
strlcat(buf, " ", buflen);
return (match && onlymatch) ? 0 : -1;
}
/*
* The "standard" wrapper
*/
void *
emalloc(size_t sz)
{
void *ret;
if ((ret = malloc(sz)) == NULL)
err(1, "malloc");
return (ret);
}
/*
* Find the first valid path to the executable whose name is <ename>.
* The resulting path is stored in <dst>, up to <dlen> - 1 bytes, and is
* NUL-terminated. If <dst> is too small, the result will be truncated to
* fit, but getexecpath() will return -1.
* Returns 0 on success, -1 on failure.
*/
int
getexecpath(const char *ename, char *dst, size_t dlen)
{
char *envp, *pathenv, *pp, *pfp;
struct stat pstat;
if (stat(ename, &pstat) == 0) {
if (strlcpy(dst, ename, dlen) >= dlen)
return (-1);
return (0);
}
if (strchr(ename, '/') != NULL) {
/* don't bother looking in PATH */
return (-1);
}
envp = getenv("PATH");
if (envp == NULL)
envp = _PATH_DEFPATH;
pathenv = strdup(envp);
if (pathenv == NULL) {
warn("failed to allocate PATH buffer");
return (-1);
}
for (pp = pathenv; (pfp = strsep(&pp, ":")) != NULL; ) {
/* skip cwd, was already tested */
if (*pfp == '\0')
continue;
if (snprintf(dst, dlen, "%s/%s", pfp, ename) >= (int)dlen)
continue;
if (stat(dst, &pstat) != -1) {
free(pathenv);
return (0);
}
}
free(pathenv);
return (-1);
}