/*
Copyright (C) 2006 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 "DebuggerCore.h"
#include "State.h"
#include "Util.h"
#include "DebugEvent.h"

#include <QStringList>
#include <QProcess>

#include <cerrno>
#include <climits>
#include <cstring>

// doesn't always seem to be defined in the headers
#ifdef Q_OS_LINUX
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
#ifndef PTRACE_GETSIGINFO
#define PTRACE_GETSIGINFO	static_cast<__ptrace_request>(0x4202)
#endif
#endif

#ifdef Q_OS_WIN32
#include <windows.h>
#endif

//#define SUPPORT_TIMEOUTS

#define SET_OK(ok, v) do { (ok) = ((v) != -1) || (errno == 0); } while(0)

#define EDB_WORDSIZE sizeof(long)

// int 3
const char DebuggerCore::m_BreakInstruction[] = { 0xcc };

//------------------------------------------------------------------------------
// Name: DebuggerCore()
// Desc: constructor
//------------------------------------------------------------------------------
DebuggerCore::DebuggerCore() : m_PID(0) {
#ifdef Q_OS_WIN32
	DebugSetProcessKillOnExit(false);
#endif
}

//------------------------------------------------------------------------------
// Name: ~DebuggerCore()
// Desc: destructor
//------------------------------------------------------------------------------
DebuggerCore::~DebuggerCore() {
	detach();
}

//------------------------------------------------------------------------------
// Name: getFaultAddress()
// Desc: gets the address of a segfault (Linux >= 2.3.99-pre6)
//------------------------------------------------------------------------------
edb::address_t DebuggerCore::getFaultAddress() {
	edb::address_t ret = static_cast<edb::address_t>(-1);
#ifdef Q_OS_LINUX
	if(attached()) {
		siginfo_t info;
		const int r = ptrace(PTRACE_GETSIGINFO, pid(), 0, &info);
		if(r != -1) {
			ret = reinterpret_cast<edb::address_t>(info.si_addr);
		}
	}
#endif
	return ret;
}

//------------------------------------------------------------------------------
// Name: executionBreakpointSize() const
// Desc: returns the size of a breakpoint
//------------------------------------------------------------------------------
int DebuggerCore::executionBreakpointSize() const {
	return sizeof(m_BreakInstruction);
}

//------------------------------------------------------------------------------
// Name: executionBreakpointData() const
// Desc: returns the value of a breakpoint
//------------------------------------------------------------------------------
QByteArray DebuggerCore::executionBreakpointData() const {
	return QByteArray(&m_BreakInstruction[0], sizeof(m_BreakInstruction));
}

//------------------------------------------------------------------------------
// Name: pageSize() const
// Desc: returns the size of a page on this system
//------------------------------------------------------------------------------
edb::address_t DebuggerCore::pageSize() const {
#ifdef Q_OS_LINUX
	#if defined(_SC_PAGESIZE)
		static const edb::address_t page_size = sysconf(_SC_PAGESIZE);
	#elif defined(_SC_PAGE_SIZE)
		static const edb::address_t page_size = sysconf(_SC_PAGE_SIZE);
	#else
		static const edb::address_t page_size = PAGE_SIZE;
	#endif
#else
	static const edb::address_t page_size = PAGE_SIZE;
#endif

	return page_size;
}

//------------------------------------------------------------------------------
// Name: waitForDebugEvent(DebugEvent *event, bool &ok, int secs)
// Desc: waits for a debug event, secs is a timeout (but is not yet respected)
//       ok will be set to false if the timeout expires
//------------------------------------------------------------------------------
void DebuggerCore::waitForDebugEvent(DebugEvent *event, bool &ok, int secs) {
	Q_UNUSED(secs);

	// TODO: implement the timeout here...
#if defined(Q_OS_LINUX)
	ok = false;
	if(attached()) {

		int status;
		const pid_t ret = waitpid(pid(), &status, 0);
		ok = (ret != static_cast<pid_t>(-1));
		if(ok && event != 0) {
			*event = DebugEvent(status);
		}
#elif defined(Q_OS_WIN32)
		// TODO: finish
		DEBUG_EVENT de;
		ok = WaitForDebugEvent(&de, secs * 1000);
#endif
	}
}

//------------------------------------------------------------------------------
// Name: readData(edb::address_t address, bool &ok)
// Desc: 
//------------------------------------------------------------------------------
long DebuggerCore::readData(edb::address_t address, bool &ok) {
	errno = 0;
	const long v = ptrace(PTRACE_PEEKTEXT, pid(), address, 0);
	SET_OK(ok, v);
	return v;
}

//------------------------------------------------------------------------------
// Name: writeData(edb::address_t address, long value, bool &ok)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::writeData(edb::address_t address, long value, bool &ok) {
	const long v = ptrace(PTRACE_POKETEXT, pid(), address, value);
	ok = (v != -1);
}

//------------------------------------------------------------------------------
// Name: readPages(edb::address_t address, void *buf, std::size_t count)
// Desc: reads <count> pages from the process starting at <address>
// Note: buf's size must be >= count * pageSize()
// Note: address should be page aligned.
//------------------------------------------------------------------------------
bool DebuggerCore::readPages(edb::address_t address, void *buf, std::size_t count) {

	Q_ASSERT(buf != 0);
	
	bool ok = false;

	if(attached()) {
		if((address & (pageSize() - 1)) == 0) {
			const edb::address_t orig_address	= address;
			long *ptr							= reinterpret_cast<long *>(buf);
			quint8 *const orig_ptr				= reinterpret_cast<quint8 *>(buf);

			const edb::address_t page_size		= pageSize();
			const edb::address_t end_address	= orig_address + page_size * count;

			for(std::size_t c = 0; c < count; ++c) {
				for(edb::address_t i = 0; i < page_size; i += EDB_WORDSIZE) {
					const long v = readData(address, ok);

					if(!ok) {
						return ok;
					}

					*ptr++ = v;
					address += EDB_WORDSIZE;
				}
			}

			// TODO: handle if breakponts have a size more than 1!
			foreach(Breakpoint bp, m_Breakpoints) {		
				if(bp.address >= orig_address && bp.address < end_address) {
					// show the original bytes in the buffer..
					orig_ptr[bp.address - orig_address] = bp.originalBytes[0];
				}
			}
		}
	}
	return ok;
}


//------------------------------------------------------------------------------
// Name: readBytes(edb::address_t address, void *buf, std::size_t len)
// Desc: reads <len> bytes into <buf> starting at <address>
// Note: if the read failed, the part of the buffer that could not be read will 
//       be filled with 0xff bytes
//------------------------------------------------------------------------------
bool DebuggerCore::readBytes(edb::address_t address, void *buf, std::size_t len) {

	Q_ASSERT(buf != 0);

	bool ok = false;
	
	if(attached()) {
		if(len != 0) {
			quint8 *p = reinterpret_cast<quint8 *>(buf);

			quint8 ch = readByte(address, ok);
			while(ok && len--) {
				*p++ = ch;
				++address;
				ch = readByte(address, ok);
			}

			if(!ok) {
				while(len--) {
					*p++ = 0xff;
				}
			}
		}
	}
	return ok;
}

//------------------------------------------------------------------------------
// Name: writeBytes(edb::address_t address, const void *buf, std::size_t len)
// Desc: writes <len> bytes from <buf> starting at <address>
//------------------------------------------------------------------------------
bool DebuggerCore::writeBytes(edb::address_t address, const void *buf, std::size_t len) {
	
	Q_ASSERT(buf != 0);
	
	bool ok = false;
	if(attached()) {
		const quint8 *p = reinterpret_cast<const quint8 *>(buf);

		while(len--) {
			writeByte(address++, *p++, ok);
			if(!ok) {
				break;
			}
		}
	}
	return ok;
}


//------------------------------------------------------------------------------
// Name: writeByte(edb::address_t address, quint8 value, bool &ok)
// Desc: writes a single byte at a given address
// Note: assumes the this will not trample any breakpoints, must be handled
//       in calling code!
//------------------------------------------------------------------------------
void DebuggerCore::writeByte(edb::address_t address, quint8 value, bool &ok) {
	writeByteBase(address, value, ok);
}

//------------------------------------------------------------------------------
// Name: readByte(edb::address_t address, bool &ok)
// Desc: reads a single bytes at a given address
//------------------------------------------------------------------------------
quint8 DebuggerCore::readByte(edb::address_t address, bool &ok) {

	// TODO: handle if breakponts have a size more than 1!
	quint8 ret = readByteBase(address, ok);
	
	if(ok) {
		Breakpoint *const bp = findBreakpoint(address);
		if(bp != 0) {
			ret = bp->originalBytes[0];
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: writeByteBase(edb::address_t address, quint8 value, bool &ok)
// Desc: the base implementation of writing a byte
//------------------------------------------------------------------------------
void DebuggerCore::writeByteBase(edb::address_t address, quint8 value, bool &ok) {
	// TOOD: assert that we are paused
	
	ok = false;
	if(attached()) {
		long v;
		long mask;
		// pageSize() - 1 will always be 0xf* because pagesizes
		// are always 0x10*, so the masking works
		// range of a is [1..n] where n=pagesize, and we have to adjust
		// if a < wordsize
		const edb::address_t page_size = pageSize();
		const edb::address_t a = page_size - (address & (page_size - 1));
		
		v = value;
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
		if (a < EDB_WORDSIZE) {
			address -= (EDB_WORDSIZE - a);							// LE + BE
			mask = ~(0xffUL << (CHAR_BIT * (EDB_WORDSIZE - a)));	// LE
			v <<= CHAR_BIT * (EDB_WORDSIZE - a);					// LE
		} else {
			mask = ~0xffUL; // LE
		}
#else /* BIG ENDIAN */
		if (a < EDB_WORDSIZE) {
			address -= (EDB_WORDSIZE - a);				// LE + BE
			mask = ~(0xffUL << (CHAR_BIT * (a - 1)));	// BE
			v <<= CHAR_BIT * (a - 1);					// BE
		} else {
			mask = ~(0xffUL << (CHAR_BIT * (EDB_WORDSIZE - 1)));	// BE
			v <<= CHAR_BIT * (EDB_WORDSIZE - 1);					// BE
		}
