/*
Copyright (C) 2006 - 2009 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "DebugEvent.h"
#include "Debugger.h"

//------------------------------------------------------------------------------
// Name: DebugEvent()
// Desc: constructor
//------------------------------------------------------------------------------
DebugEvent::DebugEvent() {
	std::memset(&event, 0, sizeof(event));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DebugEvent::DebugEvent(DEBUG_EVENT &e) {
	std::memcpy(&event, &e, sizeof(event));
}

//------------------------------------------------------------------------------
// Name: DebugEvent(const DebugEvent &other)
// Desc: copy constructor
//------------------------------------------------------------------------------
DebugEvent::DebugEvent(const DebugEvent &other) {
	std::memcpy(&event, &other.event, sizeof(event));
}

//------------------------------------------------------------------------------
// Name: operator=(const DebugEvent &rhs)
// Desc: 
//------------------------------------------------------------------------------
DebugEvent &DebugEvent::operator=(const DebugEvent &rhs) {
	if(this != &rhs) {
		std::memcpy(&event, &rhs.event, sizeof(event));
	}
	return *this;
}

//------------------------------------------------------------------------------
// Name: exited() const
// Desc: was this even caused by an exit?
//------------------------------------------------------------------------------
bool DebugEvent::exited() const {
	return false;
}

//------------------------------------------------------------------------------
// Name: signaled() const
// Desc: was this event caused by a signal
//------------------------------------------------------------------------------
bool DebugEvent::signaled() const {
	return false;
}

//------------------------------------------------------------------------------
// Name: stopped() const
// Desc: was this event caused by stop
//------------------------------------------------------------------------------
bool DebugEvent::stopped() const {
	return true;
}

//------------------------------------------------------------------------------
// Name: exitCode() const
// Desc: if it was an exit event, what was the status
//------------------------------------------------------------------------------
int DebugEvent::exitCode() const {
	return 0;
}

//------------------------------------------------------------------------------
// Name: signalCode() const
// Desc: if it was a signal, what was the signal number
//------------------------------------------------------------------------------
int DebugEvent::signalCode() const {
	return event.u.Exception.ExceptionRecord.ExceptionCode;
}

//------------------------------------------------------------------------------
// Name: stopCode() const
// Desc: if it was a stop, what was the status
//------------------------------------------------------------------------------
int DebugEvent::stopCode() const {
	switch(event.u.Exception.ExceptionRecord.ExceptionCode) {
	case EXCEPTION_SINGLE_STEP:
	case EXCEPTION_BREAKPOINT:
		return sigtrap;
	default:
		return event.u.Exception.ExceptionRecord.ExceptionCode;
	}
}

//------------------------------------------------------------------------------
// Name: thread() const
// Desc: what was the TID for this event
//------------------------------------------------------------------------------
edb::tid_t DebugEvent::thread() const {
	return event.dwThreadId;
}

//------------------------------------------------------------------------------
// Name: process() const
// Desc: what was the PID for this event
//------------------------------------------------------------------------------
edb::pid_t DebugEvent::process() const {
	return event.dwProcessId;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DebugEvent::TRAP_REASON DebugEvent::trapReason() const {
	switch(event.dwDebugEventCode) {
	case EXCEPTION_DEBUG_EVENT:
		switch(event.u.Exception.ExceptionRecord.ExceptionCode) {
		case EXCEPTION_BREAKPOINT:
			return TRAP_BREAKPOINT;
		case EXCEPTION_SINGLE_STEP:
			return TRAP_STEPPING;
		}
	}
	return TRAP_BREAKPOINT;
	
}

//------------------------------------------------------------------------------
// Name: reason() const
// Desc: what was the reason for this event
//------------------------------------------------------------------------------
DebugEvent::REASON DebugEvent::reason() const {
	switch(event.dwDebugEventCode) {
	case EXCEPTION_DEBUG_EVENT:
		switch(event.u.Exception.ExceptionRecord.ExceptionCode) {
		case EXCEPTION_BREAKPOINT:
		case EXCEPTION_SINGLE_STEP:
		case EXCEPTION_ACCESS_VIOLATION:
		case EXCEPTION_IN_PAGE_ERROR:
		case EXCEPTION_INVALID_DISPOSITION:
		case EXCEPTION_NONCONTINUABLE_EXCEPTION:
		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
		case EXCEPTION_ILLEGAL_INSTRUCTION:
		case EXCEPTION_INT_DIVIDE_BY_ZERO:
		case EXCEPTION_INT_OVERFLOW:
		case EXCEPTION_PRIV_INSTRUCTION:
		case EXCEPTION_STACK_OVERFLOW:
		case EXCEPTION_FLT_DENORMAL_OPERAND:
		case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		case EXCEPTION_FLT_INEXACT_RESULT:
		case EXCEPTION_FLT_INVALID_OPERATION:
		case EXCEPTION_FLT_OVERFLOW:
		case EXCEPTION_FLT_STACK_CHECK:
		case EXCEPTION_FLT_UNDERFLOW:
		case EXCEPTION_DATATYPE_MISALIGNMENT:
		default:
			return EVENT_STOPPED;
		}
	case CREATE_THREAD_DEBUG_EVENT:
	case CREATE_PROCESS_DEBUG_EVENT:
	case EXIT_THREAD_DEBUG_EVENT:
	case EXIT_PROCESS_DEBUG_EVENT:
	case LOAD_DLL_DEBUG_EVENT:
	case UNLOAD_DLL_DEBUG_EVENT:
	case OUTPUT_DEBUG_STRING_EVENT:
	case RIP_EVENT:
	default:
		return EVENT_UNKNOWN;
	}
	
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DebugEvent::isError() const {
	switch(event.dwDebugEventCode) {
	case EXCEPTION_DEBUG_EVENT:
		switch(event.u.Exception.ExceptionRecord.ExceptionCode) {
		case EXCEPTION_BREAKPOINT:
		case EXCEPTION_SINGLE_STEP:
			return false;
		case EXCEPTION_ACCESS_VIOLATION:
		case EXCEPTION_IN_PAGE_ERROR:
		case EXCEPTION_INVALID_DISPOSITION:
		case EXCEPTION_NONCONTINUABLE_EXCEPTION:
		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
		case EXCEPTION_ILLEGAL_INSTRUCTION:
		case EXCEPTION_INT_DIVIDE_BY_ZERO:
		case EXCEPTION_INT_OVERFLOW:
		case EXCEPTION_PRIV_INSTRUCTION:
		case EXCEPTION_STACK_OVERFLOW:
		case EXCEPTION_FLT_DENORMAL_OPERAND:
		case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		case EXCEPTION_FLT_INEXACT_RESULT:
		case EXCEPTION_FLT_INVALID_OPERATION:
		case EXCEPTION_FLT_OVERFLOW:
		case EXCEPTION_FLT_STACK_CHECK:
		case EXCEPTION_FLT_UNDERFLOW:
		case EXCEPTION_DATATYPE_MISALIGNMENT:
		default:
			return true;
		}
	case CREATE_THREAD_DEBUG_EVENT:
	case CREATE_PROCESS_DEBUG_EVENT:
	case EXIT_THREAD_DEBUG_EVENT:
	case EXIT_PROCESS_DEBUG_EVENT:
	case LOAD_DLL_DEBUG_EVENT:
	case UNLOAD_DLL_DEBUG_EVENT:
	case OUTPUT_DEBUG_STRING_EVENT:
	case RIP_EVENT:
	default:
		return false;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DebugEvent::Message DebugEvent::errorDescription() const {
	Q_ASSERT(isError());
	
	edb::address_t fault_address = static_cast<edb::address_t>(-1);
	if(event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
		fault_address = (edb::address_t)(event.u.Exception.ExceptionRecord.ExceptionInformation[1]);
	}
	
	switch(stopCode()) {
	case EXCEPTION_ACCESS_VIOLATION:
		return Message(
			QObject::tr("Illegal Access Fault"), 
			QObject::tr(
				"<p>The debugged application encountered a segmentation fault.<br />The address <strong>0x%1</strong> could not be accessed.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>").arg(edb::v1::formatPointer(fault_address))
			);
	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
		return Message(
			QObject::tr("Array Bounds Error"), 
			QObject::tr(
				"<p>The debugged application tried to access an out of bounds array element.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>").arg(edb::v1::formatPointer(fault_address))
			);
	case EXCEPTION_DATATYPE_MISALIGNMENT:
		return Message(
			QObject::tr("Bus Error"), 
			QObject::tr(
				"<p>The debugged application tried to read or write data that is misaligned.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_FLT_DENORMAL_OPERAND:
		return Message(
			QObject::tr("Floating Point Exception"),
			QObject::tr(
				"<p>One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);

	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		return Message(
			QObject::tr("Floating Point Exception"),
			QObject::tr(
				"<p>The debugged application tried to divide a floating-point value by a floating-point divisor of zero.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_FLT_INEXACT_RESULT:
		return Message(
			QObject::tr("Floating Point Exception"),
			QObject::tr(
				"<p>The result of a floating-point operation cannot be represented exactly as a decimal fraction.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_FLT_INVALID_OPERATION:
		return Message(
			QObject::tr("Floating Point Exception"),
			QObject::tr(
				"<p>The application attempted an invalid floating point operation.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_FLT_OVERFLOW:
		return Message(
			QObject::tr("Floating Point Exception"),
			QObject::tr(
				"<p>The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_FLT_STACK_CHECK:
		return Message(
			QObject::tr("Floating Point Exception"),
			QObject::tr(
				"<p>The stack overflowed or underflowed as the result of a floating-point operation.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_FLT_UNDERFLOW:
		return Message(
			QObject::tr("Floating Point Exception"),
			QObject::tr(
				"<p>The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_ILLEGAL_INSTRUCTION:
		return Message(
			QObject::tr("Illegal Instruction Fault"), 
			QObject::tr(
				"<p>The debugged application attempted to execute an illegal instruction.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_IN_PAGE_ERROR:
		return Message(
			QObject::tr("Page Error"),
			QObject::tr(
				"<p>The debugged application tried to access a page that was not present, and the system was unable to load the page.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_INT_DIVIDE_BY_ZERO:
		return Message(
			QObject::tr("Divide By Zero"),
			QObject::tr(
				"<p>The debugged application tried to divide an integer value by an integer divisor of zero.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_INT_OVERFLOW:
		return Message(
			QObject::tr("Integer Overflow"),
			QObject::tr(
				"<p>The result of an integer operation caused a carry out of the most significant bit of the result.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_INVALID_DISPOSITION:
		return Message(
			QObject::tr("Invalid Disposition"),
			QObject::tr(
				"<p>An exception handler returned an invalid disposition to the exception dispatcher.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_NONCONTINUABLE_EXCEPTION:
		return Message(
			QObject::tr("Invalid Disposition"),
			QObject::tr(
				"<p>The debugged application tried to continue execution after a noncontinuable exception occurred.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_PRIV_INSTRUCTION:
		return Message(
			QObject::tr("Privileged Instruction"),
			QObject::tr(
				"<p>The debugged application tried to execute an instruction whose operation is not allowed in the current machine mode.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	case EXCEPTION_STACK_OVERFLOW:	
		return Message(
			QObject::tr("Stack Overflow"),
			QObject::tr(
				"<p>The debugged application has exhausted its stack.</p>"
				"<p>If you would like to pass this exception to the application press Shift+[F7/F8/F9]</p>")
			);
	default:
		return Message();
	}
}
