aboutsummaryrefslogtreecommitdiff
path: root/src/libmach/frame.c
blob: 53450805afbf400d0111a0aca232850440703cfe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>

typedef struct LocRegs LocRegs;
struct LocRegs
{
	Regs r;
	Regs *oldregs;
	Map *map;
	u64int *val;
};

static int
locregrw(Regs *regs, char *name, u64int *val, int isr)
{
	int i;
	LocRegs *lr;

	lr = (LocRegs*)regs;
	i = windindex(name);
	if(i == -1)
		return lr->oldregs->rw(lr->oldregs, name, val, isr);
	if(isr){
		*val = lr->val[i];
		return 0;
	}else{
		werrstr("saved registers are immutable");
		return -1;
	}
}

int
stacktrace(Map *map, Regs *regs, Tracer trace)
{
	char *rname;
	int i, ipc, ret;
	u64int nextpc, pc, v;
	u64int *cur, *next;
	LocRegs lr;
	Symbol s, *sp;

	/*
	 * Allocate location arrays.
	 */
	ret = -1;
	cur = malloc(mach->nwindreg*sizeof(cur[0]));
	next = malloc(mach->nwindreg*sizeof(cur[0]));
	if(cur==nil || next==nil)
		goto out;

	/*
	 * Initialize current registers using regs.
	 */
	if(rget(regs, mach->pc, &pc) < 0){
		werrstr("cannot fetch initial pc: %r");
		goto out;
	}

	for(i=0; i<mach->nwindreg; i++){
		rname = mach->windreg[i];
		if(rget(regs, rname, &v) < 0)
			v = ~(ulong)0;
		cur[i] = v;
	}

	ipc = windindex(mach->pc);
	ret = 0;

	/* set up cur[i]==next[i] for unwindframe */
	memmove(next, cur, mach->nwindreg*sizeof(next[0]));
	for(;;){
		sp = &s;
		if(findsym(locaddr(pc), CTEXT, &s) < 0)
			sp = nil;

		lr.r.rw = locregrw;
		lr.oldregs = regs;
		lr.val = cur;
		lr.map = map;
		if((i = unwindframe(map, &lr.r, next, sp)) >= 0)
			nextpc = next[ipc];
		else
			nextpc = ~(ulong)0;
		if((*trace)(map, &lr.r, pc, nextpc, sp, ++ret) <= 0)
			break;
		if(i < 0)
			break;
		if(sp){
			if(strcmp(sp->name, "main") == 0
			|| strcmp(sp->name, "procscheduler") == 0
			|| strcmp(sp->name, "threadstart") == 0)
				break;
		}
		pc = nextpc;
		memmove(cur, next, mach->nwindreg*sizeof(cur[0]));
	}

out:
	free(cur);
	free(next);
	return ret;
}

int
windindex(char *reg)
{
	char **p;
	int i;

	p = mach->windreg;
	for(i=0; i<mach->nwindreg; i++)
		if(strcmp(p[i], reg) == 0)
			return i;
	werrstr("%s is not a winding register", reg);
	return -1;
}

Loc*
windreglocs(void)
{
	int i;
	Loc *loc;

	loc = malloc(mach->nwindreg*sizeof(loc[0]));
	if(loc == nil)
		return nil;
	for(i=0; i<mach->nwindreg; i++){
		loc[i].type = LREG;
		loc[i].reg = mach->windreg[i];
	}
	return loc;
}