#endif

		v = (readData(address, ok) & mask) | v;
		if(ok) {
			writeData(address, v, ok);
		}
	}
}

//------------------------------------------------------------------------------
// Name: readByteBase(edb::address_t address, bool &ok)
// Desc: the base implementation of reading a byte
//------------------------------------------------------------------------------
quint8 DebuggerCore::readByteBase(edb::address_t address, bool &ok) {
	// TOOD: assert that we are paused

	quint8 ret = 0xff;
	ok = false;
	errno = -1;
	if(attached()) {
		// if this spot is unreadable, then just return 0xff, otherwise
		// continue as normal and hide any breakpoints we may have
		
		// pageSize() - 1 will always be 0xf* because pagesizes
		// are always 0x10*, so the masking works
		// range of a is [1..n] where n=pagesize, and we have to adjust
		// if a < wordsize
		const edb::address_t page_size = pageSize();
		const edb::address_t a = page_size - (address & (page_size - 1));
				
		if (a < EDB_WORDSIZE) {
			address -= (EDB_WORDSIZE - a);		// LE + BE
		}
		
		long value = readData(address, ok);
		
		if(ok) {
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
			if (a < EDB_WORDSIZE) {
				value >>= CHAR_BIT * (EDB_WORDSIZE - a);	// LE
			}
#else
			if (a < EDB_WORDSIZE) {
				value >>= CHAR_BIT * (a - 1);				// BE
			} else {
				value >>= CHAR_BIT * (EDB_WORDSIZE - 1);	// BE
			}
#endif
			ret = value & 0xff;
		}
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: attach(edb::pid_t pid)
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerCore::attach(edb::pid_t pid) {
	detach();
	
#if defined(Q_OS_LINUX)	
	const long ret = ptrace(PTRACE_ATTACH, pid, 0, 0);
	if(ret == 0) {
		m_PID = pid;
		bool ok;
		waitForDebugEvent(0, ok, INT_MAX);
	}
	
	return ret == 0;
#elif defined(Q_OS_WIN32)
	return DebugActiveProcess(pid);
#endif
}

//------------------------------------------------------------------------------
// Name: clearBreakpoints()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::clearBreakpoints() {
	if(attached()) {
		foreach(Breakpoint bp, m_Breakpoints) {
			removeBreakpoint(bp.address);
		}
	
		m_Breakpoints.clear();
	}
}

//------------------------------------------------------------------------------
// Name: detach()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::detach() {
	if(attached()) {
		clearBreakpoints();
#if defined(Q_OS_LINUX)	
		ptrace(PTRACE_DETACH, pid(), 0, 0);
#elif defined(Q_OS_WIN32)
		DebugActiveProcessStop(pid());
#endif
		m_PID = 0;
	}
}

