This commit was generated by cvs2svn to track changes on a CVS vendor
[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 *
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
46int 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
55boolean_t db_sstep_print;
56int db_loop_count;
57int db_call_depth;
58
59int db_inst_count;
60int db_load_count;
61int db_store_count;
62
63#ifndef db_set_single_step
64void db_set_single_step(/* db_regs_t *regs */); /* forward */
65#endif
66#ifndef db_clear_single_step
67void db_clear_single_step(/* db_regs_t *regs */);
68#endif
69
70boolean_t
71db_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
164void
165db_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
212void
213db_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 */
252db_breakpoint_t db_not_taken_bkpt = 0;
253db_breakpoint_t db_taken_bkpt = 0;
254
255void
256db_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
280void
281db_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
298extern int db_cmd_loop_done;
299
300/* single-step */
301/*ARGSUSED*/
302void
303db_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*/
329void
330db_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*/
351void
352db_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*/
375void
376db_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}