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:
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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 debug ;
|
||||||
UsePrivateHeaders kernel ;
|
UsePrivateHeaders kernel ;
|
||||||
# for <util/DoublyLinkedList.h>
|
# for <util/DoublyLinkedList.h>
|
||||||
|
UsePrivateHeaders libroot ;
|
||||||
UsePrivateHeaders runtime_loader ;
|
UsePrivateHeaders runtime_loader ;
|
||||||
UsePrivateHeaders shared ;
|
UsePrivateHeaders shared ;
|
||||||
UsePrivateSystemHeaders ;
|
UsePrivateSystemHeaders ;
|
||||||
@@ -12,9 +13,13 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) arch $(TARGET_ARCH) ] ;
|
|||||||
|
|
||||||
SharedLibrary libdebug.so :
|
SharedLibrary libdebug.so :
|
||||||
debug_support.cpp
|
debug_support.cpp
|
||||||
|
DebugContext.cpp
|
||||||
DebugEventStream.cpp
|
DebugEventStream.cpp
|
||||||
|
DebugLooper.cpp
|
||||||
|
DebugMessageHandler.cpp
|
||||||
Image.cpp
|
Image.cpp
|
||||||
SymbolLookup.cpp
|
SymbolLookup.cpp
|
||||||
|
TeamDebugger.cpp
|
||||||
|
|
||||||
# architecture specific
|
# architecture specific
|
||||||
arch_debug_support.cpp
|
arch_debug_support.cpp
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user