//------------------------------------------------------------------------------
// Name: kill()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::kill() {
	if(attached()) {
		clearBreakpoints();
#if defined(Q_OS_LINUX)	
		ptrace(PTRACE_KILL, pid(), 0, 0);
#elif defined(Q_OS_WIN32)
		TerminateProcess(pid());
#endif
		m_PID = 0;
	}
}

//------------------------------------------------------------------------------
// Name: addBreakpoint(edb::address_t address, bool oneTime)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::addBreakpoint(edb::address_t address, bool oneTime) {

	if(findBreakpoint(address) == 0) {
		// backup original bytes
		char prev[executionBreakpointSize()];
		if(readBytes(address, prev, executionBreakpointSize())) {

			// write the breakpoint
			if(writeBytes(address, executionBreakpointData(), executionBreakpointSize())) {

				// create accounting structure
				Breakpoint data;
				data.address = address;
				data.setOneTime(oneTime);
				data.setEnabled(true);
				data.originalBytes = QByteArray(prev, executionBreakpointSize());

				m_Breakpoints[address] = data;
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: setBreakpointCondition(edb::address_t address, const QString &condition)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::setBreakpointCondition(edb::address_t address, const QString &condition) {
	Breakpoint *const bp = findBreakpoint(address);
	if(bp != 0) {
		bp->condition = condition;
	}
}

//------------------------------------------------------------------------------
// Name: setBreakpointCondition(edb::address_t address, const QString &condition)
// Desc: 
//------------------------------------------------------------------------------
QString DebuggerCore::getBreakpointCondition(edb::address_t address) {
	QString ret;
	Breakpoint *const bp = findBreakpoint(address);
	if(bp != 0) {
		ret = bp->condition;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: findBreakpoint(edb::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
Breakpoint *DebuggerCore::findBreakpoint(edb::address_t address) {
	Breakpoint *ret = 0;
	if(attached()) {
		BreakpointState::iterator i = m_Breakpoints.find(address);
		if(i != m_Breakpoints.end()) {
			ret = &(i.value());
		}
	}
	return ret;
}


//------------------------------------------------------------------------------
// Name: removeBreakpoint(edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::removeBreakpoint(edb::address_t address) {

	// TODO: assert paused
	if(attached()) {
		BreakpointState::iterator i = m_Breakpoints.find(address);
		if(i != m_Breakpoints.end()) {
			// this is an existing breakpoint
			if(writeBytes(address, i.value().originalBytes, executionBreakpointSize())) {
				m_Breakpoints.erase(i);
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: disableBreakpoint(edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
edb::address_t DebuggerCore::disableBreakpoint(edb::address_t address) {
	if(address != 0) {
		Breakpoint *const bp = findBreakpoint(address);
		if(bp != 0) {
			// this is an existing breakpoint
			if(writeBytes(address, bp->originalBytes, executionBreakpointSize())) {
				bp->setEnabled(false);
				return address;
			}	
		}
	}
	return 0;
}

//------------------------------------------------------------------------------
// Name: enableBreakpoint(edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
edb::address_t DebuggerCore::enableBreakpoint(edb::address_t address) {
	if(address != 0) {
		Breakpoint *const bp = findBreakpoint(address);
		if(bp != 0) {
			if(!bp->enabled()) {

				char prev[executionBreakpointSize()];
				if(readBytes(address, prev, executionBreakpointSize())) {

					if(writeBytes(address, executionBreakpointData(), executionBreakpointSize())) {
						bp->setEnabled(true);
						bp->originalBytes = QByteArray(prev, executionBreakpointSize());
						return address;
					}
				}
			}
		}
	}
	return 0;
}

//------------------------------------------------------------------------------
// Name: backupBreakpoints(BreakpointState &state) const
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::backupBreakpoints(BreakpointState &state) {
	state = m_Breakpoints;
}

//------------------------------------------------------------------------------
// Name: pause()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::pause() {

	if(attached()) {
#if defined(Q_OS_LINUX)	
		::kill(pid(), SIGSTOP);
#elif defined(Q_OS_WIN32)
		DebugBreakProcess(pid());
#endif
	}
}

//------------------------------------------------------------------------------
// Name: resume(int signum)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::resume(int signum) {
	// TOOD: assert that we are paused
	
	if(attached()) {
#if defined(Q_OS_LINUX)	
		ptrace(PTRACE_CONT, pid(), 0, reinterpret_cast<void *>(signum));
#elif defined(Q_OS_WIN32)
		if(signum == 0) {
			ContinueDebugEvent(pid(), 0, DBG_CONTINUE);
		} else {
			ContinueDebugEvent(pid(), 0, DBG_EXCEPTION_NOT_HANDLED);
		}
#endif
	}
}

//------------------------------------------------------------------------------
// Name: step(int signum)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::step(int signum) {
	// TOOD: assert that we are paused
	
	if(attached()) {
#if defined(Q_OS_LINUX)	
		ptrace(PTRACE_SINGLESTEP, pid(), 0, reinterpret_cast<void *>(signum));
#elif defined(Q_OS_WIN32)
		// TODO: get context, set trap bit, set context, continue
		CONTEXT	context;
		HANDLE	th;
		GetThreadContext(th, &context);
		// set the TF bit
		context.EFlags |= (1 << 8);
		SetThreadContext(th, &context);
#endif
	}
}

//------------------------------------------------------------------------------
// Name: getState(State &state)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::getState(State &state) {
	// TOOD: assert that we are paused
	
	if(attached()) {
#if defined(EDB_X86)
	#if defined(Q_OS_LINUX)	
		user_regs_struct	regs;
		user_fpregs_struct	i387_regs;
		
		if(ptrace(PTRACE_GETREGS, pid(), 0, &regs) != -1) {
			state.eax = regs.eax;
			state.ebx = regs.ebx;
			state.ecx = regs.ecx;
			state.edx = regs.edx;
			state.esp = regs.esp;
			state.ebp = regs.ebp;
			state.edi = regs.edi;
			state.esi = regs.esi;
			state.eip = regs.eip;
			state.eflags = regs.eflags;
			state._cs = regs.xcs;
			state._ds = regs.xds;
			state._es = regs.xes;
			state._fs = regs.xfs;
			state._gs = regs.xgs;
			state._ss = regs.xss;
			state.orig_eax = regs.orig_eax;
		}
		
		if(ptrace(PTRACE_GETFPREGS, pid(), 0, &i387_regs) != -1) {
			State::i387Register *const p = reinterpret_cast<State::i387Register *>(i387_regs.st_space);
			for(int i = 0; i < 8; ++i) {
				state.st[i] = p[i];
			}
		}
		
		state.dr[0] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[0]), 0);
		state.dr[1] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[1]), 0);
		state.dr[2] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[2]), 0);
		state.dr[3] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[3]), 0);
		state.dr[4] = 0;
		state.dr[5] = 0;
		state.dr[6] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[6]), 0);
		state.dr[7] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[7]), 0);
				
	#elif defined(Q_OS_WIN32)
		// TODO: get context properly from desired thread
		CONTEXT	context;
		HANDLE	th;
		GetThreadContext(th, &context);
		
		state.eax = context.Eax;
		state.ebx = context.Ebx;
		state.ecx = context.Ecx;
		state.edx = context.Edx;
		state.esp = context.Esp;
		state.ebp = context.Ebp;
		state.edi = context.Edi;
		state.esi = context.Esi;
		state.eip = context.Eip;
		state.eflags = context.EFlags;
		state._cs = context.SegCs;
		state._ds = context.SegDs;
		state._es = context.SegEs;
		state._fs = context.SegFs;
		state._gs = context.SegGs;
		state._ss = context.SegSs;
		state.orig_eax = 0;
	#endif
