plan9port/src/cmd/acid/main.c
rsc 4f2ac1b76b Working on better handling of multithreading in general
and core dumps in particular.  See notes:

new types: register is something that when dereferenced gives you
	the registers.  the Ureg is no longer mapped at 0.
	refconst is something that gives a constant when dereferenced.

new builtin register("AX") creates register values
new builtin refconst(0x123) creates refconst values

new builtin var("foo") is equivalent to the variable foo
	(it returns foo but can also be used as the lhs of an assignment).

new acid function getregs() returns a list of the current values of registers.
new acid function setregs() sets the current registers to those values.
	note that getregs and setregs operate on register locations, not the
		register values themselves.
new acid function resetregs() sets registers to register("AX"), etc.
new acid function clearregs() sets all registers to constant -1.
the default register settings are as in resetregs(), not small numbers.

new acid variables coretext, pids, systype, corefile, cmdline.

new behavior: local variable lookup, stk, etc., use the acid values of registers
	(*PC, *SP, and so on), so the thread support code can change the context
	completely.

unary + is applicable to more data types and prints more often.
2005-01-23 22:48:19 +00:00

723 lines
11 KiB
C

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Extern
#include "acid.h"
#include "y.tab.h"
extern int __ifmt(Fmt*);
static Biobuf bioout;
static char* lm[16];
static int nlm;
static char* mtype;
static int attachfiles(int, char**);
int xfmt(Fmt*);
int isnumeric(char*);
void die(void);
void setcore(Fhdr*);
void
usage(void)
{
fprint(2, "usage: acid [-c core] [-l module] [-m machine] [-qrw] [-k] [pid] [file]\n");
exits("usage");
}
Map*
dumbmap(int fd)
{
Map *dumb;
Seg s;
dumb = allocmap();
memset(&s, 0, sizeof s);
s.fd = fd;
s.base = 0;
s.offset = 0;
s.size = 0xFFFFFFFF;
s.name = "data";
s.file = "<dumb>";
if(addseg(dumb, s) < 0){
freemap(dumb);
return nil;
}
if(mach == nil)
mach = machcpu;
return dumb;
}
void
main(int argc, char *argv[])
{
Lsym *volatile l;
Node *n;
char buf[128], *s;
int pid, i;
argv0 = argv[0];
pid = 0;
quiet = 1;
mtype = 0;
ARGBEGIN{
case 'A':
abort();
break;
case 'm':
mtype = ARGF();
break;
case 'w':
wtflag = 1;
break;
case 'l':
s = ARGF();
if(s == 0)
usage();
lm[nlm++] = s;
break;
case 'k':
kernel++;
break;
case 'q':
quiet = 0;
break;
case 'r':
pid = 1;
remote++;
kernel++;
break;
default:
usage();
}ARGEND
fmtinstall('x', xfmt);
fmtinstall('Z', Zfmt);
fmtinstall('L', locfmt);
Binit(&bioout, 1, OWRITE);
bout = &bioout;
initexpr();
initprint();
kinit();
initialising = 1;
pushfile(0);
loadvars();
installbuiltin();
acidregs = mallocz(sizeof *acidregs, 1);
acidregs->rw = acidregsrw;
if(mtype && machbyname(mtype) == 0)
print("unknown machine %s", mtype);
if (attachfiles(argc, argv) < 0)
varreg(); /* use default register set on error */
if(mach == nil)
mach = machcpu;
symhdr = nil; /* not supposed to use this anymore */
l = mkvar("acid");
l->v->set = 1;
l->v->type = TLIST;
l->v->store.u.l = nil;
loadmodule(unsharp("#9/acid/port"));
for(i = 0; i < nlm; i++) {
if(access(lm[i], AREAD) >= 0)
loadmodule(lm[i]);
else {
sprint(buf, "#9/acid/%s", lm[i]);
loadmodule(unsharp(buf));
}
}
userinit();
varsym();
l = look("acidmap");
if(l && l->proc) {
if(setjmp(err) == 0){
n = an(ONAME, ZN, ZN);
n->sym = l;
n = an(OCALL, n, ZN);
execute(n);
}
}
interactive = 1;
initialising = 0;
line = 1;
notify(catcher);
for(;;) {
if(setjmp(err)) {
Binit(&bioout, 1, OWRITE);
unwind();
}
stacked = 0;
Bprint(bout, "acid; ");
if(yyparse() != 1)
die();
restartio();
unwind();
}
/*
Bputc(bout, '\n');
exits(0);
*/
}
void
setstring(char *var, char *s)
{
Lsym *l;
Value *v;
l = mkvar(var);
v = l->v;
v->store.fmt = 's';
v->set = 1;
v->store.u.string = strnode(s ? s : "");
v->type = TSTRING;
}
static int
attachfiles(int argc, char **argv)
{
int fd;
volatile int pid;
char *s, *t;
int i, omode;
Fhdr *hdr;
Lsym *l;
pid = 0;
interactive = 0;
if(setjmp(err))
return -1;
/*
* Unix and Plan 9 differ on what the right order of pid, text, and core is.
* I never remember anyway. Let's just accept them in any order.
*/
omode = wtflag ? ORDWR : OREAD;
for(i=0; i<argc; i++){
if(isnumeric(argv[i])){
if(pid){
fprint(2, "already have pid %d; ignoring pid %d\n", pid, argv[i]);
continue;
}
if(corhdr){
fprint(2, "already have core %s; ignoring pid %d\n", corfil, pid);
continue;
}
pid = atoi(argv[i]);
continue;
}
if((hdr = crackhdr(argv[i], omode)) == nil){
fprint(2, "crackhdr %s: %r\n", argv[i]);
if(argc == 1 && (fd = open(argv[i], omode)) > 0){
fprint(2, "loading %s direct mapped\n", argv[i]);
symmap = dumbmap(fd);
cormap = dumbmap(fd);
symfil = argv[i];
corfil = argv[i];
goto Run;
}
continue;
}
fprint(2, "%s: %s %s %s\n", argv[i], hdr->aname, hdr->mname, hdr->fname);
if(hdr->ftype == FCORE){
if(pid){
fprint(2, "already have pid %d; ignoring core %s\n", pid, argv[i]);
uncrackhdr(hdr);
continue;
}
if(corhdr){
fprint(2, "already have core %s; ignoring core %s\n", corfil, argv[i]);
uncrackhdr(hdr);
continue;
}
corhdr = hdr;
corfil = argv[i];
}else{
if(symhdr){
fprint(2, "already have text %s; ignoring text %s\n", symfil, argv[i]);
uncrackhdr(hdr);
continue;
}
symhdr = hdr;
symfil = argv[i];
}
}
if(symhdr==nil){
symfil = "a.out";
if(pid){
if((s = proctextfile(pid)) != nil){
fprint(2, "pid %d: text %s\n", pid, s);
symfil = s;
}
}
if(corhdr){
/*
* prog gives only the basename of the command,
* so try the command line for a path.
*/
if((s = strdup(corhdr->cmdline)) != nil){
t = strchr(s, ' ');
if(t)
*t = 0;
if((t = searchpath(s)) != nil){
fprint(2, "core: text %s\n", t);
symfil = t;
}
free(s);
}
}
if((symhdr = crackhdr(symfil, omode)) == nil){
fprint(2, "crackhdr %s: %r\n", symfil);
symfil = nil;
}else
fprint(2, "%s: %s %s %s\n", symfil, symhdr->aname, symhdr->mname, symhdr->fname);
}
if(symhdr)
symopen(symhdr);
if(!mach)
mach = machcpu;
/*
* Set up maps.
*/
symmap = allocmap();
cormap = allocmap();
if(symmap == nil || cormap == nil)
sysfatal("allocating maps: %r");
if(symhdr){
if(mapfile(symhdr, 0, symmap, nil) < 0)
fprint(2, "mapping %s: %r\n", symfil);
mapfile(symhdr, 0, cormap, nil);
}
Run:
setstring("objtype", mach->name);
setstring("textfile", symfil);
setstring("systype", symhdr ? symhdr->aname : "");
setstring("corefile", corfil);
l = mkvar("pids");
l->v->set = 1;
l->v->type = TLIST;
l->v->store.u.l = nil;
if(pid)
sproc(pid);
if(corhdr)
setcore(corhdr);
varreg();
return 0;
}
void
setcore(Fhdr *hdr)
{
int i;
Lsym *l;
Value *v;
List **tail, *tl;
unmapproc(cormap);
unmapfile(corhdr, cormap);
free(correg);
correg = nil;
if(hdr == nil)
error("no core");
if(mapfile(hdr, 0, cormap, &correg) < 0)
error("mapfile %s: %r", hdr->filename);
corhdr = hdr;
corfil = hdr->filename;
l = mkvar("pid");
v = l->v;
v->store.fmt = 'D';
v->set = 1;
v->store.u.ival = hdr->pid;
setstring("corefile", corfil);
setstring("cmdline", hdr->cmdline);
l = mkvar("pids");
l->v->set = 1;
l->v->type = TLIST;
l->v->store.u.l = nil;
tail = &l->v->store.u.l;
for(i=0; i<hdr->nthread; i++){
tl = al(TINT);
tl->store.u.ival = hdr->thread[i].id;
tl->store.fmt = 'X';
*tail = tl;
tail = &tl->next;
}
if(hdr->nthread)
sproc(hdr->thread[0].id);
}
void
die(void)
{
Lsym *s;
List *f;
int first;
Bprint(bout, "\n");
first = 1;
s = look("proclist");
if(s && s->v->type == TLIST) {
for(f = s->v->store.u.l; f; f = f->next){
detachproc((int)f->store.u.ival);
Bprint(bout, "%s %d", first ? "/bin/kill -9" : "", (int)f->store.u.ival);
first = 0;
}
}
if(!first)
Bprint(bout, "\n");
exits(0);
}
void
userinit(void)
{
Lsym *l;
Node *n;
char buf[128], *p;
sprint(buf, "#9/acid/%s", mach->name);
loadmodule(unsharp(buf));
p = getenv("HOME");
if(p != 0) {
sprint(buf, "%s/lib/acid", p);
silent = 1;
loadmodule(buf);
}
interactive = 0;
if(setjmp(err)) {
unwind();
return;
}
l = look("acidinit");
if(l && l->proc) {
n = an(ONAME, ZN, ZN);
n->sym = l;
n = an(OCALL, n, ZN);
execute(n);
}
}
void
loadmodule(char *s)
{
interactive = 0;
if(setjmp(err)) {
unwind();
return;
}
pushfile(s);
silent = 0;
yyparse();
popio();
return;
}
Node*
an(int op, Node *l, Node *r)
{
Node *n;
n = gmalloc(sizeof(Node));
memset(n, 0, sizeof(Node));
n->gc.gclink = gcl;
gcl = (Gc*)n;
n->op = op;
n->left = l;
n->right = r;
return n;
}
List*
al(int t)
{
List *l;
l = gmalloc(sizeof(List));
memset(l, 0, sizeof(List));
l->type = t;
l->gc.gclink = gcl;
gcl = (Gc*)l;
return l;
}
Node*
con(int v)
{
Node *n;
n = an(OCONST, ZN, ZN);
n->store.u.ival = v;
n->store.fmt = 'X';
n->type = TINT;
return n;
}
void
fatal(char *fmt, ...)
{
char buf[128];
va_list arg;
va_start(arg, fmt);
vseprint(buf, buf+sizeof(buf), fmt, arg);
va_end(arg);
fprint(2, "%s: %Z (fatal problem) %s\n", argv0, buf);
exits(buf);
}
void
yyerror(char *fmt, ...)
{
char buf[128];
va_list arg;
if(strcmp(fmt, "syntax error") == 0) {
yyerror("syntax error, near symbol '%s'", symbol);
return;
}
va_start(arg, fmt);
vseprint(buf, buf+sizeof(buf), fmt, arg);
va_end(arg);
print("%Z: %s\n", buf);
}
void
marktree(Node *n)
{
if(n == 0)
return;
marktree(n->left);
marktree(n->right);
n->gc.gcmark = 1;
if(n->op != OCONST)
return;
switch(n->type) {
case TSTRING:
n->store.u.string->gc.gcmark = 1;
break;
case TLIST:
marklist(n->store.u.l);
break;
case TCODE:
marktree(n->store.u.cc);
break;
}
}
void
marklist(List *l)
{
while(l) {
l->gc.gcmark = 1;
switch(l->type) {
case TSTRING:
l->store.u.string->gc.gcmark = 1;
break;
case TLIST:
marklist(l->store.u.l);
break;
case TCODE:
marktree(l->store.u.cc);
break;
}
l = l->next;
}
}
void
gc(void)
{
int i;
Lsym *f;
Value *v;
Gc *m, **p, *next;
if(dogc < Mempergc)
return;
dogc = 0;
/* Mark */
for(m = gcl; m; m = m->gclink)
m->gcmark = 0;
/* Scan */
for(i = 0; i < Hashsize; i++) {
for(f = hash[i]; f; f = f->hash) {
marktree(f->proc);
if(f->lexval != Tid)
continue;
for(v = f->v; v; v = v->pop) {
switch(v->type) {
case TSTRING:
v->store.u.string->gc.gcmark = 1;
break;
case TLIST:
marklist(v->store.u.l);
break;
case TCODE:
marktree(v->store.u.cc);
break;
case TCON:
marktree(v->store.u.con);
break;
}
}
}
}
/* Free */
p = &gcl;
for(m = gcl; m; m = next) {
next = m->gclink;
if(m->gcmark == 0) {
*p = next;
free(m); /* Sleazy reliance on my malloc */
}
else
p = &m->gclink;
}
}
void*
gmalloc(long l)
{
void *p;
dogc += l;
p = malloc(l);
if(p == 0)
fatal("out of memory");
return p;
}
void
checkqid(int f1, int pid)
{
int fd;
Dir *d1, *d2;
char buf[128];
if(kernel)
return;
d1 = dirfstat(f1);
if(d1 == nil){
print("checkqid: (qid not checked) dirfstat: %r\n");
return;
}
sprint(buf, "/proc/%d/text", pid);
fd = open(buf, OREAD);
if(fd < 0 || (d2 = dirfstat(fd)) == nil){
print("checkqid: (qid not checked) dirstat %s: %r\n", buf);
free(d1);
if(fd >= 0)
close(fd);
return;
}
close(fd);
if(d1->qid.path != d2->qid.path || d1->qid.vers != d2->qid.vers || d1->qid.type != d2->qid.type){
print("path %llux %llux vers %lud %lud type %d %d\n",
d1->qid.path, d2->qid.path, d1->qid.vers, d2->qid.vers, d1->qid.type, d2->qid.type);
print("warning: image does not match text for pid %d\n", pid);
}
free(d1);
free(d2);
}
void
catcher(void *junk, char *s)
{
USED(junk);
if(strstr(s, "interrupt")) {
gotint = 1;
noted(NCONT);
}
if(strstr(s, "child"))
noted(NCONT);
fprint(2, "note: %s\n", s);
noted(NDFLT);
}
char*
system(void)
{
char *cpu, *p, *q;
static char kernel[128];
cpu = getenv("cputype");
if(cpu == 0) {
cpu = "mips";
print("$cputype not set; assuming %s\n", cpu);
}
p = getenv("terminal");
if(p == 0 || (p=strchr(p, ' ')) == 0 || p[1] == ' ' || p[1] == 0) {
p = "9power";
print("missing or bad $terminal; assuming %s\n", p);
}
else{
p++;
q = strchr(p, ' ');
if(q)
*q = 0;
sprint(kernel, "/%s/9%s", cpu, p);
}
return kernel;
}
int
isnumeric(char *s)
{
while(*s) {
if(*s < '0' || *s > '9')
return 0;
s++;
}
return 1;
}
int
xfmt(Fmt *f)
{
f->flags ^= FmtSharp;
return __ifmt(f);
}