shithub: sl

ref: bbfee60f6716dce8cf7a802c044cf7d0fe8bb6f2
dir: /src/plan9/lsd.c/

View raw version
#include "sl.h"
#include <bio.h>
#include <ctype.h>
#include <mach.h>
#include "cvalues.h"
#include "io.h"

static char aout[1024];
static sl_v lsd_gpregsym, lsd_fpregsym, lsd_regsym, lsd_symsym, lsd_framesym;
static Map* coremap;
static Fhdr fhdr;
static sl_ios *proc_stdin;

void
lsd_init(void)
{
	lsd_gpregsym = mk_csym(":gpreg");
	lsd_fpregsym = mk_csym(":fpreg");
	lsd_symsym = mk_csym("symbol");
	lsd_regsym = mk_csym("reg");
	lsd_framesym = mk_csym("frame");
}

static Reglist*
rname(char *name)
{
	Reglist *rp;

	for(rp = mach->reglist; rp->rname; rp++)
	if(strcmp(name, rp->rname) == 0)
		return rp;
	return nil;
}

static uvlong
rget(Map *map, char *name)
{
	Reglist *rp;
	ulong x;
	uvlong v;
	int r;

	rp = rname(name);
	if(rp == nil)
		lerrorf(sl_errio, "invalid register name %s", name);

	switch(rp->rformat){
	default:
		r = get4(map, rp->roffs, &x);
		v = x;
		break;
	case 'V': case 'W': case 'Y': case 'Z':
		r = get8(map, rp->roffs, &v);
		break;
	}
	if(r < 0)
		lerrorf(sl_errio, "could not get register %s: %r", name);
	return v;
}

static sl_v
mk_symbol(Symbol *s)
{
	sl_v v;
	Rune r;
	static char b[2];

	b[0] = s->type;
	chartorune(&r, b);
	v = alloc_vec(4, 0);
	sl_gc_handle(&v);
	vec_elt(v, 0) = lsd_symsym;
	vec_elt(v, 1) = str_from_cstr(s->name);
	vec_elt(v, 2) = mk_rune(r);
	vec_elt(v, 3) = size_wrap(s->value);
	sl_free_gc_handles(1);
	return v;
}

static sl_v
symslist(int (*loadsym)(Symbol*, int))
{
	Symbol s;
	sl_v syms;
	int i;

	syms = sl_nil;
	sl_gc_handle(&syms);
	for(i = 0; loadsym(&s, i); i++){
		if(s.name[0] == '.' || strchr(s.name, '$') != nil)
			continue;
		syms = mk_cons(mk_symbol(&s), syms);
	}
	sl_free_gc_handles(1);
	return syms;
}

static sl_v
localslist(Symbol *fn, uvlong sp)
{
	sl_v locals;
	int i;
	Symbol s;

	locals = sl_nil;
	sl_gc_handle(&locals);
	s = *fn;
	for(i = 0; localsym(&s, i); i++){
		if(s.name[0] == '.')
			continue;
		switch(s.class){
		case CAUTO:
			s.value = sp-s.value;
			break;
		case CPARAM:
			s.value = sp+mach->szaddr+s.value;
			break;
		}
		locals = mk_cons(mk_symbol(&s), locals);
	}
	sl_free_gc_handles(1);
	return locals;
}

static void
load(void)
{
	int fd;
	long nsym;
	Map *symmap;

	fd = open(aout, OREAD);
	if(fd < 0)
		lerrorf(sl_errio, "could not open \"%s\"", aout);
	if(!crackhdr(fd, &fhdr)){
		close(fd);
		lerrorf(sl_errio, "could not decode file header for \"%s\"", aout);
	}
	machbytype(fhdr.type);
	symmap = loadmap(nil, fd, &fhdr);
	if(symmap == nil){
		close(fd);
		lerrorf(sl_errio, "could not load segments for \"%s\"", aout);
	}
	nsym = syminit(fd, &fhdr);
	if(nsym < 0){
		close(fd);
		lerrorf(sl_errio, "could not initialize the symbol table for \"%s\"", aout);
	}
	close(fd);
	ios_printf(ios_stdout, "%s:%s\n", aout, fhdr.name);
}

static void
freecore(void)
{
	int i;

	if(coremap == nil)
		return;
	for(i = 0; i < coremap->nsegs; i++)
	if(coremap->seg[i].inuse && coremap->seg[i].fd >= 0)
		close(coremap->seg[i].fd);
	free(coremap);
	coremap = nil;
}