#elif defined(EDB_X86_64)
		user_regs_struct	regs;
		user_fpregs_struct	i387_regs;
		
		if(ptrace(PTRACE_GETREGS, pid(), 0, &regs) != -1) {
			state.rax = regs.rax;
			state.rbx = regs.rbx;
			state.rcx = regs.rcx;
			state.rdx = regs.rdx;
			state.rsp = regs.rsp;
			state.rbp = regs.rbp;
			state.rdi = regs.rdi;
			state.rsi = regs.rsi;
			state.rip = regs.rip;
			state.r8 = regs.r8;
			state.r9 = regs.r9;
			state.r10 = regs.r10;
			state.r11 = regs.r11;
			state.r12 = regs.r12;
			state.r13 = regs.r13;
			state.r14 = regs.r14;
			state.r15 = regs.r15;
			state.rflags = regs.eflags;
			state.cs = regs.cs;
			state.ds = regs.ds;
			state.es = regs.es;
			state.fs = regs.fs;
			state.gs = regs.gs;
			state.ss = regs.ss;
			state.orig_rax = regs.orig_rax;
			state.fs_base = regs.fs_base;
			state.gs_base = regs.gs_base;
		}
		
		if(ptrace(PTRACE_GETFPREGS, pid(), 0, &i387_regs) != -1) {
			State::i387Register *const p = reinterpret_cast<State::i387Register *>(i387_regs.st_space);
			for(int i = 0; i < 8; ++i) {
				state.st[i] = p[i];
			}
		}
		
		state.dr[0] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[0]), 0);
		state.dr[1] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[1]), 0);
		state.dr[2] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[2]), 0);
		state.dr[3] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[3]), 0);
		state.dr[4] = 0;
		state.dr[5] = 0;
		state.dr[6] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[6]), 0);
		state.dr[7] = ptrace(PTRACE_PEEKUSER, pid(), offsetof(user, u_debugreg[7]), 0);
