/***********************************************************************************
Preliminary notes. There were sveral problems with the lab setup, the $PATH variable did
not appear to be working properly so Makefiles were set explicitly. There was also some
problems with permissions, so the command chmod -R a+rwx * was executed.
Our implementation differs slightly in that it was done at home. This entailed several
things. Minix was setup and a dual booting system was created using Mloader by
Claudio Tantignone - c_tantignone@newage.com.ar
Terry McConnell tmc@barnyard.syr.edu
which is an extended hack of Loadlin. It boots off a parttion rather than from a
bootable image ina DOS partition.
Files were copied between the two partitions using MinTools by Terry R. McConnell
it allows you to browse and copy between a DOS partition and a Minix partition
from within DOS and saved ALOT of time.
Because we did this at home first we set-up a new user 'student' and then copied
across /usr/src to our home directory. To work on there. This means that when we
demonstrate at college we shall ahve to do the same and then delete the directorys
mm, fs, tools, kernel, lib and the file Makefile and untar our version in their place.
************************************************************************************/
5.1 Preparation
/include/minix/callnr.h
#ifdef ENABLE_DEBUGGING
#define EXECDBG 49
#define DBUG 50
/* DEBUG is already used */
#endif
in include/minix/config.h
added
#define ENABLE_DEBUGGING 0
5.2 First stage : introducing the system call execdbg
introduced a global variable called
int callfunc = EXEC
copied execle as execdbg *EXCEPT* changed this bit
call_func = EXECDBG; <<<<<<<<<<<
va_start(argp, name);
/* The following cast of argp is not portable, as for execl(). */
p = (char **) argp;
while (*p++ != NIL_PTR)
; /* null statement */
result = execve(name, (char **) argp, (char **) *p);
va_end(argp);
call_func = EXEC; <<<<<<<<<<<
then in execve
#ifdef ENABLE_DEBUGGING
(void) _syscall(MM, call_func, &m);
#else
(void) _syscall(MM, EXEC, &m);
#endif
this allowed us to keep execle being called by both execdbg and execle so that we didn't have to write a new exedbgve function with all the hassle that that invloves or cat together execdbg and execve into a super function.
mm/main.c
#ifdef ENABLE_DEBUGGING
if (mm_call == EXECDBG && error == OK)
{
printf("Not replying to msg\n");
continue;
}
#endif
if (mm_call == EXEC && error == OK) continue;
mm/table.c
#ifdef ENABLE_DEBUGGING
do_execdbg, /* 49 = execdbg */
no_sys, /* 50 = debug */
#else
no_sys, /* 49 = unused */
no_sys, /* 50 = unused */
#endif
fs/table.c
#ifdef ENABLE_DEBUGGING
no_sys, /* 49 = execdbg */
do_debug, /* 50 = dbug */
#else
no_sys, /* 49 = unused */
no_sys, /* 50 = unused */
#endif
note how they are in the same place as the additions to callnr.h. This aslo implements the changes to the fs and mm in the second stage (5.3)
include/unistd.h
#ifdef ENABLE_DEBUGGING
_PROTOTYPE( int execdbg, (const char *_path, const char *_arg, ...) );
_PROTOTYPE( int debug, (const char *buffer, int instruction) );
#endif
also, and this is not mentioned in the lab spec, copied
src/lib/syscall/execle.s
as execdbg.s
.sect .text
.extern __execdbg
.define _execdbg
.align 2
_execdbg:
jmp __execdbg
5.3 Second stage: intoroducing the system call debug
src/lib/posic/_debug.c
#include <lib.h>
#include <minix/minlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#if ENABLE_DEBUGGING
#define debug _debug
#ifdef _ANSI
PUBLIC int debug(const char *buffer, int instruction)
#else
PUBLIC int debug(buffer, instruction)
char *buffer;
int instruction;
#endif
{
message msg;
msg.m2_p1 = (char *) buffer;
msg.m2_i3 = instruction;
return (_syscall(FS, DBUG, &msg));
}
#endif
fs/table.c and mm/table.c have already been changed
fs/debug.c
#include "fs.h"
#include <minix/com.h>
#include <minix/callnr.h>
#ifdef ENABLE_DEBUGGING
#include "../kernel/debugger.h"
PUBLIC int do_debug(void)
{
message msg;
msg.m_type = INSTRUCTION;
msg.REQUEST = m.REQUEST;
msg.PROC_NR = m.m_source;
msg.m2_p1 = m.m2_p1;
msg.m2_i3 = m.m2_i3;
sendrec(DEBUGGER, &msg);
switch (msg.REP_STATUS)
{
case SUSPEND:
printf("do_debug: about to suspend\n");
suspend(msg.REP_PROC_NR);
printf("do_debug: suspended\n");
break;
}
return(OK);
}
#endif
5.4 Third stage: installing the task
include/minix/com.h
#ifdef ENABLE_DEBUGGING
#define WINCHESTER (DEBUGGER - ENABLE_WINI)
/* winchester (hard) disk class */
#define DEBUGGER (SYN_ALRM_TASK - ENABLE_DEBUGGING)
/* debugger task */
#else
#define WINCHESTER (SYN_ALRM_TASK - ENABLE_WINI)
/* winchester (hard) disk class */
#endif
include/minix/const.h
#ifndef ENABLE_DEBUGGING
#define NR_TASKS (9 + ENABLE_WINI + ENABLE_SCSI + ENABLE_CDROM \
+ ENABLE_NETWORKING + 2 * ENABLE_AUDIO)
#else
#define NR_TASKS (9 + ENABLE_DEBUGGING + ENABLE_WINI + ENABLE_SCSI \
+ ENABLE_CDROM \
+ ENABLE_NETWORKING + 2 * ENABLE_AUDIO)
#endif
copied across their src/kernel/debugger.c currently ...
PUBLIC void debug_task(void)
{
/* Main routine of the debugger. Messages are received and handled here */
message msg;
init_debug();
while(TRUE){
receive (ANY, &msg);
switch(msg.m_type)
{
case CHILD_DIED: do_child_died(&msg); break;
case DEBUG_START: do_start (&msg); break;
case CANCEL: do_cancel (&msg); break;
case INSTRUCTION: do_evaluate(&msg); break;
case HARD_INT: do_interrupt(); break;
default:
{
if (msg.m_source == FS_PROC_NR)
reply (TASK_REPLY, msg.m_source,
msg.PROC_NR, EINVAL);
else mm_reply (EINVAL);
}
}
}
}
src/kernel/proto.h
/* debugger.c */
_PROTOTYPE( void debug_task, (void) );
_PROTOTYPE( void trace, (void) );
_PROTOTYPE( void breakpoint, (void) );
src/kernel/execption.c
#ifdef ENABLE_DEBUGGING
if(vec_nr == 1)
{
trace();
return;
}
if(vec_nr == 3)
{
breakpoint();
return;
}
#endif
src/kernel/table.c
#ifdef ENABLE_DEBUGGING
#define DEBUG_STACK SMALL_STACK
#define TOT_STACK_SPACE (TTY_STACK + DP8390_STACK + SCSI_STACK + \
SYN_ALRM_STACK + IDLE_STACK + HARDWARE_STACK + PRINTER_STACK + \
WINCH_STACK + FLOP_STACK + MEM_STACK + CLOCK_STACK + SYS_STACK + \
CDROM_STACK + AUDIO_STACK + MIXER_STACK + DEBUG_STACK)
#else
#define TOT_STACK_SPACE (TTY_STACK + DP8390_STACK + SCSI_STACK + \
SYN_ALRM_STACK + IDLE_STACK + HARDWARE_STACK + PRINTER_STACK + \
WINCH_STACK + FLOP_STACK + MEM_STACK + CLOCK_STACK + SYS_STACK + \
CDROM_STACK + AUDIO_STACK + MIXER_STACK)
#endif
src/kernel/Makefile
debug.o: $a
debug.o: $h/callnr.h
debug.o: $h/com.h
debug.o: proc.h
mm/exec.c
#ifdef ENABLE_DEBUGGING
#include "../kernel/debugger.h"
#include <minix/com.h>
#endif
and
#ifdef ENABLE_DEBUGGING
/*===========================================================================*
* do_execdbg *
*===========================================================================*/
PUBLIC int do_execdbg()
{
int result;
message msg;
printf("About to call exec\n");
result = do_exec();
printf("...Returned from exec (%d)\n", result);
msg.m_type = DEBUG_START;
printf("...About to send message to debugger\n");
sendrec(DEBUGGER, &msg);
printf("...Returned from sending message to debugger\n");
return(result);
}
#endif
5.5 Fourth stage: requiring information
src/kernel/debugger.c
PRIVATE void do_procadm (message *proc_m)
{
printf("DEBUGGER> do_procadm() called by %d %d\n", proc_m->PROC_NR, proc_m->m_source);
/* Remember the parent's details */
parent_proc_nr = proc_m->PROC_NR;
parent_proc = proc_addr(parent_proc_nr);
parents_copy = (vir_bytes) proc_m->m2_p1;
/* Suspend the parent */
fs_reply(TASK_REPLY, proc_m->PROC_NR, SUSPEND);
parent_suspended = 1;
/* Revive the child */
if(done_first_trace)
{
struct proc *child_proc = proc_addr(child_proc_nr);
lock_ready(child_proc);
child_suspended = 0;
}
current_state();
printf("DEBUGGER> do_procadm() finished\n");
}
5.6 Fifth stage: cancelling
src/kernel/debugger.c
PRIVATE void do_cancel(message *msg)
{
int i;
for(i = 0; i < 20; i++)
printf("_________________CANCEL_________________");
reply(TASK_REPLY, msg->m_source, msg->PROC_NR, EINTR);
init_debug();
}
*****
NB Simon: we added something in forkexit.c (mm, i think) to send a message
to the debugger when any task exits. We then check if it's the child or
parent, and deal as approprate. THis probably isn't the best way to do it,
but it works - fluffy
*****
5.7 Sixth stage: changing to tarce mode
src/kernel/debugger.c
PRIVATE void do_trace (message *trace_m)
{
printf("DEBUGGER> do_trace for child '%s' (%d)\n", child_proc->p_name, child_proc_nr);
/* Set the trace bit */
child_proc->p_reg.psw |= TRACEBIT;
child_tracebit = 1;
/* Reply to the parent with OK */
fs_reply(TASK_REPLY, trace_m->PROC_NR, OK);
current_state();
printf("DEBUGGER> do_trace() done\n");
}
PUBLIC void trace()
{
interrupt_type = STEP;
interrupt(DEBUGGER);
}
* n.b this does NOT send a message of type DEBUG_STEP as per lab spec *
also, a correction from the lab spec trace_int() deals with the arrivals of tarce interrupts not do_trace()
PRIVATE void trace_int(void)
{
printf("DEBUGGER> trace_int()\n");
/* If this is the first trace interrupt, reset the PC */
if(!done_first_trace)
{
child_proc->p_reg.pc = 0;
done_first_trace = 1;
}
/* remove the child from the ready queue */
lock_unready(child_proc);
child_suspended = 1;
/* copy the child's descriptor to the parent */
inform_debug_proc();
/* revive the parent */
reply(REVIVE, FS_PROC_NR, parent_proc_nr, OK);
parent_suspended = 0;
current_state();
printf("DEBUGGER> trace_int() finished\n");
}
5.8 Seventh stage: resuming execution
PRIVATE void do_resume (message *resume_m)
{
/* Clear the tracebit */
child_proc->p_reg.psw &= ~TRACEBIT;
child_tracebit = 0;
/* Revive the child */
lock_ready(child_proc);
child_suspended = 0;
/* Reply ok to the parent */
fs_reply(TASK_REPLY, resume_m->PROC_NR, OK);
parent_suspended = 0;
current_state();
}
5.9 Eighth stage: placing a breakpoint
PRIVATE void do_breakpnt (message *break_m)
{
printf("DEBUGGER> do_breakpoint() at %x (%x)\n", break_m->m2_p1,
proc_vir2phys(child_proc, break_m->m2_p1));
/* set the breakpoint */
place_breakpoint((int) break_m->m2_p1);
/* Clear the trace bit */
child_proc->p_reg.psw &= ~TRACEBIT;
child_tracebit = 0;
/* Revive the child */
lock_ready(child_proc);
child_suspended = 0;
/* Suspend the parent */
fs_reply(TASK_REPLY, break_m->PROC_NR, SUSPEND);
parent_suspended = 1;
current_state();
printf("DEBUGGER> do_breakpoint() done\n");
}
PRIVATE int place_breakpoint (int break_addr)
{
unsigned char breakpoint_inst = 0xcc;
/* Store the address of the breakpoint, and the current byte there */
breakpoint_at = (vir_bytes) break_addr;
phys_copy(proc_vir2phys(child_proc, breakpoint_at),
vir2phys(&replaced_byte), 1);
printf(" byte was %x\n", replaced_byte);
/* Copy the breakpoint instruction to the address */
phys_copy(vir2phys(&breakpoint_inst),
proc_vir2phys(child_proc, breakpoint_at), 1);
}
PUBLIC void breakpoint()
{
interrupt_type = BREAKPOINT;
interrupt(DEBUGGER);
}
PRIVATE void breakpoint_int(void)
{
/* replace the original byte */
phys_copy(vir2phys(replaced_byte),
proc_vir2phys(child_proc, breakpoint_at), 1);
/* reset the pc */
child_proc->p_reg.pc = breakpoint_at;
/* remove the child from the ready queue */
lock_unready(child_proc);
child_suspended = 1;
/* copy the child's descriptor to the parent */
inform_debug_proc();
/* revive the parent */
reply(REVIVE, FS_PROC_NR, parent_proc_nr, OK);
parent_suspended = 0;
current_state();
printf("DEBUGGER> breakpoint_int() finished\n");
}
syntax highlighted by Code2HTML, v. 0.9