From 35e49f2582eed6a36620f79de55d4c9f3bfe3a30 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 25 Feb 2005 14:37:36 +0000 Subject: [PATCH] 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 --- src/servers/Jamfile | 1 + src/servers/debug/DebugServer.cpp | 510 ++++++++++++++++++++++++++++ src/servers/debug/Jamfile | 14 + src/servers/debug/debug_server.rdef | 45 +++ 4 files changed, 570 insertions(+) create mode 100644 src/servers/debug/DebugServer.cpp create mode 100644 src/servers/debug/Jamfile create mode 100644 src/servers/debug/debug_server.rdef diff --git a/src/servers/Jamfile b/src/servers/Jamfile index fa73aa96c3..d7922a0442 100644 --- a/src/servers/Jamfile +++ b/src/servers/Jamfile @@ -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 ; diff --git a/src/servers/debug/DebugServer.cpp b/src/servers/debug/DebugServer.cpp new file mode 100644 index 0000000000..0d03917bd2 --- /dev/null +++ b/src/servers/debug/DebugServer.cpp @@ -0,0 +1,510 @@ +/* + * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +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 { +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 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 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 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; +} diff --git a/src/servers/debug/Jamfile b/src/servers/debug/Jamfile new file mode 100644 index 0000000000..6ebcc43dd3 --- /dev/null +++ b/src/servers/debug/Jamfile @@ -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 +; diff --git a/src/servers/debug/debug_server.rdef b/src/servers/debug/debug_server.rdef new file mode 100644 index 0000000000..eb23d2fd68 --- /dev/null +++ b/src/servers/debug/debug_server.rdef @@ -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 */