#endif
	} else {
		state.clear();
	}
}

//------------------------------------------------------------------------------
// Name: setState(const State &state)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::setState(const State &state) {
	
	// TOOD: assert that we are paused
	
	if(attached()) {
#if defined(EDB_X86)
		user_regs_struct regs;

		regs.eax = state.eax;
		regs.ebx = state.ebx;
		regs.ecx = state.ecx;
		regs.edx = state.edx;
		regs.esp = state.esp;
		regs.ebp = state.ebp;
		regs.edi = state.edi;
		regs.esi = state.esi;
		regs.eip = state.eip;
		regs.eflags = state.eflags;
		regs.xcs = state._cs;
		regs.xds = state._ds;
		regs.xes = state._es;
		regs.xfs = state._fs;
		regs.xgs = state._gs;
		regs.xss = state._ss;
		regs.orig_eax = state.orig_eax;
		
		ptrace(PTRACE_SETREGS, pid(), 0, &regs);
		
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[0]), state.dr[0]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[1]), state.dr[1]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[2]), state.dr[2]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[3]), state.dr[3]);
//		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[4]), state.dr[4]);
//		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[5]), state.dr[5]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[6]), state.dr[6]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[7]), state.dr[7]);
#elif defined(EDB_X86_64)
		user_regs_struct regs;

		regs.rax = state.rax;
		regs.rbx = state.rbx;
		regs.rcx = state.rcx;
		regs.rdx = state.rdx;
		regs.rsp = state.rsp;
		regs.rbp = state.rbp;
		regs.rdi = state.rdi;
		regs.rsi = state.rsi;
		regs.r8 = state.r8;
		regs.r9 = state.r9;
		regs.r10 = state.r10;
		regs.r11 = state.r11;
		regs.r12 = state.r12;
		regs.r13 = state.r13;
		regs.r14 = state.r14;
		regs.r15 = state.r15;
		regs.rip = state.rip;
		regs.eflags = state.rflags;
		regs.cs = state.cs;
		regs.ds = state.ds;
		regs.es = state.es;
		regs.fs = state.fs;
		regs.gs = state.gs;
		regs.ss = state.ss;
		regs.orig_rax = state.orig_rax;
		regs.fs_base = state.fs_base;
		regs.gs_base = state.gs_base;
		
		ptrace(PTRACE_SETREGS, pid(), 0, &regs);
		
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[0]), state.dr[0]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[1]), state.dr[1]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[2]), state.dr[2]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[3]), state.dr[3]);
//		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[4]), state.dr[4]);
//		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[5]), state.dr[5]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[6]), state.dr[6]);
		ptrace(PTRACE_POKEUSER, pid(), offsetof(user, u_debugreg[7]), state.dr[7]);