static void
attach(int pid)
{
	static char buf[128];
	int fd;

	if(pid < 0)
		return;

	snprint(buf, sizeof(buf), "/proc/%d/mem", pid);
	fd = open(buf, ORDWR);
	if(fd < 0)
		lerrorf(sl_errio, "could not open \"%s\"", buf);

	freecore();
	coremap = attachproc(pid, 0, fd, &fhdr);
	if(coremap == nil)
		lerrorf(sl_errio, "could not make coremap: %r");
}

static int
newproc(int *outp)
{
	int pid, fd, p[2];
	char buf[128], *argv[2];

	argv[0] = aout;
	argv[1] = nil;
	if(pipe(p) < 0)
		lerrorf(sl_errio, "could not create a pipe");
	pid = rfork(RFPROC|RFFDG|RFREND|RFNAMEG|RFNOTEG);
	switch(pid){
	case -1:
		lerrorf(sl_errio, "could not fork");
	case 0:
		snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
		fd = open(buf, ORDWR);
		if(fd < 0)
			sysfatal("could not open %s: %r", buf);
		write(fd, "hang", 4);
		close(fd);
		close(p[0]);
		dup(p[1], 0);
		close(p[1]);
		exec(argv[0], argv);
		sysfatal("could not exec %s: %r", aout);
	}
	close(p[1]);
	attach(pid);
	*outp = p[0];
	return pid;
}

static sl_v tracelist;

static void
trlist(Map *map, uvlong retpc, uvlong sp, Symbol *fn)
{
	sl_v  v;

	USED(map);
	v = alloc_vec(5, 0);
	sl_gc_handle(&v);
	vec_elt(v, 0) = lsd_framesym;
	vec_elt(v, 1) = mk_symbol(fn);
	vec_elt(v, 2) = mk_u64(retpc);
	vec_elt(v, 3) = mk_u64(sp);
	vec_elt(v, 4) = localslist(fn, sp);
	sl_free_gc_handles(1);
	tracelist = mk_cons(v, tracelist);
}

BUILTIN("lsd-ctrace", lsd_ctrace)
{
	sl_v *a;
	uvlong pc, sp, res;

	argcount(nargs, 3);
	for(a = args; a < args+3; a++)
	if(sl_unlikely(!sl_isnum(*a)))
		type_error("num", *a);

	pc = tosize(args[0]);
	sp = tosize(args[1]);
	res = tosize(args[2]);
	tracelist = sl_nil;
	sl_gc_handle(&tracelist);
	if(machdata->ctrace(coremap, pc, sp, res, trlist) <= 0){
		sl_free_gc_handles(1);
		lerrorf(sl_errio, "could not retrieve stack frame: %r");
	}
	sl_free_gc_handles(1);
	return tracelist;
}

BUILTIN("lsd-cleanup", lsd_cleanup)
{
	USED(args);
	argcount(nargs, 0);
	freecore();
	return sl_void;
}

BUILTIN("lsd-load", lsd_load)
{
	Reglist *r;
	sl_v v, registers;
	int pid, len;

	pid = -1;
	argcount(nargs, 1);
	if(sl_unlikely(!sl_isstr(args[0]) && !sl_isnum(args[0])))
		type_error("str|num", args[0]);

	if(isfixnum(args[0])){
		pid = numval(args[0]);
		snprint(aout, sizeof(aout), "/proc/%d/text", pid);
	}else{
		len = cv_len(ptr(args[0]));
		if(len+1 > sizeof(aout))
			lerrorf(sl_errio, "path too long");
		memmove(aout, cvalue_data(args[0]), len);
	}

	load();
	attach(pid);

	registers = sl_nil;
	v = sl_nil;
	sl_gc_handle(&registers);
	sl_gc_handle(&v);
	for(r = mach->reglist; r->rname != nil; r++){
		v = alloc_vec(5, 0);
		vec_elt(v, 0) = lsd_regsym;
		vec_elt(v, 1) = str_from_cstr(r->rname);
		vec_elt(v, 2) = r->rflags == RINT ? lsd_gpregsym : lsd_fpregsym;
		vec_elt(v, 3) = size_wrap(r->roffs);
		switch(r->rformat){
		default:
			vec_elt(v, 4) = sl_u32sym;
			break;
		case 'V': case 'W': case 'Y': case 'Z':
			vec_elt(v, 4) = sl_u64sym;
			break;
		}
		set(mk_sym(r->rname, true), v);
		registers = mk_cons(v, registers);
	}
	sl_free_gc_handles(2);

	v = alloc_vec(5, 0);
	sl_gc_handle(&v);
	vec_elt(v, 0) = fixnum(pid);
	vec_elt(v, 1) = registers;
	vec_elt(v, 2) = cvalue_from_ref(sl_strtype, machdata->bpinst, machdata->bpsize);
	vec_elt(v, 3) = symslist(textsym);
	vec_elt(v, 4) = symslist(globalsym);
	sl_free_gc_handles(1);

	return v;
}

