386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Thu, 16 Apr 1992 11:50:05 +0000 (03:50 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Thu, 16 Apr 1992 11:50:05 +0000 (03:50 -0800)
Work on file usr/src/sys.386bsd/ddb/db_run.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/sys.386bsd/ddb/db_run.c [new file with mode: 0644]

diff --git a/usr/src/sys.386bsd/ddb/db_run.c b/usr/src/sys.386bsd/ddb/db_run.c
new file mode 100644 (file)
index 0000000..b8e33aa
--- /dev/null
@@ -0,0 +1,426 @@
+/* 
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ * 
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ * 
+ * Carnegie Mellon requests users of this software to return to
+ * 
+ *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ * 
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: db_run.c,v $
+ * Revision 1.1  1992/03/25  21:45:24  pace
+ * Initial revision
+ *
+ * Revision 2.5  91/02/05  17:06:58  mrt
+ *     Changed to new Mach copyright
+ *     [91/01/31  16:19:05  mrt]
+ * 
+ * Revision 2.4  91/01/08  15:09:10  rpd
+ *     Fixed bug in db_restart_at_pc.
+ *     [90/12/07            rpd]
+ *     Added STEP_COUNT and count option to db_continue_cmd.
+ *     Changed db_stop_at_pc to return (modified) is_breakpoint.
+ *     Fixed db_stop_at_pc to print newlines in the right places.
+ *     [90/11/27            rpd]
+ * 
+ * Revision 2.3  90/10/25  14:43:59  rwd
+ *     Changed db_find_breakpoint to db_find_breakpoint_here.
+ *     [90/10/18            rpd]
+ * 
+ *     Fixed db_set_single_step to pass regs to branch_taken.
+ *     Added watchpoint argument to db_restart_at_pc.
+ *     [90/10/17            rpd]
+ *     Generalized the watchpoint support.
+ *     [90/10/16            rwd]
+ *     Added watchpoint support.
+ *     [90/10/16            rpd]
+ * 
+ * Revision 2.2  90/08/27  21:51:59  dbg
+ *     Fixed names for single-step functions.
+ *     [90/08/20            af]
+ *     Reduce lint.
+ *     [90/08/07            dbg]
+ *     Created.
+ *     [90/07/25            dbg]
+ * 
+ */
+/*
+ *     Author: David B. Golub, Carnegie Mellon University
+ *     Date:   7/90
+ */
+
+/*
+ * Commands to run process.
+ */
+#include "param.h"
+#include "proc.h"
+#include <machine/db_machdep.h>
+
+#include <ddb/db_lex.h>
+#include <ddb/db_break.h>
+#include <ddb/db_access.h>
+
+int    db_run_mode;
+#define        STEP_NONE       0
+#define        STEP_ONCE       1
+#define        STEP_RETURN     2
+#define        STEP_CALLT      3
+#define        STEP_CONTINUE   4
+#define STEP_INVISIBLE 5
+#define        STEP_COUNT      6
+
+boolean_t      db_sstep_print;
+int            db_loop_count;
+int            db_call_depth;
+
+int            db_inst_count;
+int            db_load_count;
+int            db_store_count;
+
+#ifndef db_set_single_step
+void           db_set_single_step(/* db_regs_t *regs */);      /* forward */
+#endif
+#ifndef db_clear_single_step
+void           db_clear_single_step(/* db_regs_t *regs */);
+#endif
+
+boolean_t
+db_stop_at_pc(is_breakpoint)
+       boolean_t       *is_breakpoint;
+{
+       register db_addr_t      pc;
+       register db_breakpoint_t bkpt;
+
+       db_clear_single_step(DDB_REGS);
+       db_clear_breakpoints();
+       db_clear_watchpoints();
+       pc = PC_REGS(DDB_REGS);
+
+#ifdef FIXUP_PC_AFTER_BREAK
+       if (*is_breakpoint) {
+           /*
+            * Breakpoint trap.  Fix up the PC if the
+            * machine requires it.
+            */
+           FIXUP_PC_AFTER_BREAK
+           pc = PC_REGS(DDB_REGS);
+       }
+#endif
+
+       /*
+        * Now check for a breakpoint at this address.
+        */
+       bkpt = db_find_breakpoint_here(pc);
+       if (bkpt) {
+           if (--bkpt->count == 0) {
+               bkpt->count = bkpt->init_count;
+               *is_breakpoint = TRUE;
+               return (TRUE);  /* stop here */
+           }
+       } else if (*is_breakpoint) {
+               ddb_regs.tf_eip += 1;
+       }
+               
+       *is_breakpoint = FALSE;
+
+       if (db_run_mode == STEP_INVISIBLE) {
+           db_run_mode = STEP_CONTINUE;
+           return (FALSE);     /* continue */
+       }
+       if (db_run_mode == STEP_COUNT) {
+           return (FALSE); /* continue */
+       }
+       if (db_run_mode == STEP_ONCE) {
+           if (--db_loop_count > 0) {
+               if (db_sstep_print) {
+                   db_printf("\t\t");
+                   db_print_loc_and_inst(pc);
+                   db_printf("\n");
+               }
+               return (FALSE); /* continue */
+           }
+       }
+       if (db_run_mode == STEP_RETURN) {
+           db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
+
+           /* continue until matching return */
+
+           if (!inst_trap_return(ins) &&
+               (!inst_return(ins) || --db_call_depth != 0)) {
+               if (db_sstep_print) {
+                   if (inst_call(ins) || inst_return(ins)) {
+                       register int i;
+
+                       db_printf("[after %6d]     ", db_inst_count);
+                       for (i = db_call_depth; --i > 0; )
+                           db_printf("  ");
+                       db_print_loc_and_inst(pc);
+                       db_printf("\n");
+                   }
+               }
+               if (inst_call(ins))
+                   db_call_depth++;
+               return (FALSE); /* continue */
+           }
+       }
+       if (db_run_mode == STEP_CALLT) {
+           db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
+
+           /* continue until call or return */
+
+           if (!inst_call(ins) &&
+               !inst_return(ins) &&
+               !inst_trap_return(ins)) {
+               return (FALSE); /* continue */
+           }
+       }
+       db_run_mode = STEP_NONE;
+       return (TRUE);
+}
+
+void
+db_restart_at_pc(watchpt)
+       boolean_t watchpt;
+{
+       register db_addr_t      pc = PC_REGS(DDB_REGS);
+
+       if ((db_run_mode == STEP_COUNT) ||
+           (db_run_mode == STEP_RETURN) ||
+           (db_run_mode == STEP_CALLT)) {
+           db_expr_t           ins;
+
+           /*
+            * We are about to execute this instruction,
+            * so count it now.
+            */
+
+           ins = db_get_value(pc, sizeof(int), FALSE);
+           db_inst_count++;
+           db_load_count += inst_load(ins);
+           db_store_count += inst_store(ins);
+#ifdef SOFTWARE_SSTEP
+           /* XXX works on mips, but... */
+           if (inst_branch(ins) || inst_call(ins)) {
+               ins = db_get_value(next_instr_address(pc,1),
+                                  sizeof(int), FALSE);
+               db_inst_count++;
+               db_load_count += inst_load(ins);
+               db_store_count += inst_store(ins);
+           }
+#endif SOFTWARE_SSTEP
+       }
+
+       if (db_run_mode == STEP_CONTINUE) {
+           if (watchpt || db_find_breakpoint_here(pc)) {
+               /*
+                * Step over breakpoint/watchpoint.
+                */
+               db_run_mode = STEP_INVISIBLE;
+               db_set_single_step(DDB_REGS);
+           } else {
+               db_set_breakpoints();
+               db_set_watchpoints();
+           }
+       } else {
+           db_set_single_step(DDB_REGS);
+       }
+}
+
+void
+db_single_step(regs)
+       db_regs_t *regs;
+{
+       if (db_run_mode == STEP_CONTINUE) {
+           db_run_mode = STEP_INVISIBLE;
+           db_set_single_step(regs);
+       }
+}
+
+#ifdef SOFTWARE_SSTEP
+/*
+ *     Software implementation of single-stepping.
+ *     If your machine does not have a trace mode
+ *     similar to the vax or sun ones you can use
+ *     this implementation, done for the mips.
+ *     Just define the above conditional and provide
+ *     the functions/macros defined below.
+ *
+ * extern boolean_t
+ *     inst_branch(),          returns true if the instruction might branch
+ * extern unsigned
+ *     branch_taken(),         return the address the instruction might
+ *                             branch to
+ *     db_getreg_val();        return the value of a user register,
+ *                             as indicated in the hardware instruction
+ *                             encoding, e.g. 8 for r8
+ *                     
+ * next_instr_address(pc,bd)   returns the address of the first
+ *                             instruction following the one at "pc",
+ *                             which is either in the taken path of
+ *                             the branch (bd==1) or not.  This is
+ *                             for machines (mips) with branch delays.
+ *
+ *     A single-step may involve at most 2 breakpoints -
+ *     one for branch-not-taken and one for branch taken.
+ *     If one of these addresses does not already have a breakpoint,
+ *     we allocate a breakpoint and save it here.
+ *     These breakpoints are deleted on return.
+ */                    
+db_breakpoint_t        db_not_taken_bkpt = 0;
+db_breakpoint_t        db_taken_bkpt = 0;
+
+void
+db_set_single_step(regs)
+       register db_regs_t *regs;
+{
+       db_addr_t pc = PC_REGS(regs);
+       register unsigned        inst, brpc;
+
+       /*
+        *      User was stopped at pc, e.g. the instruction
+        *      at pc was not executed.
+        */
+       inst = db_get_value(pc, sizeof(int), FALSE);
+       if (inst_branch(inst) || inst_call(inst)) {
+           extern unsigned getreg_val();
+
+           brpc = branch_taken(inst, pc, getreg_val, regs);
+           if (brpc != pc) {   /* self-branches are hopeless */
+               db_taken_bkpt = db_set_temp_breakpoint(brpc);
+           }
+           pc = next_instr_address(pc,1);
+       }
+       pc = next_instr_address(pc,0);
+       db_not_taken_bkpt = db_set_temp_breakpoint(pc);
+}
+
+void
+db_clear_single_step(regs)
+       db_regs_t *regs;
+{
+       register db_breakpoint_t        bkpt;
+
+       if (db_taken_bkpt != 0) {
+           db_delete_temp_breakpoint(db_taken_bkpt);
+           db_taken_bkpt = 0;
+       }
+       if (db_not_taken_bkpt != 0) {
+           db_delete_temp_breakpoint(db_not_taken_bkpt);
+           db_not_taken_bkpt = 0;
+       }
+}
+
+#endif SOFTWARE_SSTEP
+
+extern int     db_cmd_loop_done;
+
+/* single-step */
+/*ARGSUSED*/
+void
+db_single_step_cmd(addr, have_addr, count, modif)
+       db_expr_t       addr;
+       int             have_addr;
+       db_expr_t       count;
+       char *          modif;
+{
+       boolean_t       print = FALSE;
+
+       if (count == -1)
+           count = 1;
+
+       if (modif[0] == 'p')
+           print = TRUE;
+
+       db_run_mode = STEP_ONCE;
+       db_loop_count = count;
+       db_sstep_print = print;
+       db_inst_count = 0;
+       db_load_count = 0;
+       db_store_count = 0;
+
+       db_cmd_loop_done = 1;
+}
+
+/* trace and print until call/return */
+/*ARGSUSED*/
+void
+db_trace_until_call_cmd(addr, have_addr, count, modif)
+       db_expr_t       addr;
+       int             have_addr;
+       db_expr_t       count;
+       char *          modif;
+{
+       boolean_t       print = FALSE;
+
+       if (modif[0] == 'p')
+           print = TRUE;
+
+       db_run_mode = STEP_CALLT;
+       db_sstep_print = print;
+       db_inst_count = 0;
+       db_load_count = 0;
+       db_store_count = 0;
+
+       db_cmd_loop_done = 1;
+}
+
+/*ARGSUSED*/
+void
+db_trace_until_matching_cmd(addr, have_addr, count, modif)
+       db_expr_t       addr;
+       int             have_addr;
+       db_expr_t       count;
+       char *          modif;
+{
+       boolean_t       print = FALSE;
+
+       if (modif[0] == 'p')
+           print = TRUE;
+
+       db_run_mode = STEP_RETURN;
+       db_call_depth = 1;
+       db_sstep_print = print;
+       db_inst_count = 0;
+       db_load_count = 0;
+       db_store_count = 0;
+
+       db_cmd_loop_done = 1;
+}
+
+/* continue */
+/*ARGSUSED*/
+void
+db_continue_cmd(addr, have_addr, count, modif)
+       db_expr_t       addr;
+       int             have_addr;
+       db_expr_t       count;
+       char *          modif;
+{
+       if (modif[0] == 'c')
+           db_run_mode = STEP_COUNT;
+       else
+           db_run_mode = STEP_CONTINUE;
+       db_inst_count = 0;
+       db_load_count = 0;
+       db_store_count = 0;
+
+       db_cmd_loop_done = 1;
+}