#endif
	}
}

//------------------------------------------------------------------------------
// Name: doExecProcess(const QString &path, const QString &cwd, const QStringList &args)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::doExecProcess(const QString &path, const QString &cwd, const QStringList &args) {

#ifdef Q_OS_LINUX
	// change to the desired working directory
	if(chdir(qPrintable(cwd)) == 0) {

		// allocate space for all the arguments
		char **const argv_pointers = new char *[args.count() + 2];

		char **p = argv_pointers;

		*p = new char[path.length() + 1];
		std::strcpy(*p, qPrintable(path));
		++p;

		for(int i = 0; i < args.count(); ++i) {
			const QString s(args[i]);
			*p = new char[s.length() + 1];
			std::strcpy(*p, qPrintable(s));
			++p;
		}

		*p = 0;

		execvp(argv_pointers[0], &argv_pointers[0]);

		// should be no need to cleanup, the process which allocated all that space no longer exists!
		// if we get here...execvep failed!
		p = argv_pointers;
		while(*p != 0) {
			delete [] *p++;
		}
		delete [] argv_pointers;
	}
#endif
}

//------------------------------------------------------------------------------
// Name: open(const QString &path, const QString &cwd, const QStringList &args, const QString &tty)
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerCore::open(const QString &path, const QString &cwd, const QStringList &args, const QString &tty) {
	detach();
	bool ret = false;
#if defined(Q_OS_LINUX)
	pid_t pid;
	
	switch(pid = fork()) {
	case 0:
		// we are in the child now...
	
		// set ourselves (the child proc) up to be traced
		ptrace(PTRACE_TRACEME, 0, 0, 0);
		
		// redirect it's I/O
		if(!tty.isEmpty()) {
			FILE *const newout = freopen(qPrintable(tty), "r+b", stdout);
			FILE *const newin = freopen(qPrintable(tty), "r+b", stdin);
			FILE *const newerr = freopen(qPrintable(tty), "r+b", stderr);
			
			Q_UNUSED(newout);
			Q_UNUSED(newin);
			Q_UNUSED(newerr);
		}
		
		// do the actual exec
		doExecProcess(path, cwd, args);
		
		// we should never get here!
		abort();
		break;
	case -1:
		// error!
		m_PID = 0;
		break;
	default:
		// parent
		do {
			m_PID = pid;
			DebugEvent event;
			
			bool ok;
			waitForDebugEvent(&event, ok, INT_MAX);
			
			if(event.reason() == DebugEvent::EVENT_STOPPED) {
				switch(event.stopSignal()) {
				case SIGABRT:
					m_PID = 0;
					break;
				default:
					ret = true;
					break;
				}
			} else {
				m_PID = 0;
			}
		} while(0);
		break;
	}
#elif defined(Q_OS_WIN32)
	// CreateProcess, blah blah blah
#endif
	return ret;
}

//------------------------------------------------------------------------------
// Name: open(const QString &path, const QString &cwd, const QStringList &args)
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerCore::open(const QString &path, const QString &cwd, const QStringList &args) {
	return open(path, cwd, args, QString());
}

//------------------------------------------------------------------------------
// Name: pid() const
// Desc: returns the pid of the currently debugged process
//------------------------------------------------------------------------------
edb::pid_t DebuggerCore::pid() const {
	return m_PID;
}

//------------------------------------------------------------------------------
// Name: attached() const
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerCore::attached() const {
	return pid() != 0;
}

Q_EXPORT_PLUGIN2(DebuggerCorePtrace, DebuggerCore)
