Added a few classes to the debug kit:
* BDebugMessageHandler: Interface with hooks for handling of debug messages. * BDebugContext: Essentially a C++ wrapper for struct debug_context, with handy methods for controlling a debugged team. * BTeamDebugger: Proxy for a debugged team. Derived from BDebugContext. * BDebugLooper: Wraps a main debug message loop. Any number of BTeamDebuggers can be added and associated with BDebugMessageHandlers. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35953 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
adfe871902
commit
53446ebf80
57
headers/private/debug/DebugContext.h
Normal file
57
headers/private/debug/DebugContext.h
Normal file
@ -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 <debug_support.h>
|
||||
|
||||
|
||||
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
|
65
headers/private/debug/DebugLooper.h
Normal file
65
headers/private/debug/DebugLooper.h
Normal file
@ -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 <Locker.h>
|
||||
|
||||
#include <ObjectList.h>
|
||||
|
||||
|
||||
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<Debugger> 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
|
58
headers/private/debug/DebugMessageHandler.h
Normal file
58
headers/private/debug/DebugMessageHandler.h
Normal file
@ -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 <debugger.h>
|
||||
|
||||
|
||||
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
|
44
headers/private/debug/TeamDebugger.h
Normal file
44
headers/private/debug/TeamDebugger.h
Normal file
@ -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 <debugger.h>
|
||||
|
||||
#include <DebugContext.h>
|
||||
|
||||
|
||||
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
|
172
src/kits/debug/DebugContext.cpp
Normal file
172
src/kits/debug/DebugContext.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <DebugContext.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
358
src/kits/debug/DebugLooper.cpp
Normal file
358
src/kits/debug/DebugLooper.cpp
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <DebugLooper.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <AutoLocker.h>
|
||||
#include <DebugMessageHandler.h>
|
||||
#include <TeamDebugger.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
|
||||
struct BDebugLooper::Debugger {
|
||||
BTeamDebugger* debugger;
|
||||
BDebugMessageHandler* handler;
|
||||
|
||||
Debugger(BTeamDebugger* debugger, BDebugMessageHandler* handler)
|
||||
:
|
||||
debugger(debugger),
|
||||
handler(handler)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct BDebugLooper::Job : DoublyLinkedListLinkImpl<Job> {
|
||||
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<Job> {
|
||||
};
|
||||
|
||||
|
||||
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<BLocker> 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<BLocker> 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<BLocker> 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<BLocker> 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<BLocker> 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);
|
||||
}
|
195
src/kits/debug/DebugMessageHandler.cpp
Normal file
195
src/kits/debug/DebugMessageHandler.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <DebugMessageHandler.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
@ -3,6 +3,7 @@ SubDir HAIKU_TOP src kits debug ;
|
||||
UsePrivateHeaders debug ;
|
||||
UsePrivateHeaders kernel ;
|
||||
# for <util/DoublyLinkedList.h>
|
||||
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
|
||||
|
207
src/kits/debug/TeamDebugger.cpp
Normal file
207
src/kits/debug/TeamDebugger.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <TeamDebugger.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <Path.h>
|
||||
#include <String.h>
|
||||
|
||||
#include <libroot_private.h>
|
||||
#include <syscalls.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user