xref: /inferno-os/os/port/portbreak.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "portfns.h"
6 #include "ureg.h"
7 #include "../port/error.h"
8 
9 //
10 // These bits used to be in port/devdbg but were removed in
11 // order to allow for using hardware debug features on certain
12 // architectures
13 //
14 
15 extern void breakset(Bkpt *b);
16 extern void breakrestore(Bkpt *b);
17 extern Bkpt* breakclear(int id);
18 extern void breaknotify(Bkpt *b, Proc *p);
19 extern int breakmatch(BkptCond *cond, Ureg *ur, Proc *p);
20 
21 void	skipfree(Bkpt *b);
22 Bkpt*newskip(ulong addr, Bkpt *skipb, Proc *skipp);
23 Bkpt *skipalloc;
24 extern Bkpt	*breakpoints;
25 typedef struct SkipArg SkipArg;
26 struct SkipArg
27 {
28 	Bkpt *b;
29 	Proc *p;
30 };
31 
32 void
33 skiphandler(Bkpt *b)
34 {
35 	SkipArg *a = b->aux;
36 	Bkpt *l;
37 
38 	if(breakclear(b->id) == nil)
39 		panic("skiphandler: breakclear() failed");
40 	breakrestore(a->b);
41 	l = a->b->link;
42 	while(l != nil) {
43 		breakrestore(l);
44 		l = l->link;
45 	}
46 	skipfree(b);
47 	a->p->dbgstop = 0;		// Whoo!
48 	if(a->p->state == Stopped)
49 		ready(a->p);
50 }
51 
52 Bkpt*
53 newskip(ulong addr, Bkpt *skipb, Proc *skipp)
54 {
55 	Bkpt *b;
56 	SkipArg *a;
57 
58 	b = skipalloc;
59 	if(b == nil)
60 		panic("newskip(): no free skips\n");
61 	skipalloc = b->next;
62 
63 	b->addr = addr;
64 	b->conditions->val = addr;
65 	b->link = nil;
66 	a = b->aux;
67 	a->b = skipb;
68 	a->p = skipp;
69 
70 	return b;
71 }
72 
73 void
74 skipfree(Bkpt *b)
75 {
76 	b->next = skipalloc;
77 	skipalloc = b;
78 }
79 
80 //
81 // Called from the exception handler when a breakpoint instruction has been
82 // hit.  This cannot not be called unless at least one breakpoint with this
83 // address is in the list of breakpoints.  (All breakpoint notifications must
84 // previously have been set via setbreak())
85 //
86 //	foreach breakpoint in list
87 //		if breakpoint matches conditions
88 //			notify the break handler
89 //	if no breakpoints matched the conditions
90 //		pick a random breakpoint set to this address
91 //
92 //		set a breakpoint at the next instruction to be executed,
93 //		and pass the current breakpoint to the "skiphandler"
94 //
95 //		clear the current breakpoint
96 //
97 //		Tell the scheduler to stop scheduling, so the caller is
98 //		guaranteed to execute the instruction, followed by the
99 //		added breakpoint.
100 //
101 //
102 int
103 breakhit(Ureg *ur, Proc *p)
104 {
105 	Bkpt *b;
106 	int nmatched;
107 	Bkpt *skip;
108 
109 	nmatched = 0;
110 	for(b = breakpoints; b != nil; b = b->next) {
111 		if(breakmatch(b->conditions, ur, p)) {
112 			breaknotify(b, p);
113 			++nmatched;
114 		}
115 	}
116 
117 	if(nmatched)
118 		return BrkSched;
119 
120 	skip = nil;
121 	for(b = breakpoints; b != nil;  b = b->next) {
122 		if(b->addr == ur->pc) {
123 			if(breakclear(b->id) == nil)
124 				panic("breakhit: breakclear() failed");
125 
126 			if(skip == nil)
127 				skip = newskip(machnextaddr(ur), b, p);
128 			else {
129 				b->link = skip->link;
130 				skip->link = b;
131 			}
132 		}
133 	}
134 	if(skip == nil)
135 		return BrkSched;
136 	breakset(skip);
137 	return BrkNoSched;
138 }
139 
140 void
141 portbreakinit(void)
142 {
143 	Bkpt *b;
144 	int i;
145 
146 	skipalloc = mallocz(conf.nproc*(sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg)), 1);
147 	if(skipalloc == nil)
148 		error(Enomem);
149 
150 	b = skipalloc;
151 	for(i=0; i < conf.nproc-1; i++) {
152 		b->id = -(i+1);
153 		b->conditions = (BkptCond*)((uchar*)b + sizeof(Bkpt));
154 		b->conditions->op = 'b';
155 		b->handler = skiphandler;
156 		b->aux = (SkipArg*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond));
157 		b->next = (Bkpt*)((uchar*)b+sizeof(Bkpt)+sizeof(BkptCond)+sizeof(SkipArg));
158 		b = b->next;
159 	}
160 	b->next = nil;
161 }
162