404 lines
7.1 KiB
C
404 lines
7.1 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <bio.h>
|
|
#include <mach.h>
|
|
#include <ctype.h>
|
|
|
|
static int rtrace(uvlong, uvlong, uvlong);
|
|
static int ctrace(uvlong, uvlong, uvlong);
|
|
static int i386trace(uvlong, uvlong, uvlong);
|
|
static int amd64trace(uvlong, uvlong, uvlong);
|
|
static uvlong getval(uvlong);
|
|
static void inithdr(int);
|
|
static void fatal(char*, ...);
|
|
static void readstack(void);
|
|
|
|
static Fhdr fhdr;
|
|
static int interactive;
|
|
|
|
#define FRAMENAME ".frame"
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: ktrace [-i] kernel pc sp [link]\n");
|
|
exits("usage");
|
|
}
|
|
|
|
static void
|
|
printaddr(char *addr, uvlong pc)
|
|
{
|
|
int i;
|
|
char *p;
|
|
|
|
/*
|
|
* reformat the following.
|
|
*
|
|
* foo+1a1 -> src(foo+0x1a1);
|
|
* 10101010 -> src(0x10101010);
|
|
*/
|
|
|
|
if(strlen(addr) == 8 && strchr(addr, '+') == nil){
|
|
for(i=0; i<8; i++)
|
|
if(!isxdigit(addr[i]))
|
|
break;
|
|
if(i == 8){
|
|
print("src(%#.8llux); // 0x%s\n", pc, addr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(p=strchr(addr, '+')){
|
|
*p++ = 0;
|
|
print("src(%#.8llux); // %s+0x%s\n", pc, addr, p);
|
|
}else
|
|
print("src(%#.8llux); // %s\n", pc, addr);
|
|
}
|
|
|
|
static void (*fmt)(char*, uvlong) = printaddr;
|
|
|
|
void
|
|
main(int argc, char *argv[])
|
|
{
|
|
int (*t)(uvlong, uvlong, uvlong);
|
|
uvlong pc, sp, link;
|
|
int fd;
|
|
|
|
ARGBEGIN{
|
|
case 'i':
|
|
interactive++;
|
|
break;
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
|
|
link = 0;
|
|
t = ctrace;
|
|
switch(argc){
|
|
case 4:
|
|
t = rtrace;
|
|
link = strtoull(argv[3], 0, 16);
|
|
break;
|
|
case 3:
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
pc = strtoull(argv[1], 0, 16);
|
|
sp = strtoull(argv[2], 0, 16);
|
|
if(!interactive)
|
|
readstack();
|
|
|
|
fd = open(argv[0], OREAD);
|
|
if(fd < 0)
|
|
fatal("can't open %s: %r", argv[0]);
|
|
inithdr(fd);
|
|
switch(fhdr.magic){
|
|
case I_MAGIC: /* intel 386 */
|
|
t = i386trace;
|
|
break;
|
|
case S_MAGIC: /* amd64 */
|
|
t = amd64trace;
|
|
break;
|
|
case A_MAGIC: /* 68020 */
|
|
case J_MAGIC: /* intel 960 */
|
|
t = ctrace;
|
|
break;
|
|
case K_MAGIC: /* sparc */
|
|
case D_MAGIC: /* amd 29000 */
|
|
case V_MAGIC: /* mips 3000 */
|
|
case M_MAGIC: /* mips 4000 */
|
|
case E_MAGIC: /* arm 7-something */
|
|
case Q_MAGIC: /* powerpc */
|
|
case N_MAGIC: /* mips 4000 LE */
|
|
case L_MAGIC: /* dec alpha */
|
|
t = rtrace;
|
|
break;
|
|
case X_MAGIC: /* att dsp 3210 */
|
|
sysfatal("can't ktrace %s", argv[0]);
|
|
break;
|
|
default:
|
|
fprint(2, "%s: warning: can't tell what type of stack %s uses; assuming it's %s\n",
|
|
argv0, argv[0], argc == 4 ? "risc" : "cisc");
|
|
break;
|
|
}
|
|
(*t)(pc, sp, link);
|
|
exits(0);
|
|
}
|
|
|
|
static void
|
|
inithdr(int fd)
|
|
{
|
|
seek(fd, 0, 0);
|
|
if(!crackhdr(fd, &fhdr))
|
|
fatal("read text header");
|
|
|
|
if(syminit(fd, &fhdr) < 0)
|
|
fatal("%r\n");
|
|
}
|
|
|
|
static int
|
|
rtrace(uvlong pc, uvlong sp, uvlong link)
|
|
{
|
|
Symbol s, f;
|
|
char buf[128];
|
|
uvlong oldpc;
|
|
int i;
|
|
|
|
i = 0;
|
|
while(findsym(pc, CTEXT, &s)) {
|
|
if(pc == s.value) /* at first instruction */
|
|
f.value = 0;
|
|
else if(findlocal(&s, FRAMENAME, &f) == 0)
|
|
break;
|
|
|
|
symoff(buf, sizeof buf, pc, CANY);
|
|
fmt(buf, pc);
|
|
|
|
oldpc = pc;
|
|
if(s.type == 'L' || s.type == 'l' || pc <= s.value+mach->pcquant){
|
|
if(link == 0)
|
|
fprint(2, "%s: need to supply a valid link register\n", argv0);
|
|
pc = link;
|
|
}else{
|
|
pc = getval(sp);
|
|
if(pc == 0)
|
|
break;
|
|
}
|
|
|
|
if(pc == 0 || (pc == oldpc && f.value == 0))
|
|
break;
|
|
|
|
sp += f.value;
|
|
|
|
if(++i > 40)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int
|
|
ctrace(uvlong pc, uvlong sp, uvlong link)
|
|
{
|
|
Symbol s;
|
|
char buf[128];
|
|
int found;
|
|
uvlong opc, moved;
|
|
long j;
|
|
|
|
USED(link);
|
|
j = 0;
|
|
opc = 0;
|
|
while(pc && opc != pc) {
|
|
moved = pc2sp(pc);
|
|
if (moved == ~0){
|
|
print("pc2sp(%#.8llux) = -1 %r\n", pc);
|
|
break;
|
|
}
|
|
found = findsym(pc, CTEXT, &s);
|
|
if (!found){
|
|
print("findsym fails\n");
|
|
break;
|
|
}
|
|
symoff(buf, sizeof buf, pc, CANY);
|
|
fmt(buf, pc);
|
|
|
|
sp += moved;
|
|
opc = pc;
|
|
pc = getval(sp);
|
|
if(pc == 0)
|
|
break;
|
|
sp += mach->szaddr; /*assumes address size = stack width*/
|
|
if(++j > 40)
|
|
break;
|
|
}
|
|
return j;
|
|
}
|
|
|
|
static int
|
|
i386trace(uvlong pc, uvlong sp, uvlong link)
|
|
{
|
|
int i;
|
|
uvlong osp;
|
|
Symbol s, f;
|
|
char buf[128];
|
|
|
|
USED(link);
|
|
i = 0;
|
|
osp = 0;
|
|
while(findsym(pc, CTEXT, &s)) {
|
|
|
|
symoff(buf, sizeof buf, pc, CANY);
|
|
fmt(buf, pc);
|
|
|
|
//XXX s.value &= ~(uintptr)0;
|
|
if(pc != s.value) { /* not at first instruction */
|
|
if(findlocal(&s, FRAMENAME, &f) == 0)
|
|
break;
|
|
sp += f.value-mach->szaddr;
|
|
}else if(strcmp(s.name, "forkret") == 0){
|
|
//XXX
|
|
print("//passing interrupt frame; last pc found at sp=%#llux\n", osp);
|
|
|
|
sp += 15 * mach->szaddr; /* pop interrupt frame */
|
|
}
|
|
|
|
pc = getval(sp);
|
|
//XXX
|
|
if(pc == 0 && strcmp(s.name, "forkret") == 0){
|
|
sp += 3 * mach->szaddr; /* pop iret eip, cs, eflags */
|
|
print("//guessing call through invalid pointer, try again at sp=%#llux\n", sp);
|
|
s.name = "";
|
|
pc = getval(sp);
|
|
}
|
|
if(pc == 0) {
|
|
print("//didn't find pc at sp=%#llux, last pc found at sp=%#llux\n", sp, osp);
|
|
break;
|
|
}
|
|
osp = sp;
|
|
|
|
sp += mach->szaddr;
|
|
//XXX
|
|
if(strcmp(s.name, "forkret") == 0)
|
|
sp += 2 * mach->szaddr; /* pop iret cs, eflags */
|
|
|
|
if(++i > 40)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int
|
|
amd64trace(uvlong pc, uvlong sp, uvlong link)
|
|
{
|
|
int i, isintrr;
|
|
uvlong osp;
|
|
Symbol s, f;
|
|
char buf[128];
|
|
|
|
USED(link);
|
|
i = 0;
|
|
osp = 0;
|
|
while(findsym(pc, CTEXT, &s)) {
|
|
|
|
symoff(buf, sizeof buf, pc, CANY);
|
|
fmt(buf, pc);
|
|
|
|
if(strcmp(s.name, "_intrr") == 0)
|
|
isintrr = 1;
|
|
else
|
|
isintrr = 0;
|
|
if(pc != s.value) { /* not at first instruction */
|
|
if(findlocal(&s, FRAMENAME, &f) == 0)
|
|
break;
|
|
sp += f.value-mach->szaddr;
|
|
}
|
|
else if(isintrr){
|
|
print("//passing interrupt frame; last pc found at sp=%#llux\n", osp);
|
|
/*
|
|
* Pop interrupt frame (ureg.h) up to the IP value.
|
|
*/
|
|
sp += 19 * mach->szaddr;
|
|
}
|
|
|
|
pc = getval(sp);
|
|
if(pc == 0 && isintrr){
|
|
/*
|
|
* Pop IP, CS and FLAGS to get to the SP.
|
|
* The AMD64 aligns the interrupt stack on
|
|
* a 16-byte boundary so have the get the
|
|
* SP from the saved frame.
|
|
*/
|
|
sp += 3 * mach->szaddr;
|
|
print("//guessing call through invalid pointer; try again at sp=%#llux\n", sp);
|
|
s.name = "";
|
|
sp = getval(sp);
|
|
pc = getval(sp);
|
|
}
|
|
if(pc == 0) {
|
|
print("//didn't find pc at sp=%#llux, last pc found at sp=%#llux\n", sp, osp);
|
|
break;
|
|
}
|
|
osp = sp;
|
|
|
|
if(!isintrr)
|
|
sp += mach->szaddr;
|
|
|
|
if(++i > 40)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int naddr;
|
|
uvlong addr[1024];
|
|
uvlong val[1024];
|
|
|
|
static void
|
|
putval(uvlong a, uvlong v)
|
|
{
|
|
if(naddr < nelem(addr)){
|
|
addr[naddr] = a;
|
|
val[naddr] = v;
|
|
naddr++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
readstack(void)
|
|
{
|
|
Biobuf b;
|
|
char *p;
|
|
char *f[64];
|
|
int nf, i;
|
|
|
|
Binit(&b, 0, OREAD);
|
|
while(p=Brdline(&b, '\n')){
|
|
p[Blinelen(&b)-1] = 0;
|
|
nf = tokenize(p, f, nelem(f));
|
|
for(i=0; i<nf; i++){
|
|
if(p=strchr(f[i], '=')){
|
|
*p++ = 0;
|
|
putval(strtoull(f[i], 0, 16), strtoull(p, 0, 16));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static uvlong
|
|
getval(uvlong a)
|
|
{
|
|
char buf[256];
|
|
int i, n;
|
|
uvlong r;
|
|
|
|
if(interactive){
|
|
print("// data at %#8.8llux? ", a);
|
|
n = read(0, buf, sizeof(buf)-1);
|
|
if(n <= 0)
|
|
return 0;
|
|
buf[n] = '\0';
|
|
r = strtoull(buf, 0, 16);
|
|
}else{
|
|
r = 0;
|
|
for(i=0; i<naddr; i++)
|
|
if(addr[i] == a)
|
|
r = val[i];
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
fatal(char *fmt, ...)
|
|
{
|
|
char buf[4096];
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
|
va_end(arg);
|
|
fprint(2, "ktrace: %s\n", buf);
|
|
exits(buf);
|
|
}
|