[ECOS] Multi thread Debugging

Fabrice Gautier Fabrice_Gautier@sdesigns.com
Thu Aug 31 18:54:00 GMT 2000


> -----Original Message-----
> From: Nick Garnett [ mailto:nickg@cygnus.co.uk ]
> Sent: Thursday, August 31, 2000 4:08 AM
> To: ecos-discuss@sourceware.cygnus.com
> Subject: Re: [ECOS] Multi thread Debugging
> All your observations are correct. The main problem is that thread
> saved contexts and interrupt saved contexts are different. In all
> other HALs they are the same. I suspect that the simplest fix for now
> is to change the format of the HAL_SavedRegisters structure to echo
> that pushed by "pusha" and replace the "movl"s in context.S with
> "pusha" and "popa". 

I'm testing some changes like this. It seems to work with the HAL context.c
test, but with a more complicated program I've some strange crash I didn't
have before. 
The program ends with an exception (SIGSEGV, SIGILL or SIGTRAP) and it seems
this is during a thread switch. 

I suspect that in some cases the changes I've made may be unsafe. I'm not
sure if the switch context function have to be re-entrant it is safe to
assume that a context switch can't be interrupted by another context switch?

One main difference between my new version and the current version is that
the next context ptr is not saved as a part of the SavedRegisters structure.
So this pointer is pushed on the stack (as a parameter for
hal_thread_load_context) AFTER the current thread stack pointer has been
saved. So if another context switch occurs at this time I suppose the saved
context will be corrupted.

I post here the modifications I've made:

First the HAL_SavedRegisters structure:

typedef struct 
    cyg_uint32  edi;
    cyg_uint32  esi;
    cyg_uint32  ebp;
    cyg_uint32  esp;                  
    cyg_uint32  ebx;
    cyg_uint32  edx;
    cyg_uint32  ecx;
    cyg_uint32  eax;    
    cyg_uint32  vector; // if saved on interrupt contains intr vector
    cyg_uint32  eip;
    cyg_uint32  arg1;   // cs (when intr) or parameter
    cyg_uint32  arg2;	// eflags (when intr) or parameter
} HAL_SavedRegisters;


Then the context init routine:

// Context Initialization
// Initialize the context of a thread.
// Arguments:
// _sp_ name of variable containing current sp, will be written with new sp
// _thread_ thread object address, passed as argument to entry point
// _entry_ entry point address.
// _id_ bit pattern used in initializing registers, for debugging.

#define HAL_THREAD_INIT_CONTEXT( _sparg_, _thread_, _entry_, _id_ )       \
    CYG_MACRO_START                                                       \
    register CYG_WORD* _sp_ = ((CYG_WORD*)((_sparg_) &~15));              \
    register HAL_SavedRegisters *_regs_;                                  \
    /* The 'ret' executed at the end of hal_thread_load_context will  */  \
    /* use the last entry on the stack as a return pointer (_entry_). */  \
    /* Cyg_HardwareThread::thread_entry expects one argument at stack */  \
    /* offset 4 (_thread_). The (0xDEADBEEF) entry is the return addr */  \
    /* for thread_entry (which is never used).                        */  \
    *(--_sp_) = (CYG_WORD)(0);                                            \
    *(--_sp_) = (CYG_WORD)(0);                                            \
    *(--_sp_) = (CYG_WORD)(0);                                            \
    *(--_sp_) = (CYG_WORD)(0);                                            \
    _regs_ = (HAL_SavedRegisters *)                                       \
               ((unsigned long)_sp_ - sizeof(HAL_SavedRegisters));        \
    _regs_->arg2   = (CYG_WORD)(_thread_);				  \
    _regs_->arg1   = (CYG_WORD)(0);				  \
    _regs_->eip	   = (CYG_WORD)(_entry_);				  \
    _regs_->vector = (CYG_WORD)(_id_);					  \
    _sp_-=4;								  \
    _regs_->esp    = (CYG_WORD) _sp_;                                     \
    _regs_->ebp    = (CYG_WORD)(_id_);                                    \
    _regs_->esi    = (CYG_WORD)(_id_);                                    \
    _regs_->edi    = (CYG_WORD)(_id_);                                    \
    _regs_->eax    = (CYG_WORD)(_id_);					  \
    _regs_->ebx    = (CYG_WORD)(_id_);                                    \
    _regs_->ecx    = (CYG_WORD)(_id_);					  \
    _regs_->edx    = (CYG_WORD)(_id_);                                    \
    (_sparg_)      = (CYG_ADDRESS) _regs_;                                \


And the switch routine:

# hal_thread_switch_context
# Switch thread contexts
# :     0(%esp) :     return address
# :     4(%esp) :     address of sp of next thread to execute
# :     8(%esp) :     address of sp save location of current thread
# %eax, %ecx, and %edx are ours to abuse.
#        movl    4(%esp),%eax            # next context ptr
#        movl    8(%esp),%edx            # this context ptr

	popl %ebx  # save return eip
	popl %eax  # get next context ptr
	popl %edx  # get this context ptr

	# Save context
	pushfl		# save eflags
	pushw %cs	# save cs
	pushw 0		# and pad to 32 bits
	pushl %ebx	# save eip 
	pushl $0xdeaddead # push vector
	pusha		# push general registers
      # Save next context ptr in this context. Necessary because
      # hal_thread_load_context expects to find the ptr on the stack,
      # not in a register as on PPC. 
      # Store the context ptr
      movl    %esp,(%edx)
      #push next context ptr as an argument to load context
	pushl %eax
	pushl	$0xdeadbeef # load_context return pointer, never used 
      # Now fall through to hal_thread_load_context
# hal_thread_load_context
# Load thread context
# : 4(%esp) (!= i386reg_next_context(%esp)) = address of sp of thread to
# Note that this function is also the second half of
# and is simply dropped into from it.
# %eax, %ecx, and %edx are ours to abuse.

#ifdef CYGHWR_HAL_I386_FPU	
	movl	%cr0, %eax
	orl	$0x8, %eax
	movl	%eax, %cr0
      movl  4(%esp),%eax # get new context ptr
	movl	(%eax),%esp
	popal   # unstack general registers
	popl %ebx # unstack vector (should be 0xdeaddead)

More information about the Ecos-discuss mailing list