diff --git a/headers/private/debug/DebugContext.h b/headers/private/debug/DebugContext.h new file mode 100644 index 0000000000..6051383031 --- /dev/null +++ b/headers/private/debug/DebugContext.h @@ -0,0 +1,57 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _DEBUG_CONTEXT_H +#define _DEBUG_CONTEXT_H + + +#include + + +class BDebugContext { +public: + BDebugContext(); + ~BDebugContext(); + + status_t Init(team_id team, port_id nubPort); + void Uninit(); + + team_id Team() const { return fContext.team; } + port_id NubPort() const { return fContext.nub_port; } + port_id ReplyPort() const + { return fContext.reply_port; } + + status_t SendDebugMessage(int32 messageCode, + const void *message, size_t messageSize, + void* reply, size_t replySize); + + status_t SetTeamDebuggingFlags(int32 flags); + + ssize_t ReadMemoryPartial(const void* address, + void* buffer, size_t size); + ssize_t ReadMemory(const void* address, + void* buffer, size_t size); + ssize_t ReadString(const void* address, + char* buffer, size_t size); + + status_t SetBreakpoint(void* address); + status_t ClearBreakpoint(void* address); + + status_t SetWatchpoint(void* address, uint32 type, + int32 length); + status_t ClearWatchpoint(void* address); + + status_t ContinueThread(thread_id thread); + status_t SetThreadDebuggingFlags(thread_id thread, + int32 flags); + status_t GetThreadCpuState(thread_id thread, + debug_debugger_message* _messageCode, + debug_cpu_state* cpuState); + +protected: + debug_context fContext; +}; + + +#endif // _DEBUG_CONTEXT_H diff --git a/headers/private/debug/DebugLooper.h b/headers/private/debug/DebugLooper.h new file mode 100644 index 0000000000..68f0c62160 --- /dev/null +++ b/headers/private/debug/DebugLooper.h @@ -0,0 +1,65 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _DEBUG_LOOPER_H +#define _DEBUG_LOOPER_H + + +#include + +#include + + +class BDebugMessageHandler; +class BTeamDebugger; + + +class BDebugLooper { +public: + BDebugLooper(); + virtual ~BDebugLooper(); + + status_t Init(); + + thread_id Run(bool spawnThread); + void Quit(); + + status_t AddTeamDebugger(BTeamDebugger* debugger, + BDebugMessageHandler* handler); + bool RemoveTeamDebugger(BTeamDebugger* debugger); + bool RemoveTeamDebugger(team_id team); + +private: + struct Debugger; + + struct Job; + struct JobList; + struct AddDebuggerJob; + struct RemoveDebuggerJob; + + friend struct AddDebuggerJob; + friend struct RemoveDebuggerJob; + + typedef BObjectList DebuggerList; + +private: + static status_t _MessageLoopEntry(void* data); + status_t _MessageLoop(); + + status_t _DoJob(Job* job); + void _Notify(); + +private: + BLocker fLock; + thread_id fThread; + bool fOwnsThread; + bool fTerminating; + bool fNotified; + JobList* fJobs; + sem_id fEventSemaphore; + DebuggerList fDebuggers; +}; + + +#endif // _DEBUG_LOOPER_H diff --git a/headers/private/debug/DebugMessageHandler.h b/headers/private/debug/DebugMessageHandler.h new file mode 100644 index 0000000000..9d59d30f97 --- /dev/null +++ b/headers/private/debug/DebugMessageHandler.h @@ -0,0 +1,58 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _DEBUG_MESSAGE_HANDLER_H +#define _DEBUG_MESSAGE_HANDLER_H + + +#include + + +class BDebugMessageHandler { +public: + virtual ~BDebugMessageHandler(); + + virtual bool HandleDebugMessage(int32 messageCode, + const debug_debugger_message_data& message); + + virtual bool HandleThreadDebugged( + const debug_thread_debugged& message); + virtual bool HandleDebuggerCall( + const debug_debugger_call& message); + virtual bool HandleBreakpointHit( + const debug_breakpoint_hit& message); + virtual bool HandleWatchpointHit( + const debug_watchpoint_hit& message); + virtual bool HandleSingleStep( + const debug_single_step& message); + virtual bool HandlePreSyscall( + const debug_pre_syscall& message); + virtual bool HandlePostSyscall( + const debug_post_syscall& message); + virtual bool HandleSignalReceived( + const debug_signal_received& message); + virtual bool HandleExceptionOccurred( + const debug_exception_occurred& message); + virtual bool HandleTeamCreated( + const debug_team_created& message); + virtual bool HandleTeamDeleted( + const debug_team_deleted& message); + virtual bool HandleTeamExec( + const debug_team_exec& message); + virtual bool HandleThreadCreated( + const debug_thread_created& message); + virtual bool HandleThreadDeleted( + const debug_thread_deleted& message); + virtual bool HandleImageCreated( + const debug_image_created& message); + virtual bool HandleImageDeleted( + const debug_image_deleted& message); + virtual bool HandleProfilerUpdate( + const debug_profiler_update& message); + virtual bool HandleHandedOver( + const debug_handed_over& message); +}; + + +#endif // _DEBUG_MESSAGE_HANDLER_H diff --git a/headers/private/debug/TeamDebugger.h b/headers/private/debug/TeamDebugger.h new file mode 100644 index 0000000000..98339bfc13 --- /dev/null +++ b/headers/private/debug/TeamDebugger.h @@ -0,0 +1,44 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _TEAM_DEBUGGER_H +#define _TEAM_DEBUGGER_H + + +#include + +#include + + +class BPath; + + +class BTeamDebugger : public BDebugContext { +public: + BTeamDebugger(); + ~BTeamDebugger(); + + status_t Install(team_id team); + status_t Uninstall(); + + status_t LoadProgram(const char* const* args, + int32 argCount, bool traceLoading); + + status_t ReadDebugMessage(int32& _messageCode, + debug_debugger_message_data& messageBuffer); + + port_id DebuggerPort() const { return fDebuggerPort; } + +private: + static thread_id _LoadProgram(const char* const* args, + int32 argCount, bool traceLoading); + static status_t _FindProgram(const char* programName, + BPath& resolvedPath); + +private: + port_id fDebuggerPort; +}; + + +#endif // _TEAM_DEBUGGER_H diff --git a/src/kits/debug/DebugContext.cpp b/src/kits/debug/DebugContext.cpp new file mode 100644 index 0000000000..f4e2c50749 --- /dev/null +++ b/src/kits/debug/DebugContext.cpp @@ -0,0 +1,172 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + + +BDebugContext::BDebugContext() +{ + fContext.team = -1; +} + + +BDebugContext::~BDebugContext() +{ + Uninit(); +} + + +status_t +BDebugContext::Init(team_id team, port_id nubPort) +{ + Uninit(); + + status_t error = init_debug_context(&fContext, team, nubPort); + if (error != B_OK) { + fContext.team = -1; + return error; + } + + return B_OK; +} + + +void +BDebugContext::Uninit() +{ + if (fContext.team >= 0) { + destroy_debug_context(&fContext); + fContext.team = -1; + } +} + + +status_t +BDebugContext::SendDebugMessage(int32 messageCode, const void *message, + size_t messageSize, void* reply, size_t replySize) +{ + return send_debug_message(&fContext, messageCode, message, messageSize, + reply, replySize); +} + + +status_t +BDebugContext::SetTeamDebuggingFlags(int32 flags) +{ + debug_nub_set_team_flags message; + message.flags = flags; + + return SendDebugMessage(B_DEBUG_MESSAGE_SET_TEAM_FLAGS, &message, + sizeof(message), NULL, 0); +} + + +ssize_t +BDebugContext::ReadMemoryPartial(const void* address, void* buffer, size_t size) +{ + return debug_read_memory_partial(&fContext, address, buffer, size); +} + + +ssize_t +BDebugContext::ReadMemory(const void* address, void* buffer, size_t size) +{ + return debug_read_memory(&fContext, address, buffer, size); +} + + +ssize_t +BDebugContext::ReadString(const void* address, char* buffer, size_t size) +{ + return debug_read_string(&fContext, address, buffer, size); +} + + +status_t +BDebugContext::SetBreakpoint(void* address) +{ + debug_nub_set_breakpoint message; + message.reply_port = fContext.reply_port; + message.address = address; + + debug_nub_set_breakpoint_reply reply; + status_t error = SendDebugMessage(B_DEBUG_MESSAGE_SET_BREAKPOINT, &message, + sizeof(message), &reply, sizeof(reply)); + + return error == B_OK ? reply.error : error; +} + + +status_t +BDebugContext::ClearBreakpoint(void* address) +{ + debug_nub_clear_breakpoint message; + message.address = address; + + return SendDebugMessage(B_DEBUG_MESSAGE_CLEAR_BREAKPOINT, &message, + sizeof(message), NULL, 0); +} + + +status_t +BDebugContext::SetWatchpoint(void* address, uint32 type, int32 length) +{ + debug_nub_set_watchpoint message; + message.reply_port = fContext.reply_port; + message.address = address; + message.type = type; + message.length = length; + + debug_nub_set_watchpoint_reply reply; + status_t error = SendDebugMessage(B_DEBUG_MESSAGE_SET_WATCHPOINT, &message, + sizeof(message), &reply, sizeof(reply)); + + return error == B_OK ? reply.error : error; +} + + +status_t +BDebugContext::ClearWatchpoint(void* address) +{ + debug_nub_clear_watchpoint message; + message.address = address; + + return SendDebugMessage(B_DEBUG_MESSAGE_CLEAR_WATCHPOINT, &message, + sizeof(message), NULL, 0); +} + + +status_t +BDebugContext::ContinueThread(thread_id thread) +{ + debug_nub_continue_thread message; + message.thread = thread; + message.handle_event = B_THREAD_DEBUG_HANDLE_EVENT; + message.single_step = false; + + return SendDebugMessage(B_DEBUG_MESSAGE_CONTINUE_THREAD, &message, + sizeof(message), NULL, 0); +} + + +status_t +BDebugContext::SetThreadDebuggingFlags(thread_id thread, int32 flags) +{ + debug_nub_set_thread_flags message; + message.thread = thread; + message.flags = flags; + + return SendDebugMessage(B_DEBUG_MESSAGE_SET_THREAD_FLAGS, &message, + sizeof(message), NULL, 0); +} + + +status_t +BDebugContext::GetThreadCpuState(thread_id thread, + debug_debugger_message* _messageCode, debug_cpu_state* cpuState) +{ + return debug_get_cpu_state(&fContext, thread, _messageCode, cpuState); +} diff --git a/src/kits/debug/DebugLooper.cpp b/src/kits/debug/DebugLooper.cpp new file mode 100644 index 0000000000..5dbc6f3d5c --- /dev/null +++ b/src/kits/debug/DebugLooper.cpp @@ -0,0 +1,358 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + +#include +#include +#include +#include + + +struct BDebugLooper::Debugger { + BTeamDebugger* debugger; + BDebugMessageHandler* handler; + + Debugger(BTeamDebugger* debugger, BDebugMessageHandler* handler) + : + debugger(debugger), + handler(handler) + { + } +}; + + +struct BDebugLooper::Job : DoublyLinkedListLinkImpl { + Job() + : + fDoneSemaphore(-1) + { + } + + virtual ~Job() + { + } + + status_t Wait(BLocker& lock) + { + fDoneSemaphore = create_sem(0, "debug looper job"); + + lock.Unlock(); + + while (acquire_sem(fDoneSemaphore) == B_INTERRUPTED) { + } + + lock.Lock(); + + delete_sem(fDoneSemaphore); + fDoneSemaphore = -1; + + return fResult; + } + + void Done(status_t result) + { + fResult = result; + release_sem(fDoneSemaphore); + } + + virtual status_t Do(BDebugLooper* looper) = 0; + +protected: + sem_id fDoneSemaphore; + status_t fResult; +}; + + +struct BDebugLooper::JobList : DoublyLinkedList { +}; + + +struct BDebugLooper::AddDebuggerJob : Job { + AddDebuggerJob(BTeamDebugger* debugger, + BDebugMessageHandler* handler) + : + fDebugger(debugger), + fHandler(handler) + { + } + + virtual status_t Do(BDebugLooper* looper) + { + Debugger* debugger = new(std::nothrow) Debugger(fDebugger, fHandler); + if (debugger == NULL || !looper->fDebuggers.AddItem(debugger)) { + delete debugger; + return B_NO_MEMORY; + } + + return B_OK; + } + +private: + BTeamDebugger* fDebugger; + BDebugMessageHandler* fHandler; +}; + + +struct BDebugLooper::RemoveDebuggerJob : Job { + RemoveDebuggerJob(team_id team) + : + fTeam(team) + { + } + + virtual status_t Do(BDebugLooper* looper) + { + for (int32 i = 0; Debugger* debugger = looper->fDebuggers.ItemAt(i); + i++) { + if (debugger->debugger->Team() == fTeam) { + delete looper->fDebuggers.RemoveItemAt(i); + return B_OK; + } + } + + return B_ENTRY_NOT_FOUND; + } + +private: + team_id fTeam; +}; + + +// #pragma mark - + + +BDebugLooper::BDebugLooper() + : + fLock("debug looper"), + fThread(-1), + fOwnsThread(false), + fTerminating(false), + fNotified(false), + fJobs(NULL), + fEventSemaphore(-1) +{ +} + + +BDebugLooper::~BDebugLooper() +{ +} + + +status_t +BDebugLooper::Init() +{ + status_t error = fLock.InitCheck(); + if (error != B_OK) + return error; + + AutoLocker locker(fLock); + + if (fThread >= 0) + return B_BAD_VALUE; + + if (fJobs == NULL) { + fJobs = new(std::nothrow) JobList; + if (fJobs == NULL) + return B_NO_MEMORY; + } + + if (fEventSemaphore < 0) { + fEventSemaphore = create_sem(0, "debug looper event"); + if (fEventSemaphore < 0) + return fEventSemaphore; + } + + return B_OK; +} + + +thread_id +BDebugLooper::Run(bool spawnThread) +{ + AutoLocker locker(fLock); + + if (fThread >= 0) + return B_BAD_VALUE; + + fNotified = false; + + if (spawnThread) { + fThread = spawn_thread(&_MessageLoopEntry, "debug looper", + B_NORMAL_PRIORITY, this); + if (fThread < 0) + return fThread; + + fOwnsThread = true; + + resume_thread(fThread); + return B_OK; + } + + fThread = find_thread(NULL); + fOwnsThread = false; + + _MessageLoop(); + return B_OK; +} + + +void +BDebugLooper::Quit() +{ + AutoLocker locker(fLock); + + fTerminating = true; + _Notify(); +} + + +status_t +BDebugLooper::AddTeamDebugger(BTeamDebugger* debugger, + BDebugMessageHandler* handler) +{ + if (debugger == NULL || handler == NULL) + return B_BAD_VALUE; + + AddDebuggerJob job(debugger, handler); + return _DoJob(&job); +} + + +bool +BDebugLooper::RemoveTeamDebugger(BTeamDebugger* debugger) +{ + if (debugger == NULL) + return false; + + RemoveDebuggerJob job(debugger->Team()); + return _DoJob(&job) == B_OK; +} + + +bool +BDebugLooper::RemoveTeamDebugger(team_id team) +{ + if (team < 0) + return false; + + RemoveDebuggerJob job(team); + return _DoJob(&job) == B_OK; +} + + +/*static*/ status_t +BDebugLooper::_MessageLoopEntry(void* data) +{ + return ((BDebugLooper*)data)->_MessageLoop(); +} + + +status_t +BDebugLooper::_MessageLoop() +{ + while (true) { + // prepare the wait info array + int32 debuggerCount = fDebuggers.CountItems(); + object_wait_info waitInfos[debuggerCount + 1]; + + for (int32 i = 0; i < debuggerCount; i++) { + waitInfos[i].object + = fDebuggers.ItemAt(i)->debugger->DebuggerPort(); + waitInfos[i].type = B_OBJECT_TYPE_PORT; + waitInfos[i].events = B_EVENT_READ; + } + + waitInfos[debuggerCount].object = fEventSemaphore; + waitInfos[debuggerCount].type = B_OBJECT_TYPE_SEMAPHORE; + waitInfos[debuggerCount].events = B_EVENT_ACQUIRE_SEMAPHORE; + + // wait for the next event + wait_for_objects(waitInfos, debuggerCount + 1); + + AutoLocker locker(fLock); + + // handle all pending jobs + bool handledJobs = fJobs->Head() != NULL; + while (Job* job = fJobs->RemoveHead()) + job->Done(job->Do(this)); + + // acquire notification semaphore and mark unnotified + if ((waitInfos[debuggerCount].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) + acquire_sem(fEventSemaphore); + fNotified = false; + + if (fTerminating) + return B_OK; + + // Always loop when jobs were executed, since that might add/remove + // debuggers. + if (handledJobs) + continue; + + // read a pending port message + for (int32 i = 0; i < debuggerCount; i++) { + if ((waitInfos[i].events & B_EVENT_READ) != 0) { + Debugger* debugger = fDebuggers.ItemAt(i); + + // read the message + debug_debugger_message_data message; + int32 code; + ssize_t messageSize = read_port( + debugger->debugger->DebuggerPort(), &code, &message, + sizeof(message)); + if (messageSize < 0) + continue; + + // handle the message + bool continueThread = debugger->handler->HandleDebugMessage( + code, message); + + // If requested, tell the thread to continue (only when there + // is a thread and the message was synchronous). + if (continueThread && message.origin.thread >= 0 + && message.origin.nub_port >= 0) { + debugger->debugger->ContinueThread(message.origin.thread); + } + + // Handle only one message -- the hook might have added/removed + // debuggers which makes further iteration problematic. + break; + } + } + } +} + + +status_t +BDebugLooper::_DoJob(Job* job) +{ + AutoLocker locker(fLock); + + // execute directly, if in looper thread or not running yet + if (fThread < 0 || fThread == find_thread(NULL)) + return job->Do(this); + + // execute in the looper thread + fJobs->Add(job); + _Notify(); + + return job->Wait(fLock); +} + + +void +BDebugLooper::_Notify() +{ + if (fNotified) + return; + + fNotified = true; + release_sem(fEventSemaphore); +} diff --git a/src/kits/debug/DebugMessageHandler.cpp b/src/kits/debug/DebugMessageHandler.cpp new file mode 100644 index 0000000000..046067d928 --- /dev/null +++ b/src/kits/debug/DebugMessageHandler.cpp @@ -0,0 +1,195 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + + +BDebugMessageHandler::~BDebugMessageHandler() +{ +} + + +/*! Handles the supplied debugger message. + Can be overridded by subclasses. The base class implementation calls the + respective Handle*() hook for the message. + + \param messageCode The (port) message code identifying the debugger message. + \param message The message data. + \return \c true, if the caller is supposed to continue the thread, \c false + otherwise. +*/ +bool +BDebugMessageHandler::HandleDebugMessage(int32 messageCode, + const debug_debugger_message_data& message) +{ + switch (messageCode) { + case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: + return HandleThreadDebugged(message.thread_debugged); + case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: + return HandleDebuggerCall(message.debugger_call); + case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: + return HandleBreakpointHit(message.breakpoint_hit); + case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: + return HandleWatchpointHit(message.watchpoint_hit); + case B_DEBUGGER_MESSAGE_SINGLE_STEP: + return HandleSingleStep(message.single_step); + case B_DEBUGGER_MESSAGE_PRE_SYSCALL: + return HandlePreSyscall(message.pre_syscall); + case B_DEBUGGER_MESSAGE_POST_SYSCALL: + return HandlePostSyscall(message.post_syscall); + case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: + return HandleSignalReceived(message.signal_received); + case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: + return HandleExceptionOccurred(message.exception_occurred); + case B_DEBUGGER_MESSAGE_TEAM_CREATED: + return HandleTeamCreated(message.team_created); + case B_DEBUGGER_MESSAGE_TEAM_DELETED: + return HandleTeamDeleted(message.team_deleted); + case B_DEBUGGER_MESSAGE_TEAM_EXEC: + return HandleTeamExec(message.team_exec); + case B_DEBUGGER_MESSAGE_THREAD_CREATED: + return HandleThreadCreated(message.thread_created); + case B_DEBUGGER_MESSAGE_THREAD_DELETED: + return HandleThreadDeleted(message.thread_deleted); + case B_DEBUGGER_MESSAGE_IMAGE_CREATED: + return HandleImageCreated(message.image_created); + case B_DEBUGGER_MESSAGE_IMAGE_DELETED: + return HandleImageDeleted(message.image_deleted); + case B_DEBUGGER_MESSAGE_PROFILER_UPDATE: + return HandleProfilerUpdate(message.profiler_update); + case B_DEBUGGER_MESSAGE_HANDED_OVER: + return HandleHandedOver(message.handed_over); + default: + return true; + } +} + + +bool +BDebugMessageHandler::HandleThreadDebugged(const debug_thread_debugged& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleDebuggerCall(const debug_debugger_call& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleBreakpointHit(const debug_breakpoint_hit& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleWatchpointHit(const debug_watchpoint_hit& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleSingleStep(const debug_single_step& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandlePreSyscall(const debug_pre_syscall& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandlePostSyscall(const debug_post_syscall& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleSignalReceived(const debug_signal_received& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleExceptionOccurred( + const debug_exception_occurred& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleTeamCreated(const debug_team_created& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleTeamDeleted(const debug_team_deleted& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleTeamExec(const debug_team_exec& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleThreadCreated(const debug_thread_created& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleThreadDeleted(const debug_thread_deleted& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleImageCreated(const debug_image_created& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleImageDeleted(const debug_image_deleted& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleProfilerUpdate(const debug_profiler_update& message) +{ + return true; +} + + +bool +BDebugMessageHandler::HandleHandedOver(const debug_handed_over& message) +{ + return true; +} diff --git a/src/kits/debug/Jamfile b/src/kits/debug/Jamfile index 61993bffb2..ab4a898784 100644 --- a/src/kits/debug/Jamfile +++ b/src/kits/debug/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src kits debug ; UsePrivateHeaders debug ; UsePrivateHeaders kernel ; # for +UsePrivateHeaders libroot ; UsePrivateHeaders runtime_loader ; UsePrivateHeaders shared ; UsePrivateSystemHeaders ; @@ -12,9 +13,13 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) arch $(TARGET_ARCH) ] ; SharedLibrary libdebug.so : debug_support.cpp + DebugContext.cpp DebugEventStream.cpp + DebugLooper.cpp + DebugMessageHandler.cpp Image.cpp SymbolLookup.cpp + TeamDebugger.cpp # architecture specific arch_debug_support.cpp diff --git a/src/kits/debug/TeamDebugger.cpp b/src/kits/debug/TeamDebugger.cpp new file mode 100644 index 0000000000..cb925113c1 --- /dev/null +++ b/src/kits/debug/TeamDebugger.cpp @@ -0,0 +1,207 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + + +BTeamDebugger::BTeamDebugger() + : + fDebuggerPort(-1) +{ +} + + +BTeamDebugger::~BTeamDebugger() +{ + Uninstall(); +} + + +status_t +BTeamDebugger::Install(team_id team) +{ + Uninstall(); + + // create a debugger port + char name[B_OS_NAME_LENGTH]; + snprintf(name, sizeof(name), "debugger for team %" B_PRId32, team); + fDebuggerPort = create_port(100, name); + if (fDebuggerPort < 0) + return fDebuggerPort; + + port_id nubPort = install_team_debugger(team, fDebuggerPort); + if (nubPort < 0) { + delete_port(fDebuggerPort); + fDebuggerPort = -1; + return nubPort; + } + + status_t error = BDebugContext::Init(team, nubPort); + if (error != B_OK) { + remove_team_debugger(team); + delete_port(fDebuggerPort); + fDebuggerPort = -1; + return error; + } + + return B_OK; +} + + +status_t +BTeamDebugger::Uninstall() +{ + if (Team() < 0) + return B_BAD_VALUE; + + remove_team_debugger(Team()); + + delete_port(fDebuggerPort); + + BDebugContext::Uninit(); + + fDebuggerPort = -1; + + return B_OK; +} + + +status_t +BTeamDebugger::LoadProgram(const char* const* args, int32 argCount, + bool traceLoading) +{ + // load the program + thread_id thread = _LoadProgram(args, argCount, traceLoading); + if (thread < 0) + return thread; + + // install the debugger + status_t error = Install(thread); + if (error != B_OK) { + kill_team(thread); + return error; + } + + return B_OK; +} + + +status_t +BTeamDebugger::ReadDebugMessage(int32& _messageCode, + debug_debugger_message_data& messageBuffer) +{ + ssize_t bytesRead = read_port(fDebuggerPort, &_messageCode, &messageBuffer, + sizeof(messageBuffer)); + if (bytesRead < 0) + return bytesRead; + + return B_OK; +} + + + +/*static*/ thread_id +BTeamDebugger::_LoadProgram(const char* const* args, int32 argCount, + bool traceLoading) +{ + // clone the argument vector so that we can change it + const char** mutableArgs = new const char*[argCount]; + for (int i = 0; i < argCount; i++) + mutableArgs[i] = args[i]; + + // resolve the program path + BPath programPath; + status_t error = _FindProgram(args[0], programPath); + if (error != B_OK) { + delete[] mutableArgs; + return error; + } + mutableArgs[0] = programPath.Path(); + + // count environment variables + int envCount = 0; + while (environ[envCount] != NULL) + envCount++; + + // flatten the program args and environment + char** flatArgs = NULL; + size_t flatArgsSize; + error = __flatten_process_args(mutableArgs, argCount, environ, envCount, + &flatArgs, &flatArgsSize); + + // load the program + thread_id thread; + if (error == B_OK) { + thread = _kern_load_image(flatArgs, flatArgsSize, argCount, envCount, + B_NORMAL_PRIORITY, (traceLoading ? 0 : B_WAIT_TILL_LOADED), -1, 0); + + free(flatArgs); + } else + thread = error; + + delete[] mutableArgs; + + return thread; +} + + +/*static*/ status_t +BTeamDebugger::_FindProgram(const char* programName, BPath& resolvedPath) +{ + // If the program name is absolute, then there's nothing to do. + // If the program name consists of more than one path element, then we + // consider it a relative path and don't search in PATH either. + if (*programName == '/' || strchr(programName, '/')) + return resolvedPath.SetTo(programName); + + // get the PATH environment variable + const char* paths = getenv("PATH"); + if (!paths) + return B_ENTRY_NOT_FOUND; + + // iterate through the paths + do { + const char* pathEnd = strchr(paths, ':'); + int pathLen = (pathEnd ? pathEnd - paths : strlen(paths)); + + // We skip empty paths. + if (pathLen > 0) { + // get the program path + BString directory(paths, pathLen); + if (directory.Length() == 0) + return B_NO_MEMORY; + + BPath path; + status_t error = path.SetTo(directory, programName); + if (error != B_OK) + continue; + + // stat() the path to be sure, there is a file + struct stat st; + if (stat(path.Path(), &st) == 0 && S_ISREG(st.st_mode)) { + resolvedPath = path; + return B_OK; + } + } + + paths = (pathEnd ? pathEnd + 1 : NULL); + } while (paths); + + // not found in PATH + return B_ENTRY_NOT_FOUND; +}