/***********************************************************************************
 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