BUILTIN("lsd-new", lsd_new)
{
	sl_v v;
	int pid, p;

	USED(args);
	USED(nargs);

	pid = newproc(&p);

	if(proc_stdin != nil){
		free(proc_stdin->loc.filename);
		free(proc_stdin);
	}
	proc_stdin = MEM_ALLOC(sizeof(*proc_stdin));
	ios_fd(proc_stdin, p, "proc-stdin", false, false);

	v = alloc_vec(2, 0);
	sl_gc_handle(&v);
	vec_elt(v, 0) = fixnum(pid);
	vec_elt(v, 1) = cvalue_from_ref(sl_iotype, proc_stdin, sizeof(*proc_stdin));
	sl_free_gc_handles(1);
	return v;
}

BUILTIN("lsd-follow", lsd_follow)
{
	int n;
	uvlong addr, f[10], *p;
	sl_v foll;

	argcount(nargs, 1);
	if(sl_unlikely(!sl_isnum(args[0])))
		type_error("num", args[0]);
	addr = tosize(args[0]);

	n = (*machdata->foll)(coremap, addr, rget, f);
	if(n < 0)
		lerrorf(sl_errio, "follow(%ux): %r", addr);

	foll = sl_nil;
	sl_gc_handle(&foll);
	for(p = f; p < f+n; p++)
		foll = mk_cons(mk_u64(*p), foll);
	sl_free_gc_handles(1);
	return foll;
}

BUILTIN("lsd-das", lsd_das)
{
	static char buf[512];
	uvlong addr;

	argcount(nargs, 1);
	if(sl_unlikely(!sl_isnum(args[0])))
		type_error("num", args[0]);

	addr = tosize(args[0]);
	if(machdata->das(coremap, addr, 'i', buf, sizeof(buf)) < 0)
		lerrorf(sl_errio, "could not disassemble at %ud", addr);
	return str_from_cstr(buf);
}

BUILTIN("lsd-instsize", lsd_instsize)
{
	uvlong addr;
	int sz;

	argcount(nargs, 1);
	if(sl_unlikely(!sl_isnum(args[0])))
		type_error("num", args[0]);

	addr = tosize(args[0]);
	sz = machdata->instsize(coremap, addr);
	if(sz < 0)
		lerrorf(sl_errio, "could not get instruction size at %ud", addr);
	return size_wrap(sz);
}

BUILTIN("lsd-fileline", lsd_fileline)
{
	static char buf[1024];
	uvlong addr;

	argcount(nargs, 1);
	if(sl_unlikely(!sl_isnum(args[0])))
		type_error("num", args[0]);

	addr = tosize(args[0]);
	if(!fileline(buf, sizeof(buf), addr))
		lerrorf(sl_errio, "could not get locate source code line at %ud", addr);
	return str_from_cstr(buf);
}

BUILTIN("lsd-file2pc", lsd_file2pc)
{
	static char buf[1024];
	uvlong addr;
	int len, line;

	argcount(nargs, 2);
	if(sl_unlikely(!sl_isstr(args[0])))
		type_error("str", args[0]);
	if(sl_unlikely(!isfixnum(args[1])))
		type_error("num", args[1]);

	len = cv_len(ptr(args[0]));
	if(len+1 > sizeof(buf))
		lerrorf(sl_errio, "path too long");
	memmove(buf, cvalue_data(args[0]), len);
	buf[len] = '\0';
	line = numval(args[1]);
	addr = file2pc(buf, line);
	if(addr == ~0)
		lerrorf(sl_errio, "could not find address of %s:%d", buf, line);
	return size_wrap(addr);
}