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