Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Mach Operating System | |
3 | * Copyright (c) 1991,1990 Carnegie Mellon University | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * Permission to use, copy, modify and distribute this software and its | |
7 | * documentation is hereby granted, provided that both the copyright | |
8 | * notice and this permission notice appear in all copies of the | |
9 | * software, derivative works or modified versions, and any portions | |
10 | * thereof, and that both notices appear in supporting documentation. | |
11 | * | |
12 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS | |
13 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
14 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
15 | * | |
16 | * Carnegie Mellon requests users of this software to return to | |
17 | * | |
18 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
19 | * School of Computer Science | |
20 | * Carnegie Mellon University | |
21 | * Pittsburgh PA 15213-3890 | |
22 | * | |
23 | * any improvements or extensions that they make and grant Carnegie the | |
24 | * rights to redistribute these changes. | |
15637ed4 | 25 | * |
4c45483e | 26 | * $Id: db_run.c,v 1.2 1993/10/16 16:47:24 rgrimes Exp $ |
15637ed4 | 27 | */ |
cbeffc91 | 28 | |
15637ed4 RG |
29 | /* |
30 | * Author: David B. Golub, Carnegie Mellon University | |
31 | * Date: 7/90 | |
32 | */ | |
33 | ||
34 | /* | |
35 | * Commands to run process. | |
36 | */ | |
37 | #include "param.h" | |
4c45483e | 38 | #include "systm.h" |
15637ed4 | 39 | #include "proc.h" |
4c45483e | 40 | #include "ddb/ddb.h" |
15637ed4 RG |
41 | |
42 | #include <ddb/db_lex.h> | |
43 | #include <ddb/db_break.h> | |
44 | #include <ddb/db_access.h> | |
45 | ||
46 | int db_run_mode; | |
47 | #define STEP_NONE 0 | |
48 | #define STEP_ONCE 1 | |
49 | #define STEP_RETURN 2 | |
50 | #define STEP_CALLT 3 | |
51 | #define STEP_CONTINUE 4 | |
52 | #define STEP_INVISIBLE 5 | |
53 | #define STEP_COUNT 6 | |
54 | ||
55 | boolean_t db_sstep_print; | |
56 | int db_loop_count; | |
57 | int db_call_depth; | |
58 | ||
59 | int db_inst_count; | |
60 | int db_load_count; | |
61 | int db_store_count; | |
62 | ||
63 | #ifndef db_set_single_step | |
64 | void db_set_single_step(/* db_regs_t *regs */); /* forward */ | |
65 | #endif | |
66 | #ifndef db_clear_single_step | |
67 | void db_clear_single_step(/* db_regs_t *regs */); | |
68 | #endif | |
69 | ||
70 | boolean_t | |
71 | db_stop_at_pc(is_breakpoint) | |
72 | boolean_t *is_breakpoint; | |
73 | { | |
74 | register db_addr_t pc; | |
75 | register db_breakpoint_t bkpt; | |
76 | ||
77 | db_clear_single_step(DDB_REGS); | |
78 | db_clear_breakpoints(); | |
79 | db_clear_watchpoints(); | |
80 | pc = PC_REGS(DDB_REGS); | |
81 | ||
82 | #ifdef FIXUP_PC_AFTER_BREAK | |
83 | if (*is_breakpoint) { | |
84 | /* | |
85 | * Breakpoint trap. Fix up the PC if the | |
86 | * machine requires it. | |
87 | */ | |
88 | FIXUP_PC_AFTER_BREAK | |
89 | pc = PC_REGS(DDB_REGS); | |
90 | } | |
91 | #endif | |
92 | ||
93 | /* | |
94 | * Now check for a breakpoint at this address. | |
95 | */ | |
96 | bkpt = db_find_breakpoint_here(pc); | |
97 | if (bkpt) { | |
98 | if (--bkpt->count == 0) { | |
99 | bkpt->count = bkpt->init_count; | |
100 | *is_breakpoint = TRUE; | |
101 | return (TRUE); /* stop here */ | |
102 | } | |
103 | } else if (*is_breakpoint) { | |
104 | ddb_regs.tf_eip += 1; | |
105 | } | |
106 | ||
107 | *is_breakpoint = FALSE; | |
108 | ||
109 | if (db_run_mode == STEP_INVISIBLE) { | |
110 | db_run_mode = STEP_CONTINUE; | |
111 | return (FALSE); /* continue */ | |
112 | } | |
113 | if (db_run_mode == STEP_COUNT) { | |
114 | return (FALSE); /* continue */ | |
115 | } | |
116 | if (db_run_mode == STEP_ONCE) { | |
117 | if (--db_loop_count > 0) { | |
118 | if (db_sstep_print) { | |
119 | db_printf("\t\t"); | |
120 | db_print_loc_and_inst(pc); | |
121 | db_printf("\n"); | |
122 | } | |
123 | return (FALSE); /* continue */ | |
124 | } | |
125 | } | |
126 | if (db_run_mode == STEP_RETURN) { | |
127 | db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); | |
128 | ||
129 | /* continue until matching return */ | |
130 | ||
131 | if (!inst_trap_return(ins) && | |
132 | (!inst_return(ins) || --db_call_depth != 0)) { | |
133 | if (db_sstep_print) { | |
134 | if (inst_call(ins) || inst_return(ins)) { | |
135 | register int i; | |
136 | ||
137 | db_printf("[after %6d] ", db_inst_count); | |
138 | for (i = db_call_depth; --i > 0; ) | |
139 | db_printf(" "); | |
140 | db_print_loc_and_inst(pc); | |
141 | db_printf("\n"); | |
142 | } | |
143 | } | |
144 | if (inst_call(ins)) | |
145 | db_call_depth++; | |
146 | return (FALSE); /* continue */ | |
147 | } | |
148 | } | |
149 | if (db_run_mode == STEP_CALLT) { | |
150 | db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); | |
151 | ||
152 | /* continue until call or return */ | |
153 | ||
154 | if (!inst_call(ins) && | |
155 | !inst_return(ins) && | |
156 | !inst_trap_return(ins)) { | |
157 | return (FALSE); /* continue */ | |
158 | } | |
159 | } | |
160 | db_run_mode = STEP_NONE; | |
161 | return (TRUE); | |
162 | } | |
163 | ||
164 | void | |
165 | db_restart_at_pc(watchpt) | |
166 | boolean_t watchpt; | |
167 | { | |
168 | register db_addr_t pc = PC_REGS(DDB_REGS); | |
169 | ||
170 | if ((db_run_mode == STEP_COUNT) || | |
171 | (db_run_mode == STEP_RETURN) || | |
172 | (db_run_mode == STEP_CALLT)) { | |
173 | db_expr_t ins; | |
174 | ||
175 | /* | |
176 | * We are about to execute this instruction, | |
177 | * so count it now. | |
178 | */ | |
179 | ||
180 | ins = db_get_value(pc, sizeof(int), FALSE); | |
181 | db_inst_count++; | |
182 | db_load_count += inst_load(ins); | |
183 | db_store_count += inst_store(ins); | |
184 | #ifdef SOFTWARE_SSTEP | |
185 | /* XXX works on mips, but... */ | |
186 | if (inst_branch(ins) || inst_call(ins)) { | |
187 | ins = db_get_value(next_instr_address(pc,1), | |
188 | sizeof(int), FALSE); | |
189 | db_inst_count++; | |
190 | db_load_count += inst_load(ins); | |
191 | db_store_count += inst_store(ins); | |
192 | } | |
193 | #endif SOFTWARE_SSTEP | |
194 | } | |
195 | ||
196 | if (db_run_mode == STEP_CONTINUE) { | |
197 | if (watchpt || db_find_breakpoint_here(pc)) { | |
198 | /* | |
199 | * Step over breakpoint/watchpoint. | |
200 | */ | |
201 | db_run_mode = STEP_INVISIBLE; | |
202 | db_set_single_step(DDB_REGS); | |
203 | } else { | |
204 | db_set_breakpoints(); | |
205 | db_set_watchpoints(); | |
206 | } | |
207 | } else { | |
208 | db_set_single_step(DDB_REGS); | |
209 | } | |
210 | } | |
211 | ||
212 | void | |
213 | db_single_step(regs) | |
214 | db_regs_t *regs; | |
215 | { | |
216 | if (db_run_mode == STEP_CONTINUE) { | |
217 | db_run_mode = STEP_INVISIBLE; | |
218 | db_set_single_step(regs); | |
219 | } | |
220 | } | |
221 | ||
222 | #ifdef SOFTWARE_SSTEP | |
223 | /* | |
224 | * Software implementation of single-stepping. | |
225 | * If your machine does not have a trace mode | |
226 | * similar to the vax or sun ones you can use | |
227 | * this implementation, done for the mips. | |
228 | * Just define the above conditional and provide | |
229 | * the functions/macros defined below. | |
230 | * | |
231 | * extern boolean_t | |
232 | * inst_branch(), returns true if the instruction might branch | |
233 | * extern unsigned | |
234 | * branch_taken(), return the address the instruction might | |
235 | * branch to | |
236 | * db_getreg_val(); return the value of a user register, | |
237 | * as indicated in the hardware instruction | |
238 | * encoding, e.g. 8 for r8 | |
239 | * | |
240 | * next_instr_address(pc,bd) returns the address of the first | |
241 | * instruction following the one at "pc", | |
242 | * which is either in the taken path of | |
243 | * the branch (bd==1) or not. This is | |
244 | * for machines (mips) with branch delays. | |
245 | * | |
246 | * A single-step may involve at most 2 breakpoints - | |
247 | * one for branch-not-taken and one for branch taken. | |
248 | * If one of these addresses does not already have a breakpoint, | |
249 | * we allocate a breakpoint and save it here. | |
250 | * These breakpoints are deleted on return. | |
251 | */ | |
252 | db_breakpoint_t db_not_taken_bkpt = 0; | |
253 | db_breakpoint_t db_taken_bkpt = 0; | |
254 | ||
255 | void | |
256 | db_set_single_step(regs) | |
257 | register db_regs_t *regs; | |
258 | { | |
259 | db_addr_t pc = PC_REGS(regs); | |
260 | register unsigned inst, brpc; | |
261 | ||
262 | /* | |
263 | * User was stopped at pc, e.g. the instruction | |
264 | * at pc was not executed. | |
265 | */ | |
266 | inst = db_get_value(pc, sizeof(int), FALSE); | |
267 | if (inst_branch(inst) || inst_call(inst)) { | |
268 | extern unsigned getreg_val(); | |
269 | ||
270 | brpc = branch_taken(inst, pc, getreg_val, regs); | |
271 | if (brpc != pc) { /* self-branches are hopeless */ | |
272 | db_taken_bkpt = db_set_temp_breakpoint(brpc); | |
273 | } | |
274 | pc = next_instr_address(pc,1); | |
275 | } | |
276 | pc = next_instr_address(pc,0); | |
277 | db_not_taken_bkpt = db_set_temp_breakpoint(pc); | |
278 | } | |
279 | ||
280 | void | |
281 | db_clear_single_step(regs) | |
282 | db_regs_t *regs; | |
283 | { | |
284 | register db_breakpoint_t bkpt; | |
285 | ||
286 | if (db_taken_bkpt != 0) { | |
287 | db_delete_temp_breakpoint(db_taken_bkpt); | |
288 | db_taken_bkpt = 0; | |
289 | } | |
290 | if (db_not_taken_bkpt != 0) { | |
291 | db_delete_temp_breakpoint(db_not_taken_bkpt); | |
292 | db_not_taken_bkpt = 0; | |
293 | } | |
294 | } | |
295 | ||
296 | #endif SOFTWARE_SSTEP | |
297 | ||
298 | extern int db_cmd_loop_done; | |
299 | ||
300 | /* single-step */ | |
301 | /*ARGSUSED*/ | |
302 | void | |
303 | db_single_step_cmd(addr, have_addr, count, modif) | |
304 | db_expr_t addr; | |
305 | int have_addr; | |
306 | db_expr_t count; | |
307 | char * modif; | |
308 | { | |
309 | boolean_t print = FALSE; | |
310 | ||
311 | if (count == -1) | |
312 | count = 1; | |
313 | ||
314 | if (modif[0] == 'p') | |
315 | print = TRUE; | |
316 | ||
317 | db_run_mode = STEP_ONCE; | |
318 | db_loop_count = count; | |
319 | db_sstep_print = print; | |
320 | db_inst_count = 0; | |
321 | db_load_count = 0; | |
322 | db_store_count = 0; | |
323 | ||
324 | db_cmd_loop_done = 1; | |
325 | } | |
326 | ||
327 | /* trace and print until call/return */ | |
328 | /*ARGSUSED*/ | |
329 | void | |
330 | db_trace_until_call_cmd(addr, have_addr, count, modif) | |
331 | db_expr_t addr; | |
332 | int have_addr; | |
333 | db_expr_t count; | |
334 | char * modif; | |
335 | { | |
336 | boolean_t print = FALSE; | |
337 | ||
338 | if (modif[0] == 'p') | |
339 | print = TRUE; | |
340 | ||
341 | db_run_mode = STEP_CALLT; | |
342 | db_sstep_print = print; | |
343 | db_inst_count = 0; | |
344 | db_load_count = 0; | |
345 | db_store_count = 0; | |
346 | ||
347 | db_cmd_loop_done = 1; | |
348 | } | |
349 | ||
350 | /*ARGSUSED*/ | |
351 | void | |
352 | db_trace_until_matching_cmd(addr, have_addr, count, modif) | |
353 | db_expr_t addr; | |
354 | int have_addr; | |
355 | db_expr_t count; | |
356 | char * modif; | |
357 | { | |
358 | boolean_t print = FALSE; | |
359 | ||
360 | if (modif[0] == 'p') | |
361 | print = TRUE; | |
362 | ||
363 | db_run_mode = STEP_RETURN; | |
364 | db_call_depth = 1; | |
365 | db_sstep_print = print; | |
366 | db_inst_count = 0; | |
367 | db_load_count = 0; | |
368 | db_store_count = 0; | |
369 | ||
370 | db_cmd_loop_done = 1; | |
371 | } | |
372 | ||
373 | /* continue */ | |
374 | /*ARGSUSED*/ | |
375 | void | |
376 | db_continue_cmd(addr, have_addr, count, modif) | |
377 | db_expr_t addr; | |
378 | int have_addr; | |
379 | db_expr_t count; | |
380 | char * modif; | |
381 | { | |
382 | if (modif[0] == 'c') | |
383 | db_run_mode = STEP_COUNT; | |
384 | else | |
385 | db_run_mode = STEP_CONTINUE; | |
386 | db_inst_count = 0; | |
387 | db_load_count = 0; | |
388 | db_store_count = 0; | |
389 | ||
390 | db_cmd_loop_done = 1; | |
391 | } |