vfs: check userland buffers before reading them.
[haiku.git] / src / servers / debug / DebugServer.cpp
blob22dece4a0af92581b8fabab3e544835bcdd99364
1 /*
2 * Copyright 2011-2014, Rene Gollent, rene@gollent.com.
3 * Copyright 2005-2009, Ingo Weinhold, bonefish@users.sf.net.
4 * Distributed under the terms of the MIT License.
5 */
8 #include <map>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <strings.h>
14 #include <unistd.h>
16 #include <Alert.h>
17 #include <AppMisc.h>
18 #include <AutoDeleter.h>
19 #include <Autolock.h>
20 #include <Catalog.h>
21 #include <debug_support.h>
22 #include <Entry.h>
23 #include <FindDirectory.h>
24 #include <Invoker.h>
25 #include <Locale.h>
26 #include <Path.h>
28 #include <DriverSettings.h>
29 #include <MessengerPrivate.h>
30 #include <RegExp.h>
31 #include <RegistrarDefs.h>
32 #include <RosterPrivate.h>
33 #include <Server.h>
34 #include <StringList.h>
36 #include <util/DoublyLinkedList.h>
39 enum {
40 kActionKillTeam,
41 kActionDebugTeam,
42 kActionWriteCoreFile,
43 kActionSaveReportTeam,
44 kActionPromptUser
48 static const char* kDebuggerSignature = "application/x-vnd.Haiku-Debugger";
49 static const int32 MSG_DEBUG_THIS_TEAM = 'dbtt';
52 //#define HANDOVER_USE_GDB 1
53 #define HANDOVER_USE_DEBUGGER 1
55 #undef B_TRANSLATION_CONTEXT
56 #define B_TRANSLATION_CONTEXT "DebugServer"
58 #define USE_GUI true
59 // define to false if the debug server shouldn't use GUI (i.e. an alert)
61 //#define TRACE_DEBUG_SERVER
62 #ifdef TRACE_DEBUG_SERVER
63 # define TRACE(x) debug_printf x
64 #else
65 # define TRACE(x) ;
66 #endif
69 using std::map;
70 using std::nothrow;
73 static const char *kSignature = "application/x-vnd.Haiku-debug_server";
76 static status_t
77 action_for_string(const char* action, int32& _action)
79 if (strcmp(action, "kill") == 0)
80 _action = kActionKillTeam;
81 else if (strcmp(action, "debug") == 0)
82 _action = kActionDebugTeam;
83 else if (strcmp(action, "log") == 0
84 || strcmp(action, "report") == 0) {
85 _action = kActionSaveReportTeam;
86 } else if (strcasecmp(action, "core") == 0)
87 _action = kActionWriteCoreFile;
88 else if (strcasecmp(action, "user") == 0)
89 _action = kActionPromptUser;
90 else
91 return B_BAD_VALUE;
93 return B_OK;
97 static bool
98 match_team_name(const char* teamName, const char* parameterName)
100 RegExp expressionMatcher;
101 if (expressionMatcher.SetPattern(parameterName,
102 RegExp::PATTERN_TYPE_WILDCARD)) {
103 BString value = teamName;
104 if (parameterName[0] != '/') {
105 // the expression in question is a team name match only,
106 // so we need to extract that.
107 BPath path(teamName);
108 if (path.InitCheck() == B_OK)
109 value = path.Leaf();
112 RegExp::MatchResult match = expressionMatcher.Match(value);
113 if (match.HasMatched())
114 return true;
117 return false;
121 static status_t
122 action_for_team(const char* teamName, int32& _action,
123 bool& _explicitActionFound)
125 status_t error = B_OK;
126 BPath path;
127 error = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
128 if (error != B_OK)
129 return error;
131 path.Append("system/debug_server/settings");
132 BDriverSettings settings;
133 error = settings.Load(path.Path());
134 if (error != B_OK)
135 return error;
137 int32 tempAction;
138 if (action_for_string(settings.GetParameterValue("default_action",
139 "user", "user"), tempAction) == B_OK) {
140 _action = tempAction;
141 } else
142 _action = kActionPromptUser;
143 _explicitActionFound = false;
145 BDriverParameter parameter = settings.GetParameter("executable_actions");
146 for (BDriverParameterIterator iterator = parameter.ParameterIterator();
147 iterator.HasNext();) {
148 BDriverParameter child = iterator.Next();
149 if (!match_team_name(teamName, child.Name()))
150 continue;
152 if (child.CountValues() > 0) {
153 if (action_for_string(child.ValueAt(0), tempAction) == B_OK) {
154 _action = tempAction;
155 _explicitActionFound = true;
159 break;
162 return B_OK;
166 static void
167 KillTeam(team_id team, const char *appName = NULL)
169 // get a team info to verify the team still lives
170 team_info info;
171 if (!appName) {
172 status_t error = get_team_info(team, &info);
173 if (error != B_OK) {
174 debug_printf("debug_server: KillTeam(): Error getting info for "
175 "team %" B_PRId32 ": %s\n", team, strerror(error));
176 info.args[0] = '\0';
179 appName = info.args;
182 debug_printf("debug_server: Killing team %" B_PRId32 " (%s)\n", team,
183 appName);
185 kill_team(team);
189 // #pragma mark -
192 class DebugMessage : public DoublyLinkedListLinkImpl<DebugMessage> {
193 public:
194 DebugMessage()
198 void SetCode(debug_debugger_message code) { fCode = code; }
199 debug_debugger_message Code() const { return fCode; }
201 debug_debugger_message_data &Data() { return fData; }
202 const debug_debugger_message_data &Data() const { return fData; }
204 private:
205 debug_debugger_message fCode;
206 debug_debugger_message_data fData;
209 typedef DoublyLinkedList<DebugMessage> DebugMessageList;
212 class TeamDebugHandler : public BLocker {
213 public:
214 TeamDebugHandler(team_id team);
215 ~TeamDebugHandler();
217 status_t Init(port_id nubPort);
219 team_id Team() const;
221 status_t PushMessage(DebugMessage *message);
223 private:
224 status_t _PopMessage(DebugMessage *&message);
226 thread_id _EnterDebugger(bool saveReport);
227 status_t _SetupGDBArguments(BStringList &arguments, bool usingConsoled);
228 status_t _WriteCoreFile();
229 void _KillTeam();
231 int32 _HandleMessage(DebugMessage *message);
233 void _LookupSymbolAddress(debug_symbol_lookup_context *lookupContext,
234 const void *address, char *buffer, int32 bufferSize);
235 void _PrintStackTrace(thread_id thread);
236 void _NotifyAppServer(team_id team);
237 void _NotifyRegistrar(team_id team, bool openAlert, bool stopShutdown);
239 status_t _InitGUI();
241 static status_t _HandlerThreadEntry(void *data);
242 status_t _HandlerThread();
244 bool _ExecutableNameEquals(const char *name) const;
245 bool _IsAppServer() const;
246 bool _IsInputServer() const;
247 bool _IsRegistrar() const;
248 bool _IsGUIServer() const;
250 static const char *_LastPathComponent(const char *path);
251 static team_id _FindTeam(const char *name);
252 static bool _AreGUIServersAlive();
254 private:
255 DebugMessageList fMessages;
256 sem_id fMessageCountSem;
257 team_id fTeam;
258 team_info fTeamInfo;
259 char fExecutablePath[B_PATH_NAME_LENGTH];
260 thread_id fHandlerThread;
261 debug_context fDebugContext;
265 class TeamDebugHandlerRoster : public BLocker {
266 private:
267 TeamDebugHandlerRoster()
269 BLocker("team debug handler roster")
273 public:
274 static TeamDebugHandlerRoster *CreateDefault()
276 if (!sRoster)
277 sRoster = new(nothrow) TeamDebugHandlerRoster;
279 return sRoster;
282 static TeamDebugHandlerRoster *Default()
284 return sRoster;
287 bool AddHandler(TeamDebugHandler *handler)
289 if (!handler)
290 return false;
292 BAutolock _(this);
294 fHandlers[handler->Team()] = handler;
296 return true;
299 TeamDebugHandler *RemoveHandler(team_id team)
301 BAutolock _(this);
303 TeamDebugHandler *handler = NULL;
305 TeamDebugHandlerMap::iterator it = fHandlers.find(team);
306 if (it != fHandlers.end()) {
307 handler = it->second;
308 fHandlers.erase(it);
311 return handler;
314 TeamDebugHandler *HandlerFor(team_id team)
316 BAutolock _(this);
318 TeamDebugHandler *handler = NULL;
320 TeamDebugHandlerMap::iterator it = fHandlers.find(team);
321 if (it != fHandlers.end())
322 handler = it->second;
324 return handler;
327 status_t DispatchMessage(DebugMessage *message)
329 if (!message)
330 return B_BAD_VALUE;
332 ObjectDeleter<DebugMessage> messageDeleter(message);
334 team_id team = message->Data().origin.team;
336 // get the responsible team debug handler
337 BAutolock _(this);
339 TeamDebugHandler *handler = HandlerFor(team);
340 if (!handler) {
341 // no handler yet, we need to create one
342 handler = new(nothrow) TeamDebugHandler(team);
343 if (!handler) {
344 KillTeam(team);
345 return B_NO_MEMORY;
348 status_t error = handler->Init(message->Data().origin.nub_port);
349 if (error != B_OK) {
350 delete handler;
351 KillTeam(team);
352 return error;
355 if (!AddHandler(handler)) {
356 delete handler;
357 KillTeam(team);
358 return B_NO_MEMORY;
362 // hand over the message to it
363 handler->PushMessage(message);
364 messageDeleter.Detach();
366 return B_OK;
369 private:
370 typedef map<team_id, TeamDebugHandler*> TeamDebugHandlerMap;
372 static TeamDebugHandlerRoster *sRoster;
374 TeamDebugHandlerMap fHandlers;
378 TeamDebugHandlerRoster *TeamDebugHandlerRoster::sRoster = NULL;
381 class DebugServer : public BServer {
382 public:
383 DebugServer(status_t &error);
385 status_t Init();
387 virtual bool QuitRequested();
389 private:
390 static status_t _ListenerEntry(void *data);
391 status_t _Listener();
393 void _DeleteTeamDebugHandler(TeamDebugHandler *handler);
395 private:
396 typedef map<team_id, TeamDebugHandler*> TeamDebugHandlerMap;
398 port_id fListenerPort;
399 thread_id fListener;
400 bool fTerminating;
404 // #pragma mark -
407 TeamDebugHandler::TeamDebugHandler(team_id team)
409 BLocker("team debug handler"),
410 fMessages(),
411 fMessageCountSem(-1),
412 fTeam(team),
413 fHandlerThread(-1)
415 fDebugContext.nub_port = -1;
416 fDebugContext.reply_port = -1;
418 fExecutablePath[0] = '\0';
422 TeamDebugHandler::~TeamDebugHandler()
424 // delete the message count semaphore and wait for the thread to die
425 if (fMessageCountSem >= 0)
426 delete_sem(fMessageCountSem);
428 if (fHandlerThread >= 0 && find_thread(NULL) != fHandlerThread) {
429 status_t result;
430 wait_for_thread(fHandlerThread, &result);
433 // destroy debug context
434 if (fDebugContext.nub_port >= 0)
435 destroy_debug_context(&fDebugContext);
437 // delete the remaining messages
438 while (DebugMessage *message = fMessages.Head()) {
439 fMessages.Remove(message);
440 delete message;
445 status_t
446 TeamDebugHandler::Init(port_id nubPort)
448 // get the team info for the team
449 status_t error = get_team_info(fTeam, &fTeamInfo);
450 if (error != B_OK) {
451 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to get "
452 "info for team %" B_PRId32 ": %s\n", fTeam, strerror(error));
453 return error;
456 // get the executable path
457 error = BPrivate::get_app_path(fTeam, fExecutablePath);
458 if (error != B_OK) {
459 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to get "
460 "executable path of team %" B_PRId32 ": %s\n", fTeam,
461 strerror(error));
463 fExecutablePath[0] = '\0';
466 // init a debug context for the handler
467 error = init_debug_context(&fDebugContext, fTeam, nubPort);
468 if (error != B_OK) {
469 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to init "
470 "debug context for team %" B_PRId32 ", port %" B_PRId32 ": %s\n",
471 fTeam, nubPort, strerror(error));
472 return error;
475 // set team flags
476 debug_nub_set_team_flags message;
477 message.flags = B_TEAM_DEBUG_PREVENT_EXIT;
479 send_debug_message(&fDebugContext, B_DEBUG_MESSAGE_SET_TEAM_FLAGS, &message,
480 sizeof(message), NULL, 0);
482 // create the message count semaphore
483 char name[B_OS_NAME_LENGTH];
484 snprintf(name, sizeof(name), "team %" B_PRId32 " message count", fTeam);
485 fMessageCountSem = create_sem(0, name);
486 if (fMessageCountSem < 0) {
487 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to create "
488 "message count semaphore: %s\n", strerror(fMessageCountSem));
489 return fMessageCountSem;
492 // spawn the handler thread
493 snprintf(name, sizeof(name), "team %" B_PRId32 " handler", fTeam);
494 fHandlerThread = spawn_thread(&_HandlerThreadEntry, name, B_NORMAL_PRIORITY,
495 this);
496 if (fHandlerThread < 0) {
497 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to spawn "
498 "handler thread: %s\n", strerror(fHandlerThread));
499 return fHandlerThread;
502 resume_thread(fHandlerThread);
504 return B_OK;
508 team_id
509 TeamDebugHandler::Team() const
511 return fTeam;
515 status_t
516 TeamDebugHandler::PushMessage(DebugMessage *message)
518 BAutolock _(this);
520 fMessages.Add(message);
521 release_sem(fMessageCountSem);
523 return B_OK;
527 status_t
528 TeamDebugHandler::_PopMessage(DebugMessage *&message)
530 // acquire the semaphore
531 status_t error;
532 do {
533 error = acquire_sem(fMessageCountSem);
534 } while (error == B_INTERRUPTED);
536 if (error != B_OK)
537 return error;
539 // get the message
540 BAutolock _(this);
542 message = fMessages.Head();
543 fMessages.Remove(message);
545 return B_OK;
549 status_t
550 TeamDebugHandler::_SetupGDBArguments(BStringList &arguments, bool usingConsoled)
552 // prepare the argument vector
553 BString teamString;
554 teamString.SetToFormat("--pid=%" B_PRId32, fTeam);
556 status_t error;
557 BPath terminalPath;
558 if (usingConsoled) {
559 error = find_directory(B_SYSTEM_BIN_DIRECTORY, &terminalPath);
560 if (error != B_OK) {
561 debug_printf("debug_server: can't find system-bin directory: %s\n",
562 strerror(error));
563 return error;
565 error = terminalPath.Append("consoled");
566 if (error != B_OK) {
567 debug_printf("debug_server: can't append to system-bin path: %s\n",
568 strerror(error));
569 return error;
571 } else {
572 error = find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath);
573 if (error != B_OK) {
574 debug_printf("debug_server: can't find system-apps directory: %s\n",
575 strerror(error));
576 return error;
578 error = terminalPath.Append("Terminal");
579 if (error != B_OK) {
580 debug_printf("debug_server: can't append to system-apps path: %s\n",
581 strerror(error));
582 return error;
586 arguments.MakeEmpty();
587 if (!arguments.Add(terminalPath.Path()))
588 return B_NO_MEMORY;
590 if (!usingConsoled) {
591 BString windowTitle;
592 windowTitle.SetToFormat("Debug of Team %" B_PRId32 ": %s", fTeam,
593 _LastPathComponent(fExecutablePath));
594 if (!arguments.Add("-t") || !arguments.Add(windowTitle))
595 return B_NO_MEMORY;
598 BPath gdbPath;
599 error = find_directory(B_SYSTEM_BIN_DIRECTORY, &gdbPath);
600 if (error != B_OK) {
601 debug_printf("debug_server: can't find system-bin directory: %s\n",
602 strerror(error));
603 return error;
605 error = gdbPath.Append("gdb");
606 if (error != B_OK) {
607 debug_printf("debug_server: can't append to system-bin path: %s\n",
608 strerror(error));
609 return error;
611 if (!arguments.Add(gdbPath.Path()) || !arguments.Add(teamString))
612 return B_NO_MEMORY;
614 if (strlen(fExecutablePath) > 0 && !arguments.Add(fExecutablePath))
615 return B_NO_MEMORY;
617 return B_OK;
621 thread_id
622 TeamDebugHandler::_EnterDebugger(bool saveReport)
624 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): team %" B_PRId32
625 "\n", fTeam));
627 // prepare a debugger handover
628 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): preparing "
629 "debugger handover for team %" B_PRId32 "...\n", fTeam));
631 status_t error = send_debug_message(&fDebugContext,
632 B_DEBUG_MESSAGE_PREPARE_HANDOVER, NULL, 0, NULL, 0);
633 if (error != B_OK) {
634 debug_printf("debug_server: Failed to prepare debugger handover: %s\n",
635 strerror(error));
636 return error;
639 BStringList arguments;
640 const char *argv[16];
641 int argc = 0;
643 bool debugInConsoled = _IsGUIServer() || !_AreGUIServersAlive();
644 #ifdef HANDOVER_USE_GDB
646 error = _SetupGDBArguments(arguments, debugInConsoled);
647 if (error != B_OK) {
648 debug_printf("debug_server: Failed to set up gdb arguments: %s\n",
649 strerror(error));
650 return error;
653 // start the terminal
654 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): starting "
655 "terminal (debugger) for team %" B_PRId32 "...\n", fTeam));
657 #elif defined(HANDOVER_USE_DEBUGGER)
658 if (!debugInConsoled && !saveReport
659 && be_roster->IsRunning(kDebuggerSignature)) {
661 // for graphical handovers, check if Debugger is already running,
662 // and if it is, simply send it a message to attach to the requested
663 // team.
664 BMessenger messenger(kDebuggerSignature);
665 BMessage message(MSG_DEBUG_THIS_TEAM);
666 if (message.AddInt32("team", fTeam) == B_OK
667 && messenger.SendMessage(&message) == B_OK) {
668 return 0;
672 // prepare the argument vector
673 BPath debuggerPath;
674 if (debugInConsoled) {
675 error = find_directory(B_SYSTEM_BIN_DIRECTORY, &debuggerPath);
676 if (error != B_OK) {
677 debug_printf("debug_server: can't find system-bin directory: %s\n",
678 strerror(error));
679 return error;
681 error = debuggerPath.Append("consoled");
682 if (error != B_OK) {
683 debug_printf("debug_server: can't append to system-bin path: %s\n",
684 strerror(error));
685 return error;
688 if (!arguments.Add(debuggerPath.Path()))
689 return B_NO_MEMORY;
692 error = find_directory(B_SYSTEM_APPS_DIRECTORY, &debuggerPath);
693 if (error != B_OK) {
694 debug_printf("debug_server: can't find system-apps directory: %s\n",
695 strerror(error));
696 return error;
698 error = debuggerPath.Append("Debugger");
699 if (error != B_OK) {
700 debug_printf("debug_server: can't append to system-apps path: %s\n",
701 strerror(error));
702 return error;
704 if (!arguments.Add(debuggerPath.Path()))
705 return B_NO_MEMORY;
707 if (debugInConsoled && !arguments.Add("--cli"))
708 return B_NO_MEMORY;
710 BString debuggerParam;
711 debuggerParam.SetToFormat("%" B_PRId32, fTeam);
712 if (saveReport) {
713 if (!arguments.Add("--save-report"))
714 return B_NO_MEMORY;
716 if (!arguments.Add("--team") || !arguments.Add(debuggerParam))
717 return B_NO_MEMORY;
719 // start the debugger
720 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): starting "
721 "%s debugger for team %" B_PRId32 "...\n",
722 debugInConsoled ? "command line" : "graphical", fTeam));
723 #endif
725 for (int32 i = 0; i < arguments.CountStrings(); i++)
726 argv[argc++] = arguments.StringAt(i).String();
727 argv[argc] = NULL;
729 thread_id thread = load_image(argc, argv, (const char**)environ);
730 if (thread < 0) {
731 debug_printf("debug_server: Failed to start debugger: %s\n",
732 strerror(thread));
733 return thread;
735 resume_thread(thread);
737 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): debugger started "
738 "for team %" B_PRId32 ": thread: %" B_PRId32 "\n", fTeam, thread));
740 return thread;
744 void
745 TeamDebugHandler::_KillTeam()
747 KillTeam(fTeam, fTeamInfo.args);
751 status_t
752 TeamDebugHandler::_WriteCoreFile()
754 // get a usable path for the core file
755 BPath directoryPath;
756 status_t error = find_directory(B_DESKTOP_DIRECTORY, &directoryPath);
757 if (error != B_OK) {
758 debug_printf("debug_server: Couldn't get desktop directory: %s\n",
759 strerror(error));
760 return error;
763 const char* executableName = strrchr(fExecutablePath, '/');
764 if (executableName == NULL)
765 executableName = fExecutablePath;
766 else
767 executableName++;
769 BString fileBaseName("core-");
770 fileBaseName << executableName << '-' << fTeam;
771 BPath filePath;
773 for (int32 index = 0;; index++) {
774 BString fileName(fileBaseName);
775 if (index > 0)
776 fileName << '-' << index;
778 error = filePath.SetTo(directoryPath.Path(), fileName.String());
779 if (error != B_OK) {
780 debug_printf("debug_server: Couldn't get core file path for team %"
781 B_PRId32 ": %s\n", fTeam, strerror(error));
782 return error;
785 struct stat st;
786 if (lstat(filePath.Path(), &st) != 0) {
787 if (errno == B_ENTRY_NOT_FOUND)
788 break;
791 if (index > 1000) {
792 debug_printf("debug_server: Couldn't get usable core file path for "
793 "team %" B_PRId32 "\n", fTeam);
794 return B_ERROR;
798 debug_nub_write_core_file message;
799 message.reply_port = fDebugContext.reply_port;
800 strlcpy(message.path, filePath.Path(), sizeof(message.path));
802 debug_nub_write_core_file_reply reply;
804 error = send_debug_message(&fDebugContext, B_DEBUG_WRITE_CORE_FILE,
805 &message, sizeof(message), &reply, sizeof(reply));
806 if (error == B_OK)
807 error = reply.error;
808 if (error != B_OK) {
809 debug_printf("debug_server: Failed to write core file for team %"
810 B_PRId32 ": %s\n", fTeam, strerror(error));
813 return error;
817 int32
818 TeamDebugHandler::_HandleMessage(DebugMessage *message)
820 // This method is called only for the first message the debugger gets for
821 // a team. That means only a few messages are actually possible, while
822 // others wouldn't trigger the debugger in the first place. So we deal with
823 // all of them the same way, by popping up an alert.
824 TRACE(("debug_server: TeamDebugHandler::_HandleMessage(): team %" B_PRId32
825 ", code: %" B_PRId32 "\n", fTeam, (int32)message->Code()));
827 thread_id thread = message->Data().origin.thread;
829 // get some user-readable message
830 char buffer[512];
831 switch (message->Code()) {
832 case B_DEBUGGER_MESSAGE_TEAM_DELETED:
833 // This shouldn't happen.
834 debug_printf("debug_server: Got a spurious "
835 "B_DEBUGGER_MESSAGE_TEAM_DELETED message for team %" B_PRId32
836 "\n", fTeam);
837 return true;
839 case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
840 get_debug_exception_string(
841 message->Data().exception_occurred.exception, buffer,
842 sizeof(buffer));
843 break;
845 case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
847 // get the debugger() message
848 void *messageAddress = message->Data().debugger_call.message;
849 char messageBuffer[128];
850 status_t error = B_OK;
851 ssize_t bytesRead = debug_read_string(&fDebugContext,
852 messageAddress, messageBuffer, sizeof(messageBuffer));
853 if (bytesRead < 0)
854 error = bytesRead;
856 if (error == B_OK) {
857 sprintf(buffer, "Debugger call: `%s'", messageBuffer);
858 } else {
859 snprintf(buffer, sizeof(buffer), "Debugger call: %p "
860 "(Failed to read message: %s)", messageAddress,
861 strerror(error));
863 break;
866 default:
867 get_debug_message_string(message->Code(), buffer, sizeof(buffer));
868 break;
871 debug_printf("debug_server: Thread %" B_PRId32 " entered the debugger: %s\n",
872 thread, buffer);
874 _PrintStackTrace(thread);
876 int32 debugAction = kActionPromptUser;
877 bool explicitActionFound = false;
878 if (action_for_team(fExecutablePath, debugAction, explicitActionFound)
879 != B_OK) {
880 debugAction = kActionPromptUser;
881 explicitActionFound = false;
884 // ask the user whether to debug or kill the team
885 if (_IsGUIServer()) {
886 // App server, input server, or registrar. We always debug those.
887 // if not specifically overridden.
888 if (!explicitActionFound)
889 debugAction = kActionDebugTeam;
890 } else if (debugAction == kActionPromptUser && USE_GUI
891 && _AreGUIServersAlive() && _InitGUI() == B_OK) {
892 // normal app -- tell the user
893 _NotifyAppServer(fTeam);
894 _NotifyRegistrar(fTeam, true, false);
896 BString buffer(
897 B_TRANSLATE("The application:\n\n %app\n\n"
898 "has encountered an error which prevents it from continuing. Haiku "
899 "will terminate the application and clean up."));
900 buffer.ReplaceFirst("%app", fTeamInfo.args);
902 // TODO: It would be nice if the alert would go away automatically
903 // if someone else kills our teams.
904 BAlert *alert = new BAlert(NULL, buffer.String(),
905 B_TRANSLATE("Terminate"), B_TRANSLATE("Debug"),
906 B_TRANSLATE("Write core file"),
907 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
908 #ifdef HANDOVER_USE_DEBUGGER
909 alert->AddButton(B_TRANSLATE("Save report"));
910 #endif
911 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
912 debugAction = alert->Go();
913 if (debugAction < 0) {
914 // Happens when closed by escape key
915 debugAction = kActionKillTeam;
917 _NotifyRegistrar(fTeam, false, debugAction != kActionKillTeam);
920 return debugAction;
924 void
925 TeamDebugHandler::_LookupSymbolAddress(
926 debug_symbol_lookup_context *lookupContext, const void *address,
927 char *buffer, int32 bufferSize)
929 // lookup the symbol
930 void *baseAddress;
931 char symbolName[1024];
932 char imageName[B_PATH_NAME_LENGTH];
933 bool exactMatch;
934 bool lookupSucceeded = false;
935 if (lookupContext) {
936 status_t error = debug_lookup_symbol_address(lookupContext, address,
937 &baseAddress, symbolName, sizeof(symbolName), imageName,
938 sizeof(imageName), &exactMatch);
939 lookupSucceeded = (error == B_OK);
942 if (lookupSucceeded) {
943 // we were able to look something up
944 if (strlen(symbolName) > 0) {
945 // we even got a symbol
946 snprintf(buffer, bufferSize, "%s + %#lx%s", symbolName,
947 (addr_t)address - (addr_t)baseAddress,
948 (exactMatch ? "" : " (closest symbol)"));
950 } else {
951 // no symbol: image relative address
952 snprintf(buffer, bufferSize, "(%s + %#lx)", imageName,
953 (addr_t)address - (addr_t)baseAddress);
956 } else {
957 // lookup failed: find area containing the IP
958 bool useAreaInfo = false;
959 area_info info;
960 ssize_t cookie = 0;
961 while (get_next_area_info(fTeam, &cookie, &info) == B_OK) {
962 if ((addr_t)info.address <= (addr_t)address
963 && (addr_t)info.address + info.size > (addr_t)address) {
964 useAreaInfo = true;
965 break;
969 if (useAreaInfo) {
970 snprintf(buffer, bufferSize, "(%s + %#lx)", info.name,
971 (addr_t)address - (addr_t)info.address);
972 } else if (bufferSize > 0)
973 buffer[0] = '\0';
978 void
979 TeamDebugHandler::_PrintStackTrace(thread_id thread)
981 // print a stacktrace
982 void *ip = NULL;
983 void *stackFrameAddress = NULL;
984 status_t error = debug_get_instruction_pointer(&fDebugContext, thread, &ip,
985 &stackFrameAddress);
987 if (error == B_OK) {
988 // create a symbol lookup context
989 debug_symbol_lookup_context *lookupContext = NULL;
990 error = debug_create_symbol_lookup_context(fTeam, -1, &lookupContext);
991 if (error != B_OK) {
992 debug_printf("debug_server: Failed to create symbol lookup "
993 "context: %s\n", strerror(error));
996 // lookup the IP
997 char symbolBuffer[2048];
998 _LookupSymbolAddress(lookupContext, ip, symbolBuffer,
999 sizeof(symbolBuffer) - 1);
1001 debug_printf("stack trace, current PC %p %s:\n", ip, symbolBuffer);
1003 for (int32 i = 0; i < 50; i++) {
1004 debug_stack_frame_info stackFrameInfo;
1006 error = debug_get_stack_frame(&fDebugContext, stackFrameAddress,
1007 &stackFrameInfo);
1008 if (error < B_OK || stackFrameInfo.parent_frame == NULL)
1009 break;
1011 // lookup the return address
1012 _LookupSymbolAddress(lookupContext, stackFrameInfo.return_address,
1013 symbolBuffer, sizeof(symbolBuffer) - 1);
1015 debug_printf(" (%p) %p %s\n", stackFrameInfo.frame,
1016 stackFrameInfo.return_address, symbolBuffer);
1018 stackFrameAddress = stackFrameInfo.parent_frame;
1021 // delete the symbol lookup context
1022 if (lookupContext)
1023 debug_delete_symbol_lookup_context(lookupContext);
1028 void
1029 TeamDebugHandler::_NotifyAppServer(team_id team)
1031 // This will remove any kWindowScreenFeels of the application, so that
1032 // the debugger alert is visible on screen
1033 BRoster::Private roster;
1034 roster.ApplicationCrashed(team);
1038 void
1039 TeamDebugHandler::_NotifyRegistrar(team_id team, bool openAlert,
1040 bool stopShutdown)
1042 BMessage notify(BPrivate::B_REG_TEAM_DEBUGGER_ALERT);
1043 notify.AddInt32("team", team);
1044 notify.AddBool("open", openAlert);
1045 notify.AddBool("stop shutdown", stopShutdown);
1047 BRoster::Private roster;
1048 BMessage reply;
1049 roster.SendTo(&notify, &reply, false);
1053 status_t
1054 TeamDebugHandler::_InitGUI()
1056 DebugServer *app = dynamic_cast<DebugServer*>(be_app);
1057 BAutolock _(app);
1058 return app->InitGUIContext();
1062 status_t
1063 TeamDebugHandler::_HandlerThreadEntry(void *data)
1065 return ((TeamDebugHandler*)data)->_HandlerThread();
1069 status_t
1070 TeamDebugHandler::_HandlerThread()
1072 TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %" B_PRId32
1073 "\n", fTeam));
1075 // get initial message
1076 TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %" B_PRId32
1077 ": getting message...\n", fTeam));
1079 DebugMessage *message;
1080 status_t error = _PopMessage(message);
1081 int32 debugAction = kActionKillTeam;
1082 if (error == B_OK) {
1083 // handle the message
1084 debugAction = _HandleMessage(message);
1085 delete message;
1086 } else {
1087 debug_printf("TeamDebugHandler::_HandlerThread(): Failed to pop "
1088 "initial message: %s", strerror(error));
1091 // kill the team or hand it over to the debugger
1092 thread_id debuggerThread = -1;
1093 if (debugAction == kActionKillTeam) {
1094 // The team shall be killed. Since that is also the handling in case
1095 // an error occurs while handing over the team to the debugger, we do
1096 // nothing here.
1097 } else if (debugAction == kActionWriteCoreFile) {
1098 _WriteCoreFile();
1099 debugAction = kActionKillTeam;
1100 } else if ((debuggerThread = _EnterDebugger(
1101 debugAction == kActionSaveReportTeam)) >= 0) {
1102 // wait for the "handed over" or a "team deleted" message
1103 bool terminate = false;
1104 do {
1105 error = _PopMessage(message);
1106 if (error != B_OK) {
1107 debug_printf("TeamDebugHandler::_HandlerThread(): Failed to "
1108 "pop message: %s", strerror(error));
1109 debugAction = kActionKillTeam;
1110 break;
1113 if (message->Code() == B_DEBUGGER_MESSAGE_HANDED_OVER) {
1114 // The team has successfully been handed over to the debugger.
1115 // Nothing to do.
1116 terminate = true;
1117 } else if (message->Code() == B_DEBUGGER_MESSAGE_TEAM_DELETED) {
1118 // The team died. Nothing to do.
1119 terminate = true;
1120 } else {
1121 // Some message we can ignore. The debugger will take care of
1122 // it.
1124 // check whether the debugger thread still lives
1125 thread_info threadInfo;
1126 if (get_thread_info(debuggerThread, &threadInfo) != B_OK) {
1127 // the debugger is gone
1128 debug_printf("debug_server: The debugger for team %"
1129 B_PRId32 " seems to be gone.", fTeam);
1131 debugAction = kActionKillTeam;
1132 terminate = true;
1136 delete message;
1137 } while (!terminate);
1138 } else
1139 debugAction = kActionKillTeam;
1141 if (debugAction == kActionKillTeam) {
1142 // kill the team
1143 _KillTeam();
1146 // remove this handler from the roster and delete it
1147 TeamDebugHandlerRoster::Default()->RemoveHandler(fTeam);
1149 delete this;
1151 return B_OK;
1155 bool
1156 TeamDebugHandler::_ExecutableNameEquals(const char *name) const
1158 return strcmp(_LastPathComponent(fExecutablePath), name) == 0;
1162 bool
1163 TeamDebugHandler::_IsAppServer() const
1165 return _ExecutableNameEquals("app_server");
1169 bool
1170 TeamDebugHandler::_IsInputServer() const
1172 return _ExecutableNameEquals("input_server");
1176 bool
1177 TeamDebugHandler::_IsRegistrar() const
1179 return _ExecutableNameEquals("registrar");
1183 bool
1184 TeamDebugHandler::_IsGUIServer() const
1186 // app or input server
1187 return _IsAppServer() || _IsInputServer() || _IsRegistrar();
1191 const char *
1192 TeamDebugHandler::_LastPathComponent(const char *path)
1194 const char *lastSlash = strrchr(path, '/');
1195 return lastSlash ? lastSlash + 1 : path;
1199 team_id
1200 TeamDebugHandler::_FindTeam(const char *name)
1202 // Iterate through all teams and check their executable name.
1203 int32 cookie = 0;
1204 team_info teamInfo;
1205 while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1206 entry_ref ref;
1207 if (BPrivate::get_app_ref(teamInfo.team, &ref) == B_OK) {
1208 if (strcmp(ref.name, name) == 0)
1209 return teamInfo.team;
1213 return B_ENTRY_NOT_FOUND;
1217 bool
1218 TeamDebugHandler::_AreGUIServersAlive()
1220 return _FindTeam("app_server") >= 0 && _FindTeam("input_server") >= 0
1221 && _FindTeam("registrar");
1225 // #pragma mark -
1228 DebugServer::DebugServer(status_t &error)
1230 BServer(kSignature, false, &error),
1231 fListenerPort(-1),
1232 fListener(-1),
1233 fTerminating(false)
1238 status_t
1239 DebugServer::Init()
1241 // create listener port
1242 fListenerPort = create_port(10, "kernel listener");
1243 if (fListenerPort < 0)
1244 return fListenerPort;
1246 // spawn the listener thread
1247 fListener = spawn_thread(_ListenerEntry, "kernel listener",
1248 B_NORMAL_PRIORITY, this);
1249 if (fListener < 0)
1250 return fListener;
1252 // register as default debugger
1253 // TODO: could set default flags
1254 status_t error = install_default_debugger(fListenerPort);
1255 if (error != B_OK)
1256 return error;
1258 // resume the listener
1259 resume_thread(fListener);
1261 return B_OK;
1265 bool
1266 DebugServer::QuitRequested()
1268 // Never give up, never surrender. ;-)
1269 return false;
1273 status_t
1274 DebugServer::_ListenerEntry(void *data)
1276 return ((DebugServer*)data)->_Listener();
1280 status_t
1281 DebugServer::_Listener()
1283 while (!fTerminating) {
1284 // receive the next debug message
1285 DebugMessage *message = new DebugMessage;
1286 int32 code;
1287 ssize_t bytesRead;
1288 do {
1289 bytesRead = read_port(fListenerPort, &code, &message->Data(),
1290 sizeof(debug_debugger_message_data));
1291 } while (bytesRead == B_INTERRUPTED);
1293 if (bytesRead < 0) {
1294 debug_printf("debug_server: Failed to read from listener port: "
1295 "%s. Terminating!\n", strerror(bytesRead));
1296 exit(1);
1298 TRACE(("debug_server: Got debug message: team: %" B_PRId32 ", code: %" B_PRId32
1299 "\n", message->Data().origin.team, code));
1301 message->SetCode((debug_debugger_message)code);
1303 // dispatch the message
1304 TeamDebugHandlerRoster::Default()->DispatchMessage(message);
1307 return B_OK;
1311 // #pragma mark -
1315 main()
1317 status_t error;
1319 // for the time being let the debug server print to the syslog
1320 int console = open("/dev/dprintf", O_RDONLY);
1321 if (console < 0) {
1322 debug_printf("debug_server: Failed to open console: %s\n",
1323 strerror(errno));
1325 dup2(console, STDOUT_FILENO);
1326 dup2(console, STDERR_FILENO);
1327 close(console);
1329 // create the team debug handler roster
1330 if (!TeamDebugHandlerRoster::CreateDefault()) {
1331 debug_printf("debug_server: Failed to create team debug handler "
1332 "roster.\n");
1333 exit(1);
1336 // create application
1337 DebugServer server(error);
1338 if (error != B_OK) {
1339 debug_printf("debug_server: Failed to create BApplication: %s\n",
1340 strerror(error));
1341 exit(1);
1344 // init application
1345 error = server.Init();
1346 if (error != B_OK) {
1347 debug_printf("debug_server: Failed to init application: %s\n",
1348 strerror(error));
1349 exit(1);
1352 server.Run();
1354 return 0;