Initial version of our debug server. This is the guy that hangs around

waiting for teams to crash to show our favorite alert.
Due to the missing app_server we don't show a alert yet. We just print
something to /dev/dprintf and kill the team. Unlike R5's debug server
ours will probably not provide any debugging functionality itself, but
rather employ gdb directly.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11492 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2005-02-25 14:37:36 +00:00
parent 6d3794c0a1
commit 35e49f2582
4 changed files with 570 additions and 0 deletions

View File

@ -1,6 +1,7 @@
SubDir OBOS_TOP src servers ;
SubInclude OBOS_TOP src servers app ;
SubInclude OBOS_TOP src servers debug ;
SubInclude OBOS_TOP src servers input ;
SubInclude OBOS_TOP src servers mail ;
SubInclude OBOS_TOP src servers media ;

View File

@ -0,0 +1,510 @@
/*
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
* Distributed under the terms of the MIT License.
*/
#include <map>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <Application.h>
#include <Invoker.h>
#include <debugger.h>
#include <util/DoublyLinkedList.h>
static const char *kSignature = "application/x-vnd.haiku-debug-server";
// message what codes
enum {
DEBUG_MESSAGE = 'dbgm',
ALERT_MESSAGE = 'alrt',
};
class DebugServer;
class TeamDebugHandler;
// DebugMessage
class DebugMessage : public DoublyLinkedListLinkImpl<DebugMessage> {
public:
DebugMessage()
{
}
void SetCode(int32 code) { fCode = code; }
int32 Code() const { return fCode; }
debug_debugger_message_data &Data() { return fData; }
const debug_debugger_message_data &Data() const { return fData; }
private:
int32 fCode;
debug_debugger_message_data fData;
};
typedef DoublyLinkedList<DebugMessage> DebugMessageList;
// ThreadDebugHandler
class ThreadDebugHandler : public BHandler {
public:
ThreadDebugHandler(TeamDebugHandler *teamHandler, thread_id thread);
~ThreadDebugHandler();
thread_id Thread() const;
bool HandleMessage(debug_thread_stopped &message);
virtual void MessageReceived(BMessage *message);
private:
TeamDebugHandler *fTeamHandler;
thread_id fThread;
BInvoker *fInvoker;
};
// TeamDebugHandler
class TeamDebugHandler {
public:
TeamDebugHandler(DebugServer *debugServer, team_id team);
~TeamDebugHandler();
team_id Team() const;
bool HandleMessage(DebugMessage *message);
void EnterDebugger();
void KillTeam();
private:
void _ContinueThread(thread_id thread);
void _DeleteThreadHandler(ThreadDebugHandler *handler);
private:
typedef map<team_id, ThreadDebugHandler*> ThreadDebugHandlerMap;
DebugServer *fDebugServer;
team_id fTeam;
port_id fNubPort;
ThreadDebugHandlerMap fThreadDebugHandlers;
};
// DebugServer
class DebugServer : public BApplication {
public:
DebugServer(status_t &error);
status_t Init();
virtual void MessageReceived(BMessage *message);
void EnterDebugger(TeamDebugHandler *handler);
void KillTeam(TeamDebugHandler *handler);
private:
static status_t _ListenerEntry(void *data);
status_t _Listener();
void _DeleteTeamDebugHandler(TeamDebugHandler *handler);
private:
typedef map<team_id, TeamDebugHandler*> TeamDebugHandlerMap;
port_id fListenerPort;
thread_id fListener;
bool fTerminating;
TeamDebugHandlerMap fTeamDebugHandlers;
DebugMessageList fDebugMessages;
};
// #pragma mark -
// constructor
ThreadDebugHandler::ThreadDebugHandler(TeamDebugHandler *teamHandler,
thread_id thread)
: fTeamHandler(teamHandler),
fThread(thread),
fInvoker(NULL)
{
}
// destructor
ThreadDebugHandler::~ThreadDebugHandler()
{
delete fInvoker;
}
// Thread
thread_id
ThreadDebugHandler::Thread() const
{
return fThread;
}
// HandleMessage
bool
ThreadDebugHandler::HandleMessage(debug_thread_stopped &message)
{
if (!fInvoker)
fInvoker = new BInvoker(new BMessage(ALERT_MESSAGE), this);
char buffer[512];
get_why_stopped_string(message.why, buffer, sizeof(buffer));
// TODO: This would be the point to pop up an asynchronous alert.
// We just print the error and send a message to ourselves.
printf("debug_server: Thread %ld entered the debugger: %s\n", fThread,
buffer);
BMessage alertMessage(*fInvoker->Message());
alertMessage.AddInt32("which", 1);
fInvoker->Invoke(&alertMessage);
return false;
}
// MessageReceived
void
ThreadDebugHandler::MessageReceived(BMessage *message)
{
if (message->what == ALERT_MESSAGE) {
printf("debug_server: ALERT_MESSAGE received for thread %ld\n", fThread);
int32 which;
if (message->FindInt32("which", &which) != B_OK)
which = 1;
if (which == 0)
fTeamHandler->EnterDebugger();
else
fTeamHandler->KillTeam();
}
}
// #pragma mark -
// constructor
TeamDebugHandler::TeamDebugHandler(DebugServer *debugServer, team_id team)
: fDebugServer(debugServer),
fTeam(team),
fNubPort(-1),
fThreadDebugHandlers()
{
}
// destructor
TeamDebugHandler::~TeamDebugHandler()
{
// delete all thread debug handlers.
for (ThreadDebugHandlerMap::iterator it = fThreadDebugHandlers.begin();
it != fThreadDebugHandlers.end();
it = fThreadDebugHandlers.begin()) {
_DeleteThreadHandler(it->second);
}
}
// Team
team_id
TeamDebugHandler::Team() const
{
return fTeam;
}
// HandleMessage
bool
TeamDebugHandler::HandleMessage(DebugMessage *message)
{
thread_id thread = message->Data().origin.thread;
switch (message->Code()) {
case B_DEBUGGER_MESSAGE_THREAD_STOPPED:
{
fNubPort = message->Data().origin.nub_port;
// create a thread debug handler
ThreadDebugHandler *handler
= new ThreadDebugHandler(this, thread);
fThreadDebugHandlers[thread] = handler;
fDebugServer->AddHandler(handler);
// let the handler deal with the message
if (handler->HandleMessage(message->Data().thread_stopped))
_DeleteThreadHandler(handler);
break;
}
case B_DEBUGGER_MESSAGE_TEAM_DELETED:
return true;
case B_DEBUGGER_MESSAGE_THREAD_DELETED:
// a thread is gone: nothing to do
break;
case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
// A signal doesn't usually trigger the debugger.
// Just let the thread continue and see what happens next.
case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
case B_DEBUGGER_MESSAGE_POST_SYSCALL:
case B_DEBUGGER_MESSAGE_TEAM_CREATED:
case B_DEBUGGER_MESSAGE_THREAD_CREATED:
case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
fNubPort = message->Data().origin.nub_port;
_ContinueThread(thread);
break;
}
return fThreadDebugHandlers.empty();
}
// EnterDebugger
void
TeamDebugHandler::EnterDebugger()
{
fDebugServer->EnterDebugger(this);
}
// KillTeam
void
TeamDebugHandler::KillTeam()
{
fDebugServer->KillTeam(this);
}
// _ContinueThread
void
TeamDebugHandler::_ContinueThread(thread_id thread)
{
debug_nub_run_thread message;
message.thread = thread;
message.handle_event = B_THREAD_DEBUG_HANDLE_EVENT;
while (true) {
status_t error = write_port(fNubPort, B_DEBUG_MESSAGE_RUN_THREAD,
&message, sizeof(message));
if (error == B_OK)
return;
if (error != B_INTERRUPTED) {
fprintf(stderr, "debug_server: Failed to run thread %ld: %s\n",
thread, strerror(error));
return;
}
}
}
// _DeleteThreadHandler
void
TeamDebugHandler::_DeleteThreadHandler(ThreadDebugHandler *handler)
{
if (!handler)
return;
// remove handler from the map
thread_id thread = handler->Thread();
ThreadDebugHandlerMap::iterator it = fThreadDebugHandlers.find(thread);
if (it != fThreadDebugHandlers.end())
fThreadDebugHandlers.erase(it);
// remove handler from the debug server
if (handler->Looper())
handler->Looper()->RemoveHandler(handler);
delete handler;
}
// #pragma mark -
// constructor
DebugServer::DebugServer(status_t &error)
: BApplication(kSignature, &error),
fListenerPort(-1),
fListener(-1),
fTerminating(false),
fTeamDebugHandlers(),
fDebugMessages()
{
}
// Init
status_t
DebugServer::Init()
{
// create listener port
fListenerPort = create_port(10, "kernel listener");
if (fListenerPort < 0)
return fListenerPort;
// spawn the listener thread
fListener = spawn_thread(_ListenerEntry, "kernel listener",
B_NORMAL_PRIORITY, this);
if (fListener < 0)
return fListener;
// register as default debugger
status_t error = install_default_debugger(fListenerPort);
if (error != B_OK)
return error;
// resume the listener
resume_thread(fListener);
return B_OK;
}
// MessageReceived
void
DebugServer::MessageReceived(BMessage *message)
{
if (message->what == DEBUG_MESSAGE) {
// get the next debug message
if (DebugMessage *message = fDebugMessages.Head()) {
fDebugMessages.Remove(message);
// get the responsible team debug handler
team_id team = message->Data().origin.team;
TeamDebugHandlerMap::iterator it
= fTeamDebugHandlers.find(team);
TeamDebugHandler *handler;
if (it == fTeamDebugHandlers.end()) {
handler = new TeamDebugHandler(this, team);
fTeamDebugHandlers[team] = handler;
} else
handler = it->second;
// let the handler deal with the message
if (handler->HandleMessage(message))
_DeleteTeamDebugHandler(handler);
delete message;
}
} else
BApplication::MessageReceived(message);
}
// EnterDebugger
void
DebugServer::EnterDebugger(TeamDebugHandler *handler)
{
// TODO: Start gdb and hand over the team.
KillTeam(handler);
}
// KillTeam
void
DebugServer::KillTeam(TeamDebugHandler *handler)
{
team_id team = handler->Team();
fprintf(stderr, "debug_server: Killing team %ld\n", team);
kill_team(team);
_DeleteTeamDebugHandler(handler);
// TODO: We should actually iterate through the debug message list and
// remove all messages for the team.
}
// _ListenerEntry
status_t
DebugServer::_ListenerEntry(void *data)
{
return ((DebugServer*)data)->_Listener();
}
// _Listener
status_t
DebugServer::_Listener()
{
while (!fTerminating) {
// receive the next debug message
DebugMessage *message = new DebugMessage;
int32 code;
ssize_t bytesRead;
do {
bytesRead = read_port(fListenerPort, &code, &message->Data(),
sizeof(debug_debugger_message_data));
} while (bytesRead == B_INTERRUPTED);
if (bytesRead < 0) {
fprintf(stderr, "debug_server: Failed to read from listener port: "
"%s\n", strerror(bytesRead));
exit(1);
}
message->SetCode(code);
// queue the message and send notify the app
Lock();
fDebugMessages.Add(message);
Unlock();
PostMessage(DEBUG_MESSAGE);
}
return B_OK;
}
// _DeleteTeamDebugHandler
void
DebugServer::_DeleteTeamDebugHandler(TeamDebugHandler *handler)
{
if (!handler)
return;
// remove handler from the map
team_id team = handler->Team();
TeamDebugHandlerMap::iterator it = fTeamDebugHandlers.find(team);
if (it != fTeamDebugHandlers.end())
fTeamDebugHandlers.erase(it);
delete handler;
}
// #pragma mark -
// main
int
main()
{
status_t error;
// for the time being let the debug server print to the syslog
int console = open("/dev/dprintf", O_RDONLY);
if (console < 0) {
fprintf(stderr, "debug_server: Failed to open console: %s\n",
strerror(errno));
}
dup2(console, STDOUT_FILENO);
dup2(console, STDERR_FILENO);
close(console);
// create application
DebugServer server(error);
if (error != B_OK) {
fprintf(stderr, "debug_server: Failed to create BApplication: %s\n",
strerror(error));
exit(1);
}
// init application
error = server.Init();
if (error != B_OK) {
fprintf(stderr, "debug_server: Failed to init application: %s\n",
strerror(error));
exit(1);
}
server.Run();
return 0;
}

14
src/servers/debug/Jamfile Normal file
View File

@ -0,0 +1,14 @@
SubDir OBOS_TOP src servers debug ;
UsePrivateHeaders kernel ;
AddResources debug_server : debug_server.rdef ;
Server debug_server
:
DebugServer.cpp
:
libroot.so # Haiku libroot
libbe.so # Haiku libbe
stdc++.r4
;

View File

@ -0,0 +1,45 @@
/*
* DebugServer.rdef
*
*/
/* BEOS:APP_SIG :
application:/x-vnd.obos.app-name
*/
resource app_signature "application/x-vnd.haiku-debug-server";
/* BEOS:APP_FLAGS :
00000000 = SINGLE LAUNCH
00000001 = MULTIPLE LAUNCH
00000002 = EXCLUSIVE LAUNCH
00000004 = BACKGROUND APP + SINGLE LAUNCH
00000005 = BACKGROUND APP + MULTIPLE LAUNCH
00000006 = BACKGROUND APP + EXCLUSIVE LAUNCH
00000008 = ARGV_ONLY + SINGLE LAUNCH
00000009 = ARGV_ONLY + MULTIPLE LAUNCH
0000000A = ARGV_ONLY + EXCLUSIVE LAUNCH
0000000C = ARGV_ONLY + BACKGROUND APP + SINGLE LAUNCH
0000000D = ARGV_ONLY + BACKGROUND APP + MULTIPLE LAUNCH
0000000E = ARGV_ONLY + BACKGROUND APP + EXCLUSIVE LAUNCH
*/
resource app_flags B_SINGLE_LAUNCH | B_BACKGROUND_APP;
resource app_version {
major = 1,
middle = 0,
minor = 0,
/* 0 = development 1 = alpha 2 = beta
3 = gamma 4 = golden master 5 = final */
variety = 1,
internal = 0,
short_info = "debug_server",
long_info = "Haiku - debug_server"
};
/* TODO